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
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
* 缓存位置: {projectRoot}/.autosnippet/cache/
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from 'node:fs';
|
|
13
|
-
import { join, relative } from 'node:path';
|
|
14
12
|
import { createHash } from 'node:crypto';
|
|
13
|
+
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
14
|
+
import { join, relative } from 'node:path';
|
|
15
15
|
import Logger from '../logging/Logger.js';
|
|
16
16
|
|
|
17
17
|
export class GraphCache {
|
|
@@ -59,7 +59,9 @@ export class GraphCache {
|
|
|
59
59
|
load(key) {
|
|
60
60
|
try {
|
|
61
61
|
const filePath = join(this.#cacheDir, `${key}.json`);
|
|
62
|
-
if (!existsSync(filePath))
|
|
62
|
+
if (!existsSync(filePath)) {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
63
65
|
const raw = readFileSync(filePath, 'utf-8');
|
|
64
66
|
return JSON.parse(raw);
|
|
65
67
|
} catch (err) {
|
|
@@ -76,7 +78,9 @@ export class GraphCache {
|
|
|
76
78
|
*/
|
|
77
79
|
isValid(key, currentHash) {
|
|
78
80
|
const cached = this.load(key);
|
|
79
|
-
if (!cached)
|
|
81
|
+
if (!cached) {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
80
84
|
return cached.contentHash === currentHash;
|
|
81
85
|
}
|
|
82
86
|
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
* 内存缓存模式
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { cacheService as memoryCacheService } from './CacheService.js';
|
|
7
6
|
import Logger from '../logging/Logger.js';
|
|
7
|
+
import { cacheService as memoryCacheService } from './CacheService.js';
|
|
8
8
|
|
|
9
9
|
export class UnifiedCacheAdapter {
|
|
10
10
|
constructor() {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import { fileURLToPath } from 'url';
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
4
|
|
|
5
5
|
const __filename = fileURLToPath(import.meta.url);
|
|
6
6
|
const __dirname = path.dirname(__filename);
|
|
@@ -49,8 +49,12 @@ export class ConfigLoader {
|
|
|
49
49
|
const output = { ...target };
|
|
50
50
|
for (const key of Object.keys(source)) {
|
|
51
51
|
if (
|
|
52
|
-
source[key] &&
|
|
53
|
-
|
|
52
|
+
source[key] &&
|
|
53
|
+
typeof source[key] === 'object' &&
|
|
54
|
+
!Array.isArray(source[key]) &&
|
|
55
|
+
target[key] &&
|
|
56
|
+
typeof target[key] === 'object' &&
|
|
57
|
+
!Array.isArray(target[key])
|
|
54
58
|
) {
|
|
55
59
|
output[key] = this._deepMerge(target[key], source[key]);
|
|
56
60
|
} else {
|
|
@@ -52,21 +52,41 @@ export const DEFAULT_CATEGORY = 'general';
|
|
|
52
52
|
*/
|
|
53
53
|
export function inferCategory(relPath, content) {
|
|
54
54
|
const frontMatch = (content || '').match(/^---[\s\S]*?category:\s*["']?([\w-]+)["']?/m);
|
|
55
|
-
if (frontMatch)
|
|
55
|
+
if (frontMatch) {
|
|
56
|
+
return frontMatch[1];
|
|
57
|
+
}
|
|
56
58
|
const lower = (relPath || '').toLowerCase();
|
|
57
59
|
for (const { pattern, category } of CATEGORY_RULES) {
|
|
58
|
-
if (pattern.test(lower))
|
|
60
|
+
if (pattern.test(lower)) {
|
|
61
|
+
return category;
|
|
62
|
+
}
|
|
59
63
|
}
|
|
60
64
|
return DEFAULT_CATEGORY;
|
|
61
65
|
}
|
|
62
66
|
|
|
63
67
|
export default {
|
|
64
|
-
SPEC_FILENAME,
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
68
|
+
SPEC_FILENAME,
|
|
69
|
+
KNOWLEDGE_BASE_DIR,
|
|
70
|
+
RECIPES_DIR,
|
|
71
|
+
CANDIDATES_DIR,
|
|
72
|
+
RECIPES_INDEX,
|
|
73
|
+
SPMMAP_FILENAME,
|
|
74
|
+
SPMMAP_PATH,
|
|
75
|
+
DEFAULT_STORAGE_ADAPTER,
|
|
76
|
+
STORAGE_ADAPTERS,
|
|
77
|
+
SOURCE_TYPES,
|
|
78
|
+
SOURCE_TYPE_RECIPE,
|
|
79
|
+
SOURCE_TYPE_TARGET_README,
|
|
80
|
+
DEFAULT_SOURCES,
|
|
81
|
+
DEFAULT_CHUNKING,
|
|
82
|
+
CHUNKING_STRATEGIES,
|
|
83
|
+
DEFAULT_MAX_CHUNK_TOKENS,
|
|
84
|
+
DEFAULT_OVERLAP_TOKENS,
|
|
85
|
+
CHARS_PER_TOKEN,
|
|
86
|
+
README_NAMES,
|
|
87
|
+
DEFAULT_ASD_UI_URL,
|
|
88
|
+
GUARD_CONTEXT_EXCERPT_LIMIT,
|
|
89
|
+
CATEGORY_RULES,
|
|
90
|
+
DEFAULT_CATEGORY,
|
|
91
|
+
inferCategory,
|
|
72
92
|
};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import path from 'node:path';
|
|
2
1
|
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
3
|
import pathGuard from '../../shared/PathGuard.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Paths — 项目路径解析工具
|
|
7
|
-
* 提供
|
|
7
|
+
* 提供 Snippet 安装目录、缓存目录、知识库目录等路径计算能力。
|
|
8
8
|
* 所有路径均自动确保目录存在。
|
|
9
9
|
*/
|
|
10
10
|
|
|
@@ -20,25 +20,40 @@ function ensureDir(dirPath) {
|
|
|
20
20
|
if (!fs.existsSync(dirPath)) {
|
|
21
21
|
fs.mkdirSync(dirPath, { recursive: true });
|
|
22
22
|
}
|
|
23
|
-
} catch {
|
|
23
|
+
} catch {
|
|
24
|
+
/* ignore */
|
|
25
|
+
}
|
|
24
26
|
return dirPath;
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
/**
|
|
28
|
-
* Xcode CodeSnippets 输出目录
|
|
30
|
+
* Xcode CodeSnippets 输出目录 (macOS only)
|
|
29
31
|
* 可通过 ASD_SNIPPETS_PATH 环境变量覆盖
|
|
30
32
|
*/
|
|
31
33
|
export function getSnippetsPath() {
|
|
32
|
-
if (process.env.ASD_SNIPPETS_PATH)
|
|
34
|
+
if (process.env.ASD_SNIPPETS_PATH) {
|
|
35
|
+
return process.env.ASD_SNIPPETS_PATH;
|
|
36
|
+
}
|
|
33
37
|
return ensureDir(path.join(USER_HOME, 'Library/Developer/Xcode/UserData/CodeSnippets'));
|
|
34
38
|
}
|
|
35
39
|
|
|
40
|
+
/**
|
|
41
|
+
* VSCode 项目级 Snippets 目录 = projectRoot/.vscode/
|
|
42
|
+
* @param {string} projectRoot
|
|
43
|
+
* @returns {string}
|
|
44
|
+
*/
|
|
45
|
+
export function getVSCodeSnippetsPath(projectRoot) {
|
|
46
|
+
return ensureDir(path.join(projectRoot, '.vscode'));
|
|
47
|
+
}
|
|
48
|
+
|
|
36
49
|
/**
|
|
37
50
|
* AutoSnippet 全局缓存目录 ~/.autosnippet/cache
|
|
38
51
|
* 可通过 ASD_CACHE_PATH 环境变量覆盖
|
|
39
52
|
*/
|
|
40
53
|
export function getCachePath() {
|
|
41
|
-
if (process.env.ASD_CACHE_PATH)
|
|
54
|
+
if (process.env.ASD_CACHE_PATH) {
|
|
55
|
+
return process.env.ASD_CACHE_PATH;
|
|
56
|
+
}
|
|
42
57
|
return ensureDir(path.join(USER_HOME, '.autosnippet', 'cache'));
|
|
43
58
|
}
|
|
44
59
|
|
|
@@ -58,7 +73,9 @@ export function getKnowledgeBaseDirName(projectRoot) {
|
|
|
58
73
|
}
|
|
59
74
|
}
|
|
60
75
|
}
|
|
61
|
-
} catch {
|
|
76
|
+
} catch {
|
|
77
|
+
/* ignore */
|
|
78
|
+
}
|
|
62
79
|
return 'AutoSnippet';
|
|
63
80
|
}
|
|
64
81
|
|
|
@@ -113,13 +130,16 @@ export function getProjectSkillsPath(projectRoot) {
|
|
|
113
130
|
*/
|
|
114
131
|
export function getProjectRecipesPath(projectRoot, rootSpec) {
|
|
115
132
|
const dir = rootSpec?.recipes?.dir || rootSpec?.skills?.dir || null;
|
|
116
|
-
if (dir)
|
|
133
|
+
if (dir) {
|
|
134
|
+
return path.join(projectRoot, dir);
|
|
135
|
+
}
|
|
117
136
|
return path.join(getProjectKnowledgePath(projectRoot), 'recipes');
|
|
118
137
|
}
|
|
119
138
|
|
|
120
139
|
export default {
|
|
121
140
|
SPEC_FILENAME,
|
|
122
141
|
getSnippetsPath,
|
|
142
|
+
getVSCodeSnippetsPath,
|
|
123
143
|
getCachePath,
|
|
124
144
|
getKnowledgeBaseDirName,
|
|
125
145
|
getProjectKnowledgePath,
|
|
@@ -9,7 +9,9 @@ const DEFAULT_SYMBOL = '@';
|
|
|
9
9
|
|
|
10
10
|
function fromEnv() {
|
|
11
11
|
const raw = process.env.ASD_TRIGGER_SYMBOL;
|
|
12
|
-
if (raw != null && String(raw).length === 1)
|
|
12
|
+
if (raw != null && String(raw).length === 1) {
|
|
13
|
+
return String(raw);
|
|
14
|
+
}
|
|
13
15
|
return DEFAULT_SYMBOL;
|
|
14
16
|
}
|
|
15
17
|
|
|
@@ -24,20 +26,22 @@ export const TRIGGER_SYMBOL = fromEnv();
|
|
|
24
26
|
export const TRIGGER_SYMBOLS = [TRIGGER_SYMBOL];
|
|
25
27
|
|
|
26
28
|
/** 用于按触发符拆分的正则 */
|
|
27
|
-
export const TRIGGER_SPLIT_REGEX = new RegExp(
|
|
28
|
-
'[' + TRIGGER_SYMBOLS.map(escapeRegExp).join('') + ']'
|
|
29
|
-
);
|
|
29
|
+
export const TRIGGER_SPLIT_REGEX = new RegExp(`[${TRIGGER_SYMBOLS.map(escapeRegExp).join('')}]`);
|
|
30
30
|
|
|
31
31
|
/** str 是否以触发符开头 */
|
|
32
32
|
export function hasTriggerPrefix(str) {
|
|
33
|
-
if (!str || typeof str !== 'string')
|
|
33
|
+
if (!str || typeof str !== 'string') {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
34
36
|
const s = String(str).trim();
|
|
35
37
|
return TRIGGER_SYMBOLS.some((sym) => s.startsWith(sym));
|
|
36
38
|
}
|
|
37
39
|
|
|
38
40
|
/** 去掉 str 开头的连续触发符 */
|
|
39
41
|
export function stripTriggerPrefix(str) {
|
|
40
|
-
if (!str || typeof str !== 'string')
|
|
42
|
+
if (!str || typeof str !== 'string') {
|
|
43
|
+
return String(str);
|
|
44
|
+
}
|
|
41
45
|
let s = String(str).trim();
|
|
42
46
|
while (s.length && TRIGGER_SYMBOLS.some((sym) => s.startsWith(sym))) {
|
|
43
47
|
s = s.slice(1).trimStart();
|
|
@@ -47,18 +51,26 @@ export function stripTriggerPrefix(str) {
|
|
|
47
51
|
|
|
48
52
|
/** 若 str 不以触发符开头,则加上默认触发符 */
|
|
49
53
|
export function ensureTriggerPrefix(str) {
|
|
50
|
-
if (!str || typeof str !== 'string')
|
|
54
|
+
if (!str || typeof str !== 'string') {
|
|
55
|
+
return str;
|
|
56
|
+
}
|
|
51
57
|
const s = String(str).trim();
|
|
52
|
-
if (!s)
|
|
58
|
+
if (!s) {
|
|
59
|
+
return s;
|
|
60
|
+
}
|
|
53
61
|
return hasTriggerPrefix(s) ? s : TRIGGER_SYMBOL + s;
|
|
54
62
|
}
|
|
55
63
|
|
|
56
64
|
/** 获取 str 已带的触发符,否则返回默认触发符 */
|
|
57
65
|
export function getPrefixFromTrigger(str) {
|
|
58
|
-
if (!str || typeof str !== 'string')
|
|
66
|
+
if (!str || typeof str !== 'string') {
|
|
67
|
+
return TRIGGER_SYMBOL;
|
|
68
|
+
}
|
|
59
69
|
const s = String(str).trim();
|
|
60
70
|
for (const sym of TRIGGER_SYMBOLS) {
|
|
61
|
-
if (s.startsWith(sym))
|
|
71
|
+
if (s.startsWith(sym)) {
|
|
72
|
+
return sym;
|
|
73
|
+
}
|
|
62
74
|
}
|
|
63
75
|
return TRIGGER_SYMBOL;
|
|
64
76
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
1
4
|
import Database from 'better-sqlite3';
|
|
2
|
-
import fs from 'fs';
|
|
3
|
-
import path from 'path';
|
|
4
|
-
import { fileURLToPath } from 'url';
|
|
5
5
|
import pathGuard from '../../shared/PathGuard.js';
|
|
6
6
|
|
|
7
7
|
const __filename = fileURLToPath(import.meta.url);
|
|
@@ -28,13 +28,14 @@ export class DatabaseConnection {
|
|
|
28
28
|
// 使用 projectRoot(PathGuard 已配置)优先解析相对路径,
|
|
29
29
|
// 而非 path.resolve()(依赖 cwd,MCP 场景下 cwd 可能是用户主目录)
|
|
30
30
|
const projectRoot = pathGuard.projectRoot;
|
|
31
|
-
const resolvedDbPath =
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
const resolvedDbPath =
|
|
32
|
+
projectRoot && !path.isAbsolute(dbPath)
|
|
33
|
+
? path.resolve(projectRoot, dbPath)
|
|
34
|
+
: path.resolve(dbPath);
|
|
34
35
|
|
|
35
36
|
// 路径安全检查 — 防止 DB 文件创建到项目允许范围外
|
|
36
37
|
pathGuard.assertProjectWriteSafe(resolvedDbPath);
|
|
37
|
-
|
|
38
|
+
|
|
38
39
|
// 确保数据目录存在
|
|
39
40
|
const dbDir = path.dirname(resolvedDbPath);
|
|
40
41
|
if (!fs.existsSync(dbDir)) {
|
|
@@ -72,7 +73,7 @@ export class DatabaseConnection {
|
|
|
72
73
|
|
|
73
74
|
for (const file of migrationFiles) {
|
|
74
75
|
const version = file.replace(/\.(sql|js)$/, '');
|
|
75
|
-
|
|
76
|
+
|
|
76
77
|
// 检查是否已应用
|
|
77
78
|
const applied = this.db
|
|
78
79
|
.prepare('SELECT version FROM schema_migrations WHERE version = ?')
|
|
@@ -88,7 +89,9 @@ export class DatabaseConnection {
|
|
|
88
89
|
const runMigration = this.db.transaction(() => {
|
|
89
90
|
migrate(this.db);
|
|
90
91
|
this.db
|
|
91
|
-
.prepare(
|
|
92
|
+
.prepare(
|
|
93
|
+
'INSERT OR IGNORE INTO schema_migrations (version, applied_at) VALUES (?, ?)'
|
|
94
|
+
)
|
|
92
95
|
.run(version, new Date().toISOString());
|
|
93
96
|
});
|
|
94
97
|
runMigration();
|
|
@@ -97,7 +100,9 @@ export class DatabaseConnection {
|
|
|
97
100
|
const runMigration = this.db.transaction(() => {
|
|
98
101
|
this.db.exec(sql);
|
|
99
102
|
this.db
|
|
100
|
-
.prepare(
|
|
103
|
+
.prepare(
|
|
104
|
+
'INSERT OR IGNORE INTO schema_migrations (version, applied_at) VALUES (?, ?)'
|
|
105
|
+
)
|
|
101
106
|
.run(version, new Date().toISOString());
|
|
102
107
|
});
|
|
103
108
|
runMigration();
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
* 10. code_entities — 代码实体节点 (AST 解析)
|
|
18
18
|
*/
|
|
19
19
|
export default function migrate(db) {
|
|
20
|
-
|
|
21
20
|
// ═══════════════════════════════════════════════════════════════
|
|
22
21
|
// 1. knowledge_entries — 核心知识条目
|
|
23
22
|
// ═══════════════════════════════════════════════════════════════
|
|
@@ -14,7 +14,9 @@ const TIMEOUT = 3000;
|
|
|
14
14
|
* @returns {string} 剪贴板文本,失败返回空字符串
|
|
15
15
|
*/
|
|
16
16
|
export function read() {
|
|
17
|
-
if (process.platform !== 'darwin')
|
|
17
|
+
if (process.platform !== 'darwin') {
|
|
18
|
+
return '';
|
|
19
|
+
}
|
|
18
20
|
try {
|
|
19
21
|
return execSync('pbpaste', { encoding: 'utf8', timeout: TIMEOUT });
|
|
20
22
|
} catch {
|
|
@@ -28,7 +30,9 @@ export function read() {
|
|
|
28
30
|
* @returns {boolean}
|
|
29
31
|
*/
|
|
30
32
|
export function write(text) {
|
|
31
|
-
if (process.platform !== 'darwin')
|
|
33
|
+
if (process.platform !== 'darwin') {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
32
36
|
try {
|
|
33
37
|
execSync('pbcopy', { input: text, timeout: TIMEOUT, stdio: ['pipe', 'ignore', 'ignore'] });
|
|
34
38
|
return true;
|
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
* V2 ESM 版本,对应 V1 NativeUi.js
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import {
|
|
12
|
+
import { execFileSync, execSync } from 'node:child_process';
|
|
13
13
|
import { existsSync } from 'node:fs';
|
|
14
|
-
import {
|
|
14
|
+
import { dirname, join } from 'node:path';
|
|
15
15
|
import { fileURLToPath } from 'node:url';
|
|
16
16
|
|
|
17
17
|
const __filename = fileURLToPath(import.meta.url);
|
|
@@ -23,7 +23,9 @@ const NATIVE_UI_PATH = join(__dirname, '../../../resources/native-ui/native-ui')
|
|
|
23
23
|
* 检查 Swift Helper 是否可用
|
|
24
24
|
*/
|
|
25
25
|
export function isNativeUiAvailable() {
|
|
26
|
-
if (process.platform !== 'darwin')
|
|
26
|
+
if (process.platform !== 'darwin') {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
27
29
|
try {
|
|
28
30
|
return existsSync(NATIVE_UI_PATH);
|
|
29
31
|
} catch {
|
|
@@ -39,7 +41,9 @@ export function isNativeUiAvailable() {
|
|
|
39
41
|
* @returns {number} 选中的索引(0-based),-1 表示取消
|
|
40
42
|
*/
|
|
41
43
|
export function showCombinedWindow(items, keyword = '') {
|
|
42
|
-
if (!items || items.length === 0)
|
|
44
|
+
if (!items || items.length === 0) {
|
|
45
|
+
return -1;
|
|
46
|
+
}
|
|
43
47
|
|
|
44
48
|
// 1. 尝试 Swift Helper
|
|
45
49
|
if (isNativeUiAvailable()) {
|
|
@@ -47,16 +51,17 @@ export function showCombinedWindow(items, keyword = '') {
|
|
|
47
51
|
const safeKeyword = keyword.replace(/'/g, "'\\''");
|
|
48
52
|
const json = JSON.stringify(items);
|
|
49
53
|
const safeJson = json.replace(/'/g, "'\\''");
|
|
50
|
-
const result = execFileSync(
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
).trim();
|
|
54
|
+
const result = execFileSync(NATIVE_UI_PATH, ['combined', safeKeyword, safeJson], {
|
|
55
|
+
encoding: 'utf8',
|
|
56
|
+
timeout: 60000,
|
|
57
|
+
}).trim();
|
|
55
58
|
const index = parseInt(result, 10);
|
|
56
|
-
return isNaN(index) ? -1 : index;
|
|
59
|
+
return Number.isNaN(index) ? -1 : index;
|
|
57
60
|
} catch (err) {
|
|
58
61
|
// exit(1) = 用户取消,直接返回 -1,不降级
|
|
59
|
-
if (err.status === 1)
|
|
62
|
+
if (err.status === 1) {
|
|
63
|
+
return -1;
|
|
64
|
+
}
|
|
60
65
|
// 其他错误(崩溃等)才降级到 AppleScript
|
|
61
66
|
}
|
|
62
67
|
}
|
|
@@ -83,7 +88,9 @@ export function showCombinedWindow(items, keyword = '') {
|
|
|
83
88
|
* @returns {number} 选中索引(0-based),-1 取消
|
|
84
89
|
*/
|
|
85
90
|
export function showListSelection(items, title = 'AutoSnippet', prompt = '请选择:') {
|
|
86
|
-
if (!items || items.length === 0)
|
|
91
|
+
if (!items || items.length === 0) {
|
|
92
|
+
return -1;
|
|
93
|
+
}
|
|
87
94
|
|
|
88
95
|
// 1. Swift Helper
|
|
89
96
|
if (isNativeUiAvailable()) {
|
|
@@ -94,10 +101,12 @@ export function showListSelection(items, title = 'AutoSnippet', prompt = '请选
|
|
|
94
101
|
timeout: 60000,
|
|
95
102
|
}).trim();
|
|
96
103
|
const index = parseInt(result, 10);
|
|
97
|
-
return isNaN(index) ? -1 : index;
|
|
104
|
+
return Number.isNaN(index) ? -1 : index;
|
|
98
105
|
} catch (err) {
|
|
99
106
|
// exit(1) = 用户取消,直接返回
|
|
100
|
-
if (err.status === 1)
|
|
107
|
+
if (err.status === 1) {
|
|
108
|
+
return -1;
|
|
109
|
+
}
|
|
101
110
|
// 其他错误才降级
|
|
102
111
|
}
|
|
103
112
|
}
|
|
@@ -105,10 +114,15 @@ export function showListSelection(items, title = 'AutoSnippet', prompt = '请选
|
|
|
105
114
|
// 2. AppleScript
|
|
106
115
|
if (process.platform === 'darwin') {
|
|
107
116
|
try {
|
|
108
|
-
const listStr = items.map(i => `"${i.replace(/"/g, '\\"')}"`).join(', ');
|
|
117
|
+
const listStr = items.map((i) => `"${i.replace(/"/g, '\\"')}"`).join(', ');
|
|
109
118
|
const script = `choose from list {${listStr}} with title "${_escAS(title)}" with prompt "${_escAS(prompt)}" default items {"${_escAS(items[0])}"}`;
|
|
110
|
-
const result = execSync(`osascript -e '${script}'`, {
|
|
111
|
-
|
|
119
|
+
const result = execSync(`osascript -e '${script}'`, {
|
|
120
|
+
encoding: 'utf8',
|
|
121
|
+
timeout: 30000,
|
|
122
|
+
}).trim();
|
|
123
|
+
if (result === 'false') {
|
|
124
|
+
return -1;
|
|
125
|
+
}
|
|
112
126
|
return items.indexOf(result);
|
|
113
127
|
} catch {
|
|
114
128
|
return -1;
|
|
@@ -129,11 +143,10 @@ export function showPreviewConfirm(title, code) {
|
|
|
129
143
|
// 1. Swift Helper
|
|
130
144
|
if (isNativeUiAvailable()) {
|
|
131
145
|
try {
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
);
|
|
146
|
+
const _result = execFileSync(NATIVE_UI_PATH, ['preview', title, code], {
|
|
147
|
+
encoding: 'utf8',
|
|
148
|
+
timeout: 60000,
|
|
149
|
+
});
|
|
137
150
|
return true; // exit 0 = confirmed
|
|
138
151
|
} catch {
|
|
139
152
|
return false;
|
|
@@ -143,7 +156,7 @@ export function showPreviewConfirm(title, code) {
|
|
|
143
156
|
// 2. AppleScript
|
|
144
157
|
if (process.platform === 'darwin') {
|
|
145
158
|
try {
|
|
146
|
-
const preview = code.length > 300 ? code.substring(0, 297)
|
|
159
|
+
const preview = code.length > 300 ? `${code.substring(0, 297)}...` : code;
|
|
147
160
|
const script = `display dialog "${_escAS(title)}\\n\\n${_escAS(preview)}" with title "AutoSnippet" buttons {"取消", "确认"} default button "确认"`;
|
|
148
161
|
const result = execSync(`osascript -e '${script}'`, { encoding: 'utf8', timeout: 30000 });
|
|
149
162
|
return result.includes('确认');
|
|
@@ -162,19 +175,16 @@ export function showPreviewConfirm(title, code) {
|
|
|
162
175
|
*/
|
|
163
176
|
export function notify(message, title = 'AutoSnippet') {
|
|
164
177
|
if (process.platform !== 'darwin') {
|
|
165
|
-
console.log(`[${title}] ${message}`);
|
|
166
178
|
return;
|
|
167
179
|
}
|
|
168
180
|
try {
|
|
169
181
|
const safeMsg = message.replace(/"/g, '\\"').replace(/\n/g, '\\n');
|
|
170
182
|
const safeTitle = title.replace(/"/g, '\\"');
|
|
171
|
-
execSync(
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
);
|
|
175
|
-
} catch {
|
|
176
|
-
console.log(`[${title}] ${message}`);
|
|
177
|
-
}
|
|
183
|
+
execSync(`osascript -e 'display notification "${safeMsg}" with title "${safeTitle}"'`, {
|
|
184
|
+
timeout: 5000,
|
|
185
|
+
stdio: 'ignore',
|
|
186
|
+
});
|
|
187
|
+
} catch {}
|
|
178
188
|
}
|
|
179
189
|
|
|
180
190
|
/**
|
|
@@ -185,9 +195,11 @@ export function notify(message, title = 'AutoSnippet') {
|
|
|
185
195
|
* @returns {string|null} 点击的按钮名,或 null 表示取消
|
|
186
196
|
*/
|
|
187
197
|
export function promptWithButtons(message, buttons = ['确认', '取消'], title = 'AutoSnippet') {
|
|
188
|
-
if (process.platform !== 'darwin')
|
|
198
|
+
if (process.platform !== 'darwin') {
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
189
201
|
try {
|
|
190
|
-
const btnStr = buttons.map(b => `"${_escAS(b)}"`).join(', ');
|
|
202
|
+
const btnStr = buttons.map((b) => `"${_escAS(b)}"`).join(', ');
|
|
191
203
|
const script = `display dialog "${_escAS(message)}" with title "${_escAS(title)}" buttons {${btnStr}} default button "${_escAS(buttons[0])}"`;
|
|
192
204
|
const result = execSync(`osascript -e '${script}'`, { encoding: 'utf8', timeout: 30000 });
|
|
193
205
|
const match = result.match(/button returned:(.+)/);
|
|
@@ -210,21 +222,16 @@ function _appleScriptCombinedWindow(items, keyword) {
|
|
|
210
222
|
// 两步:先选择,再预览确认
|
|
211
223
|
const titles = items.map((it, i) => `${i + 1}. ${it.title || 'Recipe'}`);
|
|
212
224
|
const idx = showListSelection(titles, 'AutoSnippet Search', `搜索: ${keyword}`);
|
|
213
|
-
if (idx < 0 || idx >= items.length)
|
|
225
|
+
if (idx < 0 || idx >= items.length) {
|
|
226
|
+
return -1;
|
|
227
|
+
}
|
|
214
228
|
|
|
215
229
|
const item = items[idx];
|
|
216
|
-
const confirmed = showPreviewConfirm(
|
|
217
|
-
item.title || 'Recipe',
|
|
218
|
-
item.code || item.explanation || ''
|
|
219
|
-
);
|
|
230
|
+
const confirmed = showPreviewConfirm(item.title || 'Recipe', item.code || item.explanation || '');
|
|
220
231
|
return confirmed ? idx : -1;
|
|
221
232
|
}
|
|
222
233
|
|
|
223
234
|
function _consoleFallback(items, keyword) {
|
|
224
|
-
|
|
225
|
-
items.forEach((item, i) => {
|
|
226
|
-
console.log(` ${i + 1}. ${item.title || 'Recipe'}${item.groupSize > 1 ? ` (${item.groupSize} variants)` : ''}`);
|
|
227
|
-
});
|
|
228
|
-
console.log(` 使用 NativeUI (npm run build:native-ui) 可获得交互式选择体验`);
|
|
235
|
+
items.forEach((item, i) => {});
|
|
229
236
|
return -1;
|
|
230
237
|
}
|
|
@@ -4,17 +4,19 @@
|
|
|
4
4
|
* V2 ESM 版本,对应 V1 OpenBrowser.js
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { execFileSync, execSync } from 'node:child_process';
|
|
8
8
|
import { existsSync } from 'node:fs';
|
|
9
|
-
import { dirname, join } from 'node:path';
|
|
10
9
|
import { homedir } from 'node:os';
|
|
10
|
+
import { dirname, join } from 'node:path';
|
|
11
11
|
import { fileURLToPath } from 'node:url';
|
|
12
12
|
|
|
13
13
|
const __filename = fileURLToPath(import.meta.url);
|
|
14
14
|
const __dirname = dirname(__filename);
|
|
15
15
|
|
|
16
16
|
function isAppInstalled(appName) {
|
|
17
|
-
if (process.platform !== 'darwin')
|
|
17
|
+
if (process.platform !== 'darwin') {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
18
20
|
const candidates = [
|
|
19
21
|
`/Applications/${appName}.app`,
|
|
20
22
|
`${homedir()}/Applications/${appName}.app`,
|
|
@@ -27,7 +29,9 @@ function isAppInstalled(appName) {
|
|
|
27
29
|
* 检测当前进程是否已有控制 Chromium 系浏览器的权限
|
|
28
30
|
*/
|
|
29
31
|
export function hasMacOSBrowserControlGranted() {
|
|
30
|
-
if (process.platform !== 'darwin')
|
|
32
|
+
if (process.platform !== 'darwin') {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
31
35
|
const chromiumBrowsers = [
|
|
32
36
|
'Google Chrome Canary',
|
|
33
37
|
'Google Chrome',
|
|
@@ -37,7 +41,9 @@ export function hasMacOSBrowserControlGranted() {
|
|
|
37
41
|
'Chromium',
|
|
38
42
|
];
|
|
39
43
|
for (const browser of chromiumBrowsers) {
|
|
40
|
-
if (!isAppInstalled(browser))
|
|
44
|
+
if (!isAppInstalled(browser)) {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
41
47
|
try {
|
|
42
48
|
execSync(`osascript -e 'tell application "${browser}" to get name'`, {
|
|
43
49
|
stdio: 'ignore',
|
|
@@ -58,8 +64,7 @@ export function hasMacOSBrowserControlGranted() {
|
|
|
58
64
|
*/
|
|
59
65
|
export function openBrowserReuseTab(url, baseUrlForLookup) {
|
|
60
66
|
const skipReuse =
|
|
61
|
-
process.env.ASD_UI_NO_REUSE_TAB === '1' ||
|
|
62
|
-
process.env.ASD_UI_OPEN_REUSE === '0';
|
|
67
|
+
process.env.ASD_UI_NO_REUSE_TAB === '1' || process.env.ASD_UI_OPEN_REUSE === '0';
|
|
63
68
|
|
|
64
69
|
if (skipReuse) {
|
|
65
70
|
_fallbackOpen(url);
|
|
@@ -84,27 +89,21 @@ export function openBrowserReuseTab(url, baseUrlForLookup) {
|
|
|
84
89
|
}
|
|
85
90
|
|
|
86
91
|
if (!hasMacOSBrowserControlGranted()) {
|
|
87
|
-
console.log(
|
|
88
|
-
'💡 若已打开该页将复用标签;若系统弹出「辅助功能」权限请求,允许即可;未授权则自动新开标签。'
|
|
89
|
-
);
|
|
90
92
|
}
|
|
91
93
|
|
|
92
94
|
const lookupUrl = baseUrlForLookup || url;
|
|
93
95
|
for (const browser of availableChromium) {
|
|
94
96
|
try {
|
|
95
97
|
const args =
|
|
96
|
-
lookupUrl !== url
|
|
97
|
-
? [scriptPath, lookupUrl, url, browser]
|
|
98
|
-
: [scriptPath, url, browser];
|
|
98
|
+
lookupUrl !== url ? [scriptPath, lookupUrl, url, browser] : [scriptPath, url, browser];
|
|
99
99
|
execFileSync('osascript', args, {
|
|
100
100
|
cwd: dirname(scriptPath),
|
|
101
101
|
stdio: 'pipe',
|
|
102
102
|
timeout: 3000,
|
|
103
103
|
});
|
|
104
104
|
return;
|
|
105
|
-
} catch (
|
|
105
|
+
} catch (_err) {
|
|
106
106
|
if (process.env.ASD_DEBUG === '1') {
|
|
107
|
-
console.log(`[Debug] ${browser} 打开失败: ${err.message}`);
|
|
108
107
|
}
|
|
109
108
|
}
|
|
110
109
|
}
|
|
@@ -122,10 +121,8 @@ async function _fallbackOpen(url) {
|
|
|
122
121
|
const open = (await import('open')).default;
|
|
123
122
|
open(url).catch((err) => {
|
|
124
123
|
console.error(`⚠️ 打开浏览器失败: ${err.message}`);
|
|
125
|
-
console.log(`💡 请手动访问: ${url}`);
|
|
126
124
|
});
|
|
127
125
|
} catch (err) {
|
|
128
126
|
console.error(`⚠️ 打开浏览器失败: ${err.message}`);
|
|
129
|
-
console.log(`💡 请手动访问: ${url}`);
|
|
130
127
|
}
|
|
131
128
|
}
|