autosnippet 3.2.21 → 3.3.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.
- package/dashboard/dist/assets/{icons-C1dUryS-.js → icons-BofcEZ3f.js} +1 -1
- package/dashboard/dist/assets/index-SiN1GChm.js +128 -0
- package/dashboard/dist/index.html +2 -2
- package/dist/bin/cli.d.ts +0 -1
- package/dist/bin/cli.js +0 -133
- package/dist/lib/cli/SetupService.d.ts +46 -2
- package/dist/lib/cli/SetupService.js +2 -27
- package/dist/lib/{platform/ios/spm → core/discovery}/SpmDiscoverer.d.ts +2 -5
- package/dist/lib/{platform/ios/spm → core/discovery}/SpmDiscoverer.js +159 -44
- package/dist/lib/core/discovery/index.d.ts +1 -1
- package/dist/lib/core/discovery/index.js +2 -2
- package/dist/lib/external/mcp/handlers/guard.js +6 -3
- package/dist/lib/http/HttpServer.js +0 -6
- package/dist/lib/http/routes/commands.d.ts +1 -1
- package/dist/lib/http/routes/commands.js +1 -66
- package/dist/lib/http/routes/remote.js +0 -5
- package/dist/lib/injection/ServiceMap.d.ts +0 -9
- package/dist/lib/injection/modules/AppModule.d.ts +2 -3
- package/dist/lib/injection/modules/AppModule.js +3 -30
- package/dist/lib/injection/modules/GuardModule.js +33 -1
- package/dist/lib/service/guard/GuardCheckEngine.d.ts +13 -1
- package/dist/lib/service/guard/GuardCheckEngine.js +44 -2
- package/dist/lib/service/module/ModuleService.js +3 -13
- package/dist/lib/service/search/SearchEngine.js +1 -1
- package/dist/lib/shared/constants.d.ts +0 -15
- package/dist/lib/shared/constants.js +0 -10
- package/dist/lib/shared/schemas/config.d.ts +4 -1
- package/dist/lib/shared/schemas/config.js +8 -1
- package/dist/scripts/release.js +2 -10
- package/package.json +4 -19
- package/dashboard/dist/assets/index-DdvZE4Yd.js +0 -128
- package/dist/lib/http/routes/snippets.d.ts +0 -6
- package/dist/lib/http/routes/snippets.js +0 -49
- package/dist/lib/platform/ClipboardManager.d.ts +0 -24
- package/dist/lib/platform/ClipboardManager.js +0 -142
- package/dist/lib/platform/NativeUi.d.ts +0 -53
- package/dist/lib/platform/NativeUi.js +0 -284
- package/dist/lib/platform/ios/index.d.ts +0 -38
- package/dist/lib/platform/ios/index.js +0 -42
- package/dist/lib/platform/ios/routes/spm.d.ts +0 -9
- package/dist/lib/platform/ios/routes/spm.js +0 -371
- package/dist/lib/platform/ios/snippet/PlaceholderConverter.d.ts +0 -21
- package/dist/lib/platform/ios/snippet/PlaceholderConverter.js +0 -48
- package/dist/lib/platform/ios/snippet/XcodeCodec.d.ts +0 -23
- package/dist/lib/platform/ios/snippet/XcodeCodec.js +0 -96
- package/dist/lib/platform/ios/spm/DependencyGraph.d.ts +0 -56
- package/dist/lib/platform/ios/spm/DependencyGraph.js +0 -195
- package/dist/lib/platform/ios/spm/PackageSwiftParser.d.ts +0 -69
- package/dist/lib/platform/ios/spm/PackageSwiftParser.js +0 -231
- package/dist/lib/platform/ios/spm/PathFinder.d.ts +0 -28
- package/dist/lib/platform/ios/spm/PathFinder.js +0 -117
- package/dist/lib/platform/ios/spm/PolicyEngine.d.ts +0 -44
- package/dist/lib/platform/ios/spm/PolicyEngine.js +0 -79
- package/dist/lib/platform/ios/spm/SpmHelper.d.ts +0 -102
- package/dist/lib/platform/ios/spm/SpmHelper.js +0 -464
- package/dist/lib/platform/ios/xcode/HeaderResolver.d.ts +0 -33
- package/dist/lib/platform/ios/xcode/HeaderResolver.js +0 -90
- package/dist/lib/platform/ios/xcode/SaveEventFilter.d.ts +0 -66
- package/dist/lib/platform/ios/xcode/SaveEventFilter.js +0 -142
- package/dist/lib/platform/ios/xcode/XcodeAutomation.d.ts +0 -71
- package/dist/lib/platform/ios/xcode/XcodeAutomation.js +0 -327
- package/dist/lib/platform/ios/xcode/XcodeImportResolver.d.ts +0 -130
- package/dist/lib/platform/ios/xcode/XcodeImportResolver.js +0 -404
- package/dist/lib/platform/ios/xcode/XcodeIntegration.d.ts +0 -89
- package/dist/lib/platform/ios/xcode/XcodeIntegration.js +0 -588
- package/dist/lib/platform/ios/xcode/XcodeWriteUtils.d.ts +0 -99
- package/dist/lib/platform/ios/xcode/XcodeWriteUtils.js +0 -190
- package/dist/lib/service/automation/ActionPipeline.d.ts +0 -34
- package/dist/lib/service/automation/ActionPipeline.js +0 -53
- package/dist/lib/service/automation/AutomationOrchestrator.d.ts +0 -86
- package/dist/lib/service/automation/AutomationOrchestrator.js +0 -57
- package/dist/lib/service/automation/ContextCollector.d.ts +0 -24
- package/dist/lib/service/automation/ContextCollector.js +0 -35
- package/dist/lib/service/automation/DirectiveDetector.d.ts +0 -51
- package/dist/lib/service/automation/DirectiveDetector.js +0 -112
- package/dist/lib/service/automation/FileWatcher.d.ts +0 -51
- package/dist/lib/service/automation/FileWatcher.js +0 -366
- package/dist/lib/service/automation/TriggerResolver.d.ts +0 -36
- package/dist/lib/service/automation/TriggerResolver.js +0 -62
- package/dist/lib/service/automation/handlers/AlinkHandler.d.ts +0 -7
- package/dist/lib/service/automation/handlers/AlinkHandler.js +0 -80
- package/dist/lib/service/automation/handlers/CreateHandler.d.ts +0 -11
- package/dist/lib/service/automation/handlers/CreateHandler.js +0 -170
- package/dist/lib/service/automation/handlers/GuardHandler.d.ts +0 -17
- package/dist/lib/service/automation/handlers/GuardHandler.js +0 -218
- package/dist/lib/service/automation/handlers/HeaderHandler.d.ts +0 -2
- package/dist/lib/service/automation/handlers/HeaderHandler.js +0 -32
- package/dist/lib/service/automation/handlers/SearchHandler.d.ts +0 -11
- package/dist/lib/service/automation/handlers/SearchHandler.js +0 -278
- package/dist/lib/service/snippet/SnippetFactory.d.ts +0 -101
- package/dist/lib/service/snippet/SnippetFactory.js +0 -145
- package/dist/lib/service/snippet/SnippetInstaller.d.ts +0 -91
- package/dist/lib/service/snippet/SnippetInstaller.js +0 -276
- package/dist/lib/service/snippet/codecs/SnippetCodec.d.ts +0 -44
- package/dist/lib/service/snippet/codecs/SnippetCodec.js +0 -35
- package/dist/lib/service/snippet/codecs/VSCodeCodec.d.ts +0 -27
- package/dist/lib/service/snippet/codecs/VSCodeCodec.js +0 -82
- package/dist/scripts/build-native-ui.d.ts +0 -3
- package/dist/scripts/build-native-ui.js +0 -62
- package/dist/scripts/init-snippets.d.ts +0 -30
- package/dist/scripts/init-snippets.js +0 -298
- package/dist/scripts/install-full.d.ts +0 -7
- package/dist/scripts/install-full.js +0 -38
- package/resources/native-ui/README.md +0 -29
- package/resources/native-ui/combined-window.swift +0 -494
- package/resources/native-ui/main.swift +0 -598
- package/scripts/postinstall-safe.mjs +0 -89
|
@@ -1,371 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SPM API 路由 — 向后兼容层
|
|
3
|
-
*
|
|
4
|
-
* 所有端点统一委派到 ModuleService(语言无关模块扫描服务)。
|
|
5
|
-
* SPM Discoverer 作为 ModuleService 的一个 discoverer 自动匹配 Swift/SPM 项目。
|
|
6
|
-
* 新代码应直接使用 /api/v1/modules/* 端点。
|
|
7
|
-
*/
|
|
8
|
-
import express from 'express';
|
|
9
|
-
import { createStreamSession, getStreamSession } from '#http/utils/sse-sessions.js';
|
|
10
|
-
import Logger from '#infra/logging/Logger.js';
|
|
11
|
-
import { getServiceContainer } from '#inject/ServiceContainer.js';
|
|
12
|
-
import { ValidationError } from '#shared/errors/index.js';
|
|
13
|
-
const router = express.Router();
|
|
14
|
-
const logger = Logger.getInstance();
|
|
15
|
-
/** 获取 moduleService 并确保已加载 */
|
|
16
|
-
async function getModuleService() {
|
|
17
|
-
const container = getServiceContainer();
|
|
18
|
-
const moduleService = container.get('moduleService');
|
|
19
|
-
await moduleService.load();
|
|
20
|
-
return moduleService;
|
|
21
|
-
}
|
|
22
|
-
/** GET /api/v1/spm/targets */
|
|
23
|
-
router.get('/targets', async (req, res) => {
|
|
24
|
-
const moduleService = await getModuleService();
|
|
25
|
-
const targets = await moduleService.listTargets();
|
|
26
|
-
res.json({
|
|
27
|
-
success: true,
|
|
28
|
-
data: { targets, total: targets.length },
|
|
29
|
-
});
|
|
30
|
-
});
|
|
31
|
-
/** GET /api/v1/spm/dep-graph */
|
|
32
|
-
router.get('/dep-graph', async (req, res) => {
|
|
33
|
-
const moduleService = await getModuleService();
|
|
34
|
-
const level = String(req.query.level || 'package');
|
|
35
|
-
const _graphBase = await moduleService.getDependencyGraph({ level });
|
|
36
|
-
const graph = _graphBase;
|
|
37
|
-
if (!graph || (!graph.nodes && !graph.packages)) {
|
|
38
|
-
res.json({
|
|
39
|
-
success: true,
|
|
40
|
-
data: { nodes: [], edges: [], projectRoot: null },
|
|
41
|
-
});
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
// 标准化为 { nodes, edges } 格式
|
|
45
|
-
let nodes = [];
|
|
46
|
-
let edges = [];
|
|
47
|
-
if (graph.nodes && graph.edges) {
|
|
48
|
-
// 已经是标准格式
|
|
49
|
-
nodes = graph.nodes;
|
|
50
|
-
edges = graph.edges;
|
|
51
|
-
}
|
|
52
|
-
else if (graph.packages) {
|
|
53
|
-
// 从 packages 构建图
|
|
54
|
-
if (level === 'target') {
|
|
55
|
-
for (const [pkgName, pkgInfo] of Object.entries(graph.packages)) {
|
|
56
|
-
const targetsInfo = pkgInfo?.targetsInfo || {};
|
|
57
|
-
for (const [targetName, info] of Object.entries(targetsInfo)) {
|
|
58
|
-
const id = `${pkgName}::${targetName}`;
|
|
59
|
-
nodes.push({
|
|
60
|
-
id,
|
|
61
|
-
label: targetName,
|
|
62
|
-
type: 'target',
|
|
63
|
-
packageName: pkgName,
|
|
64
|
-
});
|
|
65
|
-
for (const d of info
|
|
66
|
-
?.dependencies || []) {
|
|
67
|
-
if (!d?.name) {
|
|
68
|
-
continue;
|
|
69
|
-
}
|
|
70
|
-
const depPkg = d?.package || pkgName;
|
|
71
|
-
edges.push({ from: id, to: `${depPkg}::${d.name}`, source: 'base' });
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
else {
|
|
77
|
-
const pkgs = graph.packages;
|
|
78
|
-
nodes = Object.keys(pkgs).map((id) => ({
|
|
79
|
-
id,
|
|
80
|
-
label: id,
|
|
81
|
-
type: 'package',
|
|
82
|
-
packageDir: pkgs[id]?.packageDir,
|
|
83
|
-
targets: pkgs[id]?.targets,
|
|
84
|
-
}));
|
|
85
|
-
for (const [from, tos] of Object.entries(graph.edges || {})) {
|
|
86
|
-
for (const to of tos || []) {
|
|
87
|
-
edges.push({ from, to, source: 'base' });
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
res.json({
|
|
93
|
-
success: true,
|
|
94
|
-
data: {
|
|
95
|
-
nodes,
|
|
96
|
-
edges,
|
|
97
|
-
projectRoot: graph.projectRoot || null,
|
|
98
|
-
generatedAt: graph.generatedAt || null,
|
|
99
|
-
},
|
|
100
|
-
});
|
|
101
|
-
});
|
|
102
|
-
/**
|
|
103
|
-
* POST /api/v1/spm/target-files
|
|
104
|
-
* 获取 Target 的文件列表
|
|
105
|
-
*/
|
|
106
|
-
router.post('/target-files', async (req, res) => {
|
|
107
|
-
const { target, targetName } = req.body;
|
|
108
|
-
if (!target && !targetName) {
|
|
109
|
-
throw new ValidationError('target object or targetName is required');
|
|
110
|
-
}
|
|
111
|
-
const moduleService = await getModuleService();
|
|
112
|
-
let resolvedTarget = target;
|
|
113
|
-
if (!resolvedTarget && targetName) {
|
|
114
|
-
const targets = await moduleService.listTargets();
|
|
115
|
-
resolvedTarget = targets.find((t) => t.name === targetName);
|
|
116
|
-
if (!resolvedTarget) {
|
|
117
|
-
res.status(404).json({
|
|
118
|
-
success: false,
|
|
119
|
-
error: { code: 'NOT_FOUND', message: `Target not found: ${targetName}` },
|
|
120
|
-
});
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
const files = await moduleService.getTargetFiles(resolvedTarget);
|
|
125
|
-
res.json({
|
|
126
|
-
success: true,
|
|
127
|
-
data: {
|
|
128
|
-
target: resolvedTarget.name || targetName,
|
|
129
|
-
files,
|
|
130
|
-
total: files.length,
|
|
131
|
-
},
|
|
132
|
-
});
|
|
133
|
-
});
|
|
134
|
-
/**
|
|
135
|
-
* POST /api/v1/spm/scan
|
|
136
|
-
* AI 扫描 Target,发现候选项
|
|
137
|
-
*/
|
|
138
|
-
router.post('/scan', async (req, res) => {
|
|
139
|
-
const { target, targetName, options = {} } = req.body;
|
|
140
|
-
if (!target && !targetName) {
|
|
141
|
-
throw new ValidationError('target object or targetName is required');
|
|
142
|
-
}
|
|
143
|
-
const moduleService = await getModuleService();
|
|
144
|
-
let resolvedTarget = target;
|
|
145
|
-
if (!resolvedTarget && targetName) {
|
|
146
|
-
const targets = await moduleService.listTargets();
|
|
147
|
-
resolvedTarget = targets.find((t) => t.name === targetName);
|
|
148
|
-
if (!resolvedTarget) {
|
|
149
|
-
res.status(404).json({
|
|
150
|
-
success: false,
|
|
151
|
-
error: { code: 'NOT_FOUND', message: `Target not found: ${targetName}` },
|
|
152
|
-
});
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
logger.info('Module scan started via /spm/', { target: resolvedTarget.name });
|
|
157
|
-
const result = await moduleService.scanTarget(resolvedTarget, options);
|
|
158
|
-
res.json({
|
|
159
|
-
success: true,
|
|
160
|
-
data: result,
|
|
161
|
-
});
|
|
162
|
-
});
|
|
163
|
-
// ── 流式 Target 扫描(SSE Session + EventSource 架构) ─────────
|
|
164
|
-
/**
|
|
165
|
-
* POST /api/v1/spm/scan/stream
|
|
166
|
-
* 创建流式扫描会话,后台异步执行 AI 扫描
|
|
167
|
-
*
|
|
168
|
-
* 协议事件(通过 SSE session 缓冲 + EventSource 交付):
|
|
169
|
-
* scan:started — 扫描启动
|
|
170
|
-
* scan:files-loaded — 文件列表就绪,含 files[] + count
|
|
171
|
-
* scan:reading — 读取文件内容中
|
|
172
|
-
* scan:ai-extracting — AI 提取开始(耗时阶段)
|
|
173
|
-
* scan:enriching — 后处理阶段
|
|
174
|
-
* scan:completed — 最终结果 {recipes, scannedFiles, recipeCount, fileCount}
|
|
175
|
-
* scan:error — 发生错误
|
|
176
|
-
* stream:done — 会话结束标记
|
|
177
|
-
*/
|
|
178
|
-
router.post('/scan/stream', async (req, res) => {
|
|
179
|
-
const { target, targetName, options = {} } = req.body;
|
|
180
|
-
if (!target && !targetName) {
|
|
181
|
-
throw new ValidationError('target object or targetName is required');
|
|
182
|
-
}
|
|
183
|
-
const moduleService = await getModuleService();
|
|
184
|
-
let resolvedTarget = target;
|
|
185
|
-
if (!resolvedTarget && targetName) {
|
|
186
|
-
const targets = await moduleService.listTargets();
|
|
187
|
-
resolvedTarget = targets.find((t) => t.name === targetName);
|
|
188
|
-
if (!resolvedTarget) {
|
|
189
|
-
res.status(404).json({
|
|
190
|
-
success: false,
|
|
191
|
-
error: { code: 'NOT_FOUND', message: `Target not found: ${targetName}` },
|
|
192
|
-
});
|
|
193
|
-
return;
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
// 创建 SSE session
|
|
197
|
-
const session = createStreamSession('scan');
|
|
198
|
-
const tName = resolvedTarget.name || targetName;
|
|
199
|
-
// 立即返回 sessionId
|
|
200
|
-
res.json({ sessionId: session.sessionId });
|
|
201
|
-
// 异步执行扫描,通过 session 推送进度事件
|
|
202
|
-
setImmediate(async () => {
|
|
203
|
-
try {
|
|
204
|
-
logger.info('Module stream scan started via /spm/', {
|
|
205
|
-
target: tName,
|
|
206
|
-
sessionId: session.sessionId,
|
|
207
|
-
});
|
|
208
|
-
const result = await moduleService.scanTarget(resolvedTarget, {
|
|
209
|
-
...options,
|
|
210
|
-
onProgress(event) {
|
|
211
|
-
session.send(event);
|
|
212
|
-
},
|
|
213
|
-
});
|
|
214
|
-
// 发送最终结果
|
|
215
|
-
session.send({
|
|
216
|
-
type: 'scan:result',
|
|
217
|
-
recipes: result.recipes || [],
|
|
218
|
-
scannedFiles: result.scannedFiles || [],
|
|
219
|
-
message: result.message || '',
|
|
220
|
-
recipeCount: (result.recipes || []).length,
|
|
221
|
-
fileCount: (result.scannedFiles || []).length,
|
|
222
|
-
});
|
|
223
|
-
session.end();
|
|
224
|
-
}
|
|
225
|
-
catch (err) {
|
|
226
|
-
const errMsg = err instanceof Error ? err.message : String(err);
|
|
227
|
-
logger.error('Module stream scan failed via /spm/', { target: tName, error: errMsg });
|
|
228
|
-
session.error(errMsg, 'SCAN_ERROR');
|
|
229
|
-
}
|
|
230
|
-
});
|
|
231
|
-
});
|
|
232
|
-
/**
|
|
233
|
-
* GET /api/v1/spm/scan/events/:sessionId
|
|
234
|
-
* EventSource SSE 端点 — 消费扫描进度事件
|
|
235
|
-
*
|
|
236
|
-
* 复用 chat/events 相同的 SSE 交付模式:回放缓冲 → 订阅实时 → 心跳保活
|
|
237
|
-
*/
|
|
238
|
-
router.get('/scan/events/:sessionId', (req, res) => {
|
|
239
|
-
const session = getStreamSession(req.params.sessionId);
|
|
240
|
-
if (!session) {
|
|
241
|
-
res.status(404).json({ success: false, error: 'Session not found or expired' });
|
|
242
|
-
return;
|
|
243
|
-
}
|
|
244
|
-
// ─── SSE Headers ───
|
|
245
|
-
res.setHeader('Content-Type', 'text/event-stream');
|
|
246
|
-
res.setHeader('Cache-Control', 'no-cache');
|
|
247
|
-
res.setHeader('Connection', 'keep-alive');
|
|
248
|
-
res.setHeader('X-Accel-Buffering', 'no');
|
|
249
|
-
res.flushHeaders();
|
|
250
|
-
if (res.socket) {
|
|
251
|
-
res.socket.setNoDelay(true);
|
|
252
|
-
res.socket.setTimeout(0);
|
|
253
|
-
}
|
|
254
|
-
function writeEvent(event) {
|
|
255
|
-
if (res.writableEnded) {
|
|
256
|
-
return;
|
|
257
|
-
}
|
|
258
|
-
res.write(`data: ${JSON.stringify(event)}\n\n`);
|
|
259
|
-
}
|
|
260
|
-
// 1) 回放缓冲区
|
|
261
|
-
let isDone = false;
|
|
262
|
-
for (const event of session.buffer) {
|
|
263
|
-
writeEvent(event);
|
|
264
|
-
if (event.type === 'stream:done' || event.type === 'stream:error') {
|
|
265
|
-
isDone = true;
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
if (isDone || session.completed) {
|
|
269
|
-
res.end();
|
|
270
|
-
return;
|
|
271
|
-
}
|
|
272
|
-
// 2) 订阅实时事件
|
|
273
|
-
const unsubscribe = session.on((event) => {
|
|
274
|
-
writeEvent(event);
|
|
275
|
-
if (event.type === 'stream:done' || event.type === 'stream:error') {
|
|
276
|
-
unsubscribe();
|
|
277
|
-
clearInterval(heartbeat);
|
|
278
|
-
res.end();
|
|
279
|
-
}
|
|
280
|
-
});
|
|
281
|
-
// 心跳保活 (每 15 秒)
|
|
282
|
-
const heartbeat = setInterval(() => {
|
|
283
|
-
if (res.writableEnded) {
|
|
284
|
-
clearInterval(heartbeat);
|
|
285
|
-
return;
|
|
286
|
-
}
|
|
287
|
-
res.write(`: ping ${Date.now()}\n\n`);
|
|
288
|
-
}, 15_000);
|
|
289
|
-
// 客户端断开连接时清理
|
|
290
|
-
res.on('close', () => {
|
|
291
|
-
unsubscribe();
|
|
292
|
-
clearInterval(heartbeat);
|
|
293
|
-
});
|
|
294
|
-
});
|
|
295
|
-
/**
|
|
296
|
-
* POST /api/v1/spm/scan-project
|
|
297
|
-
* 全项目扫描:AI 提取候选 + Guard 审计
|
|
298
|
-
*/
|
|
299
|
-
router.post('/scan-project', async (req, res) => {
|
|
300
|
-
const { options = {} } = req.body;
|
|
301
|
-
const moduleService = await getModuleService();
|
|
302
|
-
logger.info('Full project scan started via /spm/');
|
|
303
|
-
const result = await moduleService.scanProject(options);
|
|
304
|
-
res.json({
|
|
305
|
-
success: true,
|
|
306
|
-
data: result,
|
|
307
|
-
});
|
|
308
|
-
});
|
|
309
|
-
/**
|
|
310
|
-
* POST /api/v1/spm/bootstrap
|
|
311
|
-
* 冷启动:快速骨架 + 异步逐维度填充(v5)
|
|
312
|
-
*
|
|
313
|
-
* 执行策略:
|
|
314
|
-
* ① 同步阶段: Phase 1-4(文件收集 + AST + SPM + Guard + 骨架响应)→ 立即返回
|
|
315
|
-
* ② 异步阶段: Phase 5/5.5(逐维度提取 + Candidate/Skill 创建)→ 后台逐一执行
|
|
316
|
-
* ③ 进度推送: 通过 Socket.io 实时推送每个维度的完成状态
|
|
317
|
-
*
|
|
318
|
-
* 前端立即获得骨架 + 任务清单,每个维度完成后通过 Socket.io 推送更新。
|
|
319
|
-
*/
|
|
320
|
-
router.post('/bootstrap', async (req, res) => {
|
|
321
|
-
const { maxFiles, skipGuard, contentMaxLines } = req.body || {};
|
|
322
|
-
const container = getServiceContainer();
|
|
323
|
-
const agentFactory = container.get('agentFactory');
|
|
324
|
-
logger.info('Bootstrap cold start initiated (v5: async fill mode)');
|
|
325
|
-
// ── 同步阶段: 快速执行 Phase 1-4 → 返回骨架 ──
|
|
326
|
-
const bootstrapResult = await agentFactory.bootstrapKnowledge({
|
|
327
|
-
maxFiles: maxFiles || 500,
|
|
328
|
-
skipGuard: skipGuard || false,
|
|
329
|
-
contentMaxLines: contentMaxLines || 120,
|
|
330
|
-
loadSkills: true,
|
|
331
|
-
});
|
|
332
|
-
// 立即返回骨架结果给前端
|
|
333
|
-
res.json({
|
|
334
|
-
success: true,
|
|
335
|
-
data: {
|
|
336
|
-
...bootstrapResult,
|
|
337
|
-
asyncFill: true, // 告知前端:内容正在异步填充中
|
|
338
|
-
},
|
|
339
|
-
});
|
|
340
|
-
// 注意:Phase 5/5.5 异步填充已在 bootstrapKnowledge() 内部通过 setImmediate 启动
|
|
341
|
-
// 进度通过 BootstrapTaskManager → Socket.io 推送到前端
|
|
342
|
-
});
|
|
343
|
-
/**
|
|
344
|
-
* GET /api/v1/spm/bootstrap/status
|
|
345
|
-
* 查询当前 bootstrap 异步填充进度
|
|
346
|
-
*
|
|
347
|
-
* 返回当前 session 的任务状态列表,供前端轮询(Socket.io 不可用时的 fallback)
|
|
348
|
-
*/
|
|
349
|
-
router.get('/bootstrap/status', async (req, res) => {
|
|
350
|
-
const container = getServiceContainer();
|
|
351
|
-
// 从容器获取 BootstrapTaskManager(正式 DI 注册)
|
|
352
|
-
let taskManager = null;
|
|
353
|
-
try {
|
|
354
|
-
taskManager = container.get('bootstrapTaskManager');
|
|
355
|
-
}
|
|
356
|
-
catch {
|
|
357
|
-
/* not registered */
|
|
358
|
-
}
|
|
359
|
-
if (!taskManager) {
|
|
360
|
-
res.json({
|
|
361
|
-
success: true,
|
|
362
|
-
data: { status: 'idle', message: 'No bootstrap task manager initialized' },
|
|
363
|
-
});
|
|
364
|
-
return;
|
|
365
|
-
}
|
|
366
|
-
res.json({
|
|
367
|
-
success: true,
|
|
368
|
-
data: taskManager.getSessionStatus(),
|
|
369
|
-
});
|
|
370
|
-
});
|
|
371
|
-
export default router;
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* PlaceholderConverter — Xcode <#…#> ↔ VSCode ${N:…} 双向占位符转换
|
|
3
|
-
*
|
|
4
|
-
* 转换规则:
|
|
5
|
-
* Xcode → VSCode:
|
|
6
|
-
* <#name#> → ${1:name} (序号自动递增)
|
|
7
|
-
* <#T##Type#> → ${1:Type} (取最后一段)
|
|
8
|
-
* <#T##Label##Type#> → ${1:Label} (取 Label 段)
|
|
9
|
-
*
|
|
10
|
-
* VSCode → Xcode:
|
|
11
|
-
* ${1:name} → <#name#>
|
|
12
|
-
* ${1} → <#value#>
|
|
13
|
-
* $1 → <#value#>
|
|
14
|
-
* $0 → (移除,Xcode 无终止光标)
|
|
15
|
-
*/
|
|
16
|
-
export declare class PlaceholderConverter {
|
|
17
|
-
/** Xcode → VSCode 占位符转换 */
|
|
18
|
-
static xcodeToVSCode(code: string): string;
|
|
19
|
-
/** VSCode → Xcode 占位符转换 */
|
|
20
|
-
static vscodeToXcode(code: string): string;
|
|
21
|
-
}
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* PlaceholderConverter — Xcode <#…#> ↔ VSCode ${N:…} 双向占位符转换
|
|
3
|
-
*
|
|
4
|
-
* 转换规则:
|
|
5
|
-
* Xcode → VSCode:
|
|
6
|
-
* <#name#> → ${1:name} (序号自动递增)
|
|
7
|
-
* <#T##Type#> → ${1:Type} (取最后一段)
|
|
8
|
-
* <#T##Label##Type#> → ${1:Label} (取 Label 段)
|
|
9
|
-
*
|
|
10
|
-
* VSCode → Xcode:
|
|
11
|
-
* ${1:name} → <#name#>
|
|
12
|
-
* ${1} → <#value#>
|
|
13
|
-
* $1 → <#value#>
|
|
14
|
-
* $0 → (移除,Xcode 无终止光标)
|
|
15
|
-
*/
|
|
16
|
-
export class PlaceholderConverter {
|
|
17
|
-
/** Xcode → VSCode 占位符转换 */
|
|
18
|
-
static xcodeToVSCode(code) {
|
|
19
|
-
if (!code) {
|
|
20
|
-
return '';
|
|
21
|
-
}
|
|
22
|
-
let index = 0;
|
|
23
|
-
return code.replace(/<#(.*?)#>/g, (_match, inner) => {
|
|
24
|
-
index++;
|
|
25
|
-
// <#T##Label##Type#> → Label (parts[1])
|
|
26
|
-
// <#T##Type#> → Type (parts[1])
|
|
27
|
-
// <#name#> → name (inner)
|
|
28
|
-
const parts = inner.split('##');
|
|
29
|
-
const label = parts.length >= 2 ? parts[1] : inner;
|
|
30
|
-
return `\${${index}:${label}}`;
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
/** VSCode → Xcode 占位符转换 */
|
|
34
|
-
static vscodeToXcode(code) {
|
|
35
|
-
if (!code) {
|
|
36
|
-
return '';
|
|
37
|
-
}
|
|
38
|
-
return (code
|
|
39
|
-
// ${1:name} → <#name#>
|
|
40
|
-
.replace(/\$\{(\d+):([^}]*)}/g, (_m, _n, label) => `<#${label}#>`)
|
|
41
|
-
// ${1} → <#value#>
|
|
42
|
-
.replace(/\$\{(\d+)}/g, '<#value#>')
|
|
43
|
-
// $0 → remove (Xcode has no final cursor)
|
|
44
|
-
.replace(/\$0/g, '')
|
|
45
|
-
// $1..$9 → <#value#>
|
|
46
|
-
.replace(/\$([1-9]\d*)/g, '<#value#>'));
|
|
47
|
-
}
|
|
48
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* XcodeCodec — Xcode .codesnippet (plist XML) 生成器
|
|
3
|
-
*
|
|
4
|
-
* 从原 SnippetFactory 提取的 Xcode 专用逻辑:
|
|
5
|
-
* - XML plist 模板
|
|
6
|
-
* - Xcode 语言 ID 映射
|
|
7
|
-
* - XML 转义
|
|
8
|
-
*/
|
|
9
|
-
import { SnippetCodec, type SnippetSpec } from '#service/snippet/codecs/SnippetCodec.js';
|
|
10
|
-
export declare class XcodeCodec extends SnippetCodec {
|
|
11
|
-
get id(): string;
|
|
12
|
-
get fileExtension(): string;
|
|
13
|
-
/** SnippetSpec → plist XML 字符串 */
|
|
14
|
-
generate(spec: SnippetSpec): string;
|
|
15
|
-
/** Xcode: 每个 snippet 一个文件 → 返回 Array<{ filename, content }> */
|
|
16
|
-
generateBundle(specs: SnippetSpec[]): Array<{
|
|
17
|
-
filename: string;
|
|
18
|
-
content: string;
|
|
19
|
-
}>;
|
|
20
|
-
/** Xcode snippets 全局目录 (macOS only) */
|
|
21
|
-
getInstallDir(_projectRoot: string | undefined): string;
|
|
22
|
-
mapLanguage(lang: string | undefined): string;
|
|
23
|
-
}
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* XcodeCodec — Xcode .codesnippet (plist XML) 生成器
|
|
3
|
-
*
|
|
4
|
-
* 从原 SnippetFactory 提取的 Xcode 专用逻辑:
|
|
5
|
-
* - XML plist 模板
|
|
6
|
-
* - Xcode 语言 ID 映射
|
|
7
|
-
* - XML 转义
|
|
8
|
-
*/
|
|
9
|
-
import { homedir } from 'node:os';
|
|
10
|
-
import { join } from 'node:path';
|
|
11
|
-
import { SnippetCodec } from '#service/snippet/codecs/SnippetCodec.js';
|
|
12
|
-
const XCODE_LANGUAGE_MAP = {
|
|
13
|
-
swift: 'Xcode.SourceCodeLanguage.Swift',
|
|
14
|
-
'objective-c': 'Xcode.SourceCodeLanguage.Objective-C',
|
|
15
|
-
objc: 'Xcode.SourceCodeLanguage.Objective-C',
|
|
16
|
-
c: 'Xcode.SourceCodeLanguage.C',
|
|
17
|
-
'c++': 'Xcode.SourceCodeLanguage.C-Plus-Plus',
|
|
18
|
-
javascript: 'Xcode.SourceCodeLanguage.JavaScript',
|
|
19
|
-
};
|
|
20
|
-
const PLIST_TEMPLATE = `<?xml version="1.0" encoding="UTF-8"?>
|
|
21
|
-
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
22
|
-
<plist version="1.0">
|
|
23
|
-
<dict>
|
|
24
|
-
\t<key>IDECodeSnippetCompletionPrefix</key>
|
|
25
|
-
\t<string>{completion}</string>
|
|
26
|
-
\t<key>IDECodeSnippetCompletionScopes</key>
|
|
27
|
-
\t<array>
|
|
28
|
-
\t\t<string>All</string>
|
|
29
|
-
\t</array>
|
|
30
|
-
\t<key>IDECodeSnippetContents</key>
|
|
31
|
-
\t<string>{content}</string>
|
|
32
|
-
\t<key>IDECodeSnippetIdentifier</key>
|
|
33
|
-
\t<string>{identifier}</string>
|
|
34
|
-
\t<key>IDECodeSnippetLanguage</key>
|
|
35
|
-
\t<string>{language}</string>
|
|
36
|
-
\t<key>IDECodeSnippetSummary</key>
|
|
37
|
-
\t<string>{summary}</string>
|
|
38
|
-
\t<key>IDECodeSnippetTitle</key>
|
|
39
|
-
\t<string>{title}</string>
|
|
40
|
-
\t<key>IDECodeSnippetUserSnippet</key>
|
|
41
|
-
\t<true/>
|
|
42
|
-
\t<key>IDECodeSnippetVersion</key>
|
|
43
|
-
\t<integer>2</integer>
|
|
44
|
-
</dict>
|
|
45
|
-
</plist>`;
|
|
46
|
-
export class XcodeCodec extends SnippetCodec {
|
|
47
|
-
get id() {
|
|
48
|
-
return 'xcode';
|
|
49
|
-
}
|
|
50
|
-
get fileExtension() {
|
|
51
|
-
return '.codesnippet';
|
|
52
|
-
}
|
|
53
|
-
/** SnippetSpec → plist XML 字符串 */
|
|
54
|
-
generate(spec) {
|
|
55
|
-
if (!spec?.identifier || !spec?.code) {
|
|
56
|
-
throw new Error('Snippet spec must have identifier and code');
|
|
57
|
-
}
|
|
58
|
-
const content = Array.isArray(spec.code) ? spec.code.join('\n') : spec.code;
|
|
59
|
-
const languageKey = this.mapLanguage(spec.language);
|
|
60
|
-
let xml = PLIST_TEMPLATE;
|
|
61
|
-
xml = xml.replace('{identifier}', escapeXml(spec.identifier));
|
|
62
|
-
xml = xml.replace('{title}', escapeXml(spec.title || spec.identifier));
|
|
63
|
-
xml = xml.replace('{completion}', escapeXml(spec.completion || spec.identifier));
|
|
64
|
-
xml = xml.replace('{summary}', escapeXml(spec.summary || ''));
|
|
65
|
-
xml = xml.replace('{content}', escapeXml(content));
|
|
66
|
-
xml = xml.replace('{language}', languageKey);
|
|
67
|
-
return xml;
|
|
68
|
-
}
|
|
69
|
-
/** Xcode: 每个 snippet 一个文件 → 返回 Array<{ filename, content }> */
|
|
70
|
-
generateBundle(specs) {
|
|
71
|
-
return specs.map((spec) => ({
|
|
72
|
-
filename: `${spec.identifier}${this.fileExtension}`,
|
|
73
|
-
content: this.generate(spec),
|
|
74
|
-
}));
|
|
75
|
-
}
|
|
76
|
-
/** Xcode snippets 全局目录 (macOS only) */
|
|
77
|
-
getInstallDir(_projectRoot) {
|
|
78
|
-
return join(homedir(), 'Library/Developer/Xcode/UserData/CodeSnippets');
|
|
79
|
-
}
|
|
80
|
-
mapLanguage(lang) {
|
|
81
|
-
return (XCODE_LANGUAGE_MAP[lang?.toLowerCase() ?? ''] ||
|
|
82
|
-
XCODE_LANGUAGE_MAP.swift);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
/** XML 特殊字符转义 */
|
|
86
|
-
function escapeXml(str) {
|
|
87
|
-
if (!str) {
|
|
88
|
-
return '';
|
|
89
|
-
}
|
|
90
|
-
return String(str)
|
|
91
|
-
.replace(/&/g, '&')
|
|
92
|
-
.replace(/</g, '<')
|
|
93
|
-
.replace(/>/g, '>')
|
|
94
|
-
.replace(/"/g, '"')
|
|
95
|
-
.replace(/'/g, ''');
|
|
96
|
-
}
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* DependencyGraph — SPM 依赖图构建与分析
|
|
3
|
-
* 合并 V1 DepGraphService + DepGraphAnalyzer
|
|
4
|
-
* 构建 target 级依赖 DAG,支持层级计算、拓扑排序、可达性检查
|
|
5
|
-
*/
|
|
6
|
-
export declare class DependencyGraph {
|
|
7
|
-
#private;
|
|
8
|
-
constructor();
|
|
9
|
-
/**
|
|
10
|
-
* 从 PackageSwiftParser 的解析结果构建图
|
|
11
|
-
* @param parsed
|
|
12
|
-
*/
|
|
13
|
-
buildFromParsed(parsed: {
|
|
14
|
-
targets: {
|
|
15
|
-
name: string;
|
|
16
|
-
dependencies: string[];
|
|
17
|
-
}[];
|
|
18
|
-
}): void;
|
|
19
|
-
/** 添加节点 */
|
|
20
|
-
addNode(name: string): void;
|
|
21
|
-
/** 添加边: from 依赖 to */
|
|
22
|
-
addEdge(from: string, to: string): void;
|
|
23
|
-
/** BFS 可达性检查 */
|
|
24
|
-
isReachable(from: string, to: string): boolean;
|
|
25
|
-
/**
|
|
26
|
-
* 检测循环依赖
|
|
27
|
-
* @returns 循环路径列表
|
|
28
|
-
*/
|
|
29
|
-
detectCycles(): string[][];
|
|
30
|
-
/**
|
|
31
|
-
* 拓扑排序 (Kahn's algorithm)
|
|
32
|
-
* @returns 若有环则返回部分结果
|
|
33
|
-
*/
|
|
34
|
-
topologicalSort(): string[];
|
|
35
|
-
/**
|
|
36
|
-
* 层级计算: L0 = 无依赖的节点, L1 = 只依赖 L0, etc.
|
|
37
|
-
* @returns node → level
|
|
38
|
-
*/
|
|
39
|
-
computeLevels(): Map<any, any>;
|
|
40
|
-
/** 获取节点的直接依赖 */
|
|
41
|
-
getDependencies(node: string): string[];
|
|
42
|
-
/** 获取节点的直接依赖者 (谁依赖了这个节点) */
|
|
43
|
-
getDependents(node: string): string[];
|
|
44
|
-
/** 获取所有节点 */
|
|
45
|
-
getNodes(): string[];
|
|
46
|
-
edgeCount(): number;
|
|
47
|
-
clear(): void;
|
|
48
|
-
/** 导出为 JSON (可视化用) */
|
|
49
|
-
toJSON(): {
|
|
50
|
-
nodes: string[];
|
|
51
|
-
edges: {
|
|
52
|
-
from: string;
|
|
53
|
-
to: string;
|
|
54
|
-
}[];
|
|
55
|
-
};
|
|
56
|
-
}
|