autosnippet 3.0.0 → 3.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +230 -324
- package/bin/api-server.js +1 -1
- package/bin/cli.js +204 -244
- package/bin/mcp-server.js +5 -3
- package/config/knowledge-base.config.js +132 -132
- package/dashboard/dist/assets/{icons-CEfgGaZi.js → icons-Cdq22n2i.js} +95 -100
- package/dashboard/dist/assets/index-ClkyPkDX.js +133 -0
- package/dashboard/dist/assets/index-t4QrJwv1.css +1 -0
- package/dashboard/dist/index.html +3 -3
- package/lib/bootstrap.js +8 -8
- package/lib/cli/AiScanService.js +86 -40
- package/lib/cli/KnowledgeSyncService.js +113 -74
- package/lib/cli/SetupService.js +439 -277
- package/lib/cli/UpgradeService.js +63 -100
- package/lib/core/AstAnalyzer.js +276 -597
- package/lib/core/ast/ProjectGraph.js +101 -40
- package/lib/core/ast/ensure-grammars.js +232 -0
- package/lib/core/ast/index.js +115 -0
- package/lib/core/ast/lang-dart.js +661 -0
- package/lib/core/ast/lang-go.js +530 -0
- package/lib/core/ast/lang-java.js +435 -0
- package/lib/core/ast/lang-javascript.js +272 -0
- package/lib/core/ast/lang-kotlin.js +423 -0
- package/lib/core/ast/lang-objc.js +388 -0
- package/lib/core/ast/lang-python.js +371 -0
- package/lib/core/ast/lang-swift.js +337 -0
- package/lib/core/ast/lang-typescript.js +503 -0
- package/lib/core/capability/CapabilityProbe.js +18 -9
- package/lib/core/constitution/Constitution.js +2 -3
- package/lib/core/constitution/ConstitutionValidator.js +65 -24
- package/lib/core/discovery/DartDiscoverer.js +534 -0
- package/lib/core/discovery/DiscovererRegistry.js +83 -0
- package/lib/core/discovery/GenericDiscoverer.js +225 -0
- package/lib/core/discovery/GoDiscoverer.js +541 -0
- package/lib/core/discovery/JvmDiscoverer.js +506 -0
- package/lib/core/discovery/NodeDiscoverer.js +466 -0
- package/lib/core/discovery/ProjectDiscoverer.js +93 -0
- package/lib/core/discovery/PythonDiscoverer.js +338 -0
- package/lib/core/discovery/SpmDiscoverer.js +5 -0
- package/lib/core/discovery/index.js +53 -0
- package/lib/core/enhancement/EnhancementPack.js +71 -0
- package/lib/core/enhancement/EnhancementRegistry.js +47 -0
- package/lib/core/enhancement/android-enhancement.js +102 -0
- package/lib/core/enhancement/django-enhancement.js +70 -0
- package/lib/core/enhancement/fastapi-enhancement.js +63 -0
- package/lib/core/enhancement/go-grpc-enhancement.js +152 -0
- package/lib/core/enhancement/go-web-enhancement.js +201 -0
- package/lib/core/enhancement/index.js +65 -0
- package/lib/core/enhancement/node-server-enhancement.js +88 -0
- package/lib/core/enhancement/react-enhancement.js +86 -0
- package/lib/core/enhancement/spring-enhancement.js +112 -0
- package/lib/core/enhancement/vue-enhancement.js +96 -0
- package/lib/core/gateway/Gateway.js +8 -9
- package/lib/core/gateway/GatewayActionRegistry.js +1 -1
- package/lib/core/permission/PermissionManager.js +12 -8
- package/lib/domain/index.js +13 -9
- package/lib/domain/knowledge/KnowledgeEntry.js +111 -101
- package/lib/domain/knowledge/KnowledgeRepository.js +0 -1
- package/lib/domain/knowledge/Lifecycle.js +22 -22
- package/lib/domain/knowledge/index.js +9 -12
- package/lib/domain/knowledge/values/Constraints.js +31 -21
- package/lib/domain/knowledge/values/Content.js +21 -13
- package/lib/domain/knowledge/values/Quality.js +31 -18
- package/lib/domain/knowledge/values/Reasoning.js +20 -12
- package/lib/domain/knowledge/values/Relations.js +37 -25
- package/lib/domain/knowledge/values/Stats.js +18 -12
- package/lib/domain/knowledge/values/index.js +4 -3
- package/lib/domain/snippet/Snippet.js +35 -10
- package/lib/external/ai/AiFactory.js +48 -16
- package/lib/external/ai/AiProvider.js +184 -90
- package/lib/external/ai/providers/ClaudeProvider.js +25 -12
- package/lib/external/ai/providers/GoogleGeminiProvider.js +59 -30
- package/lib/external/ai/providers/MockProvider.js +9 -3
- package/lib/external/ai/providers/OpenAiProvider.js +51 -29
- package/lib/external/mcp/McpServer.js +66 -36
- package/lib/external/mcp/errorHandler.js +23 -11
- package/lib/external/mcp/handlers/LanguageExtensions.js +138 -53
- package/lib/external/mcp/handlers/TargetClassifier.js +52 -16
- package/lib/external/mcp/handlers/bootstrap/pipeline/BootstrapSnapshot.js +81 -20
- package/lib/external/mcp/handlers/bootstrap/pipeline/EpisodicMemory.js +71 -42
- package/lib/external/mcp/handlers/bootstrap/pipeline/IncrementalBootstrap.js +9 -17
- package/lib/external/mcp/handlers/bootstrap/pipeline/ToolResultCache.js +14 -9
- package/lib/external/mcp/handlers/bootstrap/pipeline/dimension-context.js +15 -7
- package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +352 -153
- package/lib/external/mcp/handlers/bootstrap/pipeline/tier-scheduler.js +52 -12
- package/lib/external/mcp/handlers/bootstrap/skills.js +143 -39
- package/lib/external/mcp/handlers/bootstrap.js +691 -168
- package/lib/external/mcp/handlers/browse.js +66 -22
- package/lib/external/mcp/handlers/candidate.js +118 -35
- package/lib/external/mcp/handlers/consolidated.js +49 -17
- package/lib/external/mcp/handlers/guard.js +104 -39
- package/lib/external/mcp/handlers/knowledge.js +60 -36
- package/lib/external/mcp/handlers/search.js +43 -14
- package/lib/external/mcp/handlers/skill.js +120 -45
- package/lib/external/mcp/handlers/structure.js +240 -86
- package/lib/external/mcp/handlers/system.js +42 -12
- package/lib/external/mcp/handlers/wiki.js +58 -33
- package/lib/external/mcp/tools.js +306 -123
- package/lib/http/HttpServer.js +72 -47
- package/lib/http/middleware/RateLimiter.js +5 -3
- package/lib/http/middleware/errorHandler.js +6 -1
- package/lib/http/middleware/requestLogger.js +14 -3
- package/lib/http/middleware/roleResolver.js +30 -23
- package/lib/http/routes/ai.js +387 -265
- package/lib/http/routes/auth.js +81 -61
- package/lib/http/routes/candidates.js +430 -320
- package/lib/http/routes/commands.js +289 -189
- package/lib/http/routes/extract.js +158 -125
- package/lib/http/routes/guardRules.js +309 -217
- package/lib/http/routes/knowledge.js +213 -154
- package/lib/http/routes/modules.js +578 -0
- package/lib/http/routes/monitoring.js +6 -6
- package/lib/http/routes/recipes.js +104 -93
- package/lib/http/routes/search.js +361 -305
- package/lib/http/routes/skills.js +145 -98
- package/lib/http/routes/snippets.js +42 -30
- package/lib/http/routes/spm.js +3 -405
- package/lib/http/routes/violations.js +113 -93
- package/lib/http/routes/wiki.js +211 -170
- package/lib/http/utils/routeHelpers.js +3 -1
- package/lib/http/utils/sse-sessions.js +16 -6
- package/lib/http/utils/sse.js +15 -5
- package/lib/infrastructure/audit/AuditLogger.js +5 -2
- package/lib/infrastructure/audit/AuditStore.js +10 -7
- package/lib/infrastructure/cache/CacheService.js +3 -1
- package/lib/infrastructure/cache/GraphCache.js +8 -4
- package/lib/infrastructure/cache/UnifiedCacheAdapter.js +1 -1
- package/lib/infrastructure/config/ConfigLoader.js +9 -5
- package/lib/infrastructure/config/Defaults.js +30 -10
- package/lib/infrastructure/config/Paths.js +28 -8
- package/lib/infrastructure/config/TriggerSymbol.js +22 -10
- package/lib/infrastructure/database/DatabaseConnection.js +15 -10
- package/lib/infrastructure/database/migrations/001_initial_schema.js +0 -1
- package/lib/infrastructure/external/ClipboardManager.js +6 -2
- package/lib/infrastructure/external/NativeUi.js +50 -43
- package/lib/infrastructure/external/OpenBrowser.js +14 -17
- package/lib/infrastructure/external/XcodeAutomation.js +14 -258
- package/lib/infrastructure/logging/Logger.js +46 -30
- package/lib/infrastructure/monitoring/ErrorTracker.js +7 -5
- package/lib/infrastructure/monitoring/PerformanceMonitor.js +12 -4
- package/lib/infrastructure/paths/HeaderResolver.js +25 -9
- package/lib/infrastructure/paths/PathFinder.js +34 -12
- package/lib/infrastructure/plugin/PluginManager.js +26 -8
- package/lib/infrastructure/realtime/RealtimeService.js +2 -2
- package/lib/infrastructure/vector/Chunker.js +22 -7
- package/lib/infrastructure/vector/IndexingPipeline.js +46 -22
- package/lib/infrastructure/vector/JsonVectorAdapter.js +90 -53
- package/lib/infrastructure/vector/VectorStore.js +28 -10
- package/lib/injection/ServiceContainer.js +247 -93
- package/lib/platform/ios/index.js +63 -0
- package/lib/platform/ios/routes/spm.js +437 -0
- package/lib/platform/ios/snippet/PlaceholderConverter.js +55 -0
- package/lib/platform/ios/snippet/XcodeCodec.js +112 -0
- package/lib/{service → platform/ios}/spm/DependencyGraph.js +41 -17
- package/lib/{service → platform/ios}/spm/PackageSwiftParser.js +41 -14
- package/lib/{service → platform/ios}/spm/PolicyEngine.js +9 -4
- package/lib/platform/ios/spm/SpmDiscoverer.js +122 -0
- package/lib/{service → platform/ios}/spm/SpmService.js +385 -127
- package/lib/{service/automation → platform/ios/xcode}/SaveEventFilter.js +8 -7
- package/lib/platform/ios/xcode/XcodeAutomation.js +350 -0
- package/lib/{service/automation → platform/ios/xcode}/XcodeIntegration.js +325 -145
- package/lib/repository/base/BaseRepository.js +7 -9
- package/lib/repository/knowledge/KnowledgeRepository.impl.js +98 -75
- package/lib/repository/token/TokenUsageStore.js +4 -2
- package/lib/service/automation/ActionPipeline.js +1 -1
- package/lib/service/automation/AutomationOrchestrator.js +8 -4
- package/lib/service/automation/ContextCollector.js +7 -5
- package/lib/service/automation/DirectiveDetector.js +23 -16
- package/lib/service/automation/FileWatcher.js +112 -56
- package/lib/service/automation/TriggerResolver.js +6 -4
- package/lib/service/automation/handlers/AlinkHandler.js +24 -12
- package/lib/service/automation/handlers/CreateHandler.js +19 -20
- package/lib/service/automation/handlers/DraftHandler.js +14 -8
- package/lib/service/automation/handlers/GuardHandler.js +93 -63
- package/lib/service/automation/handlers/HeaderHandler.js +1 -6
- package/lib/service/automation/handlers/SearchHandler.js +155 -88
- package/lib/service/bootstrap/BootstrapTaskManager.js +77 -35
- package/lib/service/candidate/SimilarityService.js +25 -9
- package/lib/service/chat/AnalystAgent.js +50 -24
- package/lib/service/chat/CandidateGuardrail.js +143 -17
- package/lib/service/chat/ChatAgent.js +759 -243
- package/lib/service/chat/ContextWindow.js +116 -71
- package/lib/service/chat/ConversationStore.js +77 -36
- package/lib/service/chat/EpisodicConsolidator.js +47 -23
- package/lib/service/chat/HandoffProtocol.js +98 -22
- package/lib/service/chat/Memory.js +34 -14
- package/lib/service/chat/ProducerAgent.js +40 -20
- package/lib/service/chat/ProjectSemanticMemory.js +109 -78
- package/lib/service/chat/ReasoningLayer.js +148 -70
- package/lib/service/chat/ReasoningTrace.js +44 -32
- package/lib/service/chat/TaskPipeline.js +39 -19
- package/lib/service/chat/ToolRegistry.js +48 -29
- package/lib/service/chat/WorkingMemory.js +44 -18
- package/lib/service/chat/tools.js +1096 -494
- package/lib/service/context/RecipeExtractor.js +132 -51
- package/lib/service/cursor/CursorDeliveryPipeline.js +82 -37
- package/lib/service/cursor/KnowledgeCompressor.js +25 -22
- package/lib/service/cursor/RulesGenerator.js +13 -7
- package/lib/service/cursor/SkillsSyncer.js +77 -27
- package/lib/service/cursor/TokenBudget.js +2 -2
- package/lib/service/cursor/TopicClassifier.js +54 -20
- package/lib/service/guard/ComplianceReporter.js +55 -43
- package/lib/service/guard/ExclusionManager.js +67 -29
- package/lib/service/guard/GuardCheckEngine.js +381 -86
- package/lib/service/guard/GuardFeedbackLoop.js +22 -10
- package/lib/service/guard/GuardService.js +29 -19
- package/lib/service/guard/RuleLearner.js +55 -23
- package/lib/service/guard/SourceFileCollector.js +27 -20
- package/lib/service/guard/ViolationsStore.js +43 -38
- package/lib/service/knowledge/CodeEntityGraph.js +147 -82
- package/lib/service/knowledge/ConfidenceRouter.js +12 -10
- package/lib/service/knowledge/KnowledgeFileWriter.js +147 -56
- package/lib/service/knowledge/KnowledgeGraphService.js +81 -34
- package/lib/service/knowledge/KnowledgeService.js +222 -112
- package/lib/service/module/ModuleService.js +969 -0
- package/lib/service/quality/FeedbackCollector.js +27 -15
- package/lib/service/quality/QualityScorer.js +78 -24
- package/lib/service/recipe/RecipeCandidateValidator.js +110 -44
- package/lib/service/recipe/RecipeParser.js +78 -45
- package/lib/service/search/CoarseRanker.js +43 -28
- package/lib/service/search/CrossEncoderReranker.js +32 -21
- package/lib/service/search/InvertedIndex.js +21 -7
- package/lib/service/search/MultiSignalRanker.js +90 -28
- package/lib/service/search/RetrievalFunnel.js +45 -24
- package/lib/service/search/SearchEngine.js +255 -103
- package/lib/service/skills/EventAggregator.js +32 -15
- package/lib/service/skills/SignalCollector.js +140 -64
- package/lib/service/skills/SkillAdvisor.js +79 -42
- package/lib/service/skills/SkillHooks.js +16 -14
- package/lib/service/snippet/PlaceholderConverter.js +5 -0
- package/lib/service/snippet/SnippetFactory.js +116 -99
- package/lib/service/snippet/SnippetInstaller.js +234 -62
- package/lib/service/snippet/codecs/SnippetCodec.js +67 -0
- package/lib/service/snippet/codecs/VSCodeCodec.js +102 -0
- package/lib/service/snippet/codecs/XcodeCodec.js +5 -0
- package/lib/service/wiki/WikiGenerator.js +637 -263
- package/lib/shared/DimensionCopyRegistry.js +472 -0
- package/lib/shared/LanguageService.js +399 -0
- package/lib/shared/PathGuard.js +45 -28
- package/lib/shared/RecipeReadinessChecker.js +72 -12
- package/lib/shared/constants.js +41 -41
- package/lib/shared/errors/BaseError.js +2 -2
- package/lib/shared/errors/index.js +4 -4
- package/lib/shared/similarity.js +25 -8
- package/lib/shared/token-utils.js +6 -2
- package/lib/shared/utils/common.js +12 -4
- package/package.json +49 -13
- package/scripts/bench-real-projects.mjs +256 -0
- package/scripts/build-native-ui.js +30 -30
- package/scripts/clear-old-vector-index.js +5 -35
- package/scripts/clear-vector-cache.js +7 -37
- package/scripts/collect-test-project-stats.mjs +160 -0
- package/scripts/diagnose-mcp.js +41 -32
- package/scripts/ensure-parse-package.js +6 -9
- package/scripts/generate-recipe-drafts.js +116 -77
- package/scripts/init-db.js +3 -20
- package/scripts/init-snippets.js +305 -0
- package/scripts/init-vector-db.js +173 -170
- package/scripts/install-cursor-skill.js +148 -104
- package/scripts/install-full.js +8 -21
- package/scripts/install-vscode-copilot.js +146 -145
- package/scripts/migrate-md-to-knowledge.mjs +139 -151
- package/scripts/postinstall-safe.js +5 -17
- package/scripts/recipe-audit.js +106 -82
- package/scripts/release.js +283 -323
- package/scripts/setup-mcp-config.js +60 -52
- package/scripts/verify-context-api.js +20 -20
- package/skills/autosnippet-analysis/SKILL.md +10 -6
- package/skills/autosnippet-candidates/SKILL.md +27 -26
- package/skills/autosnippet-coldstart/SKILL.md +555 -38
- package/skills/autosnippet-concepts/SKILL.md +349 -337
- package/skills/autosnippet-create/SKILL.md +5 -5
- package/skills/autosnippet-reference-dart/SKILL.md +543 -0
- package/skills/autosnippet-reference-go/SKILL.md +539 -0
- package/skills/autosnippet-reference-java/SKILL.md +534 -0
- package/skills/autosnippet-reference-jsts/SKILL.md +41 -9
- package/skills/autosnippet-reference-kotlin/SKILL.md +526 -0
- package/skills/autosnippet-reference-objc/SKILL.md +29 -6
- package/skills/autosnippet-reference-python/SKILL.md +800 -0
- package/skills/autosnippet-reference-swift/SKILL.md +70 -14
- package/skills/autosnippet-structure/SKILL.md +4 -4
- package/templates/cursor-rules/autosnippet-conventions.mdc +2 -2
- package/templates/recipes-setup/README.md +2 -2
- package/templates/recipes-setup/_template.md +1 -1
- package/dashboard/dist/assets/index-Bun3ld_J.css +0 -1
- package/dashboard/dist/assets/index-_Sk_Dmg3.js +0 -143
- package/resources/asd-entry/main.swift +0 -159
- package/scripts/build-asd-entry.js +0 -51
- package/scripts/init-xcode-snippets.js +0 -311
- package/template.json +0 -39
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
* 支持 toolChoice: 'auto' | 'required' | 'none'
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { AiProvider } from '../AiProvider.js';
|
|
11
10
|
import Logger from '../../../infrastructure/logging/Logger.js';
|
|
11
|
+
import { AiProvider } from '../AiProvider.js';
|
|
12
12
|
|
|
13
13
|
const GEMINI_BASE = 'https://generativelanguage.googleapis.com/v1beta';
|
|
14
14
|
const EMBED_MODEL = 'models/gemini-embedding-001';
|
|
@@ -35,7 +35,10 @@ export class GoogleGeminiProvider extends AiProvider {
|
|
|
35
35
|
const contents = [];
|
|
36
36
|
|
|
37
37
|
for (const h of history) {
|
|
38
|
-
contents.push({
|
|
38
|
+
contents.push({
|
|
39
|
+
role: h.role === 'assistant' ? 'model' : 'user',
|
|
40
|
+
parts: [{ text: h.content }],
|
|
41
|
+
});
|
|
39
42
|
}
|
|
40
43
|
contents.push({ role: 'user', parts: [{ text: prompt }] });
|
|
41
44
|
|
|
@@ -85,9 +88,10 @@ export class GoogleGeminiProvider extends AiProvider {
|
|
|
85
88
|
} = opts;
|
|
86
89
|
|
|
87
90
|
// 统一消息 → Gemini contents
|
|
88
|
-
const contents =
|
|
89
|
-
|
|
90
|
-
|
|
91
|
+
const contents =
|
|
92
|
+
messages?.length > 0
|
|
93
|
+
? this.#convertMessages(messages)
|
|
94
|
+
: [{ role: 'user', parts: [{ text: prompt }] }];
|
|
91
95
|
|
|
92
96
|
const body = {
|
|
93
97
|
contents,
|
|
@@ -99,9 +103,11 @@ export class GoogleGeminiProvider extends AiProvider {
|
|
|
99
103
|
|
|
100
104
|
// 工具声明: 标准 schema → Gemini functionDeclarations
|
|
101
105
|
if (toolSchemas?.length > 0) {
|
|
102
|
-
body.tools = [
|
|
103
|
-
|
|
104
|
-
|
|
106
|
+
body.tools = [
|
|
107
|
+
{
|
|
108
|
+
functionDeclarations: toolSchemas.map((s) => this.#toFunctionDeclaration(s)),
|
|
109
|
+
},
|
|
110
|
+
];
|
|
105
111
|
}
|
|
106
112
|
|
|
107
113
|
// toolChoice → Gemini mode
|
|
@@ -170,7 +176,9 @@ export class GoogleGeminiProvider extends AiProvider {
|
|
|
170
176
|
pushOrMerge({ role: 'user', parts: [{ text: msg.content || '' }] });
|
|
171
177
|
} else if (msg.role === 'assistant') {
|
|
172
178
|
const parts = [];
|
|
173
|
-
if (msg.content)
|
|
179
|
+
if (msg.content) {
|
|
180
|
+
parts.push({ text: msg.content });
|
|
181
|
+
}
|
|
174
182
|
if (msg.toolCalls?.length > 0) {
|
|
175
183
|
for (const tc of msg.toolCalls) {
|
|
176
184
|
const fcPart = { functionCall: { name: tc.name, args: tc.args || {} } };
|
|
@@ -181,7 +189,9 @@ export class GoogleGeminiProvider extends AiProvider {
|
|
|
181
189
|
parts.push(fcPart);
|
|
182
190
|
}
|
|
183
191
|
}
|
|
184
|
-
if (parts.length > 0)
|
|
192
|
+
if (parts.length > 0) {
|
|
193
|
+
pushOrMerge({ role: 'model', parts });
|
|
194
|
+
}
|
|
185
195
|
}
|
|
186
196
|
}
|
|
187
197
|
|
|
@@ -198,9 +208,12 @@ export class GoogleGeminiProvider extends AiProvider {
|
|
|
198
208
|
*/
|
|
199
209
|
#toGeminiMode(toolChoice) {
|
|
200
210
|
switch (toolChoice) {
|
|
201
|
-
case 'required':
|
|
202
|
-
|
|
203
|
-
|
|
211
|
+
case 'required':
|
|
212
|
+
return 'ANY';
|
|
213
|
+
case 'none':
|
|
214
|
+
return 'NONE';
|
|
215
|
+
default:
|
|
216
|
+
return 'AUTO';
|
|
204
217
|
}
|
|
205
218
|
}
|
|
206
219
|
|
|
@@ -227,7 +240,9 @@ export class GoogleGeminiProvider extends AiProvider {
|
|
|
227
240
|
const cleaned = { ...schema };
|
|
228
241
|
delete cleaned.default;
|
|
229
242
|
delete cleaned.examples;
|
|
230
|
-
if (!cleaned.type)
|
|
243
|
+
if (!cleaned.type) {
|
|
244
|
+
cleaned.type = 'object';
|
|
245
|
+
}
|
|
231
246
|
|
|
232
247
|
// 递归清理 properties
|
|
233
248
|
if (cleaned.properties) {
|
|
@@ -290,7 +305,9 @@ export class GoogleGeminiProvider extends AiProvider {
|
|
|
290
305
|
}
|
|
291
306
|
|
|
292
307
|
if (functionCalls.length > 0) {
|
|
293
|
-
this.logger.debug(
|
|
308
|
+
this.logger.debug(
|
|
309
|
+
`[GeminiProvider] native function calls: ${functionCalls.map((fc) => fc.name).join(', ')}`
|
|
310
|
+
);
|
|
294
311
|
return {
|
|
295
312
|
text: textParts.length > 0 ? textParts.join('\n') : null,
|
|
296
313
|
functionCalls,
|
|
@@ -307,10 +324,12 @@ export class GoogleGeminiProvider extends AiProvider {
|
|
|
307
324
|
|
|
308
325
|
async summarize(code) {
|
|
309
326
|
const prompt = `请对以下代码生成结构化摘要,返回 JSON 格式 {title, description, language, patterns: [], keyAPIs: []}:\n\n${code}`;
|
|
310
|
-
return
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
327
|
+
return (
|
|
328
|
+
(await this.chatWithStructuredOutput(prompt, {
|
|
329
|
+
temperature: 0.3,
|
|
330
|
+
maxTokens: 8192,
|
|
331
|
+
})) || { title: '', description: '' }
|
|
332
|
+
);
|
|
314
333
|
}
|
|
315
334
|
|
|
316
335
|
/**
|
|
@@ -321,12 +340,7 @@ export class GoogleGeminiProvider extends AiProvider {
|
|
|
321
340
|
*/
|
|
322
341
|
async chatWithStructuredOutput(prompt, opts = {}) {
|
|
323
342
|
return this._withRetry(async () => {
|
|
324
|
-
const {
|
|
325
|
-
schema,
|
|
326
|
-
temperature = 0.3,
|
|
327
|
-
maxTokens = 32768,
|
|
328
|
-
systemPrompt,
|
|
329
|
-
} = opts;
|
|
343
|
+
const { schema, temperature = 0.3, maxTokens = 32768, systemPrompt } = opts;
|
|
330
344
|
|
|
331
345
|
const contents = [{ role: 'user', parts: [{ text: prompt }] }];
|
|
332
346
|
|
|
@@ -351,7 +365,9 @@ export class GoogleGeminiProvider extends AiProvider {
|
|
|
351
365
|
const data = await this._post(url, body);
|
|
352
366
|
const text = data?.candidates?.[0]?.content?.parts?.[0]?.text || '';
|
|
353
367
|
|
|
354
|
-
if (!text)
|
|
368
|
+
if (!text) {
|
|
369
|
+
return null;
|
|
370
|
+
}
|
|
355
371
|
|
|
356
372
|
try {
|
|
357
373
|
return JSON.parse(text);
|
|
@@ -370,7 +386,7 @@ export class GoogleGeminiProvider extends AiProvider {
|
|
|
370
386
|
|
|
371
387
|
for (let i = 0; i < texts.length; i += 100) {
|
|
372
388
|
const batch = texts.slice(i, i + 100);
|
|
373
|
-
const requests = batch.map(t => ({
|
|
389
|
+
const requests = batch.map((t) => ({
|
|
374
390
|
model: EMBED_MODEL,
|
|
375
391
|
content: { parts: [{ text: t.slice(0, 8000) }] },
|
|
376
392
|
}));
|
|
@@ -378,7 +394,7 @@ export class GoogleGeminiProvider extends AiProvider {
|
|
|
378
394
|
const url = `${GEMINI_BASE}/${EMBED_MODEL}:batchEmbedContents?key=${this.apiKey}`;
|
|
379
395
|
const data = await this._post(url, { requests });
|
|
380
396
|
if (data?.embeddings) {
|
|
381
|
-
results.push(...data.embeddings.map(e => e.values));
|
|
397
|
+
results.push(...data.embeddings.map((e) => e.values));
|
|
382
398
|
}
|
|
383
399
|
}
|
|
384
400
|
|
|
@@ -386,6 +402,14 @@ export class GoogleGeminiProvider extends AiProvider {
|
|
|
386
402
|
}
|
|
387
403
|
|
|
388
404
|
async _post(url, body) {
|
|
405
|
+
if (!this.apiKey) {
|
|
406
|
+
const err = new Error(
|
|
407
|
+
'Google Gemini API Key 未配置。请在 .env 中设置 ASD_GOOGLE_API_KEY,或运行 asd setup 完成配置。'
|
|
408
|
+
);
|
|
409
|
+
err.code = 'API_KEY_MISSING';
|
|
410
|
+
throw err;
|
|
411
|
+
}
|
|
412
|
+
|
|
389
413
|
const controller = new AbortController();
|
|
390
414
|
const timer = setTimeout(() => controller.abort(), this.timeout);
|
|
391
415
|
|
|
@@ -398,8 +422,13 @@ export class GoogleGeminiProvider extends AiProvider {
|
|
|
398
422
|
});
|
|
399
423
|
if (!res.ok) {
|
|
400
424
|
let detail = '';
|
|
401
|
-
try {
|
|
402
|
-
|
|
425
|
+
try {
|
|
426
|
+
const j = await res.json();
|
|
427
|
+
detail = j?.error?.message || JSON.stringify(j).slice(0, 300);
|
|
428
|
+
} catch {
|
|
429
|
+
/* ignore */
|
|
430
|
+
}
|
|
431
|
+
const err = new Error(`Gemini API error: ${res.status}${detail ? ` — ${detail}` : ''}`);
|
|
403
432
|
err.status = res.status;
|
|
404
433
|
throw err;
|
|
405
434
|
}
|
|
@@ -16,13 +16,17 @@ export class MockProvider extends AiProvider {
|
|
|
16
16
|
|
|
17
17
|
async chat(prompt, context = {}) {
|
|
18
18
|
this.callLog.push({ method: 'chat', prompt, context });
|
|
19
|
-
if (this.responses.chat)
|
|
19
|
+
if (this.responses.chat) {
|
|
20
|
+
return this.responses.chat;
|
|
21
|
+
}
|
|
20
22
|
return `Mock response for: ${prompt.slice(0, 80)}`;
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
async summarize(code) {
|
|
24
26
|
this.callLog.push({ method: 'summarize', code: code?.slice(0, 80) });
|
|
25
|
-
if (this.responses.summarize)
|
|
27
|
+
if (this.responses.summarize) {
|
|
28
|
+
return this.responses.summarize;
|
|
29
|
+
}
|
|
26
30
|
return {
|
|
27
31
|
title: 'Mock Summary',
|
|
28
32
|
description: `Summary of ${code?.length || 0} chars`,
|
|
@@ -36,7 +40,9 @@ export class MockProvider extends AiProvider {
|
|
|
36
40
|
this.callLog.push({ method: 'embed', text: Array.isArray(text) ? text.length : 1 });
|
|
37
41
|
const dim = 768;
|
|
38
42
|
const makeVector = () => Array.from({ length: dim }, () => Math.random() * 2 - 1);
|
|
39
|
-
if (Array.isArray(text))
|
|
43
|
+
if (Array.isArray(text)) {
|
|
44
|
+
return text.map(() => makeVector());
|
|
45
|
+
}
|
|
40
46
|
return makeVector();
|
|
41
47
|
}
|
|
42
48
|
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
* - 兼容 DeepSeek / Ollama 等 OpenAI-compatible API
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { AiProvider } from '../AiProvider.js';
|
|
11
10
|
import Logger from '../../../infrastructure/logging/Logger.js';
|
|
11
|
+
import { AiProvider } from '../AiProvider.js';
|
|
12
12
|
|
|
13
13
|
const OPENAI_BASE = 'https://api.openai.com/v1';
|
|
14
14
|
|
|
@@ -80,9 +80,8 @@ export class OpenAiProvider extends AiProvider {
|
|
|
80
80
|
messages.push({ role: 'system', content: systemPrompt });
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
const srcMessages =
|
|
84
|
-
? unifiedMessages
|
|
85
|
-
: [{ role: 'user', content: prompt }];
|
|
83
|
+
const srcMessages =
|
|
84
|
+
unifiedMessages?.length > 0 ? unifiedMessages : [{ role: 'user', content: prompt }];
|
|
86
85
|
|
|
87
86
|
for (const msg of srcMessages) {
|
|
88
87
|
if (msg.role === 'user') {
|
|
@@ -90,7 +89,7 @@ export class OpenAiProvider extends AiProvider {
|
|
|
90
89
|
} else if (msg.role === 'assistant') {
|
|
91
90
|
const m = { role: 'assistant', content: msg.content || null };
|
|
92
91
|
if (msg.toolCalls?.length > 0) {
|
|
93
|
-
m.tool_calls = msg.toolCalls.map(tc => ({
|
|
92
|
+
m.tool_calls = msg.toolCalls.map((tc) => ({
|
|
94
93
|
id: tc.id,
|
|
95
94
|
type: 'function',
|
|
96
95
|
function: {
|
|
@@ -118,7 +117,7 @@ export class OpenAiProvider extends AiProvider {
|
|
|
118
117
|
|
|
119
118
|
// 标准 tool schemas → OpenAI tools format
|
|
120
119
|
if (toolSchemas?.length > 0) {
|
|
121
|
-
body.tools = toolSchemas.map(s => ({
|
|
120
|
+
body.tools = toolSchemas.map((s) => ({
|
|
122
121
|
type: 'function',
|
|
123
122
|
function: {
|
|
124
123
|
name: s.name,
|
|
@@ -129,9 +128,13 @@ export class OpenAiProvider extends AiProvider {
|
|
|
129
128
|
}
|
|
130
129
|
|
|
131
130
|
// toolChoice → OpenAI tool_choice
|
|
132
|
-
if (toolChoice === 'required')
|
|
133
|
-
|
|
134
|
-
else
|
|
131
|
+
if (toolChoice === 'required') {
|
|
132
|
+
body.tool_choice = 'required';
|
|
133
|
+
} else if (toolChoice === 'none') {
|
|
134
|
+
body.tool_choice = 'none';
|
|
135
|
+
} else {
|
|
136
|
+
body.tool_choice = 'auto';
|
|
137
|
+
}
|
|
135
138
|
|
|
136
139
|
const data = await this._post(`${this.baseUrl}/chat/completions`, body);
|
|
137
140
|
return this.#parseToolResponse(data);
|
|
@@ -156,25 +159,32 @@ export class OpenAiProvider extends AiProvider {
|
|
|
156
159
|
}
|
|
157
160
|
: null;
|
|
158
161
|
|
|
159
|
-
if (!choice)
|
|
162
|
+
if (!choice) {
|
|
163
|
+
return { text: '', functionCalls: null, usage };
|
|
164
|
+
}
|
|
160
165
|
|
|
161
166
|
const message = choice.message;
|
|
162
167
|
const text = message?.content || null;
|
|
163
168
|
|
|
164
169
|
if (message?.tool_calls?.length > 0) {
|
|
165
170
|
const functionCalls = message.tool_calls
|
|
166
|
-
.filter(tc => tc.type === 'function')
|
|
167
|
-
.map(tc => ({
|
|
171
|
+
.filter((tc) => tc.type === 'function')
|
|
172
|
+
.map((tc) => ({
|
|
168
173
|
id: tc.id,
|
|
169
174
|
name: tc.function.name,
|
|
170
175
|
args: (() => {
|
|
171
|
-
try {
|
|
172
|
-
|
|
176
|
+
try {
|
|
177
|
+
return JSON.parse(tc.function.arguments || '{}');
|
|
178
|
+
} catch {
|
|
179
|
+
return {};
|
|
180
|
+
}
|
|
173
181
|
})(),
|
|
174
182
|
}));
|
|
175
183
|
|
|
176
184
|
if (functionCalls.length > 0) {
|
|
177
|
-
this.logger.debug(
|
|
185
|
+
this.logger.debug(
|
|
186
|
+
`[OpenAI] native function calls: ${functionCalls.map((fc) => fc.name).join(', ')}`
|
|
187
|
+
);
|
|
178
188
|
return { text, functionCalls, usage };
|
|
179
189
|
}
|
|
180
190
|
}
|
|
@@ -184,10 +194,12 @@ export class OpenAiProvider extends AiProvider {
|
|
|
184
194
|
|
|
185
195
|
async summarize(code) {
|
|
186
196
|
const prompt = `请对以下代码生成结构化摘要,返回 JSON 格式 {title, description, language, patterns: [], keyAPIs: []}:\n\n${code}`;
|
|
187
|
-
return
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
197
|
+
return (
|
|
198
|
+
(await this.chatWithStructuredOutput(prompt, {
|
|
199
|
+
temperature: 0.3,
|
|
200
|
+
maxTokens: 4096,
|
|
201
|
+
})) || { title: '', description: '' }
|
|
202
|
+
);
|
|
191
203
|
}
|
|
192
204
|
|
|
193
205
|
/**
|
|
@@ -198,11 +210,7 @@ export class OpenAiProvider extends AiProvider {
|
|
|
198
210
|
*/
|
|
199
211
|
async chatWithStructuredOutput(prompt, opts = {}) {
|
|
200
212
|
return this._withRetry(async () => {
|
|
201
|
-
const {
|
|
202
|
-
temperature = 0.3,
|
|
203
|
-
maxTokens = 32768,
|
|
204
|
-
systemPrompt,
|
|
205
|
-
} = opts;
|
|
213
|
+
const { temperature = 0.3, maxTokens = 32768, systemPrompt } = opts;
|
|
206
214
|
|
|
207
215
|
const messages = [];
|
|
208
216
|
if (systemPrompt) {
|
|
@@ -221,7 +229,9 @@ export class OpenAiProvider extends AiProvider {
|
|
|
221
229
|
const data = await this._post(`${this.baseUrl}/chat/completions`, body);
|
|
222
230
|
const text = data?.choices?.[0]?.message?.content || '';
|
|
223
231
|
|
|
224
|
-
if (!text)
|
|
232
|
+
if (!text) {
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
225
235
|
|
|
226
236
|
try {
|
|
227
237
|
return JSON.parse(text);
|
|
@@ -240,15 +250,17 @@ export class OpenAiProvider extends AiProvider {
|
|
|
240
250
|
try {
|
|
241
251
|
const body = {
|
|
242
252
|
model: this.embedModel,
|
|
243
|
-
input: texts.map(t => t.slice(0, 8000)),
|
|
253
|
+
input: texts.map((t) => t.slice(0, 8000)),
|
|
244
254
|
};
|
|
245
255
|
|
|
246
256
|
const data = await this._post(`${this.baseUrl}/embeddings`, body);
|
|
247
257
|
const embeddings = (data?.data || [])
|
|
248
258
|
.sort((a, b) => a.index - b.index)
|
|
249
|
-
.map(d => d.embedding);
|
|
259
|
+
.map((d) => d.embedding);
|
|
250
260
|
|
|
251
|
-
if (embeddings.length === 0)
|
|
261
|
+
if (embeddings.length === 0) {
|
|
262
|
+
return Array.isArray(text) ? [] : [];
|
|
263
|
+
}
|
|
252
264
|
return Array.isArray(text) ? embeddings : embeddings[0];
|
|
253
265
|
} catch (err) {
|
|
254
266
|
this.logger.warn(`${this.name} embed failed, returning empty`, { error: err.message });
|
|
@@ -257,6 +269,16 @@ export class OpenAiProvider extends AiProvider {
|
|
|
257
269
|
}
|
|
258
270
|
|
|
259
271
|
async _post(url, body) {
|
|
272
|
+
// Ollama 使用固定 dummy key,不需要校验
|
|
273
|
+
if (!this.apiKey && this.name !== 'ollama') {
|
|
274
|
+
const envKey = this.name === 'deepseek' ? 'ASD_DEEPSEEK_API_KEY' : 'ASD_OPENAI_API_KEY';
|
|
275
|
+
const err = new Error(
|
|
276
|
+
`${this.name} API Key 未配置。请在 .env 中设置 ${envKey},或运行 asd setup 完成配置。`
|
|
277
|
+
);
|
|
278
|
+
err.code = 'API_KEY_MISSING';
|
|
279
|
+
throw err;
|
|
280
|
+
}
|
|
281
|
+
|
|
260
282
|
const controller = new AbortController();
|
|
261
283
|
const timer = setTimeout(() => controller.abort(), this.timeout);
|
|
262
284
|
|
|
@@ -265,7 +287,7 @@ export class OpenAiProvider extends AiProvider {
|
|
|
265
287
|
method: 'POST',
|
|
266
288
|
headers: {
|
|
267
289
|
'Content-Type': 'application/json',
|
|
268
|
-
|
|
290
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
269
291
|
},
|
|
270
292
|
body: JSON.stringify(body),
|
|
271
293
|
signal: controller.signal,
|
|
@@ -17,21 +17,18 @@
|
|
|
17
17
|
|
|
18
18
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
19
19
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
20
|
-
import {
|
|
21
|
-
CallToolRequestSchema,
|
|
22
|
-
ListToolsRequestSchema,
|
|
23
|
-
} from '@modelcontextprotocol/sdk/types.js';
|
|
20
|
+
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
24
21
|
import Logger from '../../infrastructure/logging/Logger.js';
|
|
25
22
|
import { envelope } from './envelope.js';
|
|
26
|
-
import { TOOLS, TOOL_GATEWAY_MAP, TIER_ORDER } from './tools.js';
|
|
27
23
|
import { wrapHandler } from './errorHandler.js';
|
|
24
|
+
import { TIER_ORDER, TOOL_GATEWAY_MAP, TOOLS } from './tools.js';
|
|
28
25
|
|
|
29
26
|
// ─── Handler 模块 ─────────────────────────────────────────────
|
|
30
27
|
|
|
31
|
-
import * as systemHandlers from './handlers/system.js';
|
|
32
28
|
import * as candidateHandlers from './handlers/candidate.js';
|
|
33
|
-
import * as knowledgeHandlers from './handlers/knowledge.js';
|
|
34
29
|
import * as consolidated from './handlers/consolidated.js';
|
|
30
|
+
import * as knowledgeHandlers from './handlers/knowledge.js';
|
|
31
|
+
import * as systemHandlers from './handlers/system.js';
|
|
35
32
|
|
|
36
33
|
// ─── McpServer 类 ─────────────────────────────────────────────
|
|
37
34
|
|
|
@@ -78,7 +75,9 @@ export class McpServer {
|
|
|
78
75
|
});
|
|
79
76
|
|
|
80
77
|
// 注册 Gateway action handlers
|
|
81
|
-
const { registerGatewayActions } = await import(
|
|
78
|
+
const { registerGatewayActions } = await import(
|
|
79
|
+
'../../core/gateway/GatewayActionRegistry.js'
|
|
80
|
+
);
|
|
82
81
|
const gateway = this.container.get('gateway');
|
|
83
82
|
if (gateway) {
|
|
84
83
|
registerGatewayActions(gateway, this.container);
|
|
@@ -87,7 +86,7 @@ export class McpServer {
|
|
|
87
86
|
|
|
88
87
|
this.server = new Server(
|
|
89
88
|
{ name: 'autosnippet-v3', version: '3.0.0' },
|
|
90
|
-
{ capabilities: { tools: {} } }
|
|
89
|
+
{ capabilities: { tools: {} } }
|
|
91
90
|
);
|
|
92
91
|
|
|
93
92
|
this._registerHandlers();
|
|
@@ -103,7 +102,7 @@ export class McpServer {
|
|
|
103
102
|
this.server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
104
103
|
const tierName = process.env.ASD_MCP_TIER || 'agent';
|
|
105
104
|
const maxTier = TIER_ORDER[tierName] ?? TIER_ORDER.agent;
|
|
106
|
-
const visible = TOOLS.filter(t => (TIER_ORDER[t.tier || 'agent'] ?? 0) <= maxTier);
|
|
105
|
+
const visible = TOOLS.filter((t) => (TIER_ORDER[t.tier || 'agent'] ?? 0) <= maxTier);
|
|
107
106
|
return { tools: visible };
|
|
108
107
|
});
|
|
109
108
|
|
|
@@ -113,10 +112,22 @@ export class McpServer {
|
|
|
113
112
|
const t0 = Date.now();
|
|
114
113
|
try {
|
|
115
114
|
const result = await this._handleToolCall(name, args || {});
|
|
116
|
-
return {
|
|
115
|
+
return {
|
|
116
|
+
content: [
|
|
117
|
+
{
|
|
118
|
+
type: 'text',
|
|
119
|
+
text: typeof result === 'string' ? result : JSON.stringify(result, null, 2),
|
|
120
|
+
},
|
|
121
|
+
],
|
|
122
|
+
};
|
|
117
123
|
} catch (err) {
|
|
118
124
|
this.logger.error(`MCP tool error: ${name}`, { error: err.message });
|
|
119
|
-
const env = envelope({
|
|
125
|
+
const env = envelope({
|
|
126
|
+
success: false,
|
|
127
|
+
message: err.message,
|
|
128
|
+
errorCode: 'TOOL_ERROR',
|
|
129
|
+
meta: { tool: name, responseTimeMs: Date.now() - t0 },
|
|
130
|
+
});
|
|
120
131
|
return { content: [{ type: 'text', text: JSON.stringify(env, null, 2) }], isError: true };
|
|
121
132
|
}
|
|
122
133
|
});
|
|
@@ -130,7 +141,9 @@ export class McpServer {
|
|
|
130
141
|
|
|
131
142
|
// 查找 handler 并通过 wrapHandler 统一错误处理
|
|
132
143
|
const handler = this._resolveHandler(name);
|
|
133
|
-
if (!handler)
|
|
144
|
+
if (!handler) {
|
|
145
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
146
|
+
}
|
|
134
147
|
|
|
135
148
|
const wrapped = wrapHandler(name, handler);
|
|
136
149
|
return wrapped(ctx, args);
|
|
@@ -143,23 +156,25 @@ export class McpServer {
|
|
|
143
156
|
_resolveHandler(name) {
|
|
144
157
|
const HANDLER_MAP = {
|
|
145
158
|
// ── Agent 层 (12) ──
|
|
146
|
-
autosnippet_health:
|
|
147
|
-
autosnippet_capabilities:
|
|
148
|
-
autosnippet_search:
|
|
149
|
-
autosnippet_knowledge:
|
|
150
|
-
autosnippet_structure:
|
|
151
|
-
autosnippet_graph:
|
|
152
|
-
autosnippet_guard:
|
|
153
|
-
autosnippet_submit_knowledge:
|
|
154
|
-
autosnippet_submit_knowledge_batch: (ctx, args) =>
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
159
|
+
autosnippet_health: (ctx) => systemHandlers.health(ctx),
|
|
160
|
+
autosnippet_capabilities: () => systemHandlers.capabilities(),
|
|
161
|
+
autosnippet_search: (ctx, args) => consolidated.consolidatedSearch(ctx, args),
|
|
162
|
+
autosnippet_knowledge: (ctx, args) => consolidated.consolidatedKnowledge(ctx, args),
|
|
163
|
+
autosnippet_structure: (ctx, args) => consolidated.consolidatedStructure(ctx, args),
|
|
164
|
+
autosnippet_graph: (ctx, args) => consolidated.consolidatedGraph(ctx, args),
|
|
165
|
+
autosnippet_guard: (ctx, args) => consolidated.consolidatedGuard(ctx, args),
|
|
166
|
+
autosnippet_submit_knowledge: (ctx, args) => consolidated.enhancedSubmitKnowledge(ctx, args),
|
|
167
|
+
autosnippet_submit_knowledge_batch: (ctx, args) =>
|
|
168
|
+
knowledgeHandlers.submitKnowledgeBatch(ctx, args),
|
|
169
|
+
autosnippet_save_document: (ctx, args) => knowledgeHandlers.saveDocument(ctx, args),
|
|
170
|
+
autosnippet_skill: (ctx, args) => consolidated.consolidatedSkill(ctx, args),
|
|
171
|
+
autosnippet_bootstrap: (ctx, args) => consolidated.consolidatedBootstrap(ctx, args),
|
|
158
172
|
// ── Admin 层 (+4) ──
|
|
159
|
-
autosnippet_enrich_candidates:
|
|
160
|
-
autosnippet_knowledge_lifecycle:
|
|
161
|
-
|
|
162
|
-
|
|
173
|
+
autosnippet_enrich_candidates: (ctx, args) => candidateHandlers.enrichCandidates(ctx, args),
|
|
174
|
+
autosnippet_knowledge_lifecycle: (ctx, args) =>
|
|
175
|
+
knowledgeHandlers.knowledgeLifecycle(ctx, args),
|
|
176
|
+
autosnippet_validate_candidate: (ctx, args) => candidateHandlers.validateCandidate(ctx, args),
|
|
177
|
+
autosnippet_check_duplicate: (ctx, args) => candidateHandlers.checkDuplicate(ctx, args),
|
|
163
178
|
};
|
|
164
179
|
return HANDLER_MAP[name] || null;
|
|
165
180
|
}
|
|
@@ -171,17 +186,23 @@ export class McpServer {
|
|
|
171
186
|
*/
|
|
172
187
|
async _gatewayGate(toolName, args) {
|
|
173
188
|
let mapping = TOOL_GATEWAY_MAP[toolName];
|
|
174
|
-
if (!mapping)
|
|
189
|
+
if (!mapping) {
|
|
190
|
+
return; // 只读工具,跳过
|
|
191
|
+
}
|
|
175
192
|
|
|
176
193
|
// 动态 resolver:根据 args 计算实际 action/resource
|
|
177
194
|
if (typeof mapping.resolver === 'function') {
|
|
178
195
|
mapping = mapping.resolver(args);
|
|
179
|
-
if (!mapping)
|
|
196
|
+
if (!mapping) {
|
|
197
|
+
return; // resolver 返回 null 表示只读操作
|
|
198
|
+
}
|
|
180
199
|
}
|
|
181
200
|
|
|
182
201
|
try {
|
|
183
202
|
const gateway = this.container.get('gateway');
|
|
184
|
-
if (!gateway)
|
|
203
|
+
if (!gateway) {
|
|
204
|
+
return; // Gateway 未初始化,降级放行
|
|
205
|
+
}
|
|
185
206
|
|
|
186
207
|
const result = await gateway.checkOnly({
|
|
187
208
|
actor: 'external_agent',
|
|
@@ -200,7 +221,10 @@ export class McpServer {
|
|
|
200
221
|
this.logger.debug(`MCP Gateway gating passed: ${toolName}`, { requestId: result.requestId });
|
|
201
222
|
} catch (err) {
|
|
202
223
|
// 区分 Gateway 自身错误 vs 权限拒绝
|
|
203
|
-
if (
|
|
224
|
+
if (
|
|
225
|
+
err.message?.startsWith('[PERMISSION_DENIED]') ||
|
|
226
|
+
err.message?.startsWith('[CONSTITUTION_VIOLATION]')
|
|
227
|
+
) {
|
|
204
228
|
throw err;
|
|
205
229
|
}
|
|
206
230
|
// Gateway 内部故障不应阻断业务(降级放行 + 记录)
|
|
@@ -217,15 +241,21 @@ export class McpServer {
|
|
|
217
241
|
|
|
218
242
|
const tierName = process.env.ASD_MCP_TIER || 'agent';
|
|
219
243
|
const maxTier = TIER_ORDER[tierName] ?? TIER_ORDER.agent;
|
|
220
|
-
const visibleCount = TOOLS.filter(
|
|
244
|
+
const visibleCount = TOOLS.filter(
|
|
245
|
+
(t) => (TIER_ORDER[t.tier || 'agent'] ?? 0) <= maxTier
|
|
246
|
+
).length;
|
|
221
247
|
|
|
222
248
|
this.logger.info(`MCP Server started (stdio) — ${visibleCount} tools [tier=${tierName}]`);
|
|
223
249
|
process.stderr.write(`AutoSnippet MCP ready — ${visibleCount} tools [tier=${tierName}]\n`);
|
|
224
250
|
}
|
|
225
251
|
|
|
226
252
|
async shutdown() {
|
|
227
|
-
if (this.server)
|
|
228
|
-
|
|
253
|
+
if (this.server) {
|
|
254
|
+
await this.server.close();
|
|
255
|
+
}
|
|
256
|
+
if (this.bootstrap) {
|
|
257
|
+
await this.bootstrap.shutdown();
|
|
258
|
+
}
|
|
229
259
|
}
|
|
230
260
|
}
|
|
231
261
|
|
|
@@ -10,15 +10,15 @@
|
|
|
10
10
|
* @module external/mcp/errorHandler
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import
|
|
13
|
+
import Logger from '../../infrastructure/logging/Logger.js';
|
|
14
14
|
import {
|
|
15
|
-
ValidationError,
|
|
16
|
-
NotFoundError,
|
|
17
15
|
ConflictError,
|
|
18
|
-
PermissionDenied,
|
|
19
16
|
ConstitutionViolation,
|
|
17
|
+
NotFoundError,
|
|
18
|
+
PermissionDenied,
|
|
19
|
+
ValidationError,
|
|
20
20
|
} from '../../shared/errors/index.js';
|
|
21
|
-
import
|
|
21
|
+
import { envelope } from './envelope.js';
|
|
22
22
|
|
|
23
23
|
const logger = Logger.getInstance();
|
|
24
24
|
|
|
@@ -28,12 +28,24 @@ const logger = Logger.getInstance();
|
|
|
28
28
|
* @returns {string}
|
|
29
29
|
*/
|
|
30
30
|
function inferErrorCode(err) {
|
|
31
|
-
if (err instanceof ValidationError)
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
if (err instanceof
|
|
35
|
-
|
|
36
|
-
|
|
31
|
+
if (err instanceof ValidationError) {
|
|
32
|
+
return 'VALIDATION_ERROR';
|
|
33
|
+
}
|
|
34
|
+
if (err instanceof NotFoundError) {
|
|
35
|
+
return 'NOT_FOUND';
|
|
36
|
+
}
|
|
37
|
+
if (err instanceof ConflictError) {
|
|
38
|
+
return 'CONFLICT';
|
|
39
|
+
}
|
|
40
|
+
if (err instanceof PermissionDenied) {
|
|
41
|
+
return 'PERMISSION_DENIED';
|
|
42
|
+
}
|
|
43
|
+
if (err instanceof ConstitutionViolation) {
|
|
44
|
+
return 'CONSTITUTION_VIOLATION';
|
|
45
|
+
}
|
|
46
|
+
if (err.code) {
|
|
47
|
+
return err.code;
|
|
48
|
+
}
|
|
37
49
|
return 'INTERNAL_ERROR';
|
|
38
50
|
}
|
|
39
51
|
|