@becrafter/prompt-manager 0.2.2 → 0.2.4-dev.5

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.
Files changed (67) hide show
  1. package/package.json +15 -4
  2. package/packages/server/api/admin.routes.js +385 -0
  3. package/packages/server/mcp/prompt.handler.js +6 -6
  4. package/packages/server/server.js +13 -0
  5. package/packages/server/services/TerminalService.js +37 -17
  6. package/packages/server/services/skill-sync.service.js +223 -0
  7. package/packages/server/services/skills.service.js +731 -0
  8. package/packages/server/utils/config.js +8 -0
  9. package/packages/server/utils/util.js +27 -21
  10. package/packages/web/0.26974981f2e663e001ca.js +1 -0
  11. package/packages/web/112.895e73058c36df517412.js +1 -0
  12. package/packages/web/130.2145d293d97ee88902a1.js +1 -0
  13. package/packages/web/142.4ef07b4323c04c87bd81.js +1 -0
  14. package/packages/web/165.3b013a0e62121061ce41.js +2 -0
  15. package/packages/web/165.3b013a0e62121061ce41.js.LICENSE.txt +9 -0
  16. package/packages/web/203.a3697c577f74b6538257.js +1 -0
  17. package/packages/web/241.c0b98c14268a1d46d55d.js +1 -0
  18. package/packages/web/249.e10b6b8a1ac4f66185ea.js +1 -0
  19. package/packages/web/291.8c281d2823aa37833137.js +1 -0
  20. package/packages/web/319.5b4ef8bdd727fdac0520.js +1 -0
  21. package/packages/web/32.22d5433bcd9b6b4e98f1.js +1 -0
  22. package/packages/web/325.8810a3f94093d0f19341.js +1 -0
  23. package/packages/web/366.24666676c36ca63de8e1.js +1 -0
  24. package/packages/web/378.07aa22b5f489f8f1ec36.js +1 -0
  25. package/packages/web/391.f39db51c15feb65b46cb.js +1 -0
  26. package/packages/web/393.ab70195ea156eebec40b.js +1 -0
  27. package/packages/web/412.6d38dd3b290782c51ed6.js +1 -0
  28. package/packages/web/465.fb5ab26f362d3eb53da9.js +1 -0
  29. package/packages/web/48.7643e76aedee153e680c.js +1 -0
  30. package/packages/web/480.237737e805b568087d34.js +1 -0
  31. package/packages/web/489.182e98261f30e4da62d6.js +1 -0
  32. package/packages/web/490.3f311f0c36d0c52f8b52.js +1 -0
  33. package/packages/web/492.d07d9857e3682710f3a5.js +1 -0
  34. package/packages/web/495.60324846746eea7d588b.js +1 -0
  35. package/packages/web/510.b6e2c433d2f9bc9a1edd.js +1 -0
  36. package/packages/web/543.223a1c0b8345b4e65ed3.js +1 -0
  37. package/packages/web/567.e438fd025d21373bc95b.js +1 -0
  38. package/packages/web/592.c670d3500195bdb18ca4.js +1 -0
  39. package/packages/web/616.a8b495ba380ec0bcafd9.js +1 -0
  40. package/packages/web/617.bc4db899b54108068892.js +1 -0
  41. package/packages/web/641.1e5e900d36819777d808.js +1 -0
  42. package/packages/web/672.ae0e48b24fd6863bb4d1.js +1 -0
  43. package/packages/web/731.595770c0a8f30f8c453d.js +1 -0
  44. package/packages/web/746.3faf506c16dc0e6ae25a.js +1 -0
  45. package/packages/web/756.16d54ad97aadec6eb440.js +1 -0
  46. package/packages/web/77.d3de1da7532138022a7c.js +1 -0
  47. package/packages/web/802.0a9a7f92703bd3ed0228.js +1 -0
  48. package/packages/web/815.879527c51deda81ff3e6.js +1 -0
  49. package/packages/web/821.12f33b53b488b3995bb9.js +1 -0
  50. package/packages/web/846.64e9bb4b76cae3245c47.js +1 -0
  51. package/packages/web/869.aadd9a07fd56e04ba5dd.js +1 -0
  52. package/packages/web/885.6dc8edf4c16f6b578da6.js +1 -0
  53. package/packages/web/901.45604b599d11f86ae9d8.js +1 -0
  54. package/packages/web/928.69835904eae42113a3ce.js +1 -0
  55. package/packages/web/955.827020bdb97be41eb055.js +1 -0
  56. package/packages/web/981.e4d081b9d1dac0cd275f.js +1 -0
  57. package/packages/web/992.a3843959b85c7fdc6cf9.js +1 -0
  58. package/packages/web/996.07a82c3066973c75984b.js +1 -0
  59. package/packages/web/css/codemirror-theme_xq-light.css +43 -0
  60. package/packages/web/css/codemirror.css +344 -0
  61. package/packages/web/css/main.b013c98c7a56511f8710.css +9995 -0
  62. package/packages/web/css/main.css +4404 -0
  63. package/packages/web/css/skills.css +1753 -0
  64. package/packages/web/css/terminal.css +1218 -0
  65. package/packages/web/index.html +3 -0
  66. package/packages/web/main.cf6dd52dbaa4f935d45a.js +2 -0
  67. package/packages/web/main.cf6dd52dbaa4f935d45a.js.LICENSE.txt +3 -0
@@ -0,0 +1,223 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+ import os from 'os';
4
+ import pkg from 'sync-hub';
5
+ const { createSyncer } = pkg;
6
+ import { logger } from '../utils/logger.js';
7
+ import { config } from '../utils/config.js';
8
+
9
+ function expandPath(inputPath) {
10
+ if (!inputPath) return inputPath;
11
+ if (inputPath.startsWith('~/')) {
12
+ return path.join(os.homedir(), inputPath.slice(2));
13
+ }
14
+ return inputPath.replace(/\$(\w+)|\$\{(\w+)\}/g, (match, varName1, varName2) => {
15
+ const varName = varName1 || varName2;
16
+ return process.env[varName] || match;
17
+ });
18
+ }
19
+
20
+ /**
21
+ * 技能全局同步服务
22
+ * 职责:
23
+ * 1. 管理同步配置(目标目录、开关)
24
+ * 2. 监听技能目录变更并自动同步
25
+ * 3. 提供手动同步接口
26
+ */
27
+ class SkillSyncService {
28
+ constructor() {
29
+ this.configPath = path.join(config.getConfigsDir(), 'skill-sync.json');
30
+ this.skillsDir = config.getSkillsDir();
31
+ this.syncConfig = {
32
+ enabled: false,
33
+ targets: [],
34
+ lastSyncTime: null,
35
+ lastSyncMethod: null,
36
+ error: null
37
+ };
38
+ this.watcher = null;
39
+ this.syncHub = null;
40
+ }
41
+
42
+ /**
43
+ * 初始化服务
44
+ */
45
+ async init() {
46
+ try {
47
+ await this.loadConfig();
48
+ // 在测试环境下默认不启动监听,除非配置显式开启
49
+ if (this.syncConfig.enabled && process.env.NODE_ENV !== 'test') {
50
+ await this.startWatching();
51
+ }
52
+ logger.info('SkillSyncService 初始化完成');
53
+ } catch (error) {
54
+ logger.error('SkillSyncService 初始化失败:', error);
55
+ }
56
+ }
57
+
58
+ /**
59
+ * 加载配置
60
+ */
61
+ async loadConfig() {
62
+ try {
63
+ if (await fs.pathExists(this.configPath)) {
64
+ const data = await fs.readJson(this.configPath);
65
+ this.syncConfig = { ...this.syncConfig, ...data };
66
+ } else {
67
+ await this.saveConfig();
68
+ }
69
+ } catch (error) {
70
+ logger.error('加载同步配置失败:', error);
71
+ }
72
+ }
73
+
74
+ /**
75
+ * 保存配置
76
+ */
77
+ async saveConfig() {
78
+ try {
79
+ await fs.ensureDir(path.dirname(this.configPath));
80
+ await fs.writeJson(this.configPath, this.syncConfig, { spaces: 2 });
81
+ } catch (error) {
82
+ logger.error('保存同步配置失败:', error);
83
+ }
84
+ }
85
+
86
+ /**
87
+ * 更新配置
88
+ * @param {Object} newConfig - 新配置项
89
+ */
90
+ async updateConfig(newConfig) {
91
+ const wasEnabled = this.syncConfig.enabled;
92
+ this.syncConfig = { ...this.syncConfig, ...newConfig };
93
+ await this.saveConfig();
94
+
95
+ if (this.syncConfig.enabled && !wasEnabled) {
96
+ await this.startWatching();
97
+ } else if (!this.syncConfig.enabled && wasEnabled) {
98
+ await this.stopWatching();
99
+ } else if (this.syncConfig.enabled && wasEnabled) {
100
+ // 如果目标目录改变,重启监听以确保同步到新位置
101
+ await this.stopWatching();
102
+ await this.startWatching();
103
+ }
104
+ }
105
+
106
+ /**
107
+ * 获取配置
108
+ */
109
+ getConfig() {
110
+ return {
111
+ ...this.syncConfig,
112
+ effectiveSyncMethod: this.detectSyncMethod()
113
+ };
114
+ }
115
+
116
+ getResolvedTargets() {
117
+ return (this.syncConfig.targets || []).map(target => expandPath(target)).filter(Boolean);
118
+ }
119
+
120
+ detectSyncMethod() {
121
+ const targets = this.getResolvedTargets();
122
+ if (targets.length === 0) {
123
+ return null;
124
+ }
125
+
126
+ for (const target of targets) {
127
+ try {
128
+ if (!fs.existsSync(target)) {
129
+ return 'copy';
130
+ }
131
+ const stats = fs.lstatSync(target);
132
+ if (!stats.isSymbolicLink()) {
133
+ return 'copy';
134
+ }
135
+ const realTarget = fs.realpathSync(target);
136
+ if (realTarget !== this.skillsDir) {
137
+ return 'copy';
138
+ }
139
+ } catch (error) {
140
+ return 'copy';
141
+ }
142
+ }
143
+
144
+ return 'link';
145
+ }
146
+
147
+ /**
148
+ * 开始监听目录
149
+ */
150
+ async startWatching() {
151
+ if (this.watcher) return;
152
+ const targets = this.getResolvedTargets();
153
+ if (targets.length === 0) return;
154
+
155
+ logger.info('开始监听技能目录变更并自动同步:', this.skillsDir);
156
+
157
+ this.watcher = createSyncer(this.skillsDir, targets, {
158
+ preferLink: true,
159
+ fallbackToCopy: true,
160
+ deleteOrphaned: false,
161
+ ignorePatterns: ['**/.*']
162
+ });
163
+
164
+ const method = await this.watcher.sync(); // 启动前先同步一次
165
+ this.syncConfig.lastSyncMethod = method || null;
166
+ await this.saveConfig();
167
+ if (method !== 'link') {
168
+ this.watcher.watch();
169
+ } else {
170
+ logger.info('符号链接模式无需监听自动同步');
171
+ }
172
+ }
173
+
174
+ /**
175
+ * 停止监听
176
+ */
177
+ async stopWatching() {
178
+ if (this.watcher) {
179
+ await this.watcher.stopWatch();
180
+ this.watcher = null;
181
+ logger.info('已停止监听技能目录');
182
+ }
183
+ }
184
+
185
+ /**
186
+ * 执行同步
187
+ */
188
+ async runSync() {
189
+ const targets = this.getResolvedTargets();
190
+ if (targets.length === 0) {
191
+ logger.warn('未配置同步目标目录,跳过同步');
192
+ return;
193
+ }
194
+
195
+ try {
196
+ logger.info('开始执行技能全局同步...');
197
+
198
+ const syncer = createSyncer(this.skillsDir, targets, {
199
+ preferLink: true,
200
+ fallbackToCopy: true,
201
+ deleteOrphaned: false,
202
+ ignorePatterns: ['**/.*']
203
+ });
204
+
205
+ const method = await syncer.sync();
206
+
207
+ this.syncConfig.lastSyncTime = new Date().toISOString();
208
+ this.syncConfig.lastSyncMethod = method || null;
209
+ this.syncConfig.error = null;
210
+ await this.saveConfig();
211
+
212
+ logger.info('技能全局同步完成');
213
+ return { method };
214
+ } catch (error) {
215
+ logger.error('执行技能同步时发生错误:', error);
216
+ this.syncConfig.error = error.message;
217
+ await this.saveConfig();
218
+ throw error;
219
+ }
220
+ }
221
+ }
222
+
223
+ export const skillSyncService = new SkillSyncService();