autosnippet 3.2.22 → 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/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/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,51 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* FileWatcher - V2 文件监听主服务(精简版)
|
|
3
|
-
*
|
|
4
|
-
* 监控项目文件变更,检测 // as:c、// as:s、// as:a 等指令并自动处理。
|
|
5
|
-
* 具体指令逻辑已拆分至 handlers/ 和 XcodeIntegration.js。
|
|
6
|
-
*
|
|
7
|
-
* 用法:
|
|
8
|
-
* const watcher = new FileWatcher(specPath, projectRoot, { quiet: false });
|
|
9
|
-
* watcher.start();
|
|
10
|
-
*/
|
|
11
|
-
export declare class FileWatcher {
|
|
12
|
-
_debounceTimers: Map<string, ReturnType<typeof setTimeout>>;
|
|
13
|
-
_timeoutHead: ReturnType<typeof setTimeout> | undefined;
|
|
14
|
-
_timeoutLink: ReturnType<typeof setTimeout> | undefined;
|
|
15
|
-
_watcher: import('chokidar').FSWatcher | null;
|
|
16
|
-
exts: string[] | null;
|
|
17
|
-
onEvent: ((event: string, path: string) => void) | null;
|
|
18
|
-
pathPrefix: string | null;
|
|
19
|
-
projectRoot: string;
|
|
20
|
-
quiet: boolean;
|
|
21
|
-
specPath: string;
|
|
22
|
-
/**
|
|
23
|
-
* @param specPath boxspec.json 绝对路径
|
|
24
|
-
* @param projectRoot 项目根目录
|
|
25
|
-
* @param [opts.exts] 可选扩展名列表
|
|
26
|
-
* @param [opts.pathPrefix] 可选路径前缀过滤
|
|
27
|
-
* @param [opts.onEvent] 可选事件回调
|
|
28
|
-
*/
|
|
29
|
-
constructor(specPath: string, projectRoot: string, opts?: {
|
|
30
|
-
quiet?: boolean;
|
|
31
|
-
pathPrefix?: string;
|
|
32
|
-
onEvent?: ((event: string, path: string) => void) | null;
|
|
33
|
-
exts?: string[];
|
|
34
|
-
});
|
|
35
|
-
/** 启动文件监听 */
|
|
36
|
-
start(): import("chokidar").FSWatcher;
|
|
37
|
-
/** 停止监听,释放所有资源 */
|
|
38
|
-
stop(): Promise<void>;
|
|
39
|
-
_processFile(fullPath: string, relativePath: string): Promise<void>;
|
|
40
|
-
/** 追加候选项(通过 ServiceContainer 或 HTTP API) */
|
|
41
|
-
_appendCandidates(items: Record<string, unknown>[], source: string): Promise<void>;
|
|
42
|
-
/** 为候选解析头文件 */
|
|
43
|
-
_resolveHeadersIfNeeded(item: Record<string, unknown>, relativePath: string, text: string): Promise<void>;
|
|
44
|
-
/** 打开 Dashboard 页面 */
|
|
45
|
-
_openDashboard(path: string): void;
|
|
46
|
-
/** macOS 通知 */
|
|
47
|
-
_notify(msg: string): void;
|
|
48
|
-
/** 防抖 */
|
|
49
|
-
_debounce(key: string, fn: () => void): void;
|
|
50
|
-
}
|
|
51
|
-
export default FileWatcher;
|
|
@@ -1,366 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* FileWatcher - V2 文件监听主服务(精简版)
|
|
3
|
-
*
|
|
4
|
-
* 监控项目文件变更,检测 // as:c、// as:s、// as:a 等指令并自动处理。
|
|
5
|
-
* 具体指令逻辑已拆分至 handlers/ 和 XcodeIntegration.js。
|
|
6
|
-
*
|
|
7
|
-
* 用法:
|
|
8
|
-
* const watcher = new FileWatcher(specPath, projectRoot, { quiet: false });
|
|
9
|
-
* watcher.start();
|
|
10
|
-
*/
|
|
11
|
-
import { accessSync, readFileSync, statSync } from 'node:fs';
|
|
12
|
-
import { basename, join, normalize } from 'node:path';
|
|
13
|
-
import { watch as chokidarWatch } from 'chokidar';
|
|
14
|
-
import { saveEventFilter } from '../../platform/ios/xcode/SaveEventFilter.js';
|
|
15
|
-
import { FILE_WATCHER } from '../../shared/constants.js';
|
|
16
|
-
import { detectTriggers, REGEX } from './DirectiveDetector.js';
|
|
17
|
-
import { handleAlink } from './handlers/AlinkHandler.js';
|
|
18
|
-
/* ── Handler imports ── */
|
|
19
|
-
import { handleCreate } from './handlers/CreateHandler.js';
|
|
20
|
-
import { handleGuard } from './handlers/GuardHandler.js';
|
|
21
|
-
import { handleHeader } from './handlers/HeaderHandler.js';
|
|
22
|
-
import { handleSearch } from './handlers/SearchHandler.js';
|
|
23
|
-
/* ────────── 配置 ────────── */
|
|
24
|
-
const DEFAULT_FILE_PATTERN = [
|
|
25
|
-
// ObjC/Swift
|
|
26
|
-
'**/*.m',
|
|
27
|
-
'**/*.h',
|
|
28
|
-
'**/*.mm',
|
|
29
|
-
'**/*.swift',
|
|
30
|
-
// JS/TS
|
|
31
|
-
'**/*.js',
|
|
32
|
-
'**/*.ts',
|
|
33
|
-
'**/*.jsx',
|
|
34
|
-
'**/*.tsx',
|
|
35
|
-
'**/*.vue',
|
|
36
|
-
'**/*.svelte',
|
|
37
|
-
// Python
|
|
38
|
-
'**/*.py',
|
|
39
|
-
// JVM
|
|
40
|
-
'**/*.java',
|
|
41
|
-
'**/*.kt',
|
|
42
|
-
'**/*.kts',
|
|
43
|
-
// Other languages
|
|
44
|
-
'**/*.go',
|
|
45
|
-
'**/*.rs',
|
|
46
|
-
'**/*.rb',
|
|
47
|
-
// C/C++
|
|
48
|
-
'**/*.c',
|
|
49
|
-
'**/*.cpp',
|
|
50
|
-
'**/*.cc',
|
|
51
|
-
'**/*.hpp',
|
|
52
|
-
];
|
|
53
|
-
const IGNORED = [
|
|
54
|
-
'**/node_modules/**',
|
|
55
|
-
'**/.git/**',
|
|
56
|
-
'**/.mgit/**',
|
|
57
|
-
'**/.easybox/**',
|
|
58
|
-
'**/xcuserdata/**',
|
|
59
|
-
'**/.build/**',
|
|
60
|
-
'**/*.swp',
|
|
61
|
-
'**/*.tmp',
|
|
62
|
-
'**/*~.m',
|
|
63
|
-
'**/*~.h',
|
|
64
|
-
'**/DerivedData/**',
|
|
65
|
-
'**/Pods/**',
|
|
66
|
-
'**/Carthage/**',
|
|
67
|
-
'**/__pycache__/**',
|
|
68
|
-
'**/.venv/**',
|
|
69
|
-
'**/venv/**',
|
|
70
|
-
'**/build/**',
|
|
71
|
-
'**/target/**',
|
|
72
|
-
'**/.gradle/**',
|
|
73
|
-
'**/dist/**',
|
|
74
|
-
'**/.next/**',
|
|
75
|
-
'**/.nuxt/**',
|
|
76
|
-
];
|
|
77
|
-
const DEBOUNCE_DELAY = FILE_WATCHER.DEBOUNCE_DELAY_MS;
|
|
78
|
-
/* ────────── FileWatcher ────────── */
|
|
79
|
-
export class FileWatcher {
|
|
80
|
-
_debounceTimers;
|
|
81
|
-
_timeoutHead;
|
|
82
|
-
_timeoutLink;
|
|
83
|
-
_watcher;
|
|
84
|
-
exts;
|
|
85
|
-
onEvent;
|
|
86
|
-
pathPrefix;
|
|
87
|
-
projectRoot;
|
|
88
|
-
quiet;
|
|
89
|
-
specPath;
|
|
90
|
-
/**
|
|
91
|
-
* @param specPath boxspec.json 绝对路径
|
|
92
|
-
* @param projectRoot 项目根目录
|
|
93
|
-
* @param [opts.exts] 可选扩展名列表
|
|
94
|
-
* @param [opts.pathPrefix] 可选路径前缀过滤
|
|
95
|
-
* @param [opts.onEvent] 可选事件回调
|
|
96
|
-
*/
|
|
97
|
-
constructor(specPath, projectRoot, opts = {}) {
|
|
98
|
-
this.specPath = specPath;
|
|
99
|
-
this.projectRoot = projectRoot;
|
|
100
|
-
this.quiet = !!opts.quiet;
|
|
101
|
-
this.pathPrefix = opts.pathPrefix || null;
|
|
102
|
-
this.onEvent = opts.onEvent || null;
|
|
103
|
-
this.exts = opts.exts || null;
|
|
104
|
-
this._debounceTimers = new Map();
|
|
105
|
-
this._watcher = null;
|
|
106
|
-
this._timeoutLink = undefined;
|
|
107
|
-
this._timeoutHead = undefined;
|
|
108
|
-
}
|
|
109
|
-
/** 启动文件监听 */
|
|
110
|
-
start() {
|
|
111
|
-
const watchRoot = this.projectRoot;
|
|
112
|
-
const filePattern = this.exts
|
|
113
|
-
? this.exts.map((e) => `**/*${e.startsWith('.') ? e : `.${e}`}`)
|
|
114
|
-
: DEFAULT_FILE_PATTERN;
|
|
115
|
-
if (!this.quiet) {
|
|
116
|
-
}
|
|
117
|
-
this._watcher = chokidarWatch(filePattern, {
|
|
118
|
-
cwd: watchRoot,
|
|
119
|
-
ignored: IGNORED,
|
|
120
|
-
ignoreInitial: true,
|
|
121
|
-
persistent: true,
|
|
122
|
-
});
|
|
123
|
-
const handleEvent = (relativePath) => {
|
|
124
|
-
const fullPath = join(watchRoot, relativePath);
|
|
125
|
-
if (process.env.ASD_DEBUG === '1') {
|
|
126
|
-
}
|
|
127
|
-
if (this.pathPrefix && !normalize(relativePath).startsWith(normalize(this.pathPrefix))) {
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
|
-
this._debounce(fullPath, () => {
|
|
131
|
-
this._processFile(fullPath, relativePath);
|
|
132
|
-
});
|
|
133
|
-
};
|
|
134
|
-
this._watcher.on('change', handleEvent);
|
|
135
|
-
this._watcher.on('add', handleEvent);
|
|
136
|
-
this._watcher.on('error', (err) => {
|
|
137
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
138
|
-
console.error('文件监听错误:', msg);
|
|
139
|
-
});
|
|
140
|
-
this._watcher.on('ready', () => {
|
|
141
|
-
if (!this.quiet) {
|
|
142
|
-
}
|
|
143
|
-
if (process.env.ASD_DEBUG === '1') {
|
|
144
|
-
}
|
|
145
|
-
});
|
|
146
|
-
return this._watcher;
|
|
147
|
-
}
|
|
148
|
-
/** 停止监听,释放所有资源 */
|
|
149
|
-
async stop() {
|
|
150
|
-
if (this._watcher) {
|
|
151
|
-
// 移除所有事件监听器,避免泄漏
|
|
152
|
-
this._watcher.removeAllListeners();
|
|
153
|
-
await this._watcher.close();
|
|
154
|
-
this._watcher = null;
|
|
155
|
-
}
|
|
156
|
-
// 清理所有防抖定时器
|
|
157
|
-
for (const timer of this._debounceTimers.values()) {
|
|
158
|
-
clearTimeout(timer);
|
|
159
|
-
}
|
|
160
|
-
this._debounceTimers.clear();
|
|
161
|
-
// 清理 handler 级别的延时定时器
|
|
162
|
-
if (this._timeoutLink) {
|
|
163
|
-
clearTimeout(this._timeoutLink);
|
|
164
|
-
this._timeoutLink = undefined;
|
|
165
|
-
}
|
|
166
|
-
if (this._timeoutHead) {
|
|
167
|
-
clearTimeout(this._timeoutHead);
|
|
168
|
-
this._timeoutHead = undefined;
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
/* ────────── 内部:文件处理(分派到 handlers) ────────── */
|
|
172
|
-
async _processFile(fullPath, relativePath) {
|
|
173
|
-
try {
|
|
174
|
-
accessSync(fullPath);
|
|
175
|
-
const stat = statSync(fullPath);
|
|
176
|
-
if (stat.isDirectory() || stat.size > FILE_WATCHER.MAX_FILE_SIZE_BYTES) {
|
|
177
|
-
return;
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
catch {
|
|
181
|
-
return;
|
|
182
|
-
}
|
|
183
|
-
let data;
|
|
184
|
-
try {
|
|
185
|
-
data = readFileSync(fullPath, 'utf8');
|
|
186
|
-
}
|
|
187
|
-
catch (err) {
|
|
188
|
-
console.error(`❌ 读取文件失败: ${fullPath}`, err.message);
|
|
189
|
-
return;
|
|
190
|
-
}
|
|
191
|
-
// ── 保存事件过滤:self-write / 内容未变 / Xcode 非前台 ──
|
|
192
|
-
const verdict = saveEventFilter.shouldProcess(fullPath, data);
|
|
193
|
-
if (!verdict.process) {
|
|
194
|
-
if (process.env.ASD_DEBUG === '1') {
|
|
195
|
-
}
|
|
196
|
-
return;
|
|
197
|
-
}
|
|
198
|
-
if (process.env.ASD_DEBUG === '1') {
|
|
199
|
-
}
|
|
200
|
-
const filename = basename(fullPath);
|
|
201
|
-
// 检测指令
|
|
202
|
-
const triggers = detectTriggers(data, filename);
|
|
203
|
-
if (process.env.ASD_DEBUG === '1') {
|
|
204
|
-
}
|
|
205
|
-
// // as:c — 创建候选
|
|
206
|
-
if (triggers.createLine) {
|
|
207
|
-
await handleCreate(this, fullPath, relativePath, triggers.createOption);
|
|
208
|
-
}
|
|
209
|
-
// // as:a — Guard 检查
|
|
210
|
-
if (triggers.guardLine) {
|
|
211
|
-
await handleGuard(this, fullPath, data, triggers.guardLine);
|
|
212
|
-
}
|
|
213
|
-
// // as:s — 搜索
|
|
214
|
-
if (triggers.searchLine) {
|
|
215
|
-
await handleSearch(this, fullPath, relativePath, triggers.searchLine);
|
|
216
|
-
}
|
|
217
|
-
// alink
|
|
218
|
-
if (triggers.alinkLine) {
|
|
219
|
-
clearTimeout(this._timeoutLink);
|
|
220
|
-
this._timeoutLink = setTimeout(() => {
|
|
221
|
-
handleAlink(triggers.alinkLine).catch((err) => {
|
|
222
|
-
console.warn(`[Watcher] alink handler failed: ${err.message}`);
|
|
223
|
-
});
|
|
224
|
-
}, DEBOUNCE_DELAY);
|
|
225
|
-
}
|
|
226
|
-
// ── 更新内容哈希(处理完毕后记录状态,供下次变更比对) ──
|
|
227
|
-
saveEventFilter.updateHash(fullPath, data);
|
|
228
|
-
// header 指令
|
|
229
|
-
if (triggers.headerLine) {
|
|
230
|
-
const isMatch = triggers.isSwift
|
|
231
|
-
? REGEX.HEADER_SWIFT.test(triggers.headerLine)
|
|
232
|
-
: REGEX.HEADER_OBJC.test(triggers.headerLine);
|
|
233
|
-
if (isMatch) {
|
|
234
|
-
clearTimeout(this._timeoutHead);
|
|
235
|
-
this._timeoutHead = setTimeout(() => {
|
|
236
|
-
handleHeader(this, fullPath, triggers.headerLine, triggers.importArray, triggers.isSwift).catch((err) => {
|
|
237
|
-
console.warn(`[Watcher] header handler failed: ${err.message}`);
|
|
238
|
-
});
|
|
239
|
-
}, DEBOUNCE_DELAY);
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
/* ────────── 工具方法(供 handlers 通过 watcher 引用调用) ────────── */
|
|
244
|
-
/** 追加候选项(通过 ServiceContainer 或 HTTP API) */
|
|
245
|
-
async _appendCandidates(items, source) {
|
|
246
|
-
// 过滤空 title / 空 code 的无效条目
|
|
247
|
-
const validItems = items.filter((item) => {
|
|
248
|
-
const title = String(item.title || '').trim();
|
|
249
|
-
const code = String(item.code || '').trim();
|
|
250
|
-
if (!title || !code) {
|
|
251
|
-
console.warn(`[Watcher] 跳过无效候选: title=${JSON.stringify(title)}, code length=${code.length}`);
|
|
252
|
-
return false;
|
|
253
|
-
}
|
|
254
|
-
return true;
|
|
255
|
-
});
|
|
256
|
-
if (validItems.length === 0) {
|
|
257
|
-
throw new Error('所有候选条目缺少 title 或 code,无法提交');
|
|
258
|
-
}
|
|
259
|
-
// 优先 ServiceContainer
|
|
260
|
-
let serviceError = null;
|
|
261
|
-
try {
|
|
262
|
-
const { ServiceContainer } = await import('../../injection/ServiceContainer.js');
|
|
263
|
-
const container = ServiceContainer.getInstance();
|
|
264
|
-
const knowledgeService = container.get('knowledgeService');
|
|
265
|
-
const context = { userId: 'filewatcher' };
|
|
266
|
-
for (const item of validItems) {
|
|
267
|
-
await knowledgeService.create({
|
|
268
|
-
content: {
|
|
269
|
-
pattern: String(item.code || ''),
|
|
270
|
-
},
|
|
271
|
-
language: String(item.language || 'objc'),
|
|
272
|
-
category: String(item.category || 'Utility'),
|
|
273
|
-
source: source || 'watch',
|
|
274
|
-
title: String(item.title || ''),
|
|
275
|
-
description: String(item.summary || item.description || ''),
|
|
276
|
-
moduleName: String(item.moduleName || 'watch-create'),
|
|
277
|
-
trigger: String(item.trigger || ''),
|
|
278
|
-
headers: (item.headers || []),
|
|
279
|
-
tags: (item.tags || []),
|
|
280
|
-
}, context);
|
|
281
|
-
}
|
|
282
|
-
return;
|
|
283
|
-
}
|
|
284
|
-
catch (err) {
|
|
285
|
-
serviceError = err;
|
|
286
|
-
console.warn('[Watcher] KnowledgeService 创建失败,尝试 HTTP 回退:', err.message);
|
|
287
|
-
}
|
|
288
|
-
// 回退:HTTP API(使用 knowledge 端点而非 candidates)
|
|
289
|
-
const dashboardUrl = process.env.ASD_DASHBOARD_URL || 'http://localhost:3000';
|
|
290
|
-
try {
|
|
291
|
-
for (const item of validItems) {
|
|
292
|
-
const resp = await fetch(`${dashboardUrl}/api/v1/knowledge`, {
|
|
293
|
-
method: 'POST',
|
|
294
|
-
headers: { 'Content-Type': 'application/json' },
|
|
295
|
-
body: JSON.stringify({
|
|
296
|
-
title: item.title,
|
|
297
|
-
content: { pattern: item.code || '' },
|
|
298
|
-
language: item.language || 'objc',
|
|
299
|
-
category: item.category || 'Utility',
|
|
300
|
-
source: source || 'watch',
|
|
301
|
-
description: item.summary || item.description || '',
|
|
302
|
-
moduleName: item.moduleName || 'watch-create',
|
|
303
|
-
trigger: item.trigger || '',
|
|
304
|
-
headers: item.headers || [],
|
|
305
|
-
}),
|
|
306
|
-
});
|
|
307
|
-
if (!resp.ok) {
|
|
308
|
-
throw new Error(`HTTP ${resp.status}`);
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
return;
|
|
312
|
-
}
|
|
313
|
-
catch (err) {
|
|
314
|
-
console.warn(`[Watcher] HTTP 候选提交也失败: ${err.message}`);
|
|
315
|
-
}
|
|
316
|
-
// 两条路径都失败 → 抛出原始错误
|
|
317
|
-
throw serviceError || new Error('候选提交失败:ServiceContainer 和 HTTP 均不可用');
|
|
318
|
-
}
|
|
319
|
-
/** 为候选解析头文件 */
|
|
320
|
-
async _resolveHeadersIfNeeded(item, relativePath, text) {
|
|
321
|
-
if (relativePath && (!item.headers || !item.headers.length)) {
|
|
322
|
-
try {
|
|
323
|
-
const HeaderResolver = await import('../../platform/ios/xcode/HeaderResolver.js');
|
|
324
|
-
const resolved = await HeaderResolver.resolveHeadersForText(this.projectRoot, relativePath, text);
|
|
325
|
-
if (resolved?.headers && resolved.headers.length > 0) {
|
|
326
|
-
item.headers = resolved.headers;
|
|
327
|
-
item.headerPaths = resolved.headerPaths;
|
|
328
|
-
item.moduleName = resolved.moduleName;
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
catch {
|
|
332
|
-
// 头文件解析失败不阻塞
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
/** 打开 Dashboard 页面 */
|
|
337
|
-
_openDashboard(path) {
|
|
338
|
-
const base = process.env.ASD_DASHBOARD_URL || 'http://localhost:3000';
|
|
339
|
-
const url = `${base}${path}`;
|
|
340
|
-
import('../../platform/OpenBrowser.js')
|
|
341
|
-
.then(({ openBrowserReuseTab }) => openBrowserReuseTab(url, base))
|
|
342
|
-
.catch(() => { });
|
|
343
|
-
}
|
|
344
|
-
/** macOS 通知 */
|
|
345
|
-
_notify(msg) {
|
|
346
|
-
import('../../platform/NativeUi.js').then((NU) => NU.notify(msg)).catch(() => { });
|
|
347
|
-
}
|
|
348
|
-
/** 防抖 */
|
|
349
|
-
_debounce(key, fn) {
|
|
350
|
-
if (this._debounceTimers.has(key)) {
|
|
351
|
-
clearTimeout(this._debounceTimers.get(key));
|
|
352
|
-
}
|
|
353
|
-
this._debounceTimers.set(key, setTimeout(() => {
|
|
354
|
-
this._debounceTimers.delete(key);
|
|
355
|
-
Promise.resolve()
|
|
356
|
-
.then(() => fn())
|
|
357
|
-
.catch((err) => {
|
|
358
|
-
console.error('[Watch] 处理文件失败:', err.message);
|
|
359
|
-
if (process.env.ASD_DEBUG === '1') {
|
|
360
|
-
console.error(err.stack);
|
|
361
|
-
}
|
|
362
|
-
});
|
|
363
|
-
}, DEBOUNCE_DELAY));
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
export default FileWatcher;
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* TriggerResolver — 触发器规范化
|
|
3
|
-
* 将原始触发输入规范化为标准格式,添加类型标签
|
|
4
|
-
*/
|
|
5
|
-
export declare class TriggerResolver {
|
|
6
|
-
#private;
|
|
7
|
-
/**
|
|
8
|
-
* 规范化触发器
|
|
9
|
-
* @returns }
|
|
10
|
-
*/
|
|
11
|
-
resolve(trigger: string | Record<string, unknown>): {
|
|
12
|
-
type: string;
|
|
13
|
-
name: string;
|
|
14
|
-
params: {
|
|
15
|
-
option: string;
|
|
16
|
-
};
|
|
17
|
-
raw: string;
|
|
18
|
-
} | {
|
|
19
|
-
type: string;
|
|
20
|
-
name: string;
|
|
21
|
-
params: {
|
|
22
|
-
option?: undefined;
|
|
23
|
-
};
|
|
24
|
-
raw: string;
|
|
25
|
-
} | {
|
|
26
|
-
type: string;
|
|
27
|
-
name: string;
|
|
28
|
-
params: Record<string, unknown>;
|
|
29
|
-
raw: Record<string, unknown>;
|
|
30
|
-
} | {
|
|
31
|
-
type: string;
|
|
32
|
-
raw: never;
|
|
33
|
-
name?: undefined;
|
|
34
|
-
params?: undefined;
|
|
35
|
-
};
|
|
36
|
-
}
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* TriggerResolver — 触发器规范化
|
|
3
|
-
* 将原始触发输入规范化为标准格式,添加类型标签
|
|
4
|
-
*/
|
|
5
|
-
export class TriggerResolver {
|
|
6
|
-
/**
|
|
7
|
-
* 规范化触发器
|
|
8
|
-
* @returns }
|
|
9
|
-
*/
|
|
10
|
-
resolve(trigger) {
|
|
11
|
-
if (typeof trigger === 'string') {
|
|
12
|
-
return this.#resolveString(trigger);
|
|
13
|
-
}
|
|
14
|
-
if (trigger && typeof trigger === 'object') {
|
|
15
|
-
return {
|
|
16
|
-
type: String(trigger.type || 'unknown'),
|
|
17
|
-
name: String(trigger.name || ''),
|
|
18
|
-
params: trigger.params || {},
|
|
19
|
-
raw: trigger,
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
return { type: 'unknown', raw: trigger };
|
|
23
|
-
}
|
|
24
|
-
#resolveString(input) {
|
|
25
|
-
const trimmed = input.trim();
|
|
26
|
-
// 检测 as:xxx 格式
|
|
27
|
-
const asMatch = trimmed.match(/^as:(\w+)\s*(.*)/);
|
|
28
|
-
if (asMatch) {
|
|
29
|
-
return {
|
|
30
|
-
type: this.#mapDirectiveType(asMatch[1]),
|
|
31
|
-
name: asMatch[1],
|
|
32
|
-
params: { option: asMatch[2].trim() },
|
|
33
|
-
raw: input,
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
// 检测事件类型格式 file:changed, timer:cron
|
|
37
|
-
const eventMatch = trimmed.match(/^(\w+):(\w+)\s*(.*)/);
|
|
38
|
-
if (eventMatch) {
|
|
39
|
-
return {
|
|
40
|
-
type: eventMatch[1],
|
|
41
|
-
name: eventMatch[2],
|
|
42
|
-
params: { option: eventMatch[3].trim() },
|
|
43
|
-
raw: input,
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
return { type: 'custom', name: trimmed, params: {}, raw: input };
|
|
47
|
-
}
|
|
48
|
-
#mapDirectiveType(name) {
|
|
49
|
-
const map = {
|
|
50
|
-
search: 'search',
|
|
51
|
-
s: 'search',
|
|
52
|
-
create: 'create',
|
|
53
|
-
c: 'create',
|
|
54
|
-
audit: 'audit',
|
|
55
|
-
a: 'audit',
|
|
56
|
-
include: 'injection',
|
|
57
|
-
import: 'injection',
|
|
58
|
-
alink: 'alink',
|
|
59
|
-
};
|
|
60
|
-
return map[name] || 'directive';
|
|
61
|
-
}
|
|
62
|
-
}
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AlinkHandler — 处理 alink 指令
|
|
3
|
-
*
|
|
4
|
-
* 解析编辑器中的 alink 触发行,提取 completionKey,
|
|
5
|
-
* 通过数据库查找匹配的 Recipe,打开 Dashboard 详情页。
|
|
6
|
-
*/
|
|
7
|
-
import { and, eq } from 'drizzle-orm';
|
|
8
|
-
import { getServiceContainer } from '#inject/ServiceContainer.js';
|
|
9
|
-
import { knowledgeEntries } from '../../../infrastructure/database/drizzle/schema.js';
|
|
10
|
-
export async function handleAlink(alinkLine) {
|
|
11
|
-
const { TRIGGER_SYMBOL } = await import('../../../infrastructure/config/TriggerSymbol.js');
|
|
12
|
-
let completionKey = null;
|
|
13
|
-
const alinkMark = 'alink';
|
|
14
|
-
if (alinkLine.includes(TRIGGER_SYMBOL)) {
|
|
15
|
-
const parts = alinkLine
|
|
16
|
-
.split(TRIGGER_SYMBOL)
|
|
17
|
-
.map((p) => p.trim())
|
|
18
|
-
.filter(Boolean);
|
|
19
|
-
if (parts.length >= 2 && parts[parts.length - 1] === alinkMark) {
|
|
20
|
-
completionKey = parts[parts.length - 2];
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
if (completionKey != null) {
|
|
24
|
-
try {
|
|
25
|
-
// 从 DI 容器获取数据库实例,查找匹配 trigger 的 Recipe
|
|
26
|
-
const container = getServiceContainer();
|
|
27
|
-
const db = container.get('database');
|
|
28
|
-
let recipeId = null;
|
|
29
|
-
if (db) {
|
|
30
|
-
const rawDb = typeof db.getDb === 'function' ? db.getDb() : db;
|
|
31
|
-
try {
|
|
32
|
-
// ★ Drizzle 类型安全 — 精确匹配 trigger
|
|
33
|
-
const drizzle = typeof db.getDrizzle === 'function' ? db.getDrizzle() : null;
|
|
34
|
-
if (!drizzle) {
|
|
35
|
-
throw new Error('Drizzle not available');
|
|
36
|
-
}
|
|
37
|
-
const row = drizzle
|
|
38
|
-
.select({ id: knowledgeEntries.id })
|
|
39
|
-
.from(knowledgeEntries)
|
|
40
|
-
.where(and(eq(knowledgeEntries.trigger, completionKey), eq(knowledgeEntries.lifecycle, 'active')))
|
|
41
|
-
.limit(1)
|
|
42
|
-
.get();
|
|
43
|
-
if (row) {
|
|
44
|
-
recipeId = row.id;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
catch {
|
|
48
|
-
// DB 查询失败时回退到搜索
|
|
49
|
-
}
|
|
50
|
-
// 若精确匹配失败,尝试模糊搜索(保留 raw SQL — LIKE + ESCAPE)
|
|
51
|
-
if (!recipeId) {
|
|
52
|
-
try {
|
|
53
|
-
const rawDb2 = (typeof db.getDb === 'function' ? db.getDb() : db);
|
|
54
|
-
const escaped = completionKey.replace(/[%_\\]/g, (ch) => `\\${ch}`);
|
|
55
|
-
const row = rawDb2
|
|
56
|
-
.prepare("SELECT id FROM knowledge_entries WHERE (trigger LIKE ? ESCAPE '\\' OR title LIKE ? ESCAPE '\\') AND lifecycle = 'active' LIMIT 1")
|
|
57
|
-
.get(`%${escaped}%`, `%${escaped}%`);
|
|
58
|
-
if (row) {
|
|
59
|
-
recipeId = row.id;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
catch {
|
|
63
|
-
/* silent */
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
// 构建 Dashboard URL 并打开
|
|
68
|
-
const port = process.env.ASD_DASHBOARD_PORT || 3000;
|
|
69
|
-
const host = process.env.ASD_DASHBOARD_HOST || 'localhost';
|
|
70
|
-
const url = recipeId
|
|
71
|
-
? `http://${host}:${port}/#/recipes/${recipeId}`
|
|
72
|
-
: `http://${host}:${port}/#/search?q=${encodeURIComponent(completionKey)}`;
|
|
73
|
-
const open = (await import('open')).default;
|
|
74
|
-
await open(url);
|
|
75
|
-
}
|
|
76
|
-
catch (err) {
|
|
77
|
-
console.warn(`[alink] Failed to open link: ${err.message}`);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CreateHandler — 处理 // as:c 指令
|
|
3
|
-
* 从 FileWatcher 拆分,负责候选创建逻辑
|
|
4
|
-
*/
|
|
5
|
-
/**
|
|
6
|
-
* 处理 // as:c 指令
|
|
7
|
-
* @param createOption 'c' | 'f' | undefined
|
|
8
|
-
*/
|
|
9
|
-
export declare function handleCreate(watcher: import('../FileWatcher.js').FileWatcher, fullPath: string, relativePath: string, createOption: string | null): Promise<void>;
|
|
10
|
-
/** 查找 // as:c 的行号 (1-based) */
|
|
11
|
-
export declare function findCreateLineNumber(content: string): number;
|