nova-terminal-assistant 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of nova-terminal-assistant might be problematic. Click here for more details.
- package/README.md +358 -0
- package/bin/nova +38 -0
- package/bin/nova.js +12 -0
- package/package.json +67 -0
- package/src/cli/commands/SmartCompletion.ts +458 -0
- package/src/cli/index.ts +5 -0
- package/src/cli/startup/IFlowRepl.ts +212 -0
- package/src/cli/startup/InkBasedRepl.ts +1056 -0
- package/src/cli/startup/InteractiveRepl.ts +2833 -0
- package/src/cli/startup/NovaApp.ts +1861 -0
- package/src/cli/startup/index.ts +4 -0
- package/src/cli/startup/parseArgs.ts +293 -0
- package/src/cli/test-modules.ts +27 -0
- package/src/cli/ui/IFlowDropdown.ts +425 -0
- package/src/cli/ui/ModernReplUI.ts +276 -0
- package/src/cli/ui/SimpleSelector2.ts +215 -0
- package/src/cli/ui/components/ConfirmDialog.ts +176 -0
- package/src/cli/ui/components/ErrorPanel.ts +364 -0
- package/src/cli/ui/components/InkAppRunner.tsx +67 -0
- package/src/cli/ui/components/InkComponents.tsx +613 -0
- package/src/cli/ui/components/NovaInkApp.tsx +312 -0
- package/src/cli/ui/components/ProgressBar.ts +177 -0
- package/src/cli/ui/components/ProgressIndicator.ts +298 -0
- package/src/cli/ui/components/QuickActions.ts +396 -0
- package/src/cli/ui/components/SimpleErrorPanel.ts +231 -0
- package/src/cli/ui/components/StatusBar.ts +194 -0
- package/src/cli/ui/components/ThinkingBlockRenderer.ts +401 -0
- package/src/cli/ui/components/index.ts +27 -0
- package/src/cli/ui/ink-prototype.tsx +347 -0
- package/src/cli/utils/CliUI.ts +336 -0
- package/src/cli/utils/CompletionHelper.ts +388 -0
- package/src/cli/utils/EnhancedCompleter.test.ts +226 -0
- package/src/cli/utils/EnhancedCompleter.ts +513 -0
- package/src/cli/utils/ErrorEnhancer.ts +429 -0
- package/src/cli/utils/OutputFormatter.ts +193 -0
- package/src/cli/utils/index.ts +9 -0
- package/src/core/agents/AgentOrchestrator.ts +515 -0
- package/src/core/agents/index.ts +17 -0
- package/src/core/audit/AuditLogger.ts +509 -0
- package/src/core/audit/index.ts +11 -0
- package/src/core/auth/AuthManager.d.ts.map +1 -0
- package/src/core/auth/AuthManager.ts +138 -0
- package/src/core/auth/index.d.ts.map +1 -0
- package/src/core/auth/index.ts +2 -0
- package/src/core/config/ConfigManager.d.ts.map +1 -0
- package/src/core/config/ConfigManager.test.ts +183 -0
- package/src/core/config/ConfigManager.ts +1219 -0
- package/src/core/config/index.d.ts.map +1 -0
- package/src/core/config/index.ts +1 -0
- package/src/core/context/ContextBuilder.d.ts.map +1 -0
- package/src/core/context/ContextBuilder.ts +171 -0
- package/src/core/context/ContextCompressor.d.ts.map +1 -0
- package/src/core/context/ContextCompressor.ts +642 -0
- package/src/core/context/LayeredMemoryManager.ts +657 -0
- package/src/core/context/MemoryDiscovery.d.ts.map +1 -0
- package/src/core/context/MemoryDiscovery.ts +175 -0
- package/src/core/context/defaultSystemPrompt.d.ts.map +1 -0
- package/src/core/context/defaultSystemPrompt.ts +35 -0
- package/src/core/context/index.d.ts.map +1 -0
- package/src/core/context/index.ts +22 -0
- package/src/core/extensions/SkillGenerator.ts +421 -0
- package/src/core/extensions/SkillInstaller.d.ts.map +1 -0
- package/src/core/extensions/SkillInstaller.ts +257 -0
- package/src/core/extensions/SkillRegistry.d.ts.map +1 -0
- package/src/core/extensions/SkillRegistry.ts +361 -0
- package/src/core/extensions/SkillValidator.ts +525 -0
- package/src/core/extensions/index.ts +15 -0
- package/src/core/index.d.ts.map +1 -0
- package/src/core/index.ts +42 -0
- package/src/core/mcp/McpManager.d.ts.map +1 -0
- package/src/core/mcp/McpManager.ts +632 -0
- package/src/core/mcp/index.d.ts.map +1 -0
- package/src/core/mcp/index.ts +2 -0
- package/src/core/model/ModelClient.d.ts.map +1 -0
- package/src/core/model/ModelClient.ts +217 -0
- package/src/core/model/ModelConnectionTester.ts +363 -0
- package/src/core/model/ModelValidator.ts +348 -0
- package/src/core/model/index.d.ts.map +1 -0
- package/src/core/model/index.ts +6 -0
- package/src/core/model/providers/AnthropicProvider.d.ts.map +1 -0
- package/src/core/model/providers/AnthropicProvider.ts +279 -0
- package/src/core/model/providers/CodingPlanProvider.d.ts.map +1 -0
- package/src/core/model/providers/CodingPlanProvider.ts +210 -0
- package/src/core/model/providers/OllamaCloudProvider.d.ts.map +1 -0
- package/src/core/model/providers/OllamaCloudProvider.ts +405 -0
- package/src/core/model/providers/OllamaManager.d.ts.map +1 -0
- package/src/core/model/providers/OllamaManager.ts +201 -0
- package/src/core/model/providers/OllamaProvider.d.ts.map +1 -0
- package/src/core/model/providers/OllamaProvider.ts +73 -0
- package/src/core/model/providers/OpenAICompatibleProvider.d.ts.map +1 -0
- package/src/core/model/providers/OpenAICompatibleProvider.ts +327 -0
- package/src/core/model/providers/OpenAIProvider.d.ts.map +1 -0
- package/src/core/model/providers/OpenAIProvider.ts +29 -0
- package/src/core/model/providers/index.d.ts.map +1 -0
- package/src/core/model/providers/index.ts +12 -0
- package/src/core/model/types.d.ts.map +1 -0
- package/src/core/model/types.ts +77 -0
- package/src/core/security/ApprovalManager.d.ts.map +1 -0
- package/src/core/security/ApprovalManager.ts +174 -0
- package/src/core/security/FileFilter.d.ts.map +1 -0
- package/src/core/security/FileFilter.ts +141 -0
- package/src/core/security/HookExecutor.d.ts.map +1 -0
- package/src/core/security/HookExecutor.ts +178 -0
- package/src/core/security/SandboxExecutor.ts +447 -0
- package/src/core/security/index.d.ts.map +1 -0
- package/src/core/security/index.ts +8 -0
- package/src/core/session/AgentLoop.d.ts.map +1 -0
- package/src/core/session/AgentLoop.ts +501 -0
- package/src/core/session/SessionManager.d.ts.map +1 -0
- package/src/core/session/SessionManager.test.ts +183 -0
- package/src/core/session/SessionManager.ts +460 -0
- package/src/core/session/index.d.ts.map +1 -0
- package/src/core/session/index.ts +3 -0
- package/src/core/telemetry/Telemetry.d.ts.map +1 -0
- package/src/core/telemetry/Telemetry.ts +90 -0
- package/src/core/telemetry/TelemetryService.ts +531 -0
- package/src/core/telemetry/index.d.ts.map +1 -0
- package/src/core/telemetry/index.ts +12 -0
- package/src/core/testing/AutoFixer.ts +385 -0
- package/src/core/testing/ErrorAnalyzer.ts +499 -0
- package/src/core/testing/TestRunner.ts +265 -0
- package/src/core/testing/agent-cli-tests.ts +538 -0
- package/src/core/testing/index.ts +11 -0
- package/src/core/tools/ToolRegistry.d.ts.map +1 -0
- package/src/core/tools/ToolRegistry.test.ts +206 -0
- package/src/core/tools/ToolRegistry.ts +260 -0
- package/src/core/tools/impl/EditFileTool.d.ts.map +1 -0
- package/src/core/tools/impl/EditFileTool.ts +97 -0
- package/src/core/tools/impl/ListDirectoryTool.d.ts.map +1 -0
- package/src/core/tools/impl/ListDirectoryTool.ts +142 -0
- package/src/core/tools/impl/MemoryTool.d.ts.map +1 -0
- package/src/core/tools/impl/MemoryTool.ts +102 -0
- package/src/core/tools/impl/ReadFileTool.d.ts.map +1 -0
- package/src/core/tools/impl/ReadFileTool.ts +58 -0
- package/src/core/tools/impl/SearchContentTool.d.ts.map +1 -0
- package/src/core/tools/impl/SearchContentTool.ts +94 -0
- package/src/core/tools/impl/SearchFileTool.d.ts.map +1 -0
- package/src/core/tools/impl/SearchFileTool.ts +61 -0
- package/src/core/tools/impl/ShellTool.d.ts.map +1 -0
- package/src/core/tools/impl/ShellTool.ts +118 -0
- package/src/core/tools/impl/TaskTool.d.ts.map +1 -0
- package/src/core/tools/impl/TaskTool.ts +207 -0
- package/src/core/tools/impl/TodoTool.d.ts.map +1 -0
- package/src/core/tools/impl/TodoTool.ts +122 -0
- package/src/core/tools/impl/WebFetchTool.d.ts.map +1 -0
- package/src/core/tools/impl/WebFetchTool.ts +103 -0
- package/src/core/tools/impl/WebSearchTool.d.ts.map +1 -0
- package/src/core/tools/impl/WebSearchTool.ts +89 -0
- package/src/core/tools/impl/WriteFileTool.d.ts.map +1 -0
- package/src/core/tools/impl/WriteFileTool.ts +49 -0
- package/src/core/tools/impl/index.d.ts.map +1 -0
- package/src/core/tools/impl/index.ts +16 -0
- package/src/core/tools/index.d.ts.map +1 -0
- package/src/core/tools/index.ts +7 -0
- package/src/core/tools/schemas/execution.d.ts.map +1 -0
- package/src/core/tools/schemas/execution.ts +42 -0
- package/src/core/tools/schemas/file.d.ts.map +1 -0
- package/src/core/tools/schemas/file.ts +119 -0
- package/src/core/tools/schemas/index.d.ts.map +1 -0
- package/src/core/tools/schemas/index.ts +11 -0
- package/src/core/tools/schemas/memory.d.ts.map +1 -0
- package/src/core/tools/schemas/memory.ts +52 -0
- package/src/core/tools/schemas/orchestration.d.ts.map +1 -0
- package/src/core/tools/schemas/orchestration.ts +44 -0
- package/src/core/tools/schemas/search.d.ts.map +1 -0
- package/src/core/tools/schemas/search.ts +112 -0
- package/src/core/tools/schemas/todo.d.ts.map +1 -0
- package/src/core/tools/schemas/todo.ts +32 -0
- package/src/core/tools/schemas/web.d.ts.map +1 -0
- package/src/core/tools/schemas/web.ts +86 -0
- package/src/core/types/config.d.ts.map +1 -0
- package/src/core/types/config.ts +200 -0
- package/src/core/types/errors.d.ts.map +1 -0
- package/src/core/types/errors.ts +204 -0
- package/src/core/types/index.d.ts.map +1 -0
- package/src/core/types/index.ts +8 -0
- package/src/core/types/session.d.ts.map +1 -0
- package/src/core/types/session.ts +216 -0
- package/src/core/types/tools.d.ts.map +1 -0
- package/src/core/types/tools.ts +157 -0
- package/src/core/utils/CheckpointManager.d.ts.map +1 -0
- package/src/core/utils/CheckpointManager.ts +327 -0
- package/src/core/utils/Logger.d.ts.map +1 -0
- package/src/core/utils/Logger.ts +98 -0
- package/src/core/utils/RetryManager.ts +471 -0
- package/src/core/utils/TokenCounter.d.ts.map +1 -0
- package/src/core/utils/TokenCounter.ts +414 -0
- package/src/core/utils/VectorMemoryStore.ts +440 -0
- package/src/core/utils/helpers.d.ts.map +1 -0
- package/src/core/utils/helpers.ts +89 -0
- package/src/core/utils/index.d.ts.map +1 -0
- package/src/core/utils/index.ts +19 -0
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// CodingPlanProvider - Support for Chinese Coding Plan platforms
|
|
3
|
+
// ============================================================================
|
|
4
|
+
//
|
|
5
|
+
// Supports multiple Coding Plan platforms with a single API key:
|
|
6
|
+
// - Alibaba Cloud (阿里云百炼)
|
|
7
|
+
// - Tencent Cloud (腾讯云)
|
|
8
|
+
// - Volcengine (火山引擎)
|
|
9
|
+
// - Baidu Qianfan (百度千帆)
|
|
10
|
+
// - Kimi Code
|
|
11
|
+
// - Zhipu AI (智谱)
|
|
12
|
+
// - MiniMax
|
|
13
|
+
//
|
|
14
|
+
// All platforms use OpenAI-compatible API with custom base URLs
|
|
15
|
+
// ============================================================================
|
|
16
|
+
|
|
17
|
+
import { OpenAICompatibleProvider, type OpenAICompatibleConfig } from './OpenAICompatibleProvider.js';
|
|
18
|
+
|
|
19
|
+
/** Coding Plan platform identifiers */
|
|
20
|
+
export type CodingPlanPlatform =
|
|
21
|
+
| 'alibaba'
|
|
22
|
+
| 'tencent'
|
|
23
|
+
| 'volcengine'
|
|
24
|
+
| 'baidu'
|
|
25
|
+
| 'kimi'
|
|
26
|
+
| 'zhipu'
|
|
27
|
+
| 'minimax'
|
|
28
|
+
| 'custom';
|
|
29
|
+
|
|
30
|
+
/** Coding Plan configuration */
|
|
31
|
+
export interface CodingPlanConfig extends Omit<OpenAICompatibleConfig, 'baseUrl'> {
|
|
32
|
+
platform: CodingPlanPlatform;
|
|
33
|
+
/** Custom base URL (required if platform is 'custom') */
|
|
34
|
+
customBaseUrl?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Platform-specific configurations */
|
|
38
|
+
const PLATFORM_CONFIGS: Record<CodingPlanPlatform, {
|
|
39
|
+
name: string;
|
|
40
|
+
baseUrl: string;
|
|
41
|
+
models: string[];
|
|
42
|
+
anthropicBaseUrl?: string;
|
|
43
|
+
}> = {
|
|
44
|
+
alibaba: {
|
|
45
|
+
name: 'Alibaba Cloud Coding Plan',
|
|
46
|
+
baseUrl: 'https://coding.dashscope.aliyuncs.com/v1',
|
|
47
|
+
anthropicBaseUrl: 'https://coding.dashscope.aliyuncs.com/apps/anthropic',
|
|
48
|
+
models: ['qwen3.5-plus', 'qwen3-coder', 'glm-5', 'minimax-m2.5', 'kimi-k2.5'],
|
|
49
|
+
},
|
|
50
|
+
tencent: {
|
|
51
|
+
name: 'Tencent Cloud Coding Plan',
|
|
52
|
+
baseUrl: 'https://api.hunyuan.cloud.tencent.com/v1', // 需要从控制台获取实际URL
|
|
53
|
+
models: ['hy-2.0-instruct', 'glm-5', 'kimi-k2.5', 'minimax-m2.5'],
|
|
54
|
+
},
|
|
55
|
+
volcengine: {
|
|
56
|
+
name: 'Volcengine Coding Plan',
|
|
57
|
+
baseUrl: 'https://ark.cn-beijing.volces.com/api/v3', // 需要从控制台获取实际URL
|
|
58
|
+
models: ['doubao-seed-code', 'deepseek-v3.2', 'glm-4.7', 'kimi-k2'],
|
|
59
|
+
},
|
|
60
|
+
baidu: {
|
|
61
|
+
name: 'Baidu Qianfan Coding Plan',
|
|
62
|
+
baseUrl: 'https://qianfan.baidubce.com/v2', // 需要从控制台获取实际URL
|
|
63
|
+
models: ['glm-5', 'minimax-m2.5', 'kimi-k2.5', 'ernie-4.5'],
|
|
64
|
+
},
|
|
65
|
+
kimi: {
|
|
66
|
+
name: 'Kimi Code',
|
|
67
|
+
baseUrl: 'https://api.moonshot.cn/v1',
|
|
68
|
+
models: ['kimi-k2', 'kimi-k2.5'],
|
|
69
|
+
},
|
|
70
|
+
zhipu: {
|
|
71
|
+
name: 'Zhipu AI Coding Plan',
|
|
72
|
+
baseUrl: 'https://open.bigmodel.cn/api/paas/v4',
|
|
73
|
+
models: ['glm-4.7', 'glm-5'],
|
|
74
|
+
},
|
|
75
|
+
minimax: {
|
|
76
|
+
name: 'MiniMax Coding Plan',
|
|
77
|
+
baseUrl: 'https://api.minimax.chat/v1',
|
|
78
|
+
models: ['minimax-2.7', 'abab6.5s-chat'],
|
|
79
|
+
},
|
|
80
|
+
custom: {
|
|
81
|
+
name: 'Custom Coding Plan',
|
|
82
|
+
baseUrl: '',
|
|
83
|
+
models: [],
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Coding Plan Provider - Access multiple AI models through Chinese Coding Plan platforms
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* ```typescript
|
|
92
|
+
* // 使用阿里云百炼 Coding Plan
|
|
93
|
+
* const provider = new CodingPlanProvider({
|
|
94
|
+
* platform: 'alibaba',
|
|
95
|
+
* apiKey: 'your-coding-plan-api-key',
|
|
96
|
+
* model: 'qwen3-coder',
|
|
97
|
+
* });
|
|
98
|
+
*
|
|
99
|
+
* // 使用自定义 Coding Plan
|
|
100
|
+
* const provider = new CodingPlanProvider({
|
|
101
|
+
* platform: 'custom',
|
|
102
|
+
* customBaseUrl: 'https://your-coding-plan-endpoint.com/v1',
|
|
103
|
+
* apiKey: 'your-api-key',
|
|
104
|
+
* model: 'custom-model',
|
|
105
|
+
* });
|
|
106
|
+
* ```
|
|
107
|
+
*/
|
|
108
|
+
export class CodingPlanProvider extends OpenAICompatibleProvider {
|
|
109
|
+
readonly name: string;
|
|
110
|
+
readonly platform: CodingPlanPlatform;
|
|
111
|
+
readonly supportedModels: string[];
|
|
112
|
+
|
|
113
|
+
constructor(config: CodingPlanConfig) {
|
|
114
|
+
const platformConfig = PLATFORM_CONFIGS[config.platform];
|
|
115
|
+
|
|
116
|
+
if (!platformConfig) {
|
|
117
|
+
throw new Error(`Unknown Coding Plan platform: ${config.platform}`);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (config.platform === 'custom' && !config.customBaseUrl) {
|
|
121
|
+
throw new Error('customBaseUrl is required when platform is "custom"');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const baseUrl = config.platform === 'custom'
|
|
125
|
+
? config.customBaseUrl
|
|
126
|
+
: platformConfig.baseUrl;
|
|
127
|
+
|
|
128
|
+
super({
|
|
129
|
+
apiKey: config.apiKey,
|
|
130
|
+
baseUrl: baseUrl,
|
|
131
|
+
model: config.model,
|
|
132
|
+
headers: config.headers,
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
this.name = platformConfig.name;
|
|
136
|
+
this.platform = config.platform;
|
|
137
|
+
this.supportedModels = platformConfig.models;
|
|
138
|
+
|
|
139
|
+
// Validate model if platform has known models
|
|
140
|
+
if (platformConfig.models.length > 0 && !platformConfig.models.includes(config.model)) {
|
|
141
|
+
console.warn(
|
|
142
|
+
`[CodingPlanProvider] Model "${config.model}" may not be supported by ${platformConfig.name}. ` +
|
|
143
|
+
`Known models: ${platformConfig.models.join(', ')}`
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Get list of supported models for this platform
|
|
150
|
+
*/
|
|
151
|
+
getSupportedModels(): string[] {
|
|
152
|
+
return [...this.supportedModels];
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Get platform info
|
|
157
|
+
*/
|
|
158
|
+
getPlatformInfo(): { name: string; platform: CodingPlanPlatform; baseUrl: string } {
|
|
159
|
+
const config = PLATFORM_CONFIGS[this.platform];
|
|
160
|
+
return {
|
|
161
|
+
name: this.name,
|
|
162
|
+
platform: this.platform,
|
|
163
|
+
baseUrl: this.platform === 'custom' ? '' : config.baseUrl,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Get Anthropic-compatible base URL (for Alibaba Cloud only)
|
|
169
|
+
*/
|
|
170
|
+
getAnthropicBaseUrl(): string | null {
|
|
171
|
+
const config = PLATFORM_CONFIGS[this.platform];
|
|
172
|
+
return config.anthropicBaseUrl || null;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Get all supported Coding Plan platforms
|
|
178
|
+
*/
|
|
179
|
+
export function getSupportedCodingPlanPlatforms(): Array<{
|
|
180
|
+
platform: CodingPlanPlatform;
|
|
181
|
+
name: string;
|
|
182
|
+
baseUrl: string;
|
|
183
|
+
models: string[];
|
|
184
|
+
}> {
|
|
185
|
+
return Object.entries(PLATFORM_CONFIGS)
|
|
186
|
+
.filter(([key]) => key !== 'custom')
|
|
187
|
+
.map(([platform, config]) => ({
|
|
188
|
+
platform: platform as CodingPlanPlatform,
|
|
189
|
+
name: config.name,
|
|
190
|
+
baseUrl: config.baseUrl,
|
|
191
|
+
models: config.models,
|
|
192
|
+
}));
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Quick factory for creating Coding Plan providers
|
|
197
|
+
*/
|
|
198
|
+
export function createCodingPlanProvider(
|
|
199
|
+
platform: CodingPlanPlatform,
|
|
200
|
+
apiKey: string,
|
|
201
|
+
model: string,
|
|
202
|
+
customBaseUrl?: string
|
|
203
|
+
): CodingPlanProvider {
|
|
204
|
+
return new CodingPlanProvider({
|
|
205
|
+
platform,
|
|
206
|
+
apiKey,
|
|
207
|
+
model,
|
|
208
|
+
customBaseUrl,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OllamaCloudProvider.d.ts","sourceRoot":"","sources":["OllamaCloudProvider.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,OAAO,EAAmD,MAAM,wBAAwB,CAAC;AAEvG,OAAO,KAAK,EAAE,aAAa,EAAE,mBAAmB,EAAE,aAAa,EAAc,WAAW,EAAE,MAAM,aAAa,CAAC;AAI9G,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf;AAgDD,qBAAa,mBAAoB,YAAW,aAAa;IACvD,QAAQ,CAAC,IAAI,kBAAkB;IAC/B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,OAAO,CAAS;gBAEZ,MAAM,EAAE,iBAAiB;IAK/B,QAAQ,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,aAAa,CAAC;IA4ClF,MAAM,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,mBAAmB,GAAG,cAAc,CAAC,WAAW,CAAC;IAiHvF,WAAW,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;IAWvD,OAAO,CAAC,UAAU;IASlB;;;OAGG;IACH,OAAO,CAAC,YAAY;IA2BpB;;;;OAIG;IACH,OAAO,CAAC,eAAe;IAiDvB,OAAO,CAAC,WAAW;IAYnB,OAAO,CAAC,sBAAsB;IA0B9B,OAAO,CAAC,iBAAiB;YAUX,OAAO;CAWtB"}
|
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// OllamaCloudProvider - Ollama Cloud API (https://ollama.com)
|
|
3
|
+
// ============================================================================
|
|
4
|
+
//
|
|
5
|
+
// Uses Ollama's native REST API (/api/chat) for cloud-hosted models.
|
|
6
|
+
// Supports:
|
|
7
|
+
// - All Ollama Cloud models (deepseek-v3.2, kimi-k2.5, qwen3-coder, etc.)
|
|
8
|
+
// - Streaming responses with thinking/reasoning support
|
|
9
|
+
// - Tool/function calling (Ollama native format)
|
|
10
|
+
// - Model discovery via /api/tags
|
|
11
|
+
// - API key authentication (Bearer token)
|
|
12
|
+
//
|
|
13
|
+
// NOTE: Ollama Cloud uses native API format, NOT OpenAI-compatible.
|
|
14
|
+
// ============================================================================
|
|
15
|
+
|
|
16
|
+
import type { Message, ContentBlock, ToolUseContent, ToolResultContent } from '../../types/session.js';
|
|
17
|
+
import type { ToolDefinition } from '../../types/tools.js';
|
|
18
|
+
import type { ModelProvider, ModelRequestOptions, ModelResponse, TokenUsage, StreamEvent } from '../types.js';
|
|
19
|
+
import { ModelError, RateLimitError } from '../../types/errors.js';
|
|
20
|
+
import { createToolCallId } from '../../types/session.js';
|
|
21
|
+
|
|
22
|
+
export interface OllamaCloudConfig {
|
|
23
|
+
apiKey: string;
|
|
24
|
+
baseUrl?: string;
|
|
25
|
+
model: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Ollama Cloud /api/chat response format (non-streaming) */
|
|
29
|
+
interface OllamaChatResponse {
|
|
30
|
+
model: string;
|
|
31
|
+
created_at: string;
|
|
32
|
+
message: {
|
|
33
|
+
role: string;
|
|
34
|
+
content: string;
|
|
35
|
+
thinking?: string;
|
|
36
|
+
tool_calls?: Array<{
|
|
37
|
+
function: {
|
|
38
|
+
name: string;
|
|
39
|
+
arguments: { [key: string]: unknown };
|
|
40
|
+
};
|
|
41
|
+
}>;
|
|
42
|
+
};
|
|
43
|
+
done: boolean;
|
|
44
|
+
done_reason?: string;
|
|
45
|
+
total_duration?: number;
|
|
46
|
+
load_duration?: number;
|
|
47
|
+
prompt_eval_count?: number;
|
|
48
|
+
prompt_eval_duration?: number;
|
|
49
|
+
eval_count?: number;
|
|
50
|
+
eval_duration?: number;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Ollama Cloud /api/chat streaming chunk */
|
|
54
|
+
interface OllamaChatChunk {
|
|
55
|
+
model: string;
|
|
56
|
+
created_at: string;
|
|
57
|
+
message: {
|
|
58
|
+
role: string;
|
|
59
|
+
content: string;
|
|
60
|
+
thinking?: string;
|
|
61
|
+
tool_calls?: Array<{
|
|
62
|
+
function: {
|
|
63
|
+
name: string;
|
|
64
|
+
arguments: { [key: string]: unknown };
|
|
65
|
+
};
|
|
66
|
+
}>;
|
|
67
|
+
};
|
|
68
|
+
done: boolean;
|
|
69
|
+
done_reason?: string;
|
|
70
|
+
prompt_eval_count?: number;
|
|
71
|
+
eval_count?: number;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export class OllamaCloudProvider implements ModelProvider {
|
|
75
|
+
readonly name = 'Ollama Cloud';
|
|
76
|
+
private apiKey: string;
|
|
77
|
+
private baseUrl: string;
|
|
78
|
+
|
|
79
|
+
constructor(config: OllamaCloudConfig) {
|
|
80
|
+
this.apiKey = config.apiKey;
|
|
81
|
+
this.baseUrl = (config.baseUrl || 'https://ollama.com').replace(/\/+$/, '');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async complete(messages: Message[], options: ModelRequestOptions): Promise<ModelResponse> {
|
|
85
|
+
const ollamaMessages = this.convertMessages(messages, options.systemPrompt);
|
|
86
|
+
const tools = this.convertTools(options.tools);
|
|
87
|
+
const body: Record<string, unknown> = {
|
|
88
|
+
model: options.model,
|
|
89
|
+
messages: ollamaMessages,
|
|
90
|
+
stream: false,
|
|
91
|
+
...(tools.length > 0 && { tools }),
|
|
92
|
+
...(options.maxTokens && { options: { num_predict: options.maxTokens } }),
|
|
93
|
+
...(options.temperature !== undefined && options.temperature !== 1 && {
|
|
94
|
+
options: { ...this.getOptions(options), temperature: options.temperature },
|
|
95
|
+
}),
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const res = await this.request('/api/chat', body);
|
|
99
|
+
const data = await res.json() as OllamaChatResponse;
|
|
100
|
+
|
|
101
|
+
if (!res.ok) {
|
|
102
|
+
throw new ModelError(
|
|
103
|
+
`Ollama Cloud API error: ${res.status} ${res.statusText} - ${JSON.stringify(data)}`,
|
|
104
|
+
res.status,
|
|
105
|
+
this.name,
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const content = this.convertResponseContent(data.message);
|
|
110
|
+
const usage: TokenUsage = {
|
|
111
|
+
inputTokens: data.prompt_eval_count || 0,
|
|
112
|
+
outputTokens: data.eval_count || 0,
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
// Ollama returns done_reason "stop" even for tool calls; detect from response content
|
|
116
|
+
const hasToolCalls = content.some((c): c is ToolUseContent => c.type === 'tool_use');
|
|
117
|
+
const stopReason = hasToolCalls ? 'tool_use' : this.convertStopReason(data.done_reason);
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
content,
|
|
121
|
+
model: data.model,
|
|
122
|
+
stopReason,
|
|
123
|
+
usage,
|
|
124
|
+
sessionId: options.sessionId,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async *stream(messages: Message[], options: ModelRequestOptions): AsyncGenerator<StreamEvent> {
|
|
129
|
+
const ollamaMessages = this.convertMessages(messages, options.systemPrompt);
|
|
130
|
+
const tools = this.convertTools(options.tools);
|
|
131
|
+
const body: Record<string, unknown> = {
|
|
132
|
+
model: options.model,
|
|
133
|
+
messages: ollamaMessages,
|
|
134
|
+
stream: true,
|
|
135
|
+
...(tools.length > 0 && { tools }),
|
|
136
|
+
options: this.getOptions(options),
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
let res: Response;
|
|
140
|
+
try {
|
|
141
|
+
res = await this.request('/api/chat', body);
|
|
142
|
+
} catch (err) {
|
|
143
|
+
yield { type: 'error', error: new ModelError(`Ollama Cloud request failed: ${(err as Error).message}`, undefined, this.name) };
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (!res.ok) {
|
|
148
|
+
let errorText = '';
|
|
149
|
+
try { errorText = await res.text(); } catch { /* ignore */ }
|
|
150
|
+
yield { type: 'error', error: new ModelError(`Ollama Cloud API error: ${res.status} ${errorText || res.statusText}`, res.status, this.name) };
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
yield { type: 'message_start', model: options.model };
|
|
155
|
+
|
|
156
|
+
const reader = res.body?.getReader();
|
|
157
|
+
if (!reader) {
|
|
158
|
+
yield { type: 'error', error: new ModelError('No response body from Ollama Cloud', undefined, this.name) };
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const decoder = new TextDecoder();
|
|
163
|
+
let buffer = '';
|
|
164
|
+
let totalInput = 0;
|
|
165
|
+
let totalOutput = 0;
|
|
166
|
+
// Track tool calls for streaming (Ollama sends them in the final chunk)
|
|
167
|
+
let pendingToolCalls: Array<{ name: string; arguments: string }> = [];
|
|
168
|
+
|
|
169
|
+
try {
|
|
170
|
+
while (true) {
|
|
171
|
+
const { done, value } = await reader.read();
|
|
172
|
+
if (done) break;
|
|
173
|
+
|
|
174
|
+
buffer += decoder.decode(value, { stream: true });
|
|
175
|
+
const lines = buffer.split('\n');
|
|
176
|
+
buffer = lines.pop() || '';
|
|
177
|
+
|
|
178
|
+
for (const line of lines) {
|
|
179
|
+
if (!line.trim()) continue;
|
|
180
|
+
try {
|
|
181
|
+
const chunk: OllamaChatChunk = JSON.parse(line);
|
|
182
|
+
|
|
183
|
+
// Emit thinking content
|
|
184
|
+
if (chunk.message?.thinking) {
|
|
185
|
+
yield { type: 'thinking_delta', delta: chunk.message.thinking };
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Emit text content
|
|
189
|
+
if (chunk.message?.content) {
|
|
190
|
+
yield { type: 'text_delta', delta: chunk.message.content };
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Collect tool calls (Ollama sends them in final chunk or inline)
|
|
194
|
+
if (chunk.message?.tool_calls) {
|
|
195
|
+
for (const tc of chunk.message.tool_calls) {
|
|
196
|
+
const argsStr = typeof tc.function.arguments === 'string'
|
|
197
|
+
? tc.function.arguments
|
|
198
|
+
: JSON.stringify(tc.function.arguments);
|
|
199
|
+
pendingToolCalls.push({
|
|
200
|
+
name: tc.function.name,
|
|
201
|
+
arguments: argsStr,
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Track token usage
|
|
207
|
+
if (chunk.prompt_eval_count) totalInput = chunk.prompt_eval_count;
|
|
208
|
+
if (chunk.eval_count) totalOutput = chunk.eval_count;
|
|
209
|
+
|
|
210
|
+
// Stream done
|
|
211
|
+
if (chunk.done) {
|
|
212
|
+
// Emit any pending tool calls before message_complete
|
|
213
|
+
if (pendingToolCalls.length > 0) {
|
|
214
|
+
for (const tc of pendingToolCalls) {
|
|
215
|
+
const toolCallId = createToolCallId(`tool_${Date.now()}_${Math.random()}`);
|
|
216
|
+
yield { type: 'tool_call_start', toolCallId, toolName: tc.name };
|
|
217
|
+
yield { type: 'tool_call_delta', toolCallId, delta: tc.arguments };
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Ollama uses "stop" even for tool calls; detect from pending tool calls
|
|
222
|
+
const hasToolCalls = pendingToolCalls.length > 0;
|
|
223
|
+
const stopReason = hasToolCalls ? 'tool_use' : this.convertStopReason(chunk.done_reason);
|
|
224
|
+
|
|
225
|
+
yield {
|
|
226
|
+
type: 'message_complete',
|
|
227
|
+
stopReason,
|
|
228
|
+
usage: { inputTokens: totalInput, outputTokens: totalOutput },
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
} catch {
|
|
232
|
+
// Skip malformed lines
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
} catch (err) {
|
|
237
|
+
yield { type: 'error', error: new ModelError(`Ollama Cloud stream error: ${(err as Error).message}`, undefined, this.name) };
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
async countTokens(messages: Message[]): Promise<number> {
|
|
242
|
+
// Approximate: ~4 chars per token
|
|
243
|
+
const text = messages.map(m => {
|
|
244
|
+
if (typeof m.content === 'string') return m.content;
|
|
245
|
+
return m.content.map(c => c.type === 'text' ? c.text : '').join('');
|
|
246
|
+
}).join('');
|
|
247
|
+
return Math.ceil(text.length / 4);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// --- Private helpers ---
|
|
251
|
+
|
|
252
|
+
private getOptions(options: ModelRequestOptions): Record<string, unknown> {
|
|
253
|
+
const opts: Record<string, unknown> = {};
|
|
254
|
+
if (options.maxTokens) opts.num_predict = options.maxTokens;
|
|
255
|
+
if (options.temperature !== undefined && options.temperature !== 1) {
|
|
256
|
+
opts.temperature = options.temperature;
|
|
257
|
+
}
|
|
258
|
+
return opts;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Convert internal ToolDefinition[] to Ollama's native tool format.
|
|
263
|
+
* Ollama uses the same format as OpenAI: { type: "function", function: { name, description, parameters } }
|
|
264
|
+
*/
|
|
265
|
+
private convertTools(tools: ToolDefinition[]): Array<Record<string, unknown>> {
|
|
266
|
+
if (!tools || tools.length === 0) return [];
|
|
267
|
+
|
|
268
|
+
return tools.map(tool => {
|
|
269
|
+
const parameters: Record<string, unknown> = {
|
|
270
|
+
type: 'object',
|
|
271
|
+
properties: {},
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
if (tool.inputSchema?.properties) {
|
|
275
|
+
parameters.properties = tool.inputSchema.properties;
|
|
276
|
+
}
|
|
277
|
+
if (tool.inputSchema?.required) {
|
|
278
|
+
parameters.required = tool.inputSchema.required;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return {
|
|
282
|
+
type: 'function',
|
|
283
|
+
function: {
|
|
284
|
+
name: tool.name,
|
|
285
|
+
description: tool.description || '',
|
|
286
|
+
parameters,
|
|
287
|
+
},
|
|
288
|
+
};
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Convert messages to Ollama format.
|
|
294
|
+
* Handles: system, user, assistant (with optional tool_calls), and tool results.
|
|
295
|
+
* Ollama doesn't have a native tool role; tool results are sent as user messages.
|
|
296
|
+
*/
|
|
297
|
+
private convertMessages(messages: Message[], systemPrompt?: string): Array<Record<string, unknown>> {
|
|
298
|
+
const result: Array<Record<string, unknown>> = [];
|
|
299
|
+
|
|
300
|
+
if (systemPrompt) {
|
|
301
|
+
result.push({ role: 'system', content: systemPrompt });
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
for (const msg of messages) {
|
|
305
|
+
if (msg.role === 'user') {
|
|
306
|
+
const text = this.extractText(msg.content);
|
|
307
|
+
result.push({ role: 'user', content: text });
|
|
308
|
+
} else if (msg.role === 'assistant') {
|
|
309
|
+
// Check if assistant message contains tool calls
|
|
310
|
+
const toolUseBlocks = msg.content.filter((c): c is ToolUseContent => c.type === 'tool_use');
|
|
311
|
+
const textBlocks = msg.content.filter(c => c.type === 'text');
|
|
312
|
+
|
|
313
|
+
if (toolUseBlocks.length > 0) {
|
|
314
|
+
// Ollama supports tool_calls in assistant messages
|
|
315
|
+
const msgObj: Record<string, unknown> = {
|
|
316
|
+
role: 'assistant',
|
|
317
|
+
content: textBlocks.map(c => (c as { text: string }).text).join('') || '',
|
|
318
|
+
tool_calls: toolUseBlocks.map(tc => ({
|
|
319
|
+
function: {
|
|
320
|
+
name: tc.name,
|
|
321
|
+
arguments: tc.input,
|
|
322
|
+
},
|
|
323
|
+
})),
|
|
324
|
+
};
|
|
325
|
+
result.push(msgObj);
|
|
326
|
+
} else {
|
|
327
|
+
const text = this.extractText(msg.content);
|
|
328
|
+
result.push({ role: 'assistant', content: text });
|
|
329
|
+
}
|
|
330
|
+
} else if (msg.role === 'tool') {
|
|
331
|
+
// Ollama doesn't have a native tool role; include as user message
|
|
332
|
+
const toolResults = msg.content.filter((c): c is ToolResultContent => c.type === 'tool_result');
|
|
333
|
+
const text = toolResults.map(tr => {
|
|
334
|
+
const content = typeof tr.content === 'string'
|
|
335
|
+
? tr.content
|
|
336
|
+
: JSON.stringify(tr.content);
|
|
337
|
+
return `[Tool Result for ${(tr as any).tool_use_id}]: ${content}`;
|
|
338
|
+
}).join('\n');
|
|
339
|
+
result.push({ role: 'user', content: text });
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
return result;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
private extractText(content: Message['content']): string {
|
|
347
|
+
if (typeof content === 'string') return content;
|
|
348
|
+
return content
|
|
349
|
+
.map(block => {
|
|
350
|
+
if (block.type === 'text') return block.text;
|
|
351
|
+
if (block.type === 'tool_use') return `[Calling tool: ${block.name}]`;
|
|
352
|
+
if (block.type === 'tool_result') return `[Tool result: ${typeof block.content === 'string' ? block.content : JSON.stringify(block.content)}]`;
|
|
353
|
+
return '';
|
|
354
|
+
})
|
|
355
|
+
.join('\n');
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
private convertResponseContent(message: OllamaChatResponse['message']): ContentBlock[] {
|
|
359
|
+
const content: ContentBlock[] = [];
|
|
360
|
+
|
|
361
|
+
if (message.thinking) {
|
|
362
|
+
content.push({ type: 'text', text: message.thinking });
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
if (message.content) {
|
|
366
|
+
content.push({ type: 'text', text: message.content });
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// Handle tool calls
|
|
370
|
+
if (message.tool_calls) {
|
|
371
|
+
for (const tc of message.tool_calls) {
|
|
372
|
+
content.push({
|
|
373
|
+
type: 'tool_use' as const,
|
|
374
|
+
id: createToolCallId(`tool_${Date.now()}_${Math.random()}`) as any,
|
|
375
|
+
name: tc.function.name,
|
|
376
|
+
input: tc.function.arguments,
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
return content;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
private convertStopReason(reason?: string): ModelResponse['stopReason'] {
|
|
385
|
+
if (!reason) return 'end_turn';
|
|
386
|
+
switch (reason) {
|
|
387
|
+
case 'stop': return 'end_turn';
|
|
388
|
+
case 'length': return 'max_tokens';
|
|
389
|
+
case 'tool_calls': return 'tool_use';
|
|
390
|
+
default: return 'end_turn';
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
private async request(path: string, body: Record<string, unknown>): Promise<Response> {
|
|
395
|
+
return fetch(`${this.baseUrl}${path}`, {
|
|
396
|
+
method: 'POST',
|
|
397
|
+
headers: {
|
|
398
|
+
'Content-Type': 'application/json',
|
|
399
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
400
|
+
},
|
|
401
|
+
body: JSON.stringify(body),
|
|
402
|
+
signal: AbortSignal.timeout(120000),
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OllamaManager.d.ts","sourceRoot":"","sources":["OllamaManager.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEzD,4CAA4C;AAC5C,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE;QACP,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;QACpB,cAAc,EAAE,MAAM,CAAC;QACvB,kBAAkB,EAAE,MAAM,CAAC;KAC5B,CAAC;CACH;AAED,8CAA8C;AAC9C,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,yCAAyC;AACzC,MAAM,WAAW,eAAe;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE;QACP,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;QACpB,cAAc,EAAE,MAAM,CAAC;QACvB,kBAAkB,EAAE,MAAM,CAAC;KAC5B,CAAC;IACF,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC;AAED,wCAAwC;AACxC,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,OAAO,CAAS;gBAEZ,OAAO,CAAC,EAAE,MAAM,EAAE,SAAS,SAAQ;IAK/C,wCAAwC;IAClC,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC;IAW9B,gCAAgC;IAC1B,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC;IAKhC,wCAAwC;IAClC,UAAU,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAK1C,uCAAuC;IACjC,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAIvD;;;OAGG;IACG,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,kBAAkB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAsCjG,uCAAuC;IACjC,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAK9C;;;OAGG;IACH,aAAa,CAAC,KAAK,EAAE,WAAW,GAAG,WAAW;YA4BhC,OAAO;CAsBtB"}
|