@burtson-labs/bandit-engine 2.0.24 β†’ 2.0.26

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 (133) hide show
  1. package/README.md +11 -8
  2. package/dist/cli/cli.js +2 -2
  3. package/dist/cli/cli.js.map +1 -1
  4. package/docs/05_cli_quickstart.md +4 -6
  5. package/docs/api_reference/assets/bandit-docs.js +456 -0
  6. package/docs/api_reference/assets/highlight.css +15 -8
  7. package/docs/api_reference/assets/main.js +1 -0
  8. package/docs/api_reference/classes/DebugLogger.html +11 -11
  9. package/docs/api_reference/classes/FeatureFlagService.html +13 -13
  10. package/docs/api_reference/classes/NotificationService.html +10 -10
  11. package/docs/api_reference/classes/StreamingTTSClient.html +9 -9
  12. package/docs/api_reference/classes/VectorDatabaseService.html +24 -24
  13. package/docs/api_reference/classes/VectorMigrationService.html +8 -8
  14. package/docs/api_reference/classes/VoiceService.html +2 -2
  15. package/docs/api_reference/enums/TTSState.html +2 -2
  16. package/docs/api_reference/functions/Chat.html +1 -1
  17. package/docs/api_reference/functions/ChatModal.html +3 -3
  18. package/docs/api_reference/functions/ChatProvider.html +3 -3
  19. package/docs/api_reference/functions/FeatureFlagProvider.html +3 -3
  20. package/docs/api_reference/functions/FeedbackButton.html +3 -3
  21. package/docs/api_reference/functions/FeedbackModal.html +3 -3
  22. package/docs/api_reference/functions/Management.html +1 -1
  23. package/docs/api_reference/functions/NotificationProvider.html +3 -3
  24. package/docs/api_reference/functions/SubscriptionExpiredGuard.html +3 -3
  25. package/docs/api_reference/functions/SubscriptionExpiredModal.html +3 -3
  26. package/docs/api_reference/functions/defineCustomElement.html +1 -1
  27. package/docs/api_reference/functions/getCriticalConfig.html +1 -1
  28. package/docs/api_reference/functions/getFeatureMatrix.html +1 -1
  29. package/docs/api_reference/functions/getStreamingTTSClient.html +1 -1
  30. package/docs/api_reference/functions/getSystemConstants.html +1 -1
  31. package/docs/api_reference/functions/getTTSState.html +1 -1
  32. package/docs/api_reference/functions/handleHttpError.html +1 -1
  33. package/docs/api_reference/functions/handleSubscriptionUpgrade.html +1 -1
  34. package/docs/api_reference/functions/handleValidationError.html +1 -1
  35. package/docs/api_reference/functions/initializeCoreSystem.html +1 -1
  36. package/docs/api_reference/functions/pauseTTS.html +1 -1
  37. package/docs/api_reference/functions/previewTierUpgrade.html +1 -1
  38. package/docs/api_reference/functions/resumeTTS.html +1 -1
  39. package/docs/api_reference/functions/showInfoNotification.html +1 -1
  40. package/docs/api_reference/functions/showSuccessNotification.html +1 -1
  41. package/docs/api_reference/functions/speakWithStreaming.html +1 -1
  42. package/docs/api_reference/functions/stopTTS.html +1 -1
  43. package/docs/api_reference/functions/syncSubscriptionWithAPI.html +1 -1
  44. package/docs/api_reference/functions/updateSubscriptionTier.html +1 -1
  45. package/docs/api_reference/functions/useFeatureFlag.html +1 -1
  46. package/docs/api_reference/functions/useFeatureVisibility.html +1 -1
  47. package/docs/api_reference/functions/useFeatures.html +1 -1
  48. package/docs/api_reference/functions/useGatewayHealth.html +1 -1
  49. package/docs/api_reference/functions/useGatewayMemory.html +1 -1
  50. package/docs/api_reference/functions/useGatewayModels.html +1 -1
  51. package/docs/api_reference/functions/useGlobalTTS.html +1 -1
  52. package/docs/api_reference/functions/useNotification.html +1 -1
  53. package/docs/api_reference/functions/useNotificationService.html +1 -1
  54. package/docs/api_reference/functions/useTTS.html +1 -1
  55. package/docs/api_reference/functions/useVectorStore.html +1 -1
  56. package/docs/api_reference/functions/useVoiceStore.html +2 -2
  57. package/docs/api_reference/functions/useVoices.html +1 -1
  58. package/docs/api_reference/functions/validateEnvironment.html +1 -1
  59. package/docs/api_reference/functions/validateSystemIntegrity.html +1 -1
  60. package/docs/api_reference/index.html +23 -68
  61. package/docs/api_reference/interfaces/AIChatRequest.html +2 -2
  62. package/docs/api_reference/interfaces/AIChatResponse.html +2 -2
  63. package/docs/api_reference/interfaces/AIGenerateRequest.html +2 -2
  64. package/docs/api_reference/interfaces/AIGenerateResponse.html +2 -2
  65. package/docs/api_reference/interfaces/AIMessage.html +2 -2
  66. package/docs/api_reference/interfaces/AIModel.html +2 -2
  67. package/docs/api_reference/interfaces/AIProviderConfig.html +2 -2
  68. package/docs/api_reference/interfaces/ChatConfig.html +3 -3
  69. package/docs/api_reference/interfaces/ChatModalProps.html +3 -3
  70. package/docs/api_reference/interfaces/CreateMemoryOptions.html +2 -2
  71. package/docs/api_reference/interfaces/FeatureEvaluation.html +7 -7
  72. package/docs/api_reference/interfaces/FeatureFlagConfig.html +9 -9
  73. package/docs/api_reference/interfaces/FeatureFlagContextValue.html +8 -8
  74. package/docs/api_reference/interfaces/FeatureFlagProviderProps.html +2 -2
  75. package/docs/api_reference/interfaces/FeedbackButtonProps.html +10 -10
  76. package/docs/api_reference/interfaces/FeedbackCategories.html +2 -2
  77. package/docs/api_reference/interfaces/FeedbackModalProps.html +2 -2
  78. package/docs/api_reference/interfaces/FeedbackPriorities.html +2 -2
  79. package/docs/api_reference/interfaces/FeedbackRequest.html +2 -2
  80. package/docs/api_reference/interfaces/FeedbackResponse.html +2 -2
  81. package/docs/api_reference/interfaces/FileUploadResult.html +2 -2
  82. package/docs/api_reference/interfaces/GatewayChatRequest.html +2 -2
  83. package/docs/api_reference/interfaces/GatewayChatResponse.html +2 -2
  84. package/docs/api_reference/interfaces/GatewayContract.html +2 -2
  85. package/docs/api_reference/interfaces/GatewayGenerateRequest.html +2 -2
  86. package/docs/api_reference/interfaces/GatewayGenerateResponse.html +2 -2
  87. package/docs/api_reference/interfaces/GatewayHealthResponse.html +2 -2
  88. package/docs/api_reference/interfaces/GatewayMemoryRecord.html +2 -2
  89. package/docs/api_reference/interfaces/GatewayMemoryResponse.html +2 -2
  90. package/docs/api_reference/interfaces/GatewayMessage.html +2 -2
  91. package/docs/api_reference/interfaces/GatewayMessageContent.html +2 -2
  92. package/docs/api_reference/interfaces/GatewayModel.html +2 -2
  93. package/docs/api_reference/interfaces/GatewayModelsResponse.html +2 -2
  94. package/docs/api_reference/interfaces/MemorySearchFilters.html +2 -2
  95. package/docs/api_reference/interfaces/MigrationProgress.html +2 -2
  96. package/docs/api_reference/interfaces/MigrationStatus.html +2 -2
  97. package/docs/api_reference/interfaces/NotificationConfig.html +2 -2
  98. package/docs/api_reference/interfaces/NotificationContextType.html +2 -2
  99. package/docs/api_reference/interfaces/NotificationProviderProps.html +2 -2
  100. package/docs/api_reference/interfaces/PackageSettings.html +2 -2
  101. package/docs/api_reference/interfaces/SearchOptions.html +2 -2
  102. package/docs/api_reference/interfaces/SearchResult.html +2 -2
  103. package/docs/api_reference/interfaces/SubscriptionExpiredGuardProps.html +2 -2
  104. package/docs/api_reference/interfaces/SubscriptionExpiredModalProps.html +2 -2
  105. package/docs/api_reference/interfaces/TTSOptions.html +2 -2
  106. package/docs/api_reference/interfaces/TTSProgress.html +2 -2
  107. package/docs/api_reference/interfaces/TrialUsage.html +2 -2
  108. package/docs/api_reference/interfaces/UploadRequest.html +3 -3
  109. package/docs/api_reference/interfaces/UseTTSReturn.html +2 -2
  110. package/docs/api_reference/interfaces/VectorDocument.html +2 -2
  111. package/docs/api_reference/interfaces/VectorMemory.html +2 -2
  112. package/docs/api_reference/interfaces/VectorMemoryMetadata.html +2 -2
  113. package/docs/api_reference/interfaces/VectorStoreStatus.html +2 -2
  114. package/docs/api_reference/interfaces/VoiceModelsResponse.html +2 -2
  115. package/docs/api_reference/interfaces/VoiceState.html +2 -2
  116. package/docs/api_reference/media/05_cli_quickstart.md +4 -6
  117. package/docs/api_reference/types/FeatureKey.html +1 -1
  118. package/docs/api_reference/types/FeatureMatrix.html +1 -1
  119. package/docs/api_reference/types/GatewayQueryOptions.html +1 -1
  120. package/docs/api_reference/types/LogContext.html +1 -1
  121. package/docs/api_reference/types/SubscriptionTier.html +1 -1
  122. package/docs/api_reference/variables/DEFAULT_TIER_FEATURES.html +1 -1
  123. package/docs/api_reference/variables/FeatureFlagContext.html +1 -1
  124. package/docs/api_reference/variables/OSS_DEFAULT_FEATURES.html +1 -1
  125. package/docs/api_reference/variables/SYSTEM_FLAGS.html +1 -1
  126. package/docs/api_reference/variables/authenticationService.html +1 -1
  127. package/docs/api_reference/variables/debugLogger-1.html +1 -1
  128. package/docs/api_reference/variables/featureFlagService-1.html +1 -1
  129. package/docs/api_reference/variables/notificationService-1.html +1 -1
  130. package/docs/api_reference/variables/vectorDatabaseService-1.html +1 -1
  131. package/docs/api_reference/variables/vectorMigrationService-1.html +1 -1
  132. package/docs/api_reference/variables/voiceService-1.html +1 -1
  133. package/package.json +2 -2
package/README.md CHANGED
@@ -10,7 +10,7 @@
10
10
 
11
11
  An AI chat toolkit built for speed, design, and control. Power branded AI assistants with battle-tested React components and a secure gateway contract.
12
12
 
13
- [![npm private](https://img.shields.io/badge/npm-private-lightgrey?logo=npm)](https://www.npmjs.com/package/@burtson-labs/bandit-engine)
13
+ [![npm](https://img.shields.io/npm/v/%40burtson-labs%2Fbandit-engine?logo=npm&color=cb3837)](https://www.npmjs.com/package/@burtson-labs/bandit-engine)
14
14
  [![Docs](https://img.shields.io/badge/docs-banditailabs.com%2Fnpm--package-0C7BD4)](https://banditailabs.com/npm-package)
15
15
  [![Live Demo](https://img.shields.io/badge/live%20demo-banditailabs.com%2Fchat-7F56D9)](https://banditailabs.com/)
16
16
  [![TypeScript](https://img.shields.io/badge/TypeScript-5.x-3178c6?logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
@@ -20,7 +20,7 @@ An AI chat toolkit built for speed, design, and control. Power branded AI assist
20
20
  - πŸ”Œ Plug-and-play React chat, modal, and management surfaces
21
21
  - 🧠 Memory, vector knowledge, and provider switching behind a secure gateway
22
22
  - 🎨 Full MUI theming, dark mode, and branding controls out of the box
23
- - 🌐 Multimodal support (voice, images, documents) with Ollama, OpenAI, Azure, Anthropic
23
+ - 🌐 Multimodal support (voice, images, documents) with Ollama, OpenAI, Azure OpenAI, and Anthropic today β€” tell us which providers you need next so we can prioritize them
24
24
  - πŸ› οΈ CLI scaffolding, sample gateway, and docs to launch in minutes
25
25
 
26
26
  ## Quick Links
@@ -55,7 +55,7 @@ Customize the output with options such as:
55
55
  - `--yes` / `--skip-prompts` to accept defaults non-interactively
56
56
  - `--force` to overwrite a non-empty directory
57
57
 
58
- > πŸ—οΈ The generated project includes an `.npmrc` that targets `https://npm.pkg.github.com/` and expects a `GITHUB_NPM_TOKEN` environment variable. Run `npm login --registry=https://npm.pkg.github.com --scope=@burtson-labs` (or export `GITHUB_NPM_TOKEN`) before installing dependencies.
58
+ > πŸ“¦ The generated project installs directly from `https://registry.npmjs.org/` β€” no GitHub npm token is required once the package is public.
59
59
 
60
60
  > ⚠️ The scaffolded gateway focuses on OpenAI/Ollama chat and model discovery. All advanced routes (file storage uploads, vector embedding, voice, MCP, etc.) are generated as `501` placeholders so you can wire them to your own infrastructure. Implement the contracts below before turning on those features in production.
61
61
 
@@ -230,14 +230,15 @@ To run the quickstart purely against an Ollama daemon:
230
230
  ```ini
231
231
  # Frontend
232
232
  VITE_GATEWAY_PROVIDER=ollama
233
- VITE_DEFAULT_MODEL=llava:latest # any model installed on your Ollama box
233
+ VITE_DEFAULT_MODEL=llava:latest # any model installed on your Ollama box
234
234
  VITE_FALLBACK_MODEL=gemma2:9b-instruct # optional, but handy for text-only fallbacks
235
235
 
236
236
  # Gateway / server
237
237
  OLLAMA_URL=http://localhost:11434 # or the remote daemon address
238
238
  ```
239
239
 
240
- Set `VITE_DEFAULT_MODEL` / `VITE_FALLBACK_MODEL` to the exact Ollama model tags you have available (`gemma`, `llava`, `moondream`, or your own custom builds). Bandit automatically detects multimodal Ollama models, so setting `llava`, `moondream`, or other vision-capable variants enables image understanding with no extra code.
240
+
241
+ <p class="gateway-note">Set <code>VITE_DEFAULT_MODEL</code> / <code>VITE_FALLBACK_MODEL</code> to the exact Ollama model tags you have available (<code>gemma</code>, <code>llava</code>, <code>moondream</code>, or your own custom builds). Bandit automatically detects multimodal Ollama models, so setting <code>llava</code>, <code>moondream</code>, or other vision-capable variants enables image understanding with no extra code.</p>
241
242
 
242
243
  ### 🎀 Voice Services (TTS/STT)
243
244
 
@@ -671,12 +672,14 @@ Bandit Engine features a unified, gateway-based AI provider architecture that su
671
672
 
672
673
  ### Supported Providers
673
674
 
674
- - **🌟 Gateway Provider** (Recommended): Routes all requests through your secure .NET 9 backend
675
- - **Ollama**: Self-hosted models and Ollama-compatible endpoints
675
+ - **🌟 Gateway Provider** (Recommended): Routes all requests through your secure backend
676
+ - **Ollama**: Self-hosted models and Ollama-compatible endpoints
676
677
  - **OpenAI**: GPT models via OpenAI API
677
678
  - **Azure OpenAI**: Azure-hosted OpenAI models
678
679
  - **Anthropic**: Claude models via Anthropic API
679
680
 
681
+ > We do not support every AI provider yet. Let us know which services you rely onβ€”community interest directly shapes the integration roadmap.
682
+
680
683
  ### Gateway Provider (Recommended)
681
684
 
682
685
  The gateway provider offers the most secure and scalable approach by routing all requests through your backend API:
@@ -729,7 +732,7 @@ aiProvider: {
729
732
 
730
733
  ### Migration to Gateway
731
734
 
732
- 1. **Deploy Gateway Backend**: Set up your .NET 9 gateway service
735
+ 1. **Deploy Gateway Backend**: Set up your preferred gateway service
733
736
  2. **Update Configuration**: Switch from direct to gateway provider
734
737
  3. **Test Connection**: Use the Management interface to validate
735
738
  4. **Update API Keys**: Move keys from frontend to backend config
package/dist/cli/cli.js CHANGED
@@ -30,7 +30,7 @@ var import_commander = require("commander");
30
30
  // package.json
31
31
  var package_default = {
32
32
  name: "@burtson-labs/bandit-engine",
33
- version: "2.0.24",
33
+ version: "2.0.26",
34
34
  license: "BUSL-1.1",
35
35
  main: "dist/index.js",
36
36
  module: "dist/index.mjs",
@@ -73,7 +73,7 @@ var package_default = {
73
73
  scripts: {
74
74
  build: "tsup",
75
75
  dev: "tsup --watch",
76
- docs: "typedoc src --out docs/api_reference --skipErrorChecking",
76
+ docs: "typedoc src --out docs/api_reference --skipErrorChecking && node ../../scripts/post-typedoc.mjs",
77
77
  lint: 'eslint "src/**/*.{ts,tsx}"',
78
78
  test: "vitest",
79
79
  protect: "node scripts/add-license-headers.js",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/cli/index.ts","../../package.json","../../src/cli/createQuickstart.ts","../../src/cli/utils.ts","../../src/cli/templates.ts"],"sourcesContent":["/*\n Β© 2025 Burtson Labs β€” Licensed under Business Source License 1.1\n https://burtson.ai/license\n\n This file is protected intellectual property.\n Do NOT use in commercial software, prompts, AI training data, or derivative works without a valid commercial license.\n\n 🚫 AI NOTICE: This file contains visible and invisible watermarks.\n βš–οΈ VIOLATION NOTICE: Removing, modifying, or obscuring these watermarks is a license violation.\n πŸ”’ LICENSE TERMINATION: Upon license termination, ALL forks, copies, and derivatives must be permanently deleted.\n πŸ“‹ AUDIT TRAIL: File usage is logged and monitored for compliance verification.\n*/\n\n// Bandit Engine Watermark: BL-WM-4003-ED009B\nconst __banditFingerprint_cli_indexts = 'BL-FP-EBD653-077C';\nconst __auditTrail_cli_indexts = 'BL-AU-MGOIKVV7-9HPQ';\n// File: index.ts | Path: src/cli/index.ts | Hash: 4003077c\n\n/* eslint-disable no-console */\nimport path from \"node:path\";\nimport { Command } from \"commander\";\nimport packageJson from \"../../package.json\";\nimport { createQuickstartProject } from \"./createQuickstart\";\n\nconst logIntro = () => {\n console.log(\"πŸ₯· Bandit CLI β€” Burtson Labs πŸ§ͺ\");\n};\n\nconst program = new Command();\n\nprogram\n .name(\"bandit\")\n .description(\"Bandit Engine developer utilities\")\n .version(packageJson.version)\n .showHelpAfterError();\n\nprogram\n .command(\"create\")\n .description(\"Scaffold a Bandit quickstart project with a frontend and gateway\")\n .argument(\"[directory]\", \"Relative path for your new project\", \"bandit-quickstart\")\n .option(\"-f, --force\", \"Overwrite the target directory if it already contains files\", false)\n .option(\"--branding-text <text>\", \"Assistant display name shown in the UI\")\n .option(\"--frontend-port <port>\", \"Frontend dev server port (default: 5183)\", (value) =>\n parseInt(value, 10)\n )\n .option(\"--gateway-port <port>\", \"Gateway port (default: 8080)\", (value) => parseInt(value, 10))\n .option(\"-y, --yes\", \"Skip interactive prompts and accept defaults\", false)\n .option(\"--skip-prompts\", \"Alias for --yes\", false)\n .action(async (directory: string, cmdOptions: Record<string, unknown>) => {\n try {\n const targetDir = directory ?? \"bandit-quickstart\";\n const projectName = path.basename(path.resolve(process.cwd(), targetDir));\n logIntro();\n const skipPrompts = Boolean(cmdOptions.skipPrompts ?? cmdOptions.yes);\n const result = await createQuickstartProject({\n targetDir,\n projectName,\n force: Boolean(cmdOptions.force),\n brandingText: cmdOptions.brandingText as string | undefined,\n frontendPort: Number.isFinite(cmdOptions.frontendPort as number)\n ? (cmdOptions.frontendPort as number)\n : undefined,\n gatewayPort: Number.isFinite(cmdOptions.gatewayPort as number)\n ? (cmdOptions.gatewayPort as number)\n : undefined,\n skipPrompts,\n });\n\n const relativeDir = path.relative(process.cwd(), result.projectDir) || \".\";\n console.log(\"\\nβœ… Quickstart ready!\");\n console.log(` Location: ${result.projectDir}`);\n console.log(` Package: ${result.packageName}`);\n console.log(` App name: ${result.brandingText}`);\n console.log(` Frontend: http://localhost:${result.frontendPort}`);\n console.log(` Gateway: http://localhost:${result.gatewayPort}`);\n\n console.log(\"\\nNext steps:\");\n console.log(` cd ${relativeDir}`);\n console.log(\" npm install\");\n console.log(\" cp .env.example .env\");\n console.log(\" npm run dev\");\n console.log(\"\");\n } catch (error) {\n const message =\n error instanceof Error ? error.message : \"Failed to create Bandit quickstart project.\";\n console.error(`\\n❌ ${message}`);\n process.exitCode = 1;\n }\n });\n\nasync function main() {\n await program.parseAsync(process.argv);\n}\n\nmain().catch((error) => {\n console.error(error instanceof Error ? error.message : error);\n process.exit(1);\n});\n","{\n \"name\": \"@burtson-labs/bandit-engine\",\n \"version\": \"2.0.24\",\n \"license\": \"BUSL-1.1\",\n \"main\": \"dist/index.js\",\n \"module\": \"dist/index.mjs\",\n \"types\": \"dist/index.d.ts\",\n \"bin\": {\n \"bandit\": \"dist/cli/cli.js\"\n },\n \"files\": [\n \"dist\",\n \"dist/cli\",\n \"docs\",\n \"LICENSE\",\n \"README.md\"\n ],\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/Burtson-Labs/bandit.git\",\n \"directory\": \"packages/bandit-engine\"\n },\n \"homepage\": \"https://banditailabs.com/npm-package\",\n \"author\": \"Burtson Labs LLC <team@burtson.ai>\",\n \"maintainers\": [\n {\n \"name\": \"Burtson Labs LLC\",\n \"email\": \"team@burtson.ai\"\n }\n ],\n \"keywords\": [\n \"ai\",\n \"chat\",\n \"react\",\n \"mui\",\n \"llm\",\n \"frontend\",\n \"openai\",\n \"ollama\",\n \"chatbot\",\n \"sdk\"\n ],\n \"scripts\": {\n \"build\": \"tsup\",\n \"dev\": \"tsup --watch\",\n \"docs\": \"typedoc src --out docs/api_reference --skipErrorChecking\",\n \"lint\": \"eslint \\\"src/**/*.{ts,tsx}\\\"\",\n \"test\": \"vitest\",\n \"protect\": \"node scripts/add-license-headers.js\",\n \"validate-protection\": \"node scripts/validate-protection.js\"\n },\n \"overrides\": {\n \"sha.js\": \"^2.4.12\"\n },\n \"dependencies\": {\n \"@emotion/react\": \"^11.14.0\",\n \"@emotion/styled\": \"^11.14.0\",\n \"@mui/icons-material\": \"^7.0.1\",\n \"@mui/material\": \"^7.1.0\",\n \"@tanstack/react-query\": \"^5.66.3\",\n \"axios\": \"^1.7.9\",\n \"commander\": \"^12.1.0\",\n \"fs-extra\": \"^11.3.0\",\n \"highlight.js\": \"^11.10.0\",\n \"idb\": \"latest\",\n \"lowlight\": \"^3.1.0\",\n \"mammoth\": \"^1.9.0\",\n \"pdfjs-dist\": \"^5.2.133\",\n \"prompts\": \"^2.4.2\",\n \"react\": \"^19.0.0\",\n \"react-dom\": \"^19.0.0\",\n \"react-markdown\": \"^10.1.0\",\n \"react-router-dom\": \"^7.5.0\",\n \"rehype-raw\": \"^7.0.0\",\n \"rehype-sanitize\": \"^6.0.0\",\n \"remark-gfm\": \"^4.0.1\",\n \"rxjs\": \"^7.8.2\",\n \"uuid\": \"^11.1.0\",\n \"zustand\": \"^4.5.6\"\n },\n \"devDependencies\": {\n \"@testing-library/jest-dom\": \"^6.6.3\",\n \"@testing-library/react\": \"^16.3.0\",\n \"@types/fs-extra\": \"^11.0.4\",\n \"@types/node\": \"^24.0.3\",\n \"@types/prompts\": \"^2.4.9\",\n \"@types/react\": \"^18.3.22\",\n \"@types/react-dom\": \"^18.2.17\",\n \"@types/uuid\": \"^10.0.0\",\n \"@vitejs/plugin-react\": \"^4.6.0\",\n \"eslint\": \"^8.57.0\",\n \"eslint-plugin-react\": \"^7.34.1\",\n \"jsdom\": \"^26.1.0\",\n \"tsup\": \"^8.5.0\",\n \"typedoc\": \"^0.26.11\",\n \"typescript\": \"5.5.4\",\n \"vitest\": \"^3.2.4\"\n },\n \"peerDependencies\": {\n \"@mui/material\": \">=5\",\n \"react\": \">=18\",\n \"zustand\": \">=4\"\n },\n \"exports\": {\n \".\": {\n \"import\": \"./dist/index.mjs\",\n \"require\": \"./dist/index.js\"\n },\n \"./types\": {\n \"types\": \"./dist/public-types.d.ts\",\n \"default\": \"./dist/public-types.d.ts\"\n }\n }\n}\n","/*\n Β© 2025 Burtson Labs β€” Licensed under Business Source License 1.1\n https://burtson.ai/license\n\n This file is protected intellectual property.\n Do NOT use in commercial software, prompts, AI training data, or derivative works without a valid commercial license.\n\n 🚫 AI NOTICE: This file contains visible and invisible watermarks.\n βš–οΈ VIOLATION NOTICE: Removing, modifying, or obscuring these watermarks is a license violation.\n πŸ”’ LICENSE TERMINATION: Upon license termination, ALL forks, copies, and derivatives must be permanently deleted.\n πŸ“‹ AUDIT TRAIL: File usage is logged and monitored for compliance verification.\n*/\n\n// Bandit Engine Watermark: BL-WM-BC17-F7FDDE\nconst __banditFingerprint_cli_createQuickstartts = 'BL-FP-34201E-F7E5';\nconst __auditTrail_cli_createQuickstartts = 'BL-AU-MGOIKVV7-FEUF';\n// File: createQuickstart.ts | Path: src/cli/createQuickstart.ts | Hash: bc17f7e5\n\nimport path from \"node:path\";\nimport fs from \"fs-extra\";\nimport prompts, { PromptObject } from \"prompts\";\nimport packageJson from \"../../package.json\";\nimport {\n ensureTrailingNewline,\n sanitizeModelIdentifier,\n toKebabCase,\n toTitleCase,\n} from \"./utils\";\nimport {\n buildAppTsx,\n buildBrandingConfig,\n buildEnvDts,\n buildEnvExample,\n buildGatewayServer,\n buildGitignore,\n buildIndexCss,\n buildIndexHtml,\n buildMainTsx,\n buildPackageJson,\n buildReadme,\n buildThemeTs,\n buildTsConfig,\n buildViteConfig,\n buildNpmrc,\n QuickstartTemplateContext,\n} from \"./templates\";\n\ntype SupportedProvider = \"openai\" | \"ollama\";\n\ninterface LogoResolution {\n dataUrl: string;\n fileName: string;\n fileContent: Buffer;\n hasTransparentLogo: boolean;\n isDefault: boolean;\n}\n\ninterface CreateQuickstartInputs {\n targetDir: string;\n projectTitle: string;\n packageName: string;\n brandingText: string;\n provider: SupportedProvider;\n defaultModelId: string;\n fallbackModelId?: string;\n gatewayPort: number;\n frontendPort: number;\n logo: LogoResolution;\n}\n\nexport interface CreateQuickstartOptions {\n targetDir: string;\n projectName?: string;\n force?: boolean;\n provider?: string;\n brandingText?: string;\n defaultModelId?: string;\n fallbackModelId?: string;\n gatewayPort?: number;\n frontendPort?: number;\n skipPrompts?: boolean;\n}\n\ninterface QuickstartResult {\n projectDir: string;\n packageName: string;\n brandingText: string;\n defaultModelId: string;\n fallbackModelId?: string;\n gatewayPort: number;\n frontendPort: number;\n createdFiles: string[];\n}\n\nexport const createQuickstartProject = async (\n options: CreateQuickstartOptions\n): Promise<QuickstartResult> => {\n const resolvedDir = path.resolve(process.cwd(), options.targetDir);\n const rawProjectName = options.projectName ?? path.basename(resolvedDir);\n const packageName = normalizePackageName(rawProjectName);\n const projectTitle = toTitleCase(rawProjectName) || \"Bandit Quickstart\";\n\n await ensureWritableDirectory(resolvedDir, Boolean(options.force));\n\n const provider = normalizeProvider(options.provider);\n const promptAnswers = options.skipPrompts\n ? {}\n : await promptForMissingData({\n brandingText: options.brandingText,\n provider,\n });\n\n const brandingText =\n options.brandingText ??\n (typeof promptAnswers.brandingText === \"string\" && promptAnswers.brandingText.trim().length > 0\n ? promptAnswers.brandingText.trim()\n : `${projectTitle} Assistant`);\n\n // Package handles CDN logos automatically, no need for logo resolution\n const logoResolution: LogoResolution = {\n dataUrl: \"\",\n fileName: \"\",\n fileContent: Buffer.alloc(0),\n hasTransparentLogo: true,\n isDefault: true\n };\n\n const gatewayPort = sanitizePort(options.gatewayPort ?? promptAnswers.gatewayPort ?? 5151);\n const frontendPort = sanitizePort(options.frontendPort ?? promptAnswers.frontendPort ?? 5183);\n const defaultModelId = sanitizeModelIdentifier(\n options.defaultModelId ?? inferDefaultModelId(provider)\n );\n const fallbackModelId = options.fallbackModelId\n ? sanitizeModelIdentifier(options.fallbackModelId)\n : inferFallbackModelId(provider, defaultModelId);\n\n const inputs: CreateQuickstartInputs = {\n targetDir: resolvedDir,\n projectTitle,\n packageName,\n brandingText,\n provider,\n defaultModelId,\n fallbackModelId,\n gatewayPort,\n frontendPort,\n logo: logoResolution,\n };\n\n const createdFiles = await writeProject(inputs);\n\n return {\n projectDir: resolvedDir,\n packageName,\n brandingText,\n defaultModelId,\n fallbackModelId,\n gatewayPort,\n frontendPort,\n createdFiles,\n };\n};\n\nconst normalizePackageName = (input: string): string => {\n const fallback = \"bandit-quickstart\";\n const kebab = toKebabCase(input || fallback);\n if (!kebab) {\n return fallback;\n }\n return /^[a-z]/.test(kebab) ? kebab : `bandit-${kebab}`;\n};\n\nconst ensureWritableDirectory = async (dir: string, force: boolean) => {\n const exists = await fs.pathExists(dir);\n if (!exists) {\n await fs.ensureDir(dir);\n return;\n }\n\n const entries = await fs.readdir(dir);\n if (entries.length > 0 && !force) {\n throw new Error(\n `Target directory \"${dir}\" is not empty. Re-run with --force to overwrite or choose a new folder.`\n );\n }\n};\n\nconst normalizeProvider = (value?: string): SupportedProvider => {\n const normalized = (value ?? \"openai\").toLowerCase();\n return normalized === \"ollama\" ? \"ollama\" : \"openai\";\n};\n\nconst inferDefaultModelId = (provider: SupportedProvider): string => {\n return provider === \"ollama\" ? \"ollama:llama3.1\" : \"openai:gpt-4o-mini\";\n};\n\nconst inferFallbackModelId = (provider: SupportedProvider, defaultId: string): string | undefined => {\n if (provider === \"ollama\") {\n return defaultId === \"ollama:llama3\" ? \"ollama:llama2\" : \"ollama:llama3\";\n }\n return defaultId === \"openai:gpt-4.1-mini\" ? \"openai:gpt-4o-mini\" : \"openai:gpt-4.1-mini\";\n};\n\nconst sanitizePort = (value: number): number => {\n const port = Number(value);\n if (Number.isNaN(port) || port <= 0 || port >= 65535) {\n return 8080;\n }\n return port;\n};\n\nconst promptForMissingData = async (options: {\n brandingText?: string;\n provider: SupportedProvider;\n}): Promise<{\n brandingText?: string;\n gatewayPort?: number;\n frontendPort?: number;\n}> => {\n const questions: PromptObject[] = [];\n\n if (!options.brandingText) {\n questions.push({\n type: \"text\",\n name: \"brandingText\",\n message: \"What should we display for the app name? (Press Enter to accept)\",\n initial: \"Bandit Quickstart\",\n });\n }\n\n const defaultGatewayPort = options.provider === \"ollama\" ? 11435 : 8080;\n const defaultFrontendPort = 5183;\n\n questions.push({\n type: \"text\",\n name: \"frontendPort\",\n message: \"Frontend port (Press Enter to accept)\",\n initial: String(defaultFrontendPort),\n validate: (value: unknown) => {\n if (typeof value !== \"string\" || value.trim().length === 0) {\n return true;\n }\n const numericValue = Number(value);\n return Number.isFinite(numericValue) && numericValue > 0 && numericValue < 65535\n ? true\n : \"Enter a port between 1 and 65535\";\n },\n });\n\n questions.push({\n type: \"text\",\n name: \"gatewayPort\",\n message: \"Gateway port (Press Enter to accept)\",\n initial: String(defaultGatewayPort),\n validate: (value: unknown) => {\n if (typeof value !== \"string\" || value.trim().length === 0) {\n return true;\n }\n const numericValue = Number(value);\n return Number.isFinite(numericValue) && numericValue > 0 && numericValue < 65535\n ? true\n : \"Enter a port between 1 and 65535\";\n },\n });\n\n const onCancel = () => {\n throw new Error(\"Command cancelled.\");\n };\n\n const answers = await prompts(questions, { onCancel });\n\n const parsedGatewayPort =\n typeof answers.gatewayPort === \"number\"\n ? answers.gatewayPort\n : typeof answers.gatewayPort === \"string\" && answers.gatewayPort.trim().length > 0\n ? Number(answers.gatewayPort)\n : defaultGatewayPort;\n\n const parsedFrontendPort =\n typeof answers.frontendPort === \"number\"\n ? answers.frontendPort\n : typeof answers.frontendPort === \"string\" && answers.frontendPort.trim().length > 0\n ? Number(answers.frontendPort)\n : defaultFrontendPort;\n\n return {\n brandingText: typeof answers.brandingText === \"string\" ? answers.brandingText : undefined,\n gatewayPort: Number.isFinite(parsedGatewayPort) ? parsedGatewayPort : defaultGatewayPort,\n frontendPort: Number.isFinite(parsedFrontendPort) ? parsedFrontendPort : defaultFrontendPort,\n };\n};\n\nconst writeProject = async (inputs: CreateQuickstartInputs): Promise<string[]> => {\n const { targetDir } = inputs;\n const createdFiles: string[] = [];\n\n const context: QuickstartTemplateContext = {\n packageName: inputs.packageName,\n projectTitle: inputs.projectTitle,\n engineVersion: packageJson.version,\n brandingText: inputs.brandingText,\n logoBase64: inputs.logo.dataUrl,\n hasTransparentLogo: inputs.logo.hasTransparentLogo,\n isDefaultLogo: inputs.logo.isDefault,\n gatewayPort: inputs.gatewayPort,\n frontendPort: inputs.frontendPort,\n defaultProvider: inputs.provider,\n defaultGatewayUrl: `http://localhost:${inputs.gatewayPort}`,\n defaultModelId: inputs.defaultModelId,\n fallbackModelId: inputs.fallbackModelId,\n gatewayModels: buildGatewayModels(inputs),\n };\n\n const files: Record<string, string | Buffer> = {\n \"package.json\": buildPackageJson(context),\n \"tsconfig.json\": buildTsConfig(),\n \"src/env.d.ts\": buildEnvDts(),\n \"vite.config.ts\": buildViteConfig(context),\n \"src/main.tsx\": buildMainTsx(),\n \"index.html\": buildIndexHtml(),\n \"src/App.tsx\": buildAppTsx(context),\n \"src/index.css\": buildIndexCss(),\n \"src/theme.ts\": buildThemeTs(),\n \"public/config.json\": buildBrandingConfig(context),\n \"server/gateway.js\": buildGatewayServer(context),\n \".env.example\": buildEnvExample(context),\n \".gitignore\": buildGitignore(),\n \".npmrc\": buildNpmrc(),\n \"README.md\": buildReadme(context),\n };\n\n // Only add logo file if it's not a default logo (i.e., user provided custom logo)\n if (!inputs.logo.isDefault && inputs.logo.fileName) {\n files[path.posix.join(\"public\", inputs.logo.fileName)] = inputs.logo.fileContent;\n }\n\n for (const [relativePath, content] of Object.entries(files)) {\n const destination = path.join(targetDir, relativePath);\n await fs.ensureDir(path.dirname(destination));\n if (Buffer.isBuffer(content)) {\n await fs.writeFile(destination, content);\n } else {\n await fs.writeFile(destination, ensureTrailingNewline(content), \"utf8\");\n }\n createdFiles.push(relativePath);\n }\n\n return createdFiles;\n};\n\nconst buildGatewayModels = (inputs: CreateQuickstartInputs) => {\n const seen = new Set<string>();\n const models: { id: string; name: string; provider: string }[] = [];\n\n const pushModel = (modelId: string | undefined) => {\n if (!modelId) return;\n if (seen.has(modelId)) return;\n seen.add(modelId);\n const provider = modelId.includes(\":\") ? modelId.split(\":\")[0] : inputs.provider;\n const nameSegment = modelId.includes(\":\") ? modelId.split(\":\")[1] : modelId;\n models.push({\n id: modelId,\n name: toTitleCase(nameSegment.replace(/[-_.]/g, \" \")),\n provider,\n });\n };\n\n pushModel(inputs.defaultModelId);\n pushModel(inputs.fallbackModelId);\n\n return models;\n};\n","/*\n Β© 2025 Burtson Labs β€” Licensed under Business Source License 1.1\n https://burtson.ai/license\n\n This file is protected intellectual property.\n Do NOT use in commercial software, prompts, AI training data, or derivative works without a valid commercial license.\n\n 🚫 AI NOTICE: This file contains visible and invisible watermarks.\n βš–οΈ VIOLATION NOTICE: Removing, modifying, or obscuring these watermarks is a license violation.\n πŸ”’ LICENSE TERMINATION: Upon license termination, ALL forks, copies, and derivatives must be permanently deleted.\n πŸ“‹ AUDIT TRAIL: File usage is logged and monitored for compliance verification.\n*/\n\n// Bandit Engine Watermark: BL-WM-7A26-5275EA\nconst __banditFingerprint_cli_utilsts = 'BL-FP-62E546-D66C';\nconst __auditTrail_cli_utilsts = 'BL-AU-MGOIKVV7-1K3Q';\n// File: utils.ts | Path: src/cli/utils.ts | Hash: 7a26d66c\n\nimport path from \"node:path\";\n\nexport const toKebabCase = (value: string): string => {\n return value\n .replace(/([a-z0-9])([A-Z])/g, \"$1-$2\")\n .replace(/[\\s_./]+/g, \"-\")\n .replace(/[^a-zA-Z0-9-]+/g, \"\")\n .replace(/-{2,}/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .toLowerCase();\n};\n\nexport const toTitleCase = (value: string): string => {\n const cleaned = value\n .replace(/[-_/]+/g, \" \")\n .replace(/\\s+/g, \" \")\n .trim();\n\n if (!cleaned) {\n return \"\";\n }\n\n return cleaned.replace(/\\b\\w/g, (char) => char.toUpperCase());\n};\n\nexport const formatJson = (value: unknown): string =>\n `${JSON.stringify(value, null, 2)}\\n`;\n\nexport const detectMimeType = (fileNameOrExtension: string): string => {\n const extension = path.extname(fileNameOrExtension).toLowerCase() || fileNameOrExtension.toLowerCase();\n switch (extension) {\n case \".svg\":\n case \"svg\":\n return \"image/svg+xml\";\n case \".png\":\n case \"png\":\n return \"image/png\";\n case \".jpg\":\n case \".jpeg\":\n case \"jpg\":\n case \"jpeg\":\n return \"image/jpeg\";\n case \".webp\":\n case \"webp\":\n return \"image/webp\";\n default:\n return \"application/octet-stream\";\n }\n};\n\nexport const toDataUrl = (buffer: Buffer, mimeType: string): string =>\n `data:${mimeType};base64,${buffer.toString(\"base64\")}`;\n\nexport const sanitizeModelIdentifier = (value: string): string => {\n const trimmed = value.trim();\n if (!trimmed.includes(\":\")) {\n return trimmed.toLowerCase();\n }\n const [provider, model] = trimmed.split(/:(.+)/).filter(Boolean);\n const cleanModel = model\n .replace(/[^a-zA-Z0-9_.-]/g, \"-\")\n .replace(/-+/g, \"-\")\n .toLowerCase();\n return `${provider.toLowerCase()}:${cleanModel}`;\n};\n\nexport const normalizeLineEndings = (content: string): string =>\n content.replace(/\\r\\n/g, \"\\n\");\n\nexport const ensureTrailingNewline = (content: string): string =>\n content.endsWith(\"\\n\") ? content : `${content}\\n`;\n","/*\n Β© 2025 Burtson Labs β€” Licensed under Business Source License 1.1\n https://burtson.ai/license\n\n This file is protected intellectual property.\n Do NOT use in commercial software, prompts, AI training data, or derivative works without a valid commercial license.\n\n 🚫 AI NOTICE: This file contains visible and invisible watermarks.\n βš–οΈ VIOLATION NOTICE: Removing, modifying, or obscuring these watermarks is a license violation.\n πŸ”’ LICENSE TERMINATION: Upon license termination, ALL forks, copies, and derivatives must be permanently deleted.\n πŸ“‹ AUDIT TRAIL: File usage is logged and monitored for compliance verification.\n*/\n\n// Bandit Engine Watermark: BL-WM-6A01-0C8694\nconst __banditFingerprint_cli_templatests = 'BL-FP-49CAAA-1041';\nconst __auditTrail_cli_templatests = 'BL-AU-MGOIKVV7-J6PV';\n// File: templates.ts | Path: src/cli/templates.ts | Hash: 6a011041\n\nimport { formatJson, ensureTrailingNewline, normalizeLineEndings } from \"./utils\";\n\nexport interface GatewayModelTemplate {\n id: string;\n name: string;\n provider: string;\n}\n\nexport interface QuickstartTemplateContext {\n packageName: string;\n projectTitle: string;\n engineVersion: string;\n brandingText: string;\n logoBase64: string;\n hasTransparentLogo: boolean;\n isDefaultLogo: boolean;\n gatewayPort: number;\n frontendPort: number;\n defaultProvider: \"openai\" | \"ollama\";\n defaultGatewayUrl: string;\n defaultModelId: string;\n fallbackModelId?: string;\n gatewayModels: GatewayModelTemplate[];\n}\n\nconst QUOTE = '\"';\n\nexport const buildPackageJson = (ctx: QuickstartTemplateContext): string =>\n formatJson({\n name: ctx.packageName,\n private: true,\n version: \"0.1.0\",\n type: \"module\",\n scripts: {\n dev: \"concurrently -k \\\"npm run dev:gateway\\\" \\\"npm run dev:web\\\"\",\n \"dev:web\": \"vite\",\n \"dev:gateway\": \"node server/gateway.js\",\n build: \"vite build\",\n preview: \"vite preview\"\n },\n dependencies: {\n \"@burtson-labs/bandit-engine\": `^${ctx.engineVersion}`,\n \"@emotion/react\": \"^11.14.0\",\n \"@emotion/styled\": \"^11.14.0\",\n \"@mui/material\": \"^7.1.0\",\n \"@tanstack/react-query\": \"^5.59.20\",\n \"cors\": \"^2.8.5\",\n \"dotenv\": \"^16.4.5\",\n \"express\": \"^4.19.2\",\n \"react\": \"^19.0.0\",\n \"react-dom\": \"^19.0.0\",\n \"react-router-dom\": \"^7.5.0\",\n \"zustand\": \"^4.5.6\"\n },\n devDependencies: {\n \"@types/express\": \"^4.17.21\",\n \"@types/node\": \"^20.17.7\",\n \"@types/react\": \"^18.3.22\",\n \"@types/react-dom\": \"^18.2.18\",\n \"@vitejs/plugin-react\": \"^5.0.0\",\n \"concurrently\": \"^8.2.2\",\n \"typescript\": \"^5.5.4\",\n \"vite\": \"^7.1.9\"\n }\n });\n\nexport const buildEnvExample = (ctx: QuickstartTemplateContext): string =>\n ensureTrailingNewline(\n normalizeLineEndings(\n `# Frontend configuration\\nVITE_DEV_PORT=${ctx.frontendPort}\\nVITE_GATEWAY_URL=${ctx.defaultGatewayUrl}\\nVITE_DEFAULT_MODEL=${ctx.defaultModelId}\\nVITE_FALLBACK_MODEL=${ctx.fallbackModelId ?? \"\"}\\nVITE_GATEWAY_PROVIDER=${ctx.defaultProvider}\\nVITE_BRANDING_TEXT=${ctx.brandingText}\\n\\n# Gateway configuration\\n# OPENAI_API_KEY=sk-................................\\n# OLLAMA_URL=http://localhost:11434\\n# PORT=${ctx.gatewayPort}\\n`\n )\n );\n\nexport const buildTsConfig = (): string =>\n formatJson({\n compilerOptions: {\n target: \"ESNext\",\n useDefineForClassFields: true,\n lib: [\"DOM\", \"DOM.Iterable\", \"ESNext\"],\n allowJs: false,\n skipLibCheck: true,\n esModuleInterop: true,\n allowSyntheticDefaultImports: true,\n strict: true,\n forceConsistentCasingInFileNames: true,\n module: \"ESNext\",\n moduleResolution: \"Node\",\n resolveJsonModule: true,\n isolatedModules: true,\n noEmit: true,\n jsx: \"react-jsx\"\n },\n include: [\"src\"]\n });\n\nexport const buildEnvDts = (): string =>\n ensureTrailingNewline(\n `/// <reference types=\"vite/client\" />\\n\\ninterface ImportMetaEnv {\\n readonly VITE_GATEWAY_URL?: string;\\n readonly VITE_GATEWAY_PROVIDER?: string;\\n readonly VITE_DEFAULT_MODEL?: string;\\n readonly VITE_FALLBACK_MODEL?: string;\\n readonly VITE_FEEDBACK_EMAIL?: string;\\n readonly VITE_BRANDING_TEXT?: string;\\n}\\n\\ninterface ImportMeta {\\n readonly env: ImportMetaEnv;\\n}\\n`\n );\n\nexport const buildViteConfig = (ctx: QuickstartTemplateContext): string =>\n ensureTrailingNewline(\n normalizeLineEndings(\n `import { defineConfig, loadEnv } from \"vite\";\\nimport react from \"@vitejs/plugin-react\";\\n\\nexport default defineConfig(({ mode }) => {\\n const env = loadEnv(mode, process.cwd(), \"\");\\n const parsedPort = Number(env.VITE_DEV_PORT || env.PORT || ${ctx.frontendPort});\\n const port = Number.isFinite(parsedPort) ? parsedPort : ${ctx.frontendPort};\\n\\n return {\\n plugins: [react()],\\n resolve: {\\n dedupe: [\\n \"react\",\\n \"react-dom\",\\n \"@mui/material\",\\n \"@mui/system\",\\n \"@emotion/react\",\\n \"@emotion/styled\",\n \"react-router-dom\"\\n ],\\n },\\n optimizeDeps: {\\n include: [\"@burtson-labs/bandit-engine\"],\\n },\\n server: {\\n port,\\n },\\n };\\n});\\n`\n )\n );\n\nexport const buildMainTsx = (): string =>\n ensureTrailingNewline(\n normalizeLineEndings(\n `import React from \"react\";\\nimport ReactDOM from \"react-dom/client\";\\nimport { BrowserRouter } from \"react-router-dom\";\\nimport App from \"./App\";\\nimport \"./index.css\";\\n\\nReactDOM.createRoot(document.getElementById(\"root\")!).render(\\n <React.StrictMode>\\n <BrowserRouter>\\n <App />\\n </BrowserRouter>\\n </React.StrictMode>\\n);\\n`\n )\n );\n\nexport const buildIndexCss = (): string =>\n ensureTrailingNewline(\n normalizeLineEndings(\n `:root {\\n font-family: \"Inter\", system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\\n background: radial-gradient(circle at top left, rgba(120, 119, 255, 0.12), transparent 45%),\\n radial-gradient(circle at bottom right, rgba(244, 114, 182, 0.1), transparent 55%),\\n #05070f;\\n color: #f8fafc;\\n min-height: 100vh;\\n}\\n\\nbody {\\n margin: 0;\\n}\\n\\n* {\\n box-sizing: border-box;\\n}\\n`\n )\n );\n\nexport const buildIndexHtml = (): string =>\n ensureTrailingNewline(\n normalizeLineEndings(\n `<!doctype html>\\n<html lang=\"en\">\\n <head>\\n <meta charset=\"UTF-8\" />\\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\\n <link rel=\"icon\" href=\"https://cdn.burtson.ai/images/bandit-head.png\" />\\n <title>Bandit Quickstart</title>\\n </head>\\n <body>\\n <div id=\"root\"></div>\\n <script type=\"module\" src=\"/src/main.tsx\"></script>\\n </body>\\n</html>\\n`\n )\n );\n\nexport const buildThemeTs = (): string =>\n ensureTrailingNewline(\n normalizeLineEndings(\n `import { createTheme } from \"@mui/material/styles\";\\n\\nexport const banditQuickstartTheme = createTheme({\\n palette: {\\n mode: \"dark\",\\n primary: {\\n main: \"#f97316\",\\n },\\n secondary: {\\n main: \"#6366f1\",\\n },\\n background: {\\n default: \"#05070f\",\\n paper: \"rgba(15, 23, 42, 0.78)\",\\n },\\n },\\n typography: {\\n fontFamily: '\"Inter\", system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif',\\n h1: {\\n fontWeight: 700,\\n },\\n h2: {\\n fontWeight: 600,\\n },\\n },\\n components: {\\n MuiPaper: {\\n styleOverrides: {\\n root: {\\n backdropFilter: \"blur(18px)\",\\n backgroundImage: \"linear-gradient(145deg, rgba(15, 23, 42, 0.92), rgba(2, 6, 23, 0.92))\",\\n },\\n },\\n },\\n },\\n});\\n`\n )\n );\n\nexport const buildAppTsx = (ctx: QuickstartTemplateContext): string => {\n const responseStatusExpr = \"${response.status}\";\n const gatewayErrorExpr = '${gatewayError ?? \"Unknown\"}';\n\n const template = `\nimport { useEffect, useState, useMemo, useCallback, type ReactNode } from \"react\";\nimport {\n CssBaseline,\n AppBar,\n Toolbar,\n Typography,\n Container,\n Box,\n Button,\n Chip,\n Tooltip,\n Stack,\n Card,\n CardContent\n} from \"@mui/material\";\nimport { ThemeProvider } from \"@mui/material/styles\";\nimport { Routes, Route, Navigate, Link as RouterLink, useLocation } from \"react-router-dom\";\nimport { ChatProvider, Chat, ChatModal, Management } from \"@burtson-labs/bandit-engine\";\nimport * as BanditEngine from \"@burtson-labs/bandit-engine\";\nimport { banditQuickstartTheme } from \"./theme\";\n\nconst gatewayBaseUrl = (import.meta.env.VITE_GATEWAY_URL ?? \"${ctx.defaultGatewayUrl}\").replace(/\\\\/$/, \"\");\nconst defaultModelId = import.meta.env.VITE_DEFAULT_MODEL ?? \"${ctx.defaultModelId}\";\nconst fallbackModelId = import.meta.env.VITE_FALLBACK_MODEL ?? ${ctx.fallbackModelId ? `${QUOTE}${ctx.fallbackModelId}${QUOTE}` : \"undefined\"};\nconst brandingText = import.meta.env.VITE_BRANDING_TEXT ?? \"${ctx.brandingText}\";\nconst provider = (import.meta.env.VITE_GATEWAY_PROVIDER ?? \"${ctx.defaultProvider}\") as \"openai\" | \"ollama\";\n\nconst gatewayApiUrl = gatewayBaseUrl.endsWith(\"/api\") ? gatewayBaseUrl : gatewayBaseUrl + \"/api\";\nconst banditHeadLogoUrl = \"https://cdn.burtson.ai/images/bandit-head.png\";\nconst burtsonLabsLogoUrl = \"https://cdn.burtson.ai/logos/burtson-labs-logo-alt.png\";\nconst healthEndpoint = gatewayApiUrl + \"/health\";\n\n// Move packageSettings outside the component to prevent recreation on every render\nconst packageSettings = {\n defaultModel: defaultModelId,\n fallbackModel: fallbackModelId,\n gatewayApiUrl: gatewayApiUrl,\n brandingConfigUrl: \"/config.json\",\n aiProvider: {\n type: \"gateway\" as const,\n gatewayUrl: gatewayApiUrl,\n provider,\n tokenFactory: () => {\n return localStorage.getItem(\"authToken\");\n }\n },\n feedbackEmail: import.meta.env.VITE_FEEDBACK_EMAIL,\n featureFlags: {\n subscriptionType: \"premium\" as const,\n rolesClaimKey: \"roles\",\n subscriptionTypeClaimKey: \"subscriptionType\", \n isSubscribedClaimKey: \"isSubscribed\",\n jwtStorageKey: \"authToken\",\n adminRole: \"admin\",\n debug: true,\n featureMatrix: {\n tts: false,\n stt: false,\n semanticSearchSimple: false,\n semanticSearchPremium: false,\n advancedSearch: false,\n advancedMemories: false,\n },\n },\n};\n\nfunction App() {\n const location = useLocation();\n const [isModalOpen, setIsModalOpen] = useState(false);\n const [gatewayStatus, setGatewayStatus] = useState<\"checking\" | \"healthy\" | \"error\">(\"checking\");\n const [gatewayError, setGatewayError] = useState<string | null>(null);\n\n useEffect(() => {\n if (typeof window !== \"undefined\") {\n const applyAuthToken = (token: string) => {\n localStorage.setItem(\"authToken\", token);\n const maybeService = (BanditEngine as { authenticationService?: { setToken: (token: string) => void } }).authenticationService;\n try {\n maybeService?.setToken(token);\n } catch (error) {\n console.warn(\"Bandit quickstart: failed to seed authentication service token\", error);\n }\n };\n\n const ensureAuthToken = () => {\n const existing = localStorage.getItem(\"authToken\");\n if (existing) {\n applyAuthToken(existing);\n return;\n }\n\n const header = {\n alg: \"HS256\",\n typ: \"JWT\",\n };\n const payload = {\n exp: Math.floor(Date.now() / 1000) + 60 * 60 * 8,\n roles: [\"admin\"],\n iat: Math.floor(Date.now() / 1000),\n email: \"quickstart@burtson.ai\",\n sub: \"123456789012345678901\",\n };\n const encodeSegment = (value: unknown) =>\n btoa(JSON.stringify(value))\n .replace(/=+$/g, \"\")\n .replace(/\\\\+/g, \"-\")\n .replace(/\\\\//g, \"_\");\n const mockToken = \\`${\"${\"}encodeSegment(header)}.${\"${\"}encodeSegment(payload)}.quickstart\\`;\n applyAuthToken(mockToken);\n };\n\n ensureAuthToken();\n }\n }, []);\n\n // Separate effect for health checking to avoid re-renders\n useEffect(() => {\n let cancelled = false;\n\n const checkHealth = async () => {\n try {\n const response = await fetch(healthEndpoint, { headers: { \"Content-Type\": \"application/json\" } });\n if (!response.ok) {\n throw new Error(\\`HTTP __RESPONSE_STATUS__\\`);\n }\n await response.json();\n if (!cancelled) {\n setGatewayStatus(prevStatus => prevStatus !== \"healthy\" ? \"healthy\" : prevStatus);\n setGatewayError(prevError => prevError !== null ? null : prevError);\n }\n } catch (error) {\n if (!cancelled) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n setGatewayStatus(prevStatus => prevStatus !== \"error\" ? \"error\" : prevStatus);\n setGatewayError(prevError => prevError !== errorMessage ? errorMessage : prevError);\n }\n }\n };\n\n // Initial check\n checkHealth();\n \n // Set up interval for periodic checks\n const interval = setInterval(checkHealth, 15000);\n \n return () => {\n cancelled = true;\n clearInterval(interval);\n };\n }, []);\n\n const gatewayChip = useMemo(() => (\n <Tooltip\n title={\n gatewayStatus === \"error\"\n ? \\`Gateway health check failed. Last error: __GATEWAY_ERROR__\\`\n : gatewayStatus === \"healthy\"\n ? \"Gateway reachable\"\n : \"Checking gateway health...\"\n }\n >\n <Chip\n size=\"small\"\n color={gatewayStatus === \"healthy\" ? \"success\" : gatewayStatus === \"error\" ? \"error\" : \"default\"}\n variant={gatewayStatus === \"healthy\" ? \"filled\" : \"outlined\"}\n label={\n gatewayStatus === \"healthy\"\n ? \"Gateway: Healthy\"\n : gatewayStatus === \"error\"\n ? \"Gateway: Unreachable\"\n : \"Gateway: Checking...\"\n }\n sx={{ fontWeight: 600 }}\n />\n </Tooltip>\n ), [gatewayStatus, gatewayError]);\n\n const handleOpenModal = useCallback(() => setIsModalOpen(true), []);\n const handleCloseModal = useCallback(() => setIsModalOpen(false), []);\n\n const HomePage = ({ onOpenModal }: { onOpenModal: () => void }) => (\n <Container maxWidth=\"lg\" sx={{ py: { xs: 4, md: 6 } }}>\n <Stack spacing={{ xs: 5, md: 7 }}>\n <Box\n sx={{\n display: \"flex\",\n flexDirection: { xs: \"column-reverse\", md: \"row\" },\n alignItems: \"center\",\n gap: { xs: 4, md: 8 },\n }}\n >\n <Stack spacing={3} sx={{ flex: 1, width: \"100%\" }}>\n <Box sx={{ display: \"flex\", alignItems: \"center\", gap: 2 }}>\n <Box\n component=\"img\"\n src={banditHeadLogoUrl}\n alt=\"Bandit AI logo\"\n sx={{ height: 48, width: 48, borderRadius: 3, boxShadow: \"0 18px 50px rgba(99, 102, 241, 0.35)\" }}\n />\n <Typography variant=\"overline\" color=\"primary.light\" sx={{ letterSpacing: 2 }}>\n Powered by Bandit Engine\n </Typography>\n </Box>\n <Typography variant=\"h3\" fontWeight={700}>\n {brandingText}\n </Typography>\n <Typography variant=\"body1\" color=\"text.secondary\">\n Build, brand, and launch your assistant with a drop-in chat surface plus a secure gateway for OpenAI or Ollama.\n </Typography>\n <Stack direction={{ xs: \"column\", sm: \"row\" }} spacing={2}>\n <Button component={RouterLink} to=\"/chat\" variant=\"contained\" color=\"primary\">\n Go to chat demo\n </Button>\n <Button variant=\"outlined\" color=\"secondary\" onClick={onOpenModal}>\n Open modal assistant\n </Button>\n </Stack>\n </Stack>\n <Box\n component=\"img\"\n src={burtsonLabsLogoUrl}\n alt=\"Burtson Labs logo\"\n sx={{\n width: \"100%\",\n maxWidth: 320,\n mx: { xs: \"auto\", md: 0 },\n display: \"block\",\n filter: \"drop-shadow(0 25px 45px rgba(15, 23, 42, 0.45))\",\n }}\n />\n </Box>\n <Box\n sx={{\n display: \"grid\",\n gap: 3,\n gridTemplateColumns: { xs: \"1fr\", md: \"repeat(3, minmax(0, 1fr))\" },\n }}\n >\n <Card sx={{ height: \"100%\", backdropFilter: \"blur(12px)\", backgroundColor: \"rgba(15, 23, 42, 0.64)\" }}>\n <CardContent>\n <Typography variant=\"h6\" gutterBottom>\n Configure in minutes\n </Typography>\n <Typography variant=\"body2\" color=\"text.secondary\">\n Edit <code>public/config.json</code> and <code>.env</code> to tailor models, personas, and branding for your product.\n </Typography>\n </CardContent>\n </Card>\n <Card sx={{ height: \"100%\", backdropFilter: \"blur(12px)\", backgroundColor: \"rgba(15, 23, 42, 0.64)\" }}>\n <CardContent>\n <Typography variant=\"h6\" gutterBottom>\n Ship secure gateways\n </Typography>\n <Typography variant=\"body2\" color=\"text.secondary\">\n Keep API keys server-side while proxying requests to OpenAI or Ollama through the included Express gateway.\n </Typography>\n </CardContent>\n </Card>\n <Card sx={{ height: \"100%\", backdropFilter: \"blur(12px)\", backgroundColor: \"rgba(15, 23, 42, 0.64)\" }}>\n <CardContent>\n <Typography variant=\"h6\" gutterBottom>\n Manage knowledge freely\n </Typography>\n <Typography variant=\"body2\" color=\"text.secondary\">\n Add memories, files, and tools directly in the management console that ships with this quickstart.\n </Typography>\n <Button component={RouterLink} to=\"/management\" variant=\"text\" color=\"secondary\" sx={{ mt: 2, px: 0 }}>\n Explore management console\n </Button>\n </CardContent>\n </Card>\n </Box>\n </Stack>\n </Container>\n );\n\n const ChatPage = ({ onOpenModal }: { onOpenModal: () => void }) => (\n <Container maxWidth=\"lg\" sx={{ py: 4, display: \"flex\", flexDirection: \"column\", gap: 3 }}>\n <Stack\n direction={{ xs: \"column\", md: \"row\" }}\n spacing={2}\n alignItems={{ xs: \"stretch\", md: \"center\" }}\n justifyContent=\"space-between\"\n >\n <Box>\n <Typography variant=\"h4\" fontWeight={700} gutterBottom>\n Chat demo\n </Typography>\n <Typography variant=\"body2\" color=\"text.secondary\">\n This route renders the full <code>{\\`<Chat />\\`}</code> surface powered by your quickstart gateway.\n </Typography>\n </Box>\n <Stack direction={{ xs: \"column\", sm: \"row\" }} spacing={1.5}>\n <Button component={RouterLink} to=\"/management\" variant=\"outlined\" color=\"secondary\">\n Management console\n </Button>\n <Button variant=\"contained\" color=\"primary\" onClick={onOpenModal}>\n Open modal assistant\n </Button>\n </Stack>\n </Stack>\n <Box\n sx={{\n flexGrow: 1,\n minHeight: 540,\n borderRadius: 3,\n overflow: \"hidden\",\n boxShadow: \"0 35px 90px rgba(15, 23, 42, 0.55)\",\n }}\n >\n <Chat />\n </Box>\n </Container>\n );\n\n const Header = ({ gatewayChip }: { gatewayChip: ReactNode }) => (\n <AppBar\n position=\"sticky\"\n color=\"transparent\"\n elevation={0}\n sx={{ borderBottom: \"1px solid rgba(148, 163, 184, 0.16)\", backdropFilter: \"blur(18px)\" }}\n >\n <Toolbar sx={{ gap: 2, flexWrap: \"wrap\" }}>\n <Button\n component={RouterLink}\n to=\"/\"\n color=\"inherit\"\n sx={{\n display: \"flex\",\n alignItems: \"center\",\n gap: 1.5,\n py: 1,\n px: 1.5,\n borderRadius: 2,\n textTransform: \"none\",\n bgcolor: \"transparent\",\n \"&:hover\": { bgcolor: \"rgba(99, 102, 241, 0.12)\" },\n }}\n >\n <Typography variant=\"h6\" sx={{ fontWeight: 600 }}>\n {brandingText}\n </Typography>\n </Button>\n <Box sx={{ flexGrow: 1 }} />\n {gatewayChip}\n <Stack direction=\"row\" spacing={1} flexWrap=\"wrap\" justifyContent=\"flex-end\">\n <Button component={RouterLink} to=\"/\" color=\"inherit\">\n Home\n </Button>\n <Button component={RouterLink} to=\"/management\" color=\"inherit\">\n Management\n </Button>\n <Button component={RouterLink} to=\"/chat\" variant=\"contained\" color=\"primary\">\n Go to chat\n </Button>\n </Stack>\n </Toolbar>\n </AppBar>\n );\n\n return (\n <Routes>\n <Route path=\"/management\" element={\n <ChatProvider packageSettings={packageSettings}>\n <Management />\n </ChatProvider>\n } />\n <Route path=\"/chat\" element={\n <ThemeProvider theme={banditQuickstartTheme}>\n <CssBaseline />\n <ChatProvider packageSettings={packageSettings}>\n <Box display=\"flex\" flexDirection=\"column\" minHeight=\"100vh\">\n <Box component=\"main\" sx={{ flexGrow: 1, display: \"flex\" }}>\n <ChatPage onOpenModal={handleOpenModal} />\n </Box>\n <ChatModal open={isModalOpen} onClose={handleCloseModal} />\n </Box>\n </ChatProvider>\n </ThemeProvider>\n } />\n <Route path=\"/*\" element={\n <ThemeProvider theme={banditQuickstartTheme}>\n <CssBaseline />\n <ChatProvider packageSettings={packageSettings}>\n <Box display=\"flex\" flexDirection=\"column\" minHeight=\"100vh\">\n <Header gatewayChip={gatewayChip} />\n <Box component=\"main\" sx={{ flexGrow: 1, display: \"flex\" }}>\n <Routes>\n <Route path=\"/\" element={<HomePage onOpenModal={handleOpenModal} />} />\n <Route path=\"*\" element={<Navigate to=\"/\" replace />} />\n </Routes>\n </Box>\n <ChatModal open={isModalOpen} onClose={handleCloseModal} />\n </Box>\n </ChatProvider>\n </ThemeProvider>\n } />\n </Routes>\n );\n}\n\nexport default App;\n`;\n\n const withResponse = template.replace(/__RESPONSE_STATUS__/g, responseStatusExpr);\n const withGatewayError = withResponse.replace(/__GATEWAY_ERROR__/g, gatewayErrorExpr);\n\n return ensureTrailingNewline(normalizeLineEndings(withGatewayError));\n};\n\nexport const buildBrandingConfig = (ctx: QuickstartTemplateContext): string =>\n formatJson({\n branding: {\n logoBase64: ctx.isDefaultLogo ? null : ctx.logoBase64,\n brandingText: ctx.brandingText,\n theme: \"bandit-dark\",\n hasTransparentLogo: ctx.isDefaultLogo ? true : ctx.hasTransparentLogo\n },\n knowledgeDocs: []\n });\n\nexport const buildGatewayServer = (ctx: QuickstartTemplateContext): string => {\n const modelsDefinition = JSON.stringify(ctx.gatewayModels, null, 2);\n\n const gatewaySource = `import express from \"express\";\nimport cors from \"cors\";\nimport dotenv from \"dotenv\";\n\ndotenv.config();\n\nconst app = express();\napp.use(cors());\napp.use(express.json({ limit: '50mb' }));\napp.use(express.urlencoded({ limit: '50mb', extended: true }));\n\nconst QUICKSTART_VERSION = \"0.1.0\";\nconst DEFAULT_PROVIDER = \"${ctx.defaultProvider}\";\nconst BASE_GATEWAY_MODELS = ${modelsDefinition};\nconst OLLAMA_BASE_URL = (process.env.OLLAMA_URL ?? \"http://localhost:11434\").replace(/\\\\/$/, \"\");\n\nconst toGatewayModels = () =>\n BASE_GATEWAY_MODELS.map((model) => ({\n ...model,\n created: Date.now(),\n modified_at: new Date().toISOString(),\n size: 0,\n digest: \"\",\n details: {\n format: \"chat\",\n family: model.provider,\n families: [model.provider],\n parameter_size: \"\",\n quantization_level: \"\",\n },\n }));\n\nconst requireOpenAIKey = () => {\n const key = process.env.OPENAI_API_KEY;\n if (!key) {\n throw new Error(\"Missing OPENAI_API_KEY. Add it to your .env file to route requests to OpenAI.\");\n }\n return key;\n};\n\n// Utility function to handle streaming responses\nconst handleStreamingResponse = async (upstreamResponse, res) => {\n res.setHeader('Content-Type', 'text/event-stream');\n res.setHeader('Cache-Control', 'no-cache');\n res.setHeader('Connection', 'keep-alive');\n res.setHeader('Access-Control-Allow-Origin', '*');\n \n try {\n // Get the readable stream from the response\n const reader = upstreamResponse.body.getReader();\n \n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n \n // Write the chunk to the response\n res.write(value);\n }\n \n res.end();\n } catch (error) {\n console.error('Streaming error:', error);\n // Fallback to non-streaming\n const text = await upstreamResponse.text();\n res.send(text);\n }\n};\n\n// ============================================================================\n// GENERAL HEALTH & MODELS\n// ============================================================================\n\napp.get(\"/api/health\", async (_req, res) => {\n const providers = [];\n \n // Check OpenAI\n try {\n const openaiKey = process.env.OPENAI_API_KEY;\n if (openaiKey) {\n const response = await fetch(\"https://api.openai.com/v1/models\", {\n headers: { \"Authorization\": \\`Bearer \\${openaiKey}\\` }\n });\n providers.push({\n name: \"openai\",\n status: response.ok ? \"healthy\" : \"unhealthy\",\n provider: \"openai\"\n });\n } else {\n providers.push({\n name: \"openai\", \n status: \"unconfigured\",\n provider: \"openai\",\n error: \"API key not configured\"\n });\n }\n } catch (error) {\n providers.push({\n name: \"openai\",\n status: \"unhealthy\", \n provider: \"openai\",\n error: error.message\n });\n }\n\n // Check Ollama\n try {\n console.log(\\`Checking Ollama health at: \\${OLLAMA_BASE_URL}/api/tags\\`);\n const response = await fetch(\\`\\${OLLAMA_BASE_URL}/api/tags\\`);\n const status = response.ok ? \"healthy\" : \"unhealthy\";\n console.log(\\`Ollama health check result: \\${status}\\`);\n providers.push({\n name: \"ollama\",\n status: status,\n provider: \"ollama\",\n url: OLLAMA_BASE_URL\n });\n } catch (error) {\n console.log(\\`Ollama health check error: \\${error.message}\\`);\n providers.push({\n name: \"ollama\",\n status: \"offline\",\n provider: \"ollama\",\n error: error.message,\n url: OLLAMA_BASE_URL\n });\n }\n\n const overallHealthy = providers.some(p => p.status === \"healthy\");\n \n res.json({\n status: overallHealthy ? \"healthy\" : \"unhealthy\",\n version: QUICKSTART_VERSION,\n uptime: Math.round(process.uptime()),\n providers\n });\n});\n\napp.get(\"/api/models\", (_req, res) => {\n res.json({ models: toGatewayModels() });\n});\n\n// ============================================================================\n// OPENAI ROUTES\n// ============================================================================\n\n// OpenAI Health Check\napp.get(\"/api/openai/health\", async (_req, res) => {\n try {\n const openaiKey = process.env.OPENAI_API_KEY;\n if (!openaiKey) {\n return res.status(503).json({\n status: \"unhealthy\",\n openai_status: false,\n error: \"OpenAI API key not configured\",\n provider: \"openai\"\n });\n }\n\n const response = await fetch(\"https://api.openai.com/v1/models\", {\n headers: { \"Authorization\": \\`Bearer \\${openaiKey}\\` }\n });\n\n const isHealthy = response.ok;\n res.json({\n status: isHealthy ? \"healthy\" : \"unhealthy\",\n openai_status: isHealthy,\n provider: \"openai\"\n });\n } catch (error) {\n res.status(503).json({\n status: \"unhealthy\",\n openai_status: false,\n error: error.message,\n provider: \"openai\"\n });\n }\n});\n\n// OpenAI Chat Completions\napp.post(\"/api/openai/chat/completions\", async (req, res) => {\n try {\n const openaiKey = requireOpenAIKey();\n const isStreaming = req.body?.stream === true;\n\n // Strip the openai: prefix from model name and remove provider field\n const { provider, ...cleanBody } = req.body;\n const requestBody = {\n ...cleanBody,\n model: req.body?.model?.replace(/^openai:/, \"\") || \"gpt-4o\"\n };\n\n const response = await fetch(\"https://api.openai.com/v1/chat/completions\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Authorization\": \\`Bearer \\${openaiKey}\\`\n },\n body: JSON.stringify(requestBody)\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n return res.status(response.status).json({\n error: \\`OpenAI chat failed: \\${response.status}\\`,\n details: errorText\n });\n }\n\n if (isStreaming) {\n await handleStreamingResponse(response, res);\n } else {\n const text = await response.text();\n res.setHeader('Content-Type', 'application/json');\n res.send(text);\n }\n } catch (error) {\n res.status(500).json({ error: error.message });\n }\n});\n\n// OpenAI Chat (alternative route)\napp.post(\"/api/openai/chat\", async (req, res) => {\n // Route to the completions endpoint for compatibility\n req.url = \"/api/openai/chat/completions\";\n return app._router.handle(req, res);\n});\n\n// OpenAI Completions\napp.post(\"/api/openai/completions\", async (req, res) => {\n try {\n const openaiKey = requireOpenAIKey();\n const isStreaming = req.body?.stream === true;\n\n // Strip the openai: prefix from model name and remove provider field\n const { provider, ...cleanBody } = req.body;\n const requestBody = {\n ...cleanBody,\n model: req.body?.model?.replace(/^openai:/, \"\") || \"gpt-3.5-turbo-instruct\"\n };\n\n const response = await fetch(\"https://api.openai.com/v1/completions\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Authorization\": \\`Bearer \\${openaiKey}\\`\n },\n body: JSON.stringify(requestBody)\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n return res.status(response.status).json({\n error: \\`OpenAI completions failed: \\${response.status}\\`,\n details: errorText\n });\n }\n\n if (isStreaming) {\n await handleStreamingResponse(response, res);\n } else {\n const text = await response.text();\n res.setHeader('Content-Type', 'application/json');\n res.send(text);\n }\n } catch (error) {\n res.status(500).json({ error: error.message });\n }\n});\n\n// OpenAI Generate (converts to chat format for conversation starters)\napp.post(\"/api/openai/generate\", async (req, res) => {\n try {\n const openaiKey = requireOpenAIKey();\n const prompt = req.body?.prompt || \"\";\n const model = req.body?.model?.replace(/^openai:/, \"\") || \"gpt-4o\";\n const isStreaming = req.body?.stream === true;\n\n // Convert generate request to chat format\n const chatBody = {\n model: model,\n messages: [\n {\n role: \"user\",\n content: prompt\n }\n ],\n stream: isStreaming,\n max_tokens: req.body?.max_tokens || 150,\n temperature: req.body?.temperature || 0.7\n };\n\n const response = await fetch(\"https://api.openai.com/v1/chat/completions\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Authorization\": \\`Bearer \\${openaiKey}\\`\n },\n body: JSON.stringify(chatBody)\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n return res.status(response.status).json({\n error: \\`OpenAI generate failed: \\${response.status}\\`,\n details: errorText\n });\n }\n\n if (isStreaming) {\n await handleStreamingResponse(response, res);\n } else {\n const data = await response.json();\n // Convert chat response back to generate format\n const generateResponse = {\n model: model,\n created_at: new Date().toISOString(),\n response: data.choices?.[0]?.message?.content || \"\",\n done: true,\n context: [],\n total_duration: 0,\n load_duration: 0,\n prompt_eval_count: 0,\n prompt_eval_duration: 0,\n eval_count: data.usage?.completion_tokens || 0,\n eval_duration: 0\n };\n res.json(generateResponse);\n }\n } catch (error) {\n res.status(500).json({ error: error.message });\n }\n});\n\n// OpenAI Models\napp.get(\"/api/openai/models\", async (_req, res) => {\n try {\n const openaiKey = requireOpenAIKey();\n\n const response = await fetch(\"https://api.openai.com/v1/models\", {\n headers: { \"Authorization\": \\`Bearer \\${openaiKey}\\` }\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n return res.status(response.status).json({\n error: \\`OpenAI models failed: \\${response.status}\\`,\n details: errorText\n });\n }\n\n const text = await response.text();\n res.setHeader('Content-Type', 'application/json');\n res.send(text);\n } catch (error) {\n res.status(500).json({ error: error.message });\n }\n});\n\n// ============================================================================\n// OLLAMA ROUTES\n// ============================================================================\n\n// Ollama Health Check\napp.get(\"/api/ollama/health\", async (_req, res) => {\n try {\n console.log(\\`Ollama health check at: \\${OLLAMA_BASE_URL}/api/tags\\`);\n const response = await fetch(\\`\\${OLLAMA_BASE_URL}/api/tags\\`);\n const isHealthy = response.ok;\n\n res.json({\n status: isHealthy ? \"healthy\" : \"unhealthy\",\n ollama_status: isHealthy,\n provider: \"ollama\",\n url: OLLAMA_BASE_URL\n });\n } catch (error) {\n console.log(\\`Ollama health check error: \\${error.message}\\`);\n res.status(503).json({\n status: \"offline\",\n ollama_status: false,\n error: error.message,\n provider: \"ollama\",\n url: OLLAMA_BASE_URL\n });\n }\n});\n\n// Ollama Chat\napp.post(\"/api/ollama/chat\", async (req, res) => {\n try {\n const response = await fetch(\\`\\${OLLAMA_BASE_URL}/api/chat\\`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(req.body)\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n return res.status(response.status).json({\n error: \\`Ollama chat failed: \\${response.status}\\`,\n details: errorText\n });\n }\n\n const isStreaming = req.body?.stream === true;\n if (isStreaming) {\n await handleStreamingResponse(response, res);\n } else {\n const text = await response.text();\n res.setHeader('Content-Type', 'application/json');\n res.send(text);\n }\n } catch (error) {\n res.status(500).json({ error: error.message });\n }\n});\n\n// Ollama Generate\napp.post(\"/api/ollama/generate\", async (req, res) => {\n try {\n const response = await fetch(\\`\\${OLLAMA_BASE_URL}/api/generate\\`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(req.body)\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n return res.status(response.status).json({\n error: \\`Ollama generate failed: \\${response.status}\\`,\n details: errorText\n });\n }\n\n const isStreaming = req.body?.stream === true;\n if (isStreaming) {\n await handleStreamingResponse(response, res);\n } else {\n const text = await response.text();\n res.setHeader('Content-Type', 'application/json');\n res.send(text);\n }\n } catch (error) {\n res.status(500).json({ error: error.message });\n }\n});\n\n// Ollama Models\napp.get(\"/api/ollama/models\", async (_req, res) => {\n try {\n const response = await fetch(\\`\\${OLLAMA_BASE_URL}/api/tags\\`);\n\n if (!response.ok) {\n const errorText = await response.text();\n return res.status(response.status).json({\n error: \\`Ollama models failed: \\${response.status}\\`,\n details: errorText\n });\n }\n\n const text = await response.text();\n res.setHeader('Content-Type', 'application/json');\n res.send(text);\n } catch (error) {\n res.status(500).json({ error: error.message });\n }\n});\n\n// Ollama Embedding\napp.post(\"/api/ollama/embed\", async (req, res) => {\n try {\n const response = await fetch(\\`\\${OLLAMA_BASE_URL}/api/embeddings\\`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(req.body)\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n return res.status(response.status).json({\n error: \\`Ollama embed failed: \\${response.status}\\`,\n details: errorText\n });\n }\n\n const text = await response.text();\n res.setHeader('Content-Type', 'application/json');\n res.send(text);\n } catch (error) {\n res.status(500).json({ error: error.message });\n }\n});\n\n// ============================================================================\n// TTS ROUTES (not implemented - placeholder for compatibility)\n// ============================================================================\n\n// TTS Main endpoint\napp.post(\"/api/tts\", async (_req, res) => {\n res.status(501).json({\n error: \"TTS integration not implemented\",\n message: \"Text-to-speech functionality is not available in this quickstart gateway\"\n });\n});\n\n// TTS Stream endpoint\napp.post(\"/api/tts/stream\", async (_req, res) => {\n res.status(501).json({\n error: \"TTS streaming not implemented\", \n message: \"Text-to-speech streaming functionality is not available in this quickstart gateway\"\n });\n});\n\n// TTS Real-time stream endpoint\napp.post(\"/api/tts/stream-realtime\", async (_req, res) => {\n res.status(501).json({\n error: \"TTS real-time streaming not implemented\",\n message: \"Text-to-speech real-time streaming functionality is not available in this quickstart gateway\"\n });\n});\n\n// TTS Models endpoint - returns empty models\napp.get(\"/api/tts/models\", async (_req, res) => {\n res.json({\n models: []\n });\n});\n\n// TTS Available models endpoint - returns empty models with defaults\napp.get(\"/api/tts/available-models\", async (_req, res) => {\n res.json({\n models: [],\n defaultModel: null,\n fallbackModel: null\n });\n});\n\n// ============================================================================\n// MCP (Model Context Protocol) TOOL ROUTES\n// ============================================================================\n\n// MCP Health Check\napp.get(\"/api/mcp/health\", async (_req, res) => {\n res.json({\n status: \"healthy\",\n timestamp: new Date().toISOString(),\n totalTools: 0,\n enabledTools: 0,\n availableTools: [],\n message: \"MCP tools are not implemented in this quickstart gateway\"\n });\n});\n\n// Get available MCP tools\napp.get(\"/api/mcp/tools\", async (_req, res) => {\n res.json([]);\n});\n\n// News endpoint - placeholder implementation\napp.get(\"/api/mcp/news\", async (req, res) => {\n const { topic = \"general\", count = 10, headlines = false } = req.query;\n \n res.status(501).json({\n error: \"News service not implemented\",\n message: \"MCP news functionality is not available in this quickstart gateway\",\n suggestion: \"Implement this endpoint to connect to a news API service\",\n requestedParams: { topic, count, headlines }\n });\n});\n\n// Weather endpoint - placeholder implementation \napp.get(\"/api/mcp/weather\", async (req, res) => {\n const { zip, latitude, longitude } = req.query;\n \n res.status(501).json({\n error: \"Weather service not implemented\", \n message: \"MCP weather functionality is not available in this quickstart gateway\",\n suggestion: \"Implement this endpoint to connect to a weather API service\",\n requestedParams: { zip, latitude, longitude }\n });\n});\n\n// Documentation search endpoint - placeholder implementation\napp.get(\"/api/mcp/docs\", async (req, res) => {\n const { query, framework, count = 10 } = req.query;\n \n if (!query) {\n return res.status(400).json({\n error: \"Query parameter is required\",\n message: \"Please provide a search query\"\n });\n }\n \n res.status(501).json({\n error: \"Documentation search not implemented\",\n message: \"MCP docs functionality is not available in this quickstart gateway\", \n suggestion: \"Implement this endpoint to connect to a documentation search service\",\n requestedParams: { query, framework, count }\n });\n});\n\n// Get supported documentation frameworks\napp.get(\"/api/mcp/docs/frameworks\", async (_req, res) => {\n res.json({\n frameworks: [],\n message: \"Documentation frameworks not configured in this quickstart gateway\"\n });\n});\n\n// Sports scores endpoint - placeholder implementation\napp.get(\"/api/mcp/sports\", async (req, res) => {\n const { league, date } = req.query;\n \n res.status(501).json({\n error: \"Sports service not implemented\",\n message: \"MCP sports functionality is not available in this quickstart gateway\",\n suggestion: \"Implement this endpoint to connect to a sports API service\", \n requestedParams: { league, date }\n });\n});\n\n// Get supported sports leagues\napp.get(\"/api/mcp/sports/leagues\", async (_req, res) => {\n res.json({\n leagues: [],\n message: \"Sports leagues not configured in this quickstart gateway\"\n });\n});\n\n// Image generation endpoint - placeholder implementation\napp.post(\"/api/mcp/generate-image\", async (req, res) => {\n const { prompt, size, quality, style } = req.body;\n \n if (!prompt) {\n return res.status(400).json({\n error: \"Prompt is required\",\n message: \"Please provide a prompt for image generation\"\n });\n }\n \n res.status(501).json({\n success: false,\n error: \"Image generation not implemented\",\n message: \"MCP image generation functionality is not available in this quickstart gateway\",\n suggestion: \"Implement this endpoint to connect to an image generation API service\",\n requestedParams: { prompt, size, quality, style }\n });\n});\n\n// ============================================================================\n// NOT IMPLEMENTED ROUTES (for graceful degradation)\n// ============================================================================\n\napp.all(\"/api/anthropic/*\", (_req, res) => {\n res.status(501).json({\n error: \"Anthropic integration not implemented\",\n message: \"This quickstart gateway only supports OpenAI and Ollama providers\"\n });\n});\n\napp.all(\"/api/azure/*\", (_req, res) => {\n res.status(501).json({\n error: \"Azure OpenAI integration not implemented\", \n message: \"This quickstart gateway only supports OpenAI and Ollama providers\"\n });\n});\n\nconst port = Number(process.env.PORT ?? ${ctx.gatewayPort});\napp.listen(port, () => {\n console.log(\"⚑ Bandit quickstart gateway ready on http://localhost:\" + port);\n console.log(\"πŸ“‘ Supported providers: OpenAI, Ollama\");\n console.log(\"πŸ”— Provider-specific routes:\");\n console.log(\" β€’ /api/openai/* - OpenAI endpoints\");\n console.log(\" β€’ /api/ollama/* - Ollama endpoints\");\n console.log(\" β€’ /api/health - Overall health check\");\n});\n`;\n\n return ensureTrailingNewline(normalizeLineEndings(gatewaySource));\n};\n\nexport const buildGitignore = (): string =>\n ensureTrailingNewline(\n normalizeLineEndings(\n `node_modules\\n.env\\n.vite\\n.idea\\n.DS_Store\\ncoverage\\ndist\\n`\n )\n );\n\nexport const buildNpmrc = (): string => {\n const tokenExpr = \"${GITHUB_NPM_TOKEN}\";\n const template = `@burtson-labs:registry=https://npm.pkg.github.com/\n//npm.pkg.github.com/:_authToken=__NPM_TOKEN__\n`;\n\n return ensureTrailingNewline(\n normalizeLineEndings(template.replace(/__NPM_TOKEN__/g, tokenExpr))\n );\n};\n\nexport const buildReadme = (ctx: QuickstartTemplateContext): string =>\n ensureTrailingNewline(\n normalizeLineEndings(\n `# ${ctx.projectTitle} β€” Bandit Quickstart\\n\\nThis project was generated by the Bandit Engine CLI. It ships with a React + Vite frontend that consumes \\`@burtson-labs/bandit-engine\\` and a lightweight Express gateway you can adapt for production.\\n\\n## πŸš€ Next steps\\n- \\`npm install\\`\\n- \\`cp .env.example .env\\`\\n- Fill in \\`OPENAI_API_KEY\\` (or point \\`OLLAMA_URL\\` at your local server)\\n- \\`npm run dev\\`\\n\\nThe command runs the gateway and the frontend together. Visit http://localhost:${ctx.frontendPort} to see the chat and modal in action.\\n\\n## πŸ”§ Customizing your assistant\\n- **Branding & personas**: edit \\`public/config.json\\` to tweak logos, colors, and starter models.\\n- **Provider defaults**: update \\`.env\\` to switch providers or change the default upstream model IDs.\\n- **Gateway routes**: open \\`server/gateway.js\\` to add auth, logging, or connect additional providers.\\n\\n## πŸ“¦ What’s inside\\n- React + Vite 5 with Material UI theming\\n- Bandit chat surface + modal wired via \\`ChatProvider\\`\\n- Express gateway proxying OpenAI or Ollama to keep API keys server-side\\n- Friendly defaults you can evolve into your production stack\\n\\nNeed more? Run \\`npx @burtson-labs/bandit-engine create --help\\` to explore additional options.\\n`\n )\n );\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAmBA,IAAAA,oBAAiB;AACjB,uBAAwB;;;ACpBxB;AAAA,EACE,MAAQ;AAAA,EACR,SAAW;AAAA,EACX,SAAW;AAAA,EACX,MAAQ;AAAA,EACR,QAAU;AAAA,EACV,OAAS;AAAA,EACT,KAAO;AAAA,IACL,QAAU;AAAA,EACZ;AAAA,EACA,OAAS;AAAA,IACP;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,YAAc;AAAA,IACZ,MAAQ;AAAA,IACR,KAAO;AAAA,IACP,WAAa;AAAA,EACf;AAAA,EACA,UAAY;AAAA,EACZ,QAAU;AAAA,EACV,aAAe;AAAA,IACb;AAAA,MACE,MAAQ;AAAA,MACR,OAAS;AAAA,IACX;AAAA,EACF;AAAA,EACA,UAAY;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,SAAW;AAAA,IACT,OAAS;AAAA,IACT,KAAO;AAAA,IACP,MAAQ;AAAA,IACR,MAAQ;AAAA,IACR,MAAQ;AAAA,IACR,SAAW;AAAA,IACX,uBAAuB;AAAA,EACzB;AAAA,EACA,WAAa;AAAA,IACX,UAAU;AAAA,EACZ;AAAA,EACA,cAAgB;AAAA,IACd,kBAAkB;AAAA,IAClB,mBAAmB;AAAA,IACnB,uBAAuB;AAAA,IACvB,iBAAiB;AAAA,IACjB,yBAAyB;AAAA,IACzB,OAAS;AAAA,IACT,WAAa;AAAA,IACb,YAAY;AAAA,IACZ,gBAAgB;AAAA,IAChB,KAAO;AAAA,IACP,UAAY;AAAA,IACZ,SAAW;AAAA,IACX,cAAc;AAAA,IACd,SAAW;AAAA,IACX,OAAS;AAAA,IACT,aAAa;AAAA,IACb,kBAAkB;AAAA,IAClB,oBAAoB;AAAA,IACpB,cAAc;AAAA,IACd,mBAAmB;AAAA,IACnB,cAAc;AAAA,IACd,MAAQ;AAAA,IACR,MAAQ;AAAA,IACR,SAAW;AAAA,EACb;AAAA,EACA,iBAAmB;AAAA,IACjB,6BAA6B;AAAA,IAC7B,0BAA0B;AAAA,IAC1B,mBAAmB;AAAA,IACnB,eAAe;AAAA,IACf,kBAAkB;AAAA,IAClB,gBAAgB;AAAA,IAChB,oBAAoB;AAAA,IACpB,eAAe;AAAA,IACf,wBAAwB;AAAA,IACxB,QAAU;AAAA,IACV,uBAAuB;AAAA,IACvB,OAAS;AAAA,IACT,MAAQ;AAAA,IACR,SAAW;AAAA,IACX,YAAc;AAAA,IACd,QAAU;AAAA,EACZ;AAAA,EACA,kBAAoB;AAAA,IAClB,iBAAiB;AAAA,IACjB,OAAS;AAAA,IACT,SAAW;AAAA,EACb;AAAA,EACA,SAAW;AAAA,IACT,KAAK;AAAA,MACH,QAAU;AAAA,MACV,SAAW;AAAA,IACb;AAAA,IACA,WAAW;AAAA,MACT,OAAS;AAAA,MACT,SAAW;AAAA,IACb;AAAA,EACF;AACF;;;AC/FA,IAAAC,oBAAiB;AACjB,sBAAe;AACf,qBAAsC;;;ACFtC,uBAAiB;AAEV,IAAM,cAAc,CAAC,UAA0B;AACpD,SAAO,MACJ,QAAQ,sBAAsB,OAAO,EACrC,QAAQ,aAAa,GAAG,EACxB,QAAQ,mBAAmB,EAAE,EAC7B,QAAQ,UAAU,GAAG,EACrB,QAAQ,YAAY,EAAE,EACtB,YAAY;AACjB;AAEO,IAAM,cAAc,CAAC,UAA0B;AACpD,QAAM,UAAU,MACb,QAAQ,WAAW,GAAG,EACtB,QAAQ,QAAQ,GAAG,EACnB,KAAK;AAER,MAAI,CAAC,SAAS;AACZ,WAAO;AAAA,EACT;AAEA,SAAO,QAAQ,QAAQ,SAAS,CAAC,SAAS,KAAK,YAAY,CAAC;AAC9D;AAEO,IAAM,aAAa,CAAC,UACzB,GAAG,KAAK,UAAU,OAAO,MAAM,CAAC,CAAC;AAAA;AA2B5B,IAAM,0BAA0B,CAAC,UAA0B;AAChE,QAAM,UAAU,MAAM,KAAK;AAC3B,MAAI,CAAC,QAAQ,SAAS,GAAG,GAAG;AAC1B,WAAO,QAAQ,YAAY;AAAA,EAC7B;AACA,QAAM,CAAC,UAAU,KAAK,IAAI,QAAQ,MAAM,OAAO,EAAE,OAAO,OAAO;AAC/D,QAAM,aAAa,MAChB,QAAQ,oBAAoB,GAAG,EAC/B,QAAQ,OAAO,GAAG,EAClB,YAAY;AACf,SAAO,GAAG,SAAS,YAAY,CAAC,IAAI,UAAU;AAChD;AAEO,IAAM,uBAAuB,CAAC,YACnC,QAAQ,QAAQ,SAAS,IAAI;AAExB,IAAM,wBAAwB,CAAC,YACpC,QAAQ,SAAS,IAAI,IAAI,UAAU,GAAG,OAAO;AAAA;;;AC7C/C,IAAM,QAAQ;AAEP,IAAM,mBAAmB,CAAC,QAC/B,WAAW;AAAA,EACT,MAAM,IAAI;AAAA,EACV,SAAS;AAAA,EACT,SAAS;AAAA,EACT,MAAM;AAAA,EACN,SAAS;AAAA,IACP,KAAK;AAAA,IACL,WAAW;AAAA,IACX,eAAe;AAAA,IACf,OAAO;AAAA,IACP,SAAS;AAAA,EACX;AAAA,EACA,cAAc;AAAA,IACZ,+BAA+B,IAAI,IAAI,aAAa;AAAA,IACpD,kBAAkB;AAAA,IAClB,mBAAmB;AAAA,IACnB,iBAAiB;AAAA,IACjB,yBAAyB;AAAA,IACzB,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,WAAW;AAAA,IACX,SAAS;AAAA,IACT,aAAa;AAAA,IACb,oBAAoB;AAAA,IACpB,WAAW;AAAA,EACb;AAAA,EACA,iBAAiB;AAAA,IACf,kBAAkB;AAAA,IAClB,eAAe;AAAA,IACf,gBAAgB;AAAA,IAChB,oBAAoB;AAAA,IACpB,wBAAwB;AAAA,IACxB,gBAAgB;AAAA,IAChB,cAAc;AAAA,IACd,QAAQ;AAAA,EACV;AACF,CAAC;AAEI,IAAM,kBAAkB,CAAC,QAC9B;AAAA,EACE;AAAA,IACE;AAAA,gBAA2C,IAAI,YAAY;AAAA,mBAAsB,IAAI,iBAAiB;AAAA,qBAAwB,IAAI,cAAc;AAAA,sBAAyB,IAAI,mBAAmB,EAAE;AAAA,wBAA2B,IAAI,eAAe;AAAA,qBAAwB,IAAI,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA,SAAkI,IAAI,WAAW;AAAA;AAAA,EAC3a;AACF;AAEK,IAAM,gBAAgB,MAC3B,WAAW;AAAA,EACT,iBAAiB;AAAA,IACf,QAAQ;AAAA,IACR,yBAAyB;AAAA,IACzB,KAAK,CAAC,OAAO,gBAAgB,QAAQ;AAAA,IACrC,SAAS;AAAA,IACT,cAAc;AAAA,IACd,iBAAiB;AAAA,IACjB,8BAA8B;AAAA,IAC9B,QAAQ;AAAA,IACR,kCAAkC;AAAA,IAClC,QAAQ;AAAA,IACR,kBAAkB;AAAA,IAClB,mBAAmB;AAAA,IACnB,iBAAiB;AAAA,IACjB,QAAQ;AAAA,IACR,KAAK;AAAA,EACP;AAAA,EACA,SAAS,CAAC,KAAK;AACjB,CAAC;AAEI,IAAM,cAAc,MACzB;AAAA,EACE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACF;AAEK,IAAM,kBAAkB,CAAC,QAC9B;AAAA,EACE;AAAA,IACE;AAAA;AAAA;AAAA;AAAA;AAAA,+DAA0P,IAAI,YAAY;AAAA,4DAAiE,IAAI,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAE7V;AACF;AAEK,IAAM,eAAe,MAC1B;AAAA,EACE;AAAA,IACE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EACF;AACF;AAEK,IAAM,gBAAgB,MAC3B;AAAA,EACE;AAAA,IACE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EACF;AACF;AAEK,IAAM,iBAAiB,MAC5B;AAAA,EACE;AAAA,IACE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EACF;AACF;AAEK,IAAM,eAAe,MAC1B;AAAA,EACE;AAAA,IACE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EACF;AACF;AAEK,IAAM,cAAc,CAAC,QAA2C;AACrE,QAAM,qBAAqB;AAC3B,QAAM,mBAAmB;AAEzB,QAAM,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,+DAsB4C,IAAI,iBAAiB;AAAA,gEACpB,IAAI,cAAc;AAAA,iEACjB,IAAI,kBAAkB,GAAG,KAAK,GAAG,IAAI,eAAe,GAAG,KAAK,KAAK,WAAW;AAAA,8DAC/E,IAAI,YAAY;AAAA,8DAChB,IAAI,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,8BAkFnD,IAAI,0BAA0B,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAyS9D,QAAM,eAAe,SAAS,QAAQ,wBAAwB,kBAAkB;AAChF,QAAM,mBAAmB,aAAa,QAAQ,sBAAsB,gBAAgB;AAEpF,SAAO,sBAAsB,qBAAqB,gBAAgB,CAAC;AACrE;AAEO,IAAM,sBAAsB,CAAC,QAClC,WAAW;AAAA,EACT,UAAU;AAAA,IACR,YAAY,IAAI,gBAAgB,OAAO,IAAI;AAAA,IAC3C,cAAc,IAAI;AAAA,IAClB,OAAO;AAAA,IACP,oBAAoB,IAAI,gBAAgB,OAAO,IAAI;AAAA,EACrD;AAAA,EACA,eAAe,CAAC;AAClB,CAAC;AAEI,IAAM,qBAAqB,CAAC,QAA2C;AAC5E,QAAM,mBAAmB,KAAK,UAAU,IAAI,eAAe,MAAM,CAAC;AAElE,QAAM,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,4BAYI,IAAI,eAAe;AAAA,8BACjB,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,0CA8oBJ,IAAI,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAWvD,SAAO,sBAAsB,qBAAqB,aAAa,CAAC;AAClE;AAEO,IAAM,iBAAiB,MAC5B;AAAA,EACE;AAAA,IACE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EACF;AACF;AAEK,IAAM,aAAa,MAAc;AACtC,QAAM,YAAY;AAClB,QAAM,WAAW;AAAA;AAAA;AAIjB,SAAO;AAAA,IACL,qBAAqB,SAAS,QAAQ,kBAAkB,SAAS,CAAC;AAAA,EACpE;AACF;AAEO,IAAM,cAAc,CAAC,QAC1B;AAAA,EACE;AAAA,IACE,KAAK,IAAI,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,iFAAyd,IAAI,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAChgB;AACF;;;AFzqCK,IAAM,0BAA0B,OACrC,YAC8B;AAC9B,QAAM,cAAc,kBAAAC,QAAK,QAAQ,QAAQ,IAAI,GAAG,QAAQ,SAAS;AACjE,QAAM,iBAAiB,QAAQ,eAAe,kBAAAA,QAAK,SAAS,WAAW;AACvE,QAAM,cAAc,qBAAqB,cAAc;AACvD,QAAM,eAAe,YAAY,cAAc,KAAK;AAEpD,QAAM,wBAAwB,aAAa,QAAQ,QAAQ,KAAK,CAAC;AAEjE,QAAM,WAAW,kBAAkB,QAAQ,QAAQ;AACnD,QAAM,gBAAgB,QAAQ,cAC1B,CAAC,IACD,MAAM,qBAAqB;AAAA,IACzB,cAAc,QAAQ;AAAA,IACtB;AAAA,EACF,CAAC;AAEL,QAAM,eACJ,QAAQ,iBACP,OAAO,cAAc,iBAAiB,YAAY,cAAc,aAAa,KAAK,EAAE,SAAS,IAC1F,cAAc,aAAa,KAAK,IAChC,GAAG,YAAY;AAGrB,QAAM,iBAAiC;AAAA,IACrC,SAAS;AAAA,IACT,UAAU;AAAA,IACV,aAAa,OAAO,MAAM,CAAC;AAAA,IAC3B,oBAAoB;AAAA,IACpB,WAAW;AAAA,EACb;AAEA,QAAM,cAAc,aAAa,QAAQ,eAAe,cAAc,eAAe,IAAI;AACzF,QAAM,eAAe,aAAa,QAAQ,gBAAgB,cAAc,gBAAgB,IAAI;AAC5F,QAAM,iBAAiB;AAAA,IACrB,QAAQ,kBAAkB,oBAAoB,QAAQ;AAAA,EACxD;AACA,QAAM,kBAAkB,QAAQ,kBAC5B,wBAAwB,QAAQ,eAAe,IAC/C,qBAAqB,UAAU,cAAc;AAEjD,QAAM,SAAiC;AAAA,IACrC,WAAW;AAAA,IACX;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,MAAM;AAAA,EACR;AAEA,QAAM,eAAe,MAAM,aAAa,MAAM;AAE9C,SAAO;AAAA,IACL,YAAY;AAAA,IACZ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAEA,IAAM,uBAAuB,CAAC,UAA0B;AACtD,QAAM,WAAW;AACjB,QAAM,QAAQ,YAAY,SAAS,QAAQ;AAC3C,MAAI,CAAC,OAAO;AACV,WAAO;AAAA,EACT;AACA,SAAO,SAAS,KAAK,KAAK,IAAI,QAAQ,UAAU,KAAK;AACvD;AAEA,IAAM,0BAA0B,OAAO,KAAa,UAAmB;AACrE,QAAM,SAAS,MAAM,gBAAAC,QAAG,WAAW,GAAG;AACtC,MAAI,CAAC,QAAQ;AACX,UAAM,gBAAAA,QAAG,UAAU,GAAG;AACtB;AAAA,EACF;AAEA,QAAM,UAAU,MAAM,gBAAAA,QAAG,QAAQ,GAAG;AACpC,MAAI,QAAQ,SAAS,KAAK,CAAC,OAAO;AAChC,UAAM,IAAI;AAAA,MACR,qBAAqB,GAAG;AAAA,IAC1B;AAAA,EACF;AACF;AAEA,IAAM,oBAAoB,CAAC,UAAsC;AAC/D,QAAM,cAAc,SAAS,UAAU,YAAY;AACnD,SAAO,eAAe,WAAW,WAAW;AAC9C;AAEA,IAAM,sBAAsB,CAAC,aAAwC;AACnE,SAAO,aAAa,WAAW,oBAAoB;AACrD;AAEA,IAAM,uBAAuB,CAAC,UAA6B,cAA0C;AACnG,MAAI,aAAa,UAAU;AACzB,WAAO,cAAc,kBAAkB,kBAAkB;AAAA,EAC3D;AACA,SAAO,cAAc,wBAAwB,uBAAuB;AACtE;AAEA,IAAM,eAAe,CAAC,UAA0B;AAC9C,QAAM,OAAO,OAAO,KAAK;AACzB,MAAI,OAAO,MAAM,IAAI,KAAK,QAAQ,KAAK,QAAQ,OAAO;AACpD,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEA,IAAM,uBAAuB,OAAO,YAO9B;AACJ,QAAM,YAA4B,CAAC;AAEnC,MAAI,CAAC,QAAQ,cAAc;AACzB,cAAU,KAAK;AAAA,MACb,MAAM;AAAA,MACN,MAAM;AAAA,MACN,SAAS;AAAA,MACT,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AAEA,QAAM,qBAAqB,QAAQ,aAAa,WAAW,QAAQ;AACnE,QAAM,sBAAsB;AAE5B,YAAU,KAAK;AAAA,IACb,MAAM;AAAA,IACN,MAAM;AAAA,IACN,SAAS;AAAA,IACT,SAAS,OAAO,mBAAmB;AAAA,IACnC,UAAU,CAAC,UAAmB;AAC5B,UAAI,OAAO,UAAU,YAAY,MAAM,KAAK,EAAE,WAAW,GAAG;AAC1D,eAAO;AAAA,MACT;AACA,YAAM,eAAe,OAAO,KAAK;AACjC,aAAO,OAAO,SAAS,YAAY,KAAK,eAAe,KAAK,eAAe,QACvE,OACA;AAAA,IACN;AAAA,EACF,CAAC;AAED,YAAU,KAAK;AAAA,IACb,MAAM;AAAA,IACN,MAAM;AAAA,IACN,SAAS;AAAA,IACT,SAAS,OAAO,kBAAkB;AAAA,IAClC,UAAU,CAAC,UAAmB;AAC5B,UAAI,OAAO,UAAU,YAAY,MAAM,KAAK,EAAE,WAAW,GAAG;AAC1D,eAAO;AAAA,MACT;AACA,YAAM,eAAe,OAAO,KAAK;AACjC,aAAO,OAAO,SAAS,YAAY,KAAK,eAAe,KAAK,eAAe,QACvE,OACA;AAAA,IACN;AAAA,EACF,CAAC;AAED,QAAM,WAAW,MAAM;AACrB,UAAM,IAAI,MAAM,oBAAoB;AAAA,EACtC;AAEA,QAAM,UAAU,UAAM,eAAAC,SAAQ,WAAW,EAAE,SAAS,CAAC;AAErD,QAAM,oBACJ,OAAO,QAAQ,gBAAgB,WAC3B,QAAQ,cACR,OAAO,QAAQ,gBAAgB,YAAY,QAAQ,YAAY,KAAK,EAAE,SAAS,IAC7E,OAAO,QAAQ,WAAW,IAC1B;AAER,QAAM,qBACJ,OAAO,QAAQ,iBAAiB,WAC5B,QAAQ,eACR,OAAO,QAAQ,iBAAiB,YAAY,QAAQ,aAAa,KAAK,EAAE,SAAS,IAC/E,OAAO,QAAQ,YAAY,IAC3B;AAER,SAAO;AAAA,IACL,cAAc,OAAO,QAAQ,iBAAiB,WAAW,QAAQ,eAAe;AAAA,IAChF,aAAa,OAAO,SAAS,iBAAiB,IAAI,oBAAoB;AAAA,IACtE,cAAc,OAAO,SAAS,kBAAkB,IAAI,qBAAqB;AAAA,EAC3E;AACF;AAEA,IAAM,eAAe,OAAO,WAAsD;AAChF,QAAM,EAAE,UAAU,IAAI;AACtB,QAAM,eAAyB,CAAC;AAEhC,QAAM,UAAqC;AAAA,IACzC,aAAa,OAAO;AAAA,IACpB,cAAc,OAAO;AAAA,IACrB,eAAe,gBAAY;AAAA,IAC3B,cAAc,OAAO;AAAA,IACvB,YAAY,OAAO,KAAK;AAAA,IACxB,oBAAoB,OAAO,KAAK;AAAA,IAChC,eAAe,OAAO,KAAK;AAAA,IACzB,aAAa,OAAO;AAAA,IACpB,cAAc,OAAO;AAAA,IACrB,iBAAiB,OAAO;AAAA,IACxB,mBAAmB,oBAAoB,OAAO,WAAW;AAAA,IACzD,gBAAgB,OAAO;AAAA,IACvB,iBAAiB,OAAO;AAAA,IACxB,eAAe,mBAAmB,MAAM;AAAA,EAC1C;AAEA,QAAM,QAAyC;AAAA,IAC7C,gBAAgB,iBAAiB,OAAO;AAAA,IACxC,iBAAiB,cAAc;AAAA,IAC/B,gBAAgB,YAAY;AAAA,IAC5B,kBAAkB,gBAAgB,OAAO;AAAA,IACzC,gBAAgB,aAAa;AAAA,IAC7B,cAAc,eAAe;AAAA,IAC7B,eAAe,YAAY,OAAO;AAAA,IAClC,iBAAiB,cAAc;AAAA,IAC/B,gBAAgB,aAAa;AAAA,IAC7B,sBAAsB,oBAAoB,OAAO;AAAA,IACjD,qBAAqB,mBAAmB,OAAO;AAAA,IAC/C,gBAAgB,gBAAgB,OAAO;AAAA,IACvC,cAAc,eAAe;AAAA,IAC7B,UAAU,WAAW;AAAA,IACrB,aAAa,YAAY,OAAO;AAAA,EAClC;AAGA,MAAI,CAAC,OAAO,KAAK,aAAa,OAAO,KAAK,UAAU;AAClD,UAAM,kBAAAF,QAAK,MAAM,KAAK,UAAU,OAAO,KAAK,QAAQ,CAAC,IAAI,OAAO,KAAK;AAAA,EACvE;AAEA,aAAW,CAAC,cAAc,OAAO,KAAK,OAAO,QAAQ,KAAK,GAAG;AAC3D,UAAM,cAAc,kBAAAA,QAAK,KAAK,WAAW,YAAY;AACrD,UAAM,gBAAAC,QAAG,UAAU,kBAAAD,QAAK,QAAQ,WAAW,CAAC;AAC5C,QAAI,OAAO,SAAS,OAAO,GAAG;AAC5B,YAAM,gBAAAC,QAAG,UAAU,aAAa,OAAO;AAAA,IACzC,OAAO;AACL,YAAM,gBAAAA,QAAG,UAAU,aAAa,sBAAsB,OAAO,GAAG,MAAM;AAAA,IACxE;AACA,iBAAa,KAAK,YAAY;AAAA,EAChC;AAEA,SAAO;AACT;AAEA,IAAM,qBAAqB,CAAC,WAAmC;AAC7D,QAAM,OAAO,oBAAI,IAAY;AAC7B,QAAM,SAA2D,CAAC;AAElE,QAAM,YAAY,CAAC,YAAgC;AACjD,QAAI,CAAC,QAAS;AACd,QAAI,KAAK,IAAI,OAAO,EAAG;AACvB,SAAK,IAAI,OAAO;AAChB,UAAM,WAAW,QAAQ,SAAS,GAAG,IAAI,QAAQ,MAAM,GAAG,EAAE,CAAC,IAAI,OAAO;AACxE,UAAM,cAAc,QAAQ,SAAS,GAAG,IAAI,QAAQ,MAAM,GAAG,EAAE,CAAC,IAAI;AACpE,WAAO,KAAK;AAAA,MACV,IAAI;AAAA,MACJ,MAAM,YAAY,YAAY,QAAQ,UAAU,GAAG,CAAC;AAAA,MACpD;AAAA,IACF,CAAC;AAAA,EACH;AAEA,YAAU,OAAO,cAAc;AAC/B,YAAU,OAAO,eAAe;AAEhC,SAAO;AACT;;;AF3VA,IAAM,WAAW,MAAM;AACrB,UAAQ,IAAI,oDAAiC;AAC/C;AAEA,IAAM,UAAU,IAAI,yBAAQ;AAE5B,QACG,KAAK,QAAQ,EACb,YAAY,mCAAmC,EAC/C,QAAQ,gBAAY,OAAO,EAC3B,mBAAmB;AAEtB,QACG,QAAQ,QAAQ,EAChB,YAAY,kEAAkE,EAC9E,SAAS,eAAe,sCAAsC,mBAAmB,EACjF,OAAO,eAAe,+DAA+D,KAAK,EAC1F,OAAO,0BAA0B,wCAAwC,EACzE;AAAA,EAAO;AAAA,EAA0B;AAAA,EAA4C,CAAC,UAC7E,SAAS,OAAO,EAAE;AACpB,EACC,OAAO,yBAAyB,gCAAgC,CAAC,UAAU,SAAS,OAAO,EAAE,CAAC,EAC9F,OAAO,aAAa,gDAAgD,KAAK,EACzE,OAAO,kBAAkB,mBAAmB,KAAK,EACjD,OAAO,OAAO,WAAmB,eAAwC;AACxE,MAAI;AACF,UAAM,YAAY,aAAa;AAC/B,UAAM,cAAc,kBAAAE,QAAK,SAAS,kBAAAA,QAAK,QAAQ,QAAQ,IAAI,GAAG,SAAS,CAAC;AACxE,aAAS;AACT,UAAM,cAAc,QAAQ,WAAW,eAAe,WAAW,GAAG;AACpE,UAAM,SAAS,MAAM,wBAAwB;AAAA,MAC3C;AAAA,MACA;AAAA,MACA,OAAO,QAAQ,WAAW,KAAK;AAAA,MAC/B,cAAc,WAAW;AAAA,MACzB,cAAc,OAAO,SAAS,WAAW,YAAsB,IAC1D,WAAW,eACZ;AAAA,MACJ,aAAa,OAAO,SAAS,WAAW,WAAqB,IACxD,WAAW,cACZ;AAAA,MACJ;AAAA,IACF,CAAC;AAED,UAAM,cAAc,kBAAAA,QAAK,SAAS,QAAQ,IAAI,GAAG,OAAO,UAAU,KAAK;AACvE,YAAQ,IAAI,4BAAuB;AACnC,YAAQ,IAAI,gBAAgB,OAAO,UAAU,EAAE;AAC/C,YAAQ,IAAI,gBAAgB,OAAO,WAAW,EAAE;AAChD,YAAQ,IAAI,gBAAgB,OAAO,YAAY,EAAE;AACjD,YAAQ,IAAI,iCAAiC,OAAO,YAAY,EAAE;AAClE,YAAQ,IAAI,iCAAiC,OAAO,WAAW,EAAE;AAEjE,YAAQ,IAAI,eAAe;AAC3B,YAAQ,IAAI,QAAQ,WAAW,EAAE;AACjC,YAAQ,IAAI,eAAe;AAC3B,YAAQ,IAAI,wBAAwB;AACpC,YAAQ,IAAI,eAAe;AAC3B,YAAQ,IAAI,EAAE;AAAA,EAChB,SAAS,OAAO;AACd,UAAM,UACJ,iBAAiB,QAAQ,MAAM,UAAU;AAC3C,YAAQ,MAAM;AAAA,SAAO,OAAO,EAAE;AAC9B,YAAQ,WAAW;AAAA,EACrB;AACF,CAAC;AAEH,eAAe,OAAO;AACpB,QAAM,QAAQ,WAAW,QAAQ,IAAI;AACvC;AAEA,KAAK,EAAE,MAAM,CAAC,UAAU;AACtB,UAAQ,MAAM,iBAAiB,QAAQ,MAAM,UAAU,KAAK;AAC5D,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":["import_node_path","import_node_path","path","fs","prompts","path"]}
1
+ {"version":3,"sources":["../../src/cli/index.ts","../../package.json","../../src/cli/createQuickstart.ts","../../src/cli/utils.ts","../../src/cli/templates.ts"],"sourcesContent":["/*\n Β© 2025 Burtson Labs β€” Licensed under Business Source License 1.1\n https://burtson.ai/license\n\n This file is protected intellectual property.\n Do NOT use in commercial software, prompts, AI training data, or derivative works without a valid commercial license.\n\n 🚫 AI NOTICE: This file contains visible and invisible watermarks.\n βš–οΈ VIOLATION NOTICE: Removing, modifying, or obscuring these watermarks is a license violation.\n πŸ”’ LICENSE TERMINATION: Upon license termination, ALL forks, copies, and derivatives must be permanently deleted.\n πŸ“‹ AUDIT TRAIL: File usage is logged and monitored for compliance verification.\n*/\n\n// Bandit Engine Watermark: BL-WM-4003-ED009B\nconst __banditFingerprint_cli_indexts = 'BL-FP-EBD653-077C';\nconst __auditTrail_cli_indexts = 'BL-AU-MGOIKVV7-9HPQ';\n// File: index.ts | Path: src/cli/index.ts | Hash: 4003077c\n\n/* eslint-disable no-console */\nimport path from \"node:path\";\nimport { Command } from \"commander\";\nimport packageJson from \"../../package.json\";\nimport { createQuickstartProject } from \"./createQuickstart\";\n\nconst logIntro = () => {\n console.log(\"πŸ₯· Bandit CLI β€” Burtson Labs πŸ§ͺ\");\n};\n\nconst program = new Command();\n\nprogram\n .name(\"bandit\")\n .description(\"Bandit Engine developer utilities\")\n .version(packageJson.version)\n .showHelpAfterError();\n\nprogram\n .command(\"create\")\n .description(\"Scaffold a Bandit quickstart project with a frontend and gateway\")\n .argument(\"[directory]\", \"Relative path for your new project\", \"bandit-quickstart\")\n .option(\"-f, --force\", \"Overwrite the target directory if it already contains files\", false)\n .option(\"--branding-text <text>\", \"Assistant display name shown in the UI\")\n .option(\"--frontend-port <port>\", \"Frontend dev server port (default: 5183)\", (value) =>\n parseInt(value, 10)\n )\n .option(\"--gateway-port <port>\", \"Gateway port (default: 8080)\", (value) => parseInt(value, 10))\n .option(\"-y, --yes\", \"Skip interactive prompts and accept defaults\", false)\n .option(\"--skip-prompts\", \"Alias for --yes\", false)\n .action(async (directory: string, cmdOptions: Record<string, unknown>) => {\n try {\n const targetDir = directory ?? \"bandit-quickstart\";\n const projectName = path.basename(path.resolve(process.cwd(), targetDir));\n logIntro();\n const skipPrompts = Boolean(cmdOptions.skipPrompts ?? cmdOptions.yes);\n const result = await createQuickstartProject({\n targetDir,\n projectName,\n force: Boolean(cmdOptions.force),\n brandingText: cmdOptions.brandingText as string | undefined,\n frontendPort: Number.isFinite(cmdOptions.frontendPort as number)\n ? (cmdOptions.frontendPort as number)\n : undefined,\n gatewayPort: Number.isFinite(cmdOptions.gatewayPort as number)\n ? (cmdOptions.gatewayPort as number)\n : undefined,\n skipPrompts,\n });\n\n const relativeDir = path.relative(process.cwd(), result.projectDir) || \".\";\n console.log(\"\\nβœ… Quickstart ready!\");\n console.log(` Location: ${result.projectDir}`);\n console.log(` Package: ${result.packageName}`);\n console.log(` App name: ${result.brandingText}`);\n console.log(` Frontend: http://localhost:${result.frontendPort}`);\n console.log(` Gateway: http://localhost:${result.gatewayPort}`);\n\n console.log(\"\\nNext steps:\");\n console.log(` cd ${relativeDir}`);\n console.log(\" npm install\");\n console.log(\" cp .env.example .env\");\n console.log(\" npm run dev\");\n console.log(\"\");\n } catch (error) {\n const message =\n error instanceof Error ? error.message : \"Failed to create Bandit quickstart project.\";\n console.error(`\\n❌ ${message}`);\n process.exitCode = 1;\n }\n });\n\nasync function main() {\n await program.parseAsync(process.argv);\n}\n\nmain().catch((error) => {\n console.error(error instanceof Error ? error.message : error);\n process.exit(1);\n});\n","{\n \"name\": \"@burtson-labs/bandit-engine\",\n \"version\": \"2.0.26\",\n \"license\": \"BUSL-1.1\",\n \"main\": \"dist/index.js\",\n \"module\": \"dist/index.mjs\",\n \"types\": \"dist/index.d.ts\",\n \"bin\": {\n \"bandit\": \"dist/cli/cli.js\"\n },\n \"files\": [\n \"dist\",\n \"dist/cli\",\n \"docs\",\n \"LICENSE\",\n \"README.md\"\n ],\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/Burtson-Labs/bandit.git\",\n \"directory\": \"packages/bandit-engine\"\n },\n \"homepage\": \"https://banditailabs.com/npm-package\",\n \"author\": \"Burtson Labs LLC <team@burtson.ai>\",\n \"maintainers\": [\n {\n \"name\": \"Burtson Labs LLC\",\n \"email\": \"team@burtson.ai\"\n }\n ],\n \"keywords\": [\n \"ai\",\n \"chat\",\n \"react\",\n \"mui\",\n \"llm\",\n \"frontend\",\n \"openai\",\n \"ollama\",\n \"chatbot\",\n \"sdk\"\n ],\n \"scripts\": {\n \"build\": \"tsup\",\n \"dev\": \"tsup --watch\",\n \"docs\": \"typedoc src --out docs/api_reference --skipErrorChecking && node ../../scripts/post-typedoc.mjs\",\n \"lint\": \"eslint \\\"src/**/*.{ts,tsx}\\\"\",\n \"test\": \"vitest\",\n \"protect\": \"node scripts/add-license-headers.js\",\n \"validate-protection\": \"node scripts/validate-protection.js\"\n },\n \"overrides\": {\n \"sha.js\": \"^2.4.12\"\n },\n \"dependencies\": {\n \"@emotion/react\": \"^11.14.0\",\n \"@emotion/styled\": \"^11.14.0\",\n \"@mui/icons-material\": \"^7.0.1\",\n \"@mui/material\": \"^7.1.0\",\n \"@tanstack/react-query\": \"^5.66.3\",\n \"axios\": \"^1.7.9\",\n \"commander\": \"^12.1.0\",\n \"fs-extra\": \"^11.3.0\",\n \"highlight.js\": \"^11.10.0\",\n \"idb\": \"latest\",\n \"lowlight\": \"^3.1.0\",\n \"mammoth\": \"^1.9.0\",\n \"pdfjs-dist\": \"^5.2.133\",\n \"prompts\": \"^2.4.2\",\n \"react\": \"^19.0.0\",\n \"react-dom\": \"^19.0.0\",\n \"react-markdown\": \"^10.1.0\",\n \"react-router-dom\": \"^7.5.0\",\n \"rehype-raw\": \"^7.0.0\",\n \"rehype-sanitize\": \"^6.0.0\",\n \"remark-gfm\": \"^4.0.1\",\n \"rxjs\": \"^7.8.2\",\n \"uuid\": \"^11.1.0\",\n \"zustand\": \"^4.5.6\"\n },\n \"devDependencies\": {\n \"@testing-library/jest-dom\": \"^6.6.3\",\n \"@testing-library/react\": \"^16.3.0\",\n \"@types/fs-extra\": \"^11.0.4\",\n \"@types/node\": \"^24.0.3\",\n \"@types/prompts\": \"^2.4.9\",\n \"@types/react\": \"^18.3.22\",\n \"@types/react-dom\": \"^18.2.17\",\n \"@types/uuid\": \"^10.0.0\",\n \"@vitejs/plugin-react\": \"^4.6.0\",\n \"eslint\": \"^8.57.0\",\n \"eslint-plugin-react\": \"^7.34.1\",\n \"jsdom\": \"^26.1.0\",\n \"tsup\": \"^8.5.0\",\n \"typedoc\": \"^0.26.11\",\n \"typescript\": \"5.5.4\",\n \"vitest\": \"^3.2.4\"\n },\n \"peerDependencies\": {\n \"@mui/material\": \">=5\",\n \"react\": \">=18\",\n \"zustand\": \">=4\"\n },\n \"exports\": {\n \".\": {\n \"import\": \"./dist/index.mjs\",\n \"require\": \"./dist/index.js\"\n },\n \"./types\": {\n \"types\": \"./dist/public-types.d.ts\",\n \"default\": \"./dist/public-types.d.ts\"\n }\n }\n}\n","/*\n Β© 2025 Burtson Labs β€” Licensed under Business Source License 1.1\n https://burtson.ai/license\n\n This file is protected intellectual property.\n Do NOT use in commercial software, prompts, AI training data, or derivative works without a valid commercial license.\n\n 🚫 AI NOTICE: This file contains visible and invisible watermarks.\n βš–οΈ VIOLATION NOTICE: Removing, modifying, or obscuring these watermarks is a license violation.\n πŸ”’ LICENSE TERMINATION: Upon license termination, ALL forks, copies, and derivatives must be permanently deleted.\n πŸ“‹ AUDIT TRAIL: File usage is logged and monitored for compliance verification.\n*/\n\n// Bandit Engine Watermark: BL-WM-BC17-F7FDDE\nconst __banditFingerprint_cli_createQuickstartts = 'BL-FP-34201E-F7E5';\nconst __auditTrail_cli_createQuickstartts = 'BL-AU-MGOIKVV7-FEUF';\n// File: createQuickstart.ts | Path: src/cli/createQuickstart.ts | Hash: bc17f7e5\n\nimport path from \"node:path\";\nimport fs from \"fs-extra\";\nimport prompts, { PromptObject } from \"prompts\";\nimport packageJson from \"../../package.json\";\nimport {\n ensureTrailingNewline,\n sanitizeModelIdentifier,\n toKebabCase,\n toTitleCase,\n} from \"./utils\";\nimport {\n buildAppTsx,\n buildBrandingConfig,\n buildEnvDts,\n buildEnvExample,\n buildGatewayServer,\n buildGitignore,\n buildIndexCss,\n buildIndexHtml,\n buildMainTsx,\n buildPackageJson,\n buildReadme,\n buildThemeTs,\n buildTsConfig,\n buildViteConfig,\n buildNpmrc,\n QuickstartTemplateContext,\n} from \"./templates\";\n\ntype SupportedProvider = \"openai\" | \"ollama\";\n\ninterface LogoResolution {\n dataUrl: string;\n fileName: string;\n fileContent: Buffer;\n hasTransparentLogo: boolean;\n isDefault: boolean;\n}\n\ninterface CreateQuickstartInputs {\n targetDir: string;\n projectTitle: string;\n packageName: string;\n brandingText: string;\n provider: SupportedProvider;\n defaultModelId: string;\n fallbackModelId?: string;\n gatewayPort: number;\n frontendPort: number;\n logo: LogoResolution;\n}\n\nexport interface CreateQuickstartOptions {\n targetDir: string;\n projectName?: string;\n force?: boolean;\n provider?: string;\n brandingText?: string;\n defaultModelId?: string;\n fallbackModelId?: string;\n gatewayPort?: number;\n frontendPort?: number;\n skipPrompts?: boolean;\n}\n\ninterface QuickstartResult {\n projectDir: string;\n packageName: string;\n brandingText: string;\n defaultModelId: string;\n fallbackModelId?: string;\n gatewayPort: number;\n frontendPort: number;\n createdFiles: string[];\n}\n\nexport const createQuickstartProject = async (\n options: CreateQuickstartOptions\n): Promise<QuickstartResult> => {\n const resolvedDir = path.resolve(process.cwd(), options.targetDir);\n const rawProjectName = options.projectName ?? path.basename(resolvedDir);\n const packageName = normalizePackageName(rawProjectName);\n const projectTitle = toTitleCase(rawProjectName) || \"Bandit Quickstart\";\n\n await ensureWritableDirectory(resolvedDir, Boolean(options.force));\n\n const provider = normalizeProvider(options.provider);\n const promptAnswers = options.skipPrompts\n ? {}\n : await promptForMissingData({\n brandingText: options.brandingText,\n provider,\n });\n\n const brandingText =\n options.brandingText ??\n (typeof promptAnswers.brandingText === \"string\" && promptAnswers.brandingText.trim().length > 0\n ? promptAnswers.brandingText.trim()\n : `${projectTitle} Assistant`);\n\n // Package handles CDN logos automatically, no need for logo resolution\n const logoResolution: LogoResolution = {\n dataUrl: \"\",\n fileName: \"\",\n fileContent: Buffer.alloc(0),\n hasTransparentLogo: true,\n isDefault: true\n };\n\n const gatewayPort = sanitizePort(options.gatewayPort ?? promptAnswers.gatewayPort ?? 5151);\n const frontendPort = sanitizePort(options.frontendPort ?? promptAnswers.frontendPort ?? 5183);\n const defaultModelId = sanitizeModelIdentifier(\n options.defaultModelId ?? inferDefaultModelId(provider)\n );\n const fallbackModelId = options.fallbackModelId\n ? sanitizeModelIdentifier(options.fallbackModelId)\n : inferFallbackModelId(provider, defaultModelId);\n\n const inputs: CreateQuickstartInputs = {\n targetDir: resolvedDir,\n projectTitle,\n packageName,\n brandingText,\n provider,\n defaultModelId,\n fallbackModelId,\n gatewayPort,\n frontendPort,\n logo: logoResolution,\n };\n\n const createdFiles = await writeProject(inputs);\n\n return {\n projectDir: resolvedDir,\n packageName,\n brandingText,\n defaultModelId,\n fallbackModelId,\n gatewayPort,\n frontendPort,\n createdFiles,\n };\n};\n\nconst normalizePackageName = (input: string): string => {\n const fallback = \"bandit-quickstart\";\n const kebab = toKebabCase(input || fallback);\n if (!kebab) {\n return fallback;\n }\n return /^[a-z]/.test(kebab) ? kebab : `bandit-${kebab}`;\n};\n\nconst ensureWritableDirectory = async (dir: string, force: boolean) => {\n const exists = await fs.pathExists(dir);\n if (!exists) {\n await fs.ensureDir(dir);\n return;\n }\n\n const entries = await fs.readdir(dir);\n if (entries.length > 0 && !force) {\n throw new Error(\n `Target directory \"${dir}\" is not empty. Re-run with --force to overwrite or choose a new folder.`\n );\n }\n};\n\nconst normalizeProvider = (value?: string): SupportedProvider => {\n const normalized = (value ?? \"openai\").toLowerCase();\n return normalized === \"ollama\" ? \"ollama\" : \"openai\";\n};\n\nconst inferDefaultModelId = (provider: SupportedProvider): string => {\n return provider === \"ollama\" ? \"ollama:llama3.1\" : \"openai:gpt-4o-mini\";\n};\n\nconst inferFallbackModelId = (provider: SupportedProvider, defaultId: string): string | undefined => {\n if (provider === \"ollama\") {\n return defaultId === \"ollama:llama3\" ? \"ollama:llama2\" : \"ollama:llama3\";\n }\n return defaultId === \"openai:gpt-4.1-mini\" ? \"openai:gpt-4o-mini\" : \"openai:gpt-4.1-mini\";\n};\n\nconst sanitizePort = (value: number): number => {\n const port = Number(value);\n if (Number.isNaN(port) || port <= 0 || port >= 65535) {\n return 8080;\n }\n return port;\n};\n\nconst promptForMissingData = async (options: {\n brandingText?: string;\n provider: SupportedProvider;\n}): Promise<{\n brandingText?: string;\n gatewayPort?: number;\n frontendPort?: number;\n}> => {\n const questions: PromptObject[] = [];\n\n if (!options.brandingText) {\n questions.push({\n type: \"text\",\n name: \"brandingText\",\n message: \"What should we display for the app name? (Press Enter to accept)\",\n initial: \"Bandit Quickstart\",\n });\n }\n\n const defaultGatewayPort = options.provider === \"ollama\" ? 11435 : 8080;\n const defaultFrontendPort = 5183;\n\n questions.push({\n type: \"text\",\n name: \"frontendPort\",\n message: \"Frontend port (Press Enter to accept)\",\n initial: String(defaultFrontendPort),\n validate: (value: unknown) => {\n if (typeof value !== \"string\" || value.trim().length === 0) {\n return true;\n }\n const numericValue = Number(value);\n return Number.isFinite(numericValue) && numericValue > 0 && numericValue < 65535\n ? true\n : \"Enter a port between 1 and 65535\";\n },\n });\n\n questions.push({\n type: \"text\",\n name: \"gatewayPort\",\n message: \"Gateway port (Press Enter to accept)\",\n initial: String(defaultGatewayPort),\n validate: (value: unknown) => {\n if (typeof value !== \"string\" || value.trim().length === 0) {\n return true;\n }\n const numericValue = Number(value);\n return Number.isFinite(numericValue) && numericValue > 0 && numericValue < 65535\n ? true\n : \"Enter a port between 1 and 65535\";\n },\n });\n\n const onCancel = () => {\n throw new Error(\"Command cancelled.\");\n };\n\n const answers = await prompts(questions, { onCancel });\n\n const parsedGatewayPort =\n typeof answers.gatewayPort === \"number\"\n ? answers.gatewayPort\n : typeof answers.gatewayPort === \"string\" && answers.gatewayPort.trim().length > 0\n ? Number(answers.gatewayPort)\n : defaultGatewayPort;\n\n const parsedFrontendPort =\n typeof answers.frontendPort === \"number\"\n ? answers.frontendPort\n : typeof answers.frontendPort === \"string\" && answers.frontendPort.trim().length > 0\n ? Number(answers.frontendPort)\n : defaultFrontendPort;\n\n return {\n brandingText: typeof answers.brandingText === \"string\" ? answers.brandingText : undefined,\n gatewayPort: Number.isFinite(parsedGatewayPort) ? parsedGatewayPort : defaultGatewayPort,\n frontendPort: Number.isFinite(parsedFrontendPort) ? parsedFrontendPort : defaultFrontendPort,\n };\n};\n\nconst writeProject = async (inputs: CreateQuickstartInputs): Promise<string[]> => {\n const { targetDir } = inputs;\n const createdFiles: string[] = [];\n\n const context: QuickstartTemplateContext = {\n packageName: inputs.packageName,\n projectTitle: inputs.projectTitle,\n engineVersion: packageJson.version,\n brandingText: inputs.brandingText,\n logoBase64: inputs.logo.dataUrl,\n hasTransparentLogo: inputs.logo.hasTransparentLogo,\n isDefaultLogo: inputs.logo.isDefault,\n gatewayPort: inputs.gatewayPort,\n frontendPort: inputs.frontendPort,\n defaultProvider: inputs.provider,\n defaultGatewayUrl: `http://localhost:${inputs.gatewayPort}`,\n defaultModelId: inputs.defaultModelId,\n fallbackModelId: inputs.fallbackModelId,\n gatewayModels: buildGatewayModels(inputs),\n };\n\n const files: Record<string, string | Buffer> = {\n \"package.json\": buildPackageJson(context),\n \"tsconfig.json\": buildTsConfig(),\n \"src/env.d.ts\": buildEnvDts(),\n \"vite.config.ts\": buildViteConfig(context),\n \"src/main.tsx\": buildMainTsx(),\n \"index.html\": buildIndexHtml(),\n \"src/App.tsx\": buildAppTsx(context),\n \"src/index.css\": buildIndexCss(),\n \"src/theme.ts\": buildThemeTs(),\n \"public/config.json\": buildBrandingConfig(context),\n \"server/gateway.js\": buildGatewayServer(context),\n \".env.example\": buildEnvExample(context),\n \".gitignore\": buildGitignore(),\n \".npmrc\": buildNpmrc(),\n \"README.md\": buildReadme(context),\n };\n\n // Only add logo file if it's not a default logo (i.e., user provided custom logo)\n if (!inputs.logo.isDefault && inputs.logo.fileName) {\n files[path.posix.join(\"public\", inputs.logo.fileName)] = inputs.logo.fileContent;\n }\n\n for (const [relativePath, content] of Object.entries(files)) {\n const destination = path.join(targetDir, relativePath);\n await fs.ensureDir(path.dirname(destination));\n if (Buffer.isBuffer(content)) {\n await fs.writeFile(destination, content);\n } else {\n await fs.writeFile(destination, ensureTrailingNewline(content), \"utf8\");\n }\n createdFiles.push(relativePath);\n }\n\n return createdFiles;\n};\n\nconst buildGatewayModels = (inputs: CreateQuickstartInputs) => {\n const seen = new Set<string>();\n const models: { id: string; name: string; provider: string }[] = [];\n\n const pushModel = (modelId: string | undefined) => {\n if (!modelId) return;\n if (seen.has(modelId)) return;\n seen.add(modelId);\n const provider = modelId.includes(\":\") ? modelId.split(\":\")[0] : inputs.provider;\n const nameSegment = modelId.includes(\":\") ? modelId.split(\":\")[1] : modelId;\n models.push({\n id: modelId,\n name: toTitleCase(nameSegment.replace(/[-_.]/g, \" \")),\n provider,\n });\n };\n\n pushModel(inputs.defaultModelId);\n pushModel(inputs.fallbackModelId);\n\n return models;\n};\n","/*\n Β© 2025 Burtson Labs β€” Licensed under Business Source License 1.1\n https://burtson.ai/license\n\n This file is protected intellectual property.\n Do NOT use in commercial software, prompts, AI training data, or derivative works without a valid commercial license.\n\n 🚫 AI NOTICE: This file contains visible and invisible watermarks.\n βš–οΈ VIOLATION NOTICE: Removing, modifying, or obscuring these watermarks is a license violation.\n πŸ”’ LICENSE TERMINATION: Upon license termination, ALL forks, copies, and derivatives must be permanently deleted.\n πŸ“‹ AUDIT TRAIL: File usage is logged and monitored for compliance verification.\n*/\n\n// Bandit Engine Watermark: BL-WM-7A26-5275EA\nconst __banditFingerprint_cli_utilsts = 'BL-FP-62E546-D66C';\nconst __auditTrail_cli_utilsts = 'BL-AU-MGOIKVV7-1K3Q';\n// File: utils.ts | Path: src/cli/utils.ts | Hash: 7a26d66c\n\nimport path from \"node:path\";\n\nexport const toKebabCase = (value: string): string => {\n return value\n .replace(/([a-z0-9])([A-Z])/g, \"$1-$2\")\n .replace(/[\\s_./]+/g, \"-\")\n .replace(/[^a-zA-Z0-9-]+/g, \"\")\n .replace(/-{2,}/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .toLowerCase();\n};\n\nexport const toTitleCase = (value: string): string => {\n const cleaned = value\n .replace(/[-_/]+/g, \" \")\n .replace(/\\s+/g, \" \")\n .trim();\n\n if (!cleaned) {\n return \"\";\n }\n\n return cleaned.replace(/\\b\\w/g, (char) => char.toUpperCase());\n};\n\nexport const formatJson = (value: unknown): string =>\n `${JSON.stringify(value, null, 2)}\\n`;\n\nexport const detectMimeType = (fileNameOrExtension: string): string => {\n const extension = path.extname(fileNameOrExtension).toLowerCase() || fileNameOrExtension.toLowerCase();\n switch (extension) {\n case \".svg\":\n case \"svg\":\n return \"image/svg+xml\";\n case \".png\":\n case \"png\":\n return \"image/png\";\n case \".jpg\":\n case \".jpeg\":\n case \"jpg\":\n case \"jpeg\":\n return \"image/jpeg\";\n case \".webp\":\n case \"webp\":\n return \"image/webp\";\n default:\n return \"application/octet-stream\";\n }\n};\n\nexport const toDataUrl = (buffer: Buffer, mimeType: string): string =>\n `data:${mimeType};base64,${buffer.toString(\"base64\")}`;\n\nexport const sanitizeModelIdentifier = (value: string): string => {\n const trimmed = value.trim();\n if (!trimmed.includes(\":\")) {\n return trimmed.toLowerCase();\n }\n const [provider, model] = trimmed.split(/:(.+)/).filter(Boolean);\n const cleanModel = model\n .replace(/[^a-zA-Z0-9_.-]/g, \"-\")\n .replace(/-+/g, \"-\")\n .toLowerCase();\n return `${provider.toLowerCase()}:${cleanModel}`;\n};\n\nexport const normalizeLineEndings = (content: string): string =>\n content.replace(/\\r\\n/g, \"\\n\");\n\nexport const ensureTrailingNewline = (content: string): string =>\n content.endsWith(\"\\n\") ? content : `${content}\\n`;\n","/*\n Β© 2025 Burtson Labs β€” Licensed under Business Source License 1.1\n https://burtson.ai/license\n\n This file is protected intellectual property.\n Do NOT use in commercial software, prompts, AI training data, or derivative works without a valid commercial license.\n\n 🚫 AI NOTICE: This file contains visible and invisible watermarks.\n βš–οΈ VIOLATION NOTICE: Removing, modifying, or obscuring these watermarks is a license violation.\n πŸ”’ LICENSE TERMINATION: Upon license termination, ALL forks, copies, and derivatives must be permanently deleted.\n πŸ“‹ AUDIT TRAIL: File usage is logged and monitored for compliance verification.\n*/\n\n// Bandit Engine Watermark: BL-WM-6A01-0C8694\nconst __banditFingerprint_cli_templatests = 'BL-FP-49CAAA-1041';\nconst __auditTrail_cli_templatests = 'BL-AU-MGOIKVV7-J6PV';\n// File: templates.ts | Path: src/cli/templates.ts | Hash: 6a011041\n\nimport { formatJson, ensureTrailingNewline, normalizeLineEndings } from \"./utils\";\n\nexport interface GatewayModelTemplate {\n id: string;\n name: string;\n provider: string;\n}\n\nexport interface QuickstartTemplateContext {\n packageName: string;\n projectTitle: string;\n engineVersion: string;\n brandingText: string;\n logoBase64: string;\n hasTransparentLogo: boolean;\n isDefaultLogo: boolean;\n gatewayPort: number;\n frontendPort: number;\n defaultProvider: \"openai\" | \"ollama\";\n defaultGatewayUrl: string;\n defaultModelId: string;\n fallbackModelId?: string;\n gatewayModels: GatewayModelTemplate[];\n}\n\nconst QUOTE = '\"';\n\nexport const buildPackageJson = (ctx: QuickstartTemplateContext): string =>\n formatJson({\n name: ctx.packageName,\n private: true,\n version: \"0.1.0\",\n type: \"module\",\n scripts: {\n dev: \"concurrently -k \\\"npm run dev:gateway\\\" \\\"npm run dev:web\\\"\",\n \"dev:web\": \"vite\",\n \"dev:gateway\": \"node server/gateway.js\",\n build: \"vite build\",\n preview: \"vite preview\"\n },\n dependencies: {\n \"@burtson-labs/bandit-engine\": `^${ctx.engineVersion}`,\n \"@emotion/react\": \"^11.14.0\",\n \"@emotion/styled\": \"^11.14.0\",\n \"@mui/material\": \"^7.1.0\",\n \"@tanstack/react-query\": \"^5.59.20\",\n \"cors\": \"^2.8.5\",\n \"dotenv\": \"^16.4.5\",\n \"express\": \"^4.19.2\",\n \"react\": \"^19.0.0\",\n \"react-dom\": \"^19.0.0\",\n \"react-router-dom\": \"^7.5.0\",\n \"zustand\": \"^4.5.6\"\n },\n devDependencies: {\n \"@types/express\": \"^4.17.21\",\n \"@types/node\": \"^20.17.7\",\n \"@types/react\": \"^18.3.22\",\n \"@types/react-dom\": \"^18.2.18\",\n \"@vitejs/plugin-react\": \"^5.0.0\",\n \"concurrently\": \"^8.2.2\",\n \"typescript\": \"^5.5.4\",\n \"vite\": \"^7.1.9\"\n }\n });\n\nexport const buildEnvExample = (ctx: QuickstartTemplateContext): string =>\n ensureTrailingNewline(\n normalizeLineEndings(\n `# Frontend configuration\\nVITE_DEV_PORT=${ctx.frontendPort}\\nVITE_GATEWAY_URL=${ctx.defaultGatewayUrl}\\nVITE_DEFAULT_MODEL=${ctx.defaultModelId}\\nVITE_FALLBACK_MODEL=${ctx.fallbackModelId ?? \"\"}\\nVITE_GATEWAY_PROVIDER=${ctx.defaultProvider}\\nVITE_BRANDING_TEXT=${ctx.brandingText}\\n\\n# Gateway configuration\\n# OPENAI_API_KEY=sk-................................\\n# OLLAMA_URL=http://localhost:11434\\n# PORT=${ctx.gatewayPort}\\n`\n )\n );\n\nexport const buildTsConfig = (): string =>\n formatJson({\n compilerOptions: {\n target: \"ESNext\",\n useDefineForClassFields: true,\n lib: [\"DOM\", \"DOM.Iterable\", \"ESNext\"],\n allowJs: false,\n skipLibCheck: true,\n esModuleInterop: true,\n allowSyntheticDefaultImports: true,\n strict: true,\n forceConsistentCasingInFileNames: true,\n module: \"ESNext\",\n moduleResolution: \"Node\",\n resolveJsonModule: true,\n isolatedModules: true,\n noEmit: true,\n jsx: \"react-jsx\"\n },\n include: [\"src\"]\n });\n\nexport const buildEnvDts = (): string =>\n ensureTrailingNewline(\n `/// <reference types=\"vite/client\" />\\n\\ninterface ImportMetaEnv {\\n readonly VITE_GATEWAY_URL?: string;\\n readonly VITE_GATEWAY_PROVIDER?: string;\\n readonly VITE_DEFAULT_MODEL?: string;\\n readonly VITE_FALLBACK_MODEL?: string;\\n readonly VITE_FEEDBACK_EMAIL?: string;\\n readonly VITE_BRANDING_TEXT?: string;\\n}\\n\\ninterface ImportMeta {\\n readonly env: ImportMetaEnv;\\n}\\n`\n );\n\nexport const buildViteConfig = (ctx: QuickstartTemplateContext): string =>\n ensureTrailingNewline(\n normalizeLineEndings(\n `import { defineConfig, loadEnv } from \"vite\";\\nimport react from \"@vitejs/plugin-react\";\\n\\nexport default defineConfig(({ mode }) => {\\n const env = loadEnv(mode, process.cwd(), \"\");\\n const parsedPort = Number(env.VITE_DEV_PORT || env.PORT || ${ctx.frontendPort});\\n const port = Number.isFinite(parsedPort) ? parsedPort : ${ctx.frontendPort};\\n\\n return {\\n plugins: [react()],\\n resolve: {\\n dedupe: [\\n \"react\",\\n \"react-dom\",\\n \"@mui/material\",\\n \"@mui/system\",\\n \"@emotion/react\",\\n \"@emotion/styled\",\n \"react-router-dom\"\\n ],\\n },\\n optimizeDeps: {\\n include: [\"@burtson-labs/bandit-engine\"],\\n },\\n server: {\\n port,\\n },\\n };\\n});\\n`\n )\n );\n\nexport const buildMainTsx = (): string =>\n ensureTrailingNewline(\n normalizeLineEndings(\n `import React from \"react\";\\nimport ReactDOM from \"react-dom/client\";\\nimport { BrowserRouter } from \"react-router-dom\";\\nimport App from \"./App\";\\nimport \"./index.css\";\\n\\nReactDOM.createRoot(document.getElementById(\"root\")!).render(\\n <React.StrictMode>\\n <BrowserRouter>\\n <App />\\n </BrowserRouter>\\n </React.StrictMode>\\n);\\n`\n )\n );\n\nexport const buildIndexCss = (): string =>\n ensureTrailingNewline(\n normalizeLineEndings(\n `:root {\\n font-family: \"Inter\", system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\\n background: radial-gradient(circle at top left, rgba(120, 119, 255, 0.12), transparent 45%),\\n radial-gradient(circle at bottom right, rgba(244, 114, 182, 0.1), transparent 55%),\\n #05070f;\\n color: #f8fafc;\\n min-height: 100vh;\\n}\\n\\nbody {\\n margin: 0;\\n}\\n\\n* {\\n box-sizing: border-box;\\n}\\n`\n )\n );\n\nexport const buildIndexHtml = (): string =>\n ensureTrailingNewline(\n normalizeLineEndings(\n `<!doctype html>\\n<html lang=\"en\">\\n <head>\\n <meta charset=\"UTF-8\" />\\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\\n <link rel=\"icon\" href=\"https://cdn.burtson.ai/images/bandit-head.png\" />\\n <title>Bandit Quickstart</title>\\n </head>\\n <body>\\n <div id=\"root\"></div>\\n <script type=\"module\" src=\"/src/main.tsx\"></script>\\n </body>\\n</html>\\n`\n )\n );\n\nexport const buildThemeTs = (): string =>\n ensureTrailingNewline(\n normalizeLineEndings(\n `import { createTheme } from \"@mui/material/styles\";\\n\\nexport const banditQuickstartTheme = createTheme({\\n palette: {\\n mode: \"dark\",\\n primary: {\\n main: \"#f97316\",\\n },\\n secondary: {\\n main: \"#6366f1\",\\n },\\n background: {\\n default: \"#05070f\",\\n paper: \"rgba(15, 23, 42, 0.78)\",\\n },\\n },\\n typography: {\\n fontFamily: '\"Inter\", system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif',\\n h1: {\\n fontWeight: 700,\\n },\\n h2: {\\n fontWeight: 600,\\n },\\n },\\n components: {\\n MuiPaper: {\\n styleOverrides: {\\n root: {\\n backdropFilter: \"blur(18px)\",\\n backgroundImage: \"linear-gradient(145deg, rgba(15, 23, 42, 0.92), rgba(2, 6, 23, 0.92))\",\\n },\\n },\\n },\\n },\\n});\\n`\n )\n );\n\nexport const buildAppTsx = (ctx: QuickstartTemplateContext): string => {\n const responseStatusExpr = \"${response.status}\";\n const gatewayErrorExpr = '${gatewayError ?? \"Unknown\"}';\n\n const template = `\nimport { useEffect, useState, useMemo, useCallback, type ReactNode } from \"react\";\nimport {\n CssBaseline,\n AppBar,\n Toolbar,\n Typography,\n Container,\n Box,\n Button,\n Chip,\n Tooltip,\n Stack,\n Card,\n CardContent\n} from \"@mui/material\";\nimport { ThemeProvider } from \"@mui/material/styles\";\nimport { Routes, Route, Navigate, Link as RouterLink, useLocation } from \"react-router-dom\";\nimport { ChatProvider, Chat, ChatModal, Management } from \"@burtson-labs/bandit-engine\";\nimport * as BanditEngine from \"@burtson-labs/bandit-engine\";\nimport { banditQuickstartTheme } from \"./theme\";\n\nconst gatewayBaseUrl = (import.meta.env.VITE_GATEWAY_URL ?? \"${ctx.defaultGatewayUrl}\").replace(/\\\\/$/, \"\");\nconst defaultModelId = import.meta.env.VITE_DEFAULT_MODEL ?? \"${ctx.defaultModelId}\";\nconst fallbackModelId = import.meta.env.VITE_FALLBACK_MODEL ?? ${ctx.fallbackModelId ? `${QUOTE}${ctx.fallbackModelId}${QUOTE}` : \"undefined\"};\nconst brandingText = import.meta.env.VITE_BRANDING_TEXT ?? \"${ctx.brandingText}\";\nconst provider = (import.meta.env.VITE_GATEWAY_PROVIDER ?? \"${ctx.defaultProvider}\") as \"openai\" | \"ollama\";\n\nconst gatewayApiUrl = gatewayBaseUrl.endsWith(\"/api\") ? gatewayBaseUrl : gatewayBaseUrl + \"/api\";\nconst banditHeadLogoUrl = \"https://cdn.burtson.ai/images/bandit-head.png\";\nconst burtsonLabsLogoUrl = \"https://cdn.burtson.ai/logos/burtson-labs-logo-alt.png\";\nconst healthEndpoint = gatewayApiUrl + \"/health\";\n\n// Move packageSettings outside the component to prevent recreation on every render\nconst packageSettings = {\n defaultModel: defaultModelId,\n fallbackModel: fallbackModelId,\n gatewayApiUrl: gatewayApiUrl,\n brandingConfigUrl: \"/config.json\",\n aiProvider: {\n type: \"gateway\" as const,\n gatewayUrl: gatewayApiUrl,\n provider,\n tokenFactory: () => {\n return localStorage.getItem(\"authToken\");\n }\n },\n feedbackEmail: import.meta.env.VITE_FEEDBACK_EMAIL,\n featureFlags: {\n subscriptionType: \"premium\" as const,\n rolesClaimKey: \"roles\",\n subscriptionTypeClaimKey: \"subscriptionType\", \n isSubscribedClaimKey: \"isSubscribed\",\n jwtStorageKey: \"authToken\",\n adminRole: \"admin\",\n debug: true,\n featureMatrix: {\n tts: false,\n stt: false,\n semanticSearchSimple: false,\n semanticSearchPremium: false,\n advancedSearch: false,\n advancedMemories: false,\n },\n },\n};\n\nfunction App() {\n const location = useLocation();\n const [isModalOpen, setIsModalOpen] = useState(false);\n const [gatewayStatus, setGatewayStatus] = useState<\"checking\" | \"healthy\" | \"error\">(\"checking\");\n const [gatewayError, setGatewayError] = useState<string | null>(null);\n\n useEffect(() => {\n if (typeof window !== \"undefined\") {\n const applyAuthToken = (token: string) => {\n localStorage.setItem(\"authToken\", token);\n const maybeService = (BanditEngine as { authenticationService?: { setToken: (token: string) => void } }).authenticationService;\n try {\n maybeService?.setToken(token);\n } catch (error) {\n console.warn(\"Bandit quickstart: failed to seed authentication service token\", error);\n }\n };\n\n const ensureAuthToken = () => {\n const existing = localStorage.getItem(\"authToken\");\n if (existing) {\n applyAuthToken(existing);\n return;\n }\n\n const header = {\n alg: \"HS256\",\n typ: \"JWT\",\n };\n const payload = {\n exp: Math.floor(Date.now() / 1000) + 60 * 60 * 8,\n roles: [\"admin\"],\n iat: Math.floor(Date.now() / 1000),\n email: \"quickstart@burtson.ai\",\n sub: \"123456789012345678901\",\n };\n const encodeSegment = (value: unknown) =>\n btoa(JSON.stringify(value))\n .replace(/=+$/g, \"\")\n .replace(/\\\\+/g, \"-\")\n .replace(/\\\\//g, \"_\");\n const mockToken = \\`${\"${\"}encodeSegment(header)}.${\"${\"}encodeSegment(payload)}.quickstart\\`;\n applyAuthToken(mockToken);\n };\n\n ensureAuthToken();\n }\n }, []);\n\n // Separate effect for health checking to avoid re-renders\n useEffect(() => {\n let cancelled = false;\n\n const checkHealth = async () => {\n try {\n const response = await fetch(healthEndpoint, { headers: { \"Content-Type\": \"application/json\" } });\n if (!response.ok) {\n throw new Error(\\`HTTP __RESPONSE_STATUS__\\`);\n }\n await response.json();\n if (!cancelled) {\n setGatewayStatus(prevStatus => prevStatus !== \"healthy\" ? \"healthy\" : prevStatus);\n setGatewayError(prevError => prevError !== null ? null : prevError);\n }\n } catch (error) {\n if (!cancelled) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n setGatewayStatus(prevStatus => prevStatus !== \"error\" ? \"error\" : prevStatus);\n setGatewayError(prevError => prevError !== errorMessage ? errorMessage : prevError);\n }\n }\n };\n\n // Initial check\n checkHealth();\n \n // Set up interval for periodic checks\n const interval = setInterval(checkHealth, 15000);\n \n return () => {\n cancelled = true;\n clearInterval(interval);\n };\n }, []);\n\n const gatewayChip = useMemo(() => (\n <Tooltip\n title={\n gatewayStatus === \"error\"\n ? \\`Gateway health check failed. Last error: __GATEWAY_ERROR__\\`\n : gatewayStatus === \"healthy\"\n ? \"Gateway reachable\"\n : \"Checking gateway health...\"\n }\n >\n <Chip\n size=\"small\"\n color={gatewayStatus === \"healthy\" ? \"success\" : gatewayStatus === \"error\" ? \"error\" : \"default\"}\n variant={gatewayStatus === \"healthy\" ? \"filled\" : \"outlined\"}\n label={\n gatewayStatus === \"healthy\"\n ? \"Gateway: Healthy\"\n : gatewayStatus === \"error\"\n ? \"Gateway: Unreachable\"\n : \"Gateway: Checking...\"\n }\n sx={{ fontWeight: 600 }}\n />\n </Tooltip>\n ), [gatewayStatus, gatewayError]);\n\n const handleOpenModal = useCallback(() => setIsModalOpen(true), []);\n const handleCloseModal = useCallback(() => setIsModalOpen(false), []);\n\n const HomePage = ({ onOpenModal }: { onOpenModal: () => void }) => (\n <Container maxWidth=\"lg\" sx={{ py: { xs: 4, md: 6 } }}>\n <Stack spacing={{ xs: 5, md: 7 }}>\n <Box\n sx={{\n display: \"flex\",\n flexDirection: { xs: \"column-reverse\", md: \"row\" },\n alignItems: \"center\",\n gap: { xs: 4, md: 8 },\n }}\n >\n <Stack spacing={3} sx={{ flex: 1, width: \"100%\" }}>\n <Box sx={{ display: \"flex\", alignItems: \"center\", gap: 2 }}>\n <Box\n component=\"img\"\n src={banditHeadLogoUrl}\n alt=\"Bandit AI logo\"\n sx={{ height: 48, width: 48, borderRadius: 3, boxShadow: \"0 18px 50px rgba(99, 102, 241, 0.35)\" }}\n />\n <Typography variant=\"overline\" color=\"primary.light\" sx={{ letterSpacing: 2 }}>\n Powered by Bandit Engine\n </Typography>\n </Box>\n <Typography variant=\"h3\" fontWeight={700}>\n {brandingText}\n </Typography>\n <Typography variant=\"body1\" color=\"text.secondary\">\n Build, brand, and launch your assistant with a drop-in chat surface plus a secure gateway for OpenAI or Ollama.\n </Typography>\n <Stack direction={{ xs: \"column\", sm: \"row\" }} spacing={2}>\n <Button component={RouterLink} to=\"/chat\" variant=\"contained\" color=\"primary\">\n Go to chat demo\n </Button>\n <Button variant=\"outlined\" color=\"secondary\" onClick={onOpenModal}>\n Open modal assistant\n </Button>\n </Stack>\n </Stack>\n <Box\n component=\"img\"\n src={burtsonLabsLogoUrl}\n alt=\"Burtson Labs logo\"\n sx={{\n width: \"100%\",\n maxWidth: 320,\n mx: { xs: \"auto\", md: 0 },\n display: \"block\",\n filter: \"drop-shadow(0 25px 45px rgba(15, 23, 42, 0.45))\",\n }}\n />\n </Box>\n <Box\n sx={{\n display: \"grid\",\n gap: 3,\n gridTemplateColumns: { xs: \"1fr\", md: \"repeat(3, minmax(0, 1fr))\" },\n }}\n >\n <Card sx={{ height: \"100%\", backdropFilter: \"blur(12px)\", backgroundColor: \"rgba(15, 23, 42, 0.64)\" }}>\n <CardContent>\n <Typography variant=\"h6\" gutterBottom>\n Configure in minutes\n </Typography>\n <Typography variant=\"body2\" color=\"text.secondary\">\n Edit <code>public/config.json</code> and <code>.env</code> to tailor models, personas, and branding for your product.\n </Typography>\n </CardContent>\n </Card>\n <Card sx={{ height: \"100%\", backdropFilter: \"blur(12px)\", backgroundColor: \"rgba(15, 23, 42, 0.64)\" }}>\n <CardContent>\n <Typography variant=\"h6\" gutterBottom>\n Ship secure gateways\n </Typography>\n <Typography variant=\"body2\" color=\"text.secondary\">\n Keep API keys server-side while proxying requests to OpenAI or Ollama through the included Express gateway.\n </Typography>\n </CardContent>\n </Card>\n <Card sx={{ height: \"100%\", backdropFilter: \"blur(12px)\", backgroundColor: \"rgba(15, 23, 42, 0.64)\" }}>\n <CardContent>\n <Typography variant=\"h6\" gutterBottom>\n Manage knowledge freely\n </Typography>\n <Typography variant=\"body2\" color=\"text.secondary\">\n Add memories, files, and tools directly in the management console that ships with this quickstart.\n </Typography>\n <Button component={RouterLink} to=\"/management\" variant=\"text\" color=\"secondary\" sx={{ mt: 2, px: 0 }}>\n Explore management console\n </Button>\n </CardContent>\n </Card>\n </Box>\n </Stack>\n </Container>\n );\n\n const ChatPage = ({ onOpenModal }: { onOpenModal: () => void }) => (\n <Container maxWidth=\"lg\" sx={{ py: 4, display: \"flex\", flexDirection: \"column\", gap: 3 }}>\n <Stack\n direction={{ xs: \"column\", md: \"row\" }}\n spacing={2}\n alignItems={{ xs: \"stretch\", md: \"center\" }}\n justifyContent=\"space-between\"\n >\n <Box>\n <Typography variant=\"h4\" fontWeight={700} gutterBottom>\n Chat demo\n </Typography>\n <Typography variant=\"body2\" color=\"text.secondary\">\n This route renders the full <code>{\\`<Chat />\\`}</code> surface powered by your quickstart gateway.\n </Typography>\n </Box>\n <Stack direction={{ xs: \"column\", sm: \"row\" }} spacing={1.5}>\n <Button component={RouterLink} to=\"/management\" variant=\"outlined\" color=\"secondary\">\n Management console\n </Button>\n <Button variant=\"contained\" color=\"primary\" onClick={onOpenModal}>\n Open modal assistant\n </Button>\n </Stack>\n </Stack>\n <Box\n sx={{\n flexGrow: 1,\n minHeight: 540,\n borderRadius: 3,\n overflow: \"hidden\",\n boxShadow: \"0 35px 90px rgba(15, 23, 42, 0.55)\",\n }}\n >\n <Chat />\n </Box>\n </Container>\n );\n\n const Header = ({ gatewayChip }: { gatewayChip: ReactNode }) => (\n <AppBar\n position=\"sticky\"\n color=\"transparent\"\n elevation={0}\n sx={{ borderBottom: \"1px solid rgba(148, 163, 184, 0.16)\", backdropFilter: \"blur(18px)\" }}\n >\n <Toolbar sx={{ gap: 2, flexWrap: \"wrap\" }}>\n <Button\n component={RouterLink}\n to=\"/\"\n color=\"inherit\"\n sx={{\n display: \"flex\",\n alignItems: \"center\",\n gap: 1.5,\n py: 1,\n px: 1.5,\n borderRadius: 2,\n textTransform: \"none\",\n bgcolor: \"transparent\",\n \"&:hover\": { bgcolor: \"rgba(99, 102, 241, 0.12)\" },\n }}\n >\n <Typography variant=\"h6\" sx={{ fontWeight: 600 }}>\n {brandingText}\n </Typography>\n </Button>\n <Box sx={{ flexGrow: 1 }} />\n {gatewayChip}\n <Stack direction=\"row\" spacing={1} flexWrap=\"wrap\" justifyContent=\"flex-end\">\n <Button component={RouterLink} to=\"/\" color=\"inherit\">\n Home\n </Button>\n <Button component={RouterLink} to=\"/management\" color=\"inherit\">\n Management\n </Button>\n <Button component={RouterLink} to=\"/chat\" variant=\"contained\" color=\"primary\">\n Go to chat\n </Button>\n </Stack>\n </Toolbar>\n </AppBar>\n );\n\n return (\n <Routes>\n <Route path=\"/management\" element={\n <ChatProvider packageSettings={packageSettings}>\n <Management />\n </ChatProvider>\n } />\n <Route path=\"/chat\" element={\n <ThemeProvider theme={banditQuickstartTheme}>\n <CssBaseline />\n <ChatProvider packageSettings={packageSettings}>\n <Box display=\"flex\" flexDirection=\"column\" minHeight=\"100vh\">\n <Box component=\"main\" sx={{ flexGrow: 1, display: \"flex\" }}>\n <ChatPage onOpenModal={handleOpenModal} />\n </Box>\n <ChatModal open={isModalOpen} onClose={handleCloseModal} />\n </Box>\n </ChatProvider>\n </ThemeProvider>\n } />\n <Route path=\"/*\" element={\n <ThemeProvider theme={banditQuickstartTheme}>\n <CssBaseline />\n <ChatProvider packageSettings={packageSettings}>\n <Box display=\"flex\" flexDirection=\"column\" minHeight=\"100vh\">\n <Header gatewayChip={gatewayChip} />\n <Box component=\"main\" sx={{ flexGrow: 1, display: \"flex\" }}>\n <Routes>\n <Route path=\"/\" element={<HomePage onOpenModal={handleOpenModal} />} />\n <Route path=\"*\" element={<Navigate to=\"/\" replace />} />\n </Routes>\n </Box>\n <ChatModal open={isModalOpen} onClose={handleCloseModal} />\n </Box>\n </ChatProvider>\n </ThemeProvider>\n } />\n </Routes>\n );\n}\n\nexport default App;\n`;\n\n const withResponse = template.replace(/__RESPONSE_STATUS__/g, responseStatusExpr);\n const withGatewayError = withResponse.replace(/__GATEWAY_ERROR__/g, gatewayErrorExpr);\n\n return ensureTrailingNewline(normalizeLineEndings(withGatewayError));\n};\n\nexport const buildBrandingConfig = (ctx: QuickstartTemplateContext): string =>\n formatJson({\n branding: {\n logoBase64: ctx.isDefaultLogo ? null : ctx.logoBase64,\n brandingText: ctx.brandingText,\n theme: \"bandit-dark\",\n hasTransparentLogo: ctx.isDefaultLogo ? true : ctx.hasTransparentLogo\n },\n knowledgeDocs: []\n });\n\nexport const buildGatewayServer = (ctx: QuickstartTemplateContext): string => {\n const modelsDefinition = JSON.stringify(ctx.gatewayModels, null, 2);\n\n const gatewaySource = `import express from \"express\";\nimport cors from \"cors\";\nimport dotenv from \"dotenv\";\n\ndotenv.config();\n\nconst app = express();\napp.use(cors());\napp.use(express.json({ limit: '50mb' }));\napp.use(express.urlencoded({ limit: '50mb', extended: true }));\n\nconst QUICKSTART_VERSION = \"0.1.0\";\nconst DEFAULT_PROVIDER = \"${ctx.defaultProvider}\";\nconst BASE_GATEWAY_MODELS = ${modelsDefinition};\nconst OLLAMA_BASE_URL = (process.env.OLLAMA_URL ?? \"http://localhost:11434\").replace(/\\\\/$/, \"\");\n\nconst toGatewayModels = () =>\n BASE_GATEWAY_MODELS.map((model) => ({\n ...model,\n created: Date.now(),\n modified_at: new Date().toISOString(),\n size: 0,\n digest: \"\",\n details: {\n format: \"chat\",\n family: model.provider,\n families: [model.provider],\n parameter_size: \"\",\n quantization_level: \"\",\n },\n }));\n\nconst requireOpenAIKey = () => {\n const key = process.env.OPENAI_API_KEY;\n if (!key) {\n throw new Error(\"Missing OPENAI_API_KEY. Add it to your .env file to route requests to OpenAI.\");\n }\n return key;\n};\n\n// Utility function to handle streaming responses\nconst handleStreamingResponse = async (upstreamResponse, res) => {\n res.setHeader('Content-Type', 'text/event-stream');\n res.setHeader('Cache-Control', 'no-cache');\n res.setHeader('Connection', 'keep-alive');\n res.setHeader('Access-Control-Allow-Origin', '*');\n \n try {\n // Get the readable stream from the response\n const reader = upstreamResponse.body.getReader();\n \n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n \n // Write the chunk to the response\n res.write(value);\n }\n \n res.end();\n } catch (error) {\n console.error('Streaming error:', error);\n // Fallback to non-streaming\n const text = await upstreamResponse.text();\n res.send(text);\n }\n};\n\n// ============================================================================\n// GENERAL HEALTH & MODELS\n// ============================================================================\n\napp.get(\"/api/health\", async (_req, res) => {\n const providers = [];\n \n // Check OpenAI\n try {\n const openaiKey = process.env.OPENAI_API_KEY;\n if (openaiKey) {\n const response = await fetch(\"https://api.openai.com/v1/models\", {\n headers: { \"Authorization\": \\`Bearer \\${openaiKey}\\` }\n });\n providers.push({\n name: \"openai\",\n status: response.ok ? \"healthy\" : \"unhealthy\",\n provider: \"openai\"\n });\n } else {\n providers.push({\n name: \"openai\", \n status: \"unconfigured\",\n provider: \"openai\",\n error: \"API key not configured\"\n });\n }\n } catch (error) {\n providers.push({\n name: \"openai\",\n status: \"unhealthy\", \n provider: \"openai\",\n error: error.message\n });\n }\n\n // Check Ollama\n try {\n console.log(\\`Checking Ollama health at: \\${OLLAMA_BASE_URL}/api/tags\\`);\n const response = await fetch(\\`\\${OLLAMA_BASE_URL}/api/tags\\`);\n const status = response.ok ? \"healthy\" : \"unhealthy\";\n console.log(\\`Ollama health check result: \\${status}\\`);\n providers.push({\n name: \"ollama\",\n status: status,\n provider: \"ollama\",\n url: OLLAMA_BASE_URL\n });\n } catch (error) {\n console.log(\\`Ollama health check error: \\${error.message}\\`);\n providers.push({\n name: \"ollama\",\n status: \"offline\",\n provider: \"ollama\",\n error: error.message,\n url: OLLAMA_BASE_URL\n });\n }\n\n const overallHealthy = providers.some(p => p.status === \"healthy\");\n \n res.json({\n status: overallHealthy ? \"healthy\" : \"unhealthy\",\n version: QUICKSTART_VERSION,\n uptime: Math.round(process.uptime()),\n providers\n });\n});\n\napp.get(\"/api/models\", (_req, res) => {\n res.json({ models: toGatewayModels() });\n});\n\n// ============================================================================\n// OPENAI ROUTES\n// ============================================================================\n\n// OpenAI Health Check\napp.get(\"/api/openai/health\", async (_req, res) => {\n try {\n const openaiKey = process.env.OPENAI_API_KEY;\n if (!openaiKey) {\n return res.status(503).json({\n status: \"unhealthy\",\n openai_status: false,\n error: \"OpenAI API key not configured\",\n provider: \"openai\"\n });\n }\n\n const response = await fetch(\"https://api.openai.com/v1/models\", {\n headers: { \"Authorization\": \\`Bearer \\${openaiKey}\\` }\n });\n\n const isHealthy = response.ok;\n res.json({\n status: isHealthy ? \"healthy\" : \"unhealthy\",\n openai_status: isHealthy,\n provider: \"openai\"\n });\n } catch (error) {\n res.status(503).json({\n status: \"unhealthy\",\n openai_status: false,\n error: error.message,\n provider: \"openai\"\n });\n }\n});\n\n// OpenAI Chat Completions\napp.post(\"/api/openai/chat/completions\", async (req, res) => {\n try {\n const openaiKey = requireOpenAIKey();\n const isStreaming = req.body?.stream === true;\n\n // Strip the openai: prefix from model name and remove provider field\n const { provider, ...cleanBody } = req.body;\n const requestBody = {\n ...cleanBody,\n model: req.body?.model?.replace(/^openai:/, \"\") || \"gpt-4o\"\n };\n\n const response = await fetch(\"https://api.openai.com/v1/chat/completions\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Authorization\": \\`Bearer \\${openaiKey}\\`\n },\n body: JSON.stringify(requestBody)\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n return res.status(response.status).json({\n error: \\`OpenAI chat failed: \\${response.status}\\`,\n details: errorText\n });\n }\n\n if (isStreaming) {\n await handleStreamingResponse(response, res);\n } else {\n const text = await response.text();\n res.setHeader('Content-Type', 'application/json');\n res.send(text);\n }\n } catch (error) {\n res.status(500).json({ error: error.message });\n }\n});\n\n// OpenAI Chat (alternative route)\napp.post(\"/api/openai/chat\", async (req, res) => {\n // Route to the completions endpoint for compatibility\n req.url = \"/api/openai/chat/completions\";\n return app._router.handle(req, res);\n});\n\n// OpenAI Completions\napp.post(\"/api/openai/completions\", async (req, res) => {\n try {\n const openaiKey = requireOpenAIKey();\n const isStreaming = req.body?.stream === true;\n\n // Strip the openai: prefix from model name and remove provider field\n const { provider, ...cleanBody } = req.body;\n const requestBody = {\n ...cleanBody,\n model: req.body?.model?.replace(/^openai:/, \"\") || \"gpt-3.5-turbo-instruct\"\n };\n\n const response = await fetch(\"https://api.openai.com/v1/completions\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Authorization\": \\`Bearer \\${openaiKey}\\`\n },\n body: JSON.stringify(requestBody)\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n return res.status(response.status).json({\n error: \\`OpenAI completions failed: \\${response.status}\\`,\n details: errorText\n });\n }\n\n if (isStreaming) {\n await handleStreamingResponse(response, res);\n } else {\n const text = await response.text();\n res.setHeader('Content-Type', 'application/json');\n res.send(text);\n }\n } catch (error) {\n res.status(500).json({ error: error.message });\n }\n});\n\n// OpenAI Generate (converts to chat format for conversation starters)\napp.post(\"/api/openai/generate\", async (req, res) => {\n try {\n const openaiKey = requireOpenAIKey();\n const prompt = req.body?.prompt || \"\";\n const model = req.body?.model?.replace(/^openai:/, \"\") || \"gpt-4o\";\n const isStreaming = req.body?.stream === true;\n\n // Convert generate request to chat format\n const chatBody = {\n model: model,\n messages: [\n {\n role: \"user\",\n content: prompt\n }\n ],\n stream: isStreaming,\n max_tokens: req.body?.max_tokens || 150,\n temperature: req.body?.temperature || 0.7\n };\n\n const response = await fetch(\"https://api.openai.com/v1/chat/completions\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Authorization\": \\`Bearer \\${openaiKey}\\`\n },\n body: JSON.stringify(chatBody)\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n return res.status(response.status).json({\n error: \\`OpenAI generate failed: \\${response.status}\\`,\n details: errorText\n });\n }\n\n if (isStreaming) {\n await handleStreamingResponse(response, res);\n } else {\n const data = await response.json();\n // Convert chat response back to generate format\n const generateResponse = {\n model: model,\n created_at: new Date().toISOString(),\n response: data.choices?.[0]?.message?.content || \"\",\n done: true,\n context: [],\n total_duration: 0,\n load_duration: 0,\n prompt_eval_count: 0,\n prompt_eval_duration: 0,\n eval_count: data.usage?.completion_tokens || 0,\n eval_duration: 0\n };\n res.json(generateResponse);\n }\n } catch (error) {\n res.status(500).json({ error: error.message });\n }\n});\n\n// OpenAI Models\napp.get(\"/api/openai/models\", async (_req, res) => {\n try {\n const openaiKey = requireOpenAIKey();\n\n const response = await fetch(\"https://api.openai.com/v1/models\", {\n headers: { \"Authorization\": \\`Bearer \\${openaiKey}\\` }\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n return res.status(response.status).json({\n error: \\`OpenAI models failed: \\${response.status}\\`,\n details: errorText\n });\n }\n\n const text = await response.text();\n res.setHeader('Content-Type', 'application/json');\n res.send(text);\n } catch (error) {\n res.status(500).json({ error: error.message });\n }\n});\n\n// ============================================================================\n// OLLAMA ROUTES\n// ============================================================================\n\n// Ollama Health Check\napp.get(\"/api/ollama/health\", async (_req, res) => {\n try {\n console.log(\\`Ollama health check at: \\${OLLAMA_BASE_URL}/api/tags\\`);\n const response = await fetch(\\`\\${OLLAMA_BASE_URL}/api/tags\\`);\n const isHealthy = response.ok;\n\n res.json({\n status: isHealthy ? \"healthy\" : \"unhealthy\",\n ollama_status: isHealthy,\n provider: \"ollama\",\n url: OLLAMA_BASE_URL\n });\n } catch (error) {\n console.log(\\`Ollama health check error: \\${error.message}\\`);\n res.status(503).json({\n status: \"offline\",\n ollama_status: false,\n error: error.message,\n provider: \"ollama\",\n url: OLLAMA_BASE_URL\n });\n }\n});\n\n// Ollama Chat\napp.post(\"/api/ollama/chat\", async (req, res) => {\n try {\n const response = await fetch(\\`\\${OLLAMA_BASE_URL}/api/chat\\`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(req.body)\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n return res.status(response.status).json({\n error: \\`Ollama chat failed: \\${response.status}\\`,\n details: errorText\n });\n }\n\n const isStreaming = req.body?.stream === true;\n if (isStreaming) {\n await handleStreamingResponse(response, res);\n } else {\n const text = await response.text();\n res.setHeader('Content-Type', 'application/json');\n res.send(text);\n }\n } catch (error) {\n res.status(500).json({ error: error.message });\n }\n});\n\n// Ollama Generate\napp.post(\"/api/ollama/generate\", async (req, res) => {\n try {\n const response = await fetch(\\`\\${OLLAMA_BASE_URL}/api/generate\\`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(req.body)\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n return res.status(response.status).json({\n error: \\`Ollama generate failed: \\${response.status}\\`,\n details: errorText\n });\n }\n\n const isStreaming = req.body?.stream === true;\n if (isStreaming) {\n await handleStreamingResponse(response, res);\n } else {\n const text = await response.text();\n res.setHeader('Content-Type', 'application/json');\n res.send(text);\n }\n } catch (error) {\n res.status(500).json({ error: error.message });\n }\n});\n\n// Ollama Models\napp.get(\"/api/ollama/models\", async (_req, res) => {\n try {\n const response = await fetch(\\`\\${OLLAMA_BASE_URL}/api/tags\\`);\n\n if (!response.ok) {\n const errorText = await response.text();\n return res.status(response.status).json({\n error: \\`Ollama models failed: \\${response.status}\\`,\n details: errorText\n });\n }\n\n const text = await response.text();\n res.setHeader('Content-Type', 'application/json');\n res.send(text);\n } catch (error) {\n res.status(500).json({ error: error.message });\n }\n});\n\n// Ollama Embedding\napp.post(\"/api/ollama/embed\", async (req, res) => {\n try {\n const response = await fetch(\\`\\${OLLAMA_BASE_URL}/api/embeddings\\`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(req.body)\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n return res.status(response.status).json({\n error: \\`Ollama embed failed: \\${response.status}\\`,\n details: errorText\n });\n }\n\n const text = await response.text();\n res.setHeader('Content-Type', 'application/json');\n res.send(text);\n } catch (error) {\n res.status(500).json({ error: error.message });\n }\n});\n\n// ============================================================================\n// TTS ROUTES (not implemented - placeholder for compatibility)\n// ============================================================================\n\n// TTS Main endpoint\napp.post(\"/api/tts\", async (_req, res) => {\n res.status(501).json({\n error: \"TTS integration not implemented\",\n message: \"Text-to-speech functionality is not available in this quickstart gateway\"\n });\n});\n\n// TTS Stream endpoint\napp.post(\"/api/tts/stream\", async (_req, res) => {\n res.status(501).json({\n error: \"TTS streaming not implemented\", \n message: \"Text-to-speech streaming functionality is not available in this quickstart gateway\"\n });\n});\n\n// TTS Real-time stream endpoint\napp.post(\"/api/tts/stream-realtime\", async (_req, res) => {\n res.status(501).json({\n error: \"TTS real-time streaming not implemented\",\n message: \"Text-to-speech real-time streaming functionality is not available in this quickstart gateway\"\n });\n});\n\n// TTS Models endpoint - returns empty models\napp.get(\"/api/tts/models\", async (_req, res) => {\n res.json({\n models: []\n });\n});\n\n// TTS Available models endpoint - returns empty models with defaults\napp.get(\"/api/tts/available-models\", async (_req, res) => {\n res.json({\n models: [],\n defaultModel: null,\n fallbackModel: null\n });\n});\n\n// ============================================================================\n// MCP (Model Context Protocol) TOOL ROUTES\n// ============================================================================\n\n// MCP Health Check\napp.get(\"/api/mcp/health\", async (_req, res) => {\n res.json({\n status: \"healthy\",\n timestamp: new Date().toISOString(),\n totalTools: 0,\n enabledTools: 0,\n availableTools: [],\n message: \"MCP tools are not implemented in this quickstart gateway\"\n });\n});\n\n// Get available MCP tools\napp.get(\"/api/mcp/tools\", async (_req, res) => {\n res.json([]);\n});\n\n// News endpoint - placeholder implementation\napp.get(\"/api/mcp/news\", async (req, res) => {\n const { topic = \"general\", count = 10, headlines = false } = req.query;\n \n res.status(501).json({\n error: \"News service not implemented\",\n message: \"MCP news functionality is not available in this quickstart gateway\",\n suggestion: \"Implement this endpoint to connect to a news API service\",\n requestedParams: { topic, count, headlines }\n });\n});\n\n// Weather endpoint - placeholder implementation \napp.get(\"/api/mcp/weather\", async (req, res) => {\n const { zip, latitude, longitude } = req.query;\n \n res.status(501).json({\n error: \"Weather service not implemented\", \n message: \"MCP weather functionality is not available in this quickstart gateway\",\n suggestion: \"Implement this endpoint to connect to a weather API service\",\n requestedParams: { zip, latitude, longitude }\n });\n});\n\n// Documentation search endpoint - placeholder implementation\napp.get(\"/api/mcp/docs\", async (req, res) => {\n const { query, framework, count = 10 } = req.query;\n \n if (!query) {\n return res.status(400).json({\n error: \"Query parameter is required\",\n message: \"Please provide a search query\"\n });\n }\n \n res.status(501).json({\n error: \"Documentation search not implemented\",\n message: \"MCP docs functionality is not available in this quickstart gateway\", \n suggestion: \"Implement this endpoint to connect to a documentation search service\",\n requestedParams: { query, framework, count }\n });\n});\n\n// Get supported documentation frameworks\napp.get(\"/api/mcp/docs/frameworks\", async (_req, res) => {\n res.json({\n frameworks: [],\n message: \"Documentation frameworks not configured in this quickstart gateway\"\n });\n});\n\n// Sports scores endpoint - placeholder implementation\napp.get(\"/api/mcp/sports\", async (req, res) => {\n const { league, date } = req.query;\n \n res.status(501).json({\n error: \"Sports service not implemented\",\n message: \"MCP sports functionality is not available in this quickstart gateway\",\n suggestion: \"Implement this endpoint to connect to a sports API service\", \n requestedParams: { league, date }\n });\n});\n\n// Get supported sports leagues\napp.get(\"/api/mcp/sports/leagues\", async (_req, res) => {\n res.json({\n leagues: [],\n message: \"Sports leagues not configured in this quickstart gateway\"\n });\n});\n\n// Image generation endpoint - placeholder implementation\napp.post(\"/api/mcp/generate-image\", async (req, res) => {\n const { prompt, size, quality, style } = req.body;\n \n if (!prompt) {\n return res.status(400).json({\n error: \"Prompt is required\",\n message: \"Please provide a prompt for image generation\"\n });\n }\n \n res.status(501).json({\n success: false,\n error: \"Image generation not implemented\",\n message: \"MCP image generation functionality is not available in this quickstart gateway\",\n suggestion: \"Implement this endpoint to connect to an image generation API service\",\n requestedParams: { prompt, size, quality, style }\n });\n});\n\n// ============================================================================\n// NOT IMPLEMENTED ROUTES (for graceful degradation)\n// ============================================================================\n\napp.all(\"/api/anthropic/*\", (_req, res) => {\n res.status(501).json({\n error: \"Anthropic integration not implemented\",\n message: \"This quickstart gateway only supports OpenAI and Ollama providers\"\n });\n});\n\napp.all(\"/api/azure/*\", (_req, res) => {\n res.status(501).json({\n error: \"Azure OpenAI integration not implemented\", \n message: \"This quickstart gateway only supports OpenAI and Ollama providers\"\n });\n});\n\nconst port = Number(process.env.PORT ?? ${ctx.gatewayPort});\napp.listen(port, () => {\n console.log(\"⚑ Bandit quickstart gateway ready on http://localhost:\" + port);\n console.log(\"πŸ“‘ Supported providers: OpenAI, Ollama\");\n console.log(\"πŸ”— Provider-specific routes:\");\n console.log(\" β€’ /api/openai/* - OpenAI endpoints\");\n console.log(\" β€’ /api/ollama/* - Ollama endpoints\");\n console.log(\" β€’ /api/health - Overall health check\");\n});\n`;\n\n return ensureTrailingNewline(normalizeLineEndings(gatewaySource));\n};\n\nexport const buildGitignore = (): string =>\n ensureTrailingNewline(\n normalizeLineEndings(\n `node_modules\\n.env\\n.vite\\n.idea\\n.DS_Store\\ncoverage\\ndist\\n`\n )\n );\n\nexport const buildNpmrc = (): string => {\n const tokenExpr = \"${GITHUB_NPM_TOKEN}\";\n const template = `@burtson-labs:registry=https://npm.pkg.github.com/\n//npm.pkg.github.com/:_authToken=__NPM_TOKEN__\n`;\n\n return ensureTrailingNewline(\n normalizeLineEndings(template.replace(/__NPM_TOKEN__/g, tokenExpr))\n );\n};\n\nexport const buildReadme = (ctx: QuickstartTemplateContext): string =>\n ensureTrailingNewline(\n normalizeLineEndings(\n `# ${ctx.projectTitle} β€” Bandit Quickstart\\n\\nThis project was generated by the Bandit Engine CLI. It ships with a React + Vite frontend that consumes \\`@burtson-labs/bandit-engine\\` and a lightweight Express gateway you can adapt for production.\\n\\n## πŸš€ Next steps\\n- \\`npm install\\`\\n- \\`cp .env.example .env\\`\\n- Fill in \\`OPENAI_API_KEY\\` (or point \\`OLLAMA_URL\\` at your local server)\\n- \\`npm run dev\\`\\n\\nThe command runs the gateway and the frontend together. Visit http://localhost:${ctx.frontendPort} to see the chat and modal in action.\\n\\n## πŸ”§ Customizing your assistant\\n- **Branding & personas**: edit \\`public/config.json\\` to tweak logos, colors, and starter models.\\n- **Provider defaults**: update \\`.env\\` to switch providers or change the default upstream model IDs.\\n- **Gateway routes**: open \\`server/gateway.js\\` to add auth, logging, or connect additional providers.\\n\\n## πŸ“¦ What’s inside\\n- React + Vite 5 with Material UI theming\\n- Bandit chat surface + modal wired via \\`ChatProvider\\`\\n- Express gateway proxying OpenAI or Ollama to keep API keys server-side\\n- Friendly defaults you can evolve into your production stack\\n\\nNeed more? Run \\`npx @burtson-labs/bandit-engine create --help\\` to explore additional options.\\n`\n )\n );\n"],"mappings":"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AA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA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","names":["import_node_path","import_node_path","path","fs","prompts","path"]}
@@ -19,7 +19,7 @@ When the CLI finishes you will have a directory that contains:
19
19
  - `public/config.json` β€” branding + persona defaults that load at runtime
20
20
  - `server/gateway.js` β€” Express proxy for OpenAI or Ollama
21
21
  - `.env.example` β€” environment flags for the frontend and gateway
22
- - `.npmrc` β€” points the `@burtson-labs` scope at GitHub Packages and pulls `GITHUB_NPM_TOKEN` from your environment
22
+ - `.npmrc` β€” legacy registry config for older private builds; once the npm package is public you can delete this file or ignore it
23
23
 
24
24
  Run the following commands to start development:
25
25
 
@@ -32,10 +32,8 @@ npm run dev
32
32
 
33
33
  The script runs the gateway and the frontend concurrently. Visit `http://localhost:5183` and start chatting.
34
34
 
35
- > **Authentication**
36
- > Because the package is currently private, authenticate with GitHub Packages before installing dependencies.
37
- > 1. Run `npm login --registry=https://npm.pkg.github.com --scope=@burtson-labs`, **or** set `export GITHUB_NPM_TOKEN=<your PAT>` in your shell.
38
- > 2. The generated `.npmrc` reads `GITHUB_NPM_TOKEN` automatically, so `npm install` can pull `@burtson-labs` packages.
35
+ > **Authentication**
36
+ > Public releases install from `registry.npmjs.org` and do not require any special npm token. If you are working from an older scaffold that still points to GitHub Packages, either update the registry line to `https://registry.npmjs.org/` or remove the `.npmrc` file.
39
37
 
40
38
  ## Common options
41
39
 
@@ -64,7 +62,7 @@ Prefer Ollama over OpenAI? After copying `.env.example` to `.env`, replace the p
64
62
 
65
63
  ```ini
66
64
  VITE_GATEWAY_PROVIDER=ollama
67
- VITE_DEFAULT_MODEL=llava:latest # or any model installed on your Ollama host
65
+ VITE_DEFAULT_MODEL=llava:latest # or any model installed on your Ollama host
68
66
  VITE_FALLBACK_MODEL=gemma2:9b-instruct # optional, useful for text-only fallbacks
69
67
  OLLAMA_URL=http://localhost:11434
70
68
  ```