@myassis/gateway 1.0.1 → 1.0.3

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 (54) hide show
  1. package/dist/api/index.js +69 -59
  2. package/dist/config/index.js +2 -2
  3. package/dist/index.js +85 -11
  4. package/dist/middleware/auth.js +9 -25
  5. package/dist/middleware/errorHandler.js +1 -1
  6. package/dist/routes/agent.js +30 -19
  7. package/dist/routes/auth.js +35 -26
  8. package/dist/routes/chat.js +1 -1
  9. package/dist/routes/models.js +11 -14
  10. package/dist/routes/service.js +3 -6
  11. package/dist/routes/settings.js +11 -9
  12. package/dist/routes/skillHub.js +10 -6
  13. package/dist/routes/skills.js +13 -10
  14. package/dist/routes/tasks.js +6 -3
  15. package/dist/routes/upload.js +5 -2
  16. package/dist/routes/version.js +4 -4
  17. package/dist/services/LocalTaskService.js +2 -2
  18. package/dist/services/NotificationService.js +4 -4
  19. package/dist/services/ServiceManager.js +12 -12
  20. package/dist/services/TaskSchedulerService.js +21 -12
  21. package/dist/services/TaskService.js +25 -22
  22. package/dist/services/WebSocketService.js +16 -6
  23. package/dist/services/agent/Agent.js +7 -7
  24. package/dist/services/agent/AgentManager.js +26 -18
  25. package/dist/services/agent/AgentStore.js +1 -1
  26. package/dist/services/dataService.js +63 -73
  27. package/dist/services/index.js +9 -9
  28. package/dist/services/llm/LLMClient.js +11 -9
  29. package/dist/services/memory/MemoryManager.js +180 -17
  30. package/dist/services/model/index.js +1 -1
  31. package/dist/services/session/MigrationManager.js +1 -1
  32. package/dist/services/session/Session.js +74 -50
  33. package/dist/services/session/SessionManager.js +14 -7
  34. package/dist/services/session/SessionStore.js +2 -28
  35. package/dist/services/session/index.js +2 -2
  36. package/dist/services/systemPrompt.js +9 -5
  37. package/dist/services/task/PushTokenStore.js +2 -20
  38. package/dist/services/task/TaskStore.js +2 -21
  39. package/dist/services/tools/edit.js +122 -148
  40. package/dist/services/tools/exec.js +1 -1
  41. package/dist/services/tools/fetch.js +1 -1
  42. package/dist/services/tools/file.js +4 -9
  43. package/dist/services/tools/index.js +18 -17
  44. package/dist/services/tools/model.js +9 -7
  45. package/dist/services/tools/sessionsSpawn.js +54 -0
  46. package/dist/services/tools/skill.js +14 -13
  47. package/dist/services/tools/task.js +3 -5
  48. package/dist/stores/authStore.js +52 -66
  49. package/dist/stores/index.js +3 -3
  50. package/dist/stores/persistStore.js +37 -3
  51. package/package.json +7 -3
  52. package/scripts/fix-dist-imports.js +95 -0
  53. package/scripts/fix-imports.js +90 -0
  54. package/scripts/postbuild.js +39 -0
@@ -1,6 +1,6 @@
1
- import { skillsService, skillHubService } from '../dataService';
2
- import { persistStore } from '@/stores';
3
- import { getLogger } from '@pocketclaw/shared';
1
+ import { skillsService, skillHubService } from '../dataService.js';
2
+ import { authStore, persistStore } from '@/stores';
3
+ import { getLogger } from '@myassis/shared';
4
4
  const logger = getLogger('skill');
5
5
  export const skillTool = {
6
6
  name: 'skill',
@@ -22,19 +22,20 @@ export const skillTool = {
22
22
  },
23
23
  required: ['action'],
24
24
  },
25
- handler: async (args) => {
25
+ handler: async (args, sessionId, messageId, userId) => {
26
26
  try {
27
27
  const { action, query, skillId, hubId, apiKey, apiBaseUrl, rating } = args;
28
+ const token = authStore.get(userId).accessToken;
28
29
  switch (action) {
29
30
  case 'listInstalled': {
30
- const response = await skillsService.list();
31
+ const response = await skillsService.list(token);
31
32
  if (!response.success)
32
33
  return { success: false, errorMessage: response.error || '获取列表失败' };
33
34
  const skills = response.data || [];
34
35
  return { success: true, output: JSON.stringify({ total: skills.length, skills: skills.map((s) => ({ id: s.id, name: s.name, description: s.description, version: s.version, status: s.status })) }) };
35
36
  }
36
37
  case 'search': {
37
- const response = await skillHubService.list({ keyword: query, pageSize: 10 });
38
+ const response = await skillHubService.list(token, { keyword: query, pageSize: 10 });
38
39
  if (!response.success)
39
40
  return { success: false, errorMessage: response.error || '搜索失败' };
40
41
  const skills = response.data?.items || response.data || [];
@@ -44,7 +45,7 @@ export const skillTool = {
44
45
  const installId = hubId || query;
45
46
  if (!installId)
46
47
  return { success: false, errorMessage: '安装需要提供 hubId 或 query' };
47
- const response = await skillsService.install(installId);
48
+ const response = await skillsService.install(installId, token);
48
49
  if (!response.success)
49
50
  return { success: false, errorMessage: response.error || '安装失败' };
50
51
  return { success: true };
@@ -52,7 +53,7 @@ export const skillTool = {
52
53
  case 'uninstall': {
53
54
  if (!skillId)
54
55
  return { success: false, errorMessage: '卸载需要提供技能 ID' };
55
- const response = await skillsService.uninstall(skillId);
56
+ const response = await skillsService.uninstall(skillId, token);
56
57
  if (!response.success)
57
58
  return { success: false, errorMessage: response.error || '卸载失败' };
58
59
  return { success: true };
@@ -60,21 +61,21 @@ export const skillTool = {
60
61
  case 'detail': {
61
62
  if (!skillId)
62
63
  return { success: false, errorMessage: '查看详情需要提供技能 ID' };
63
- const response = await skillsService.get(skillId);
64
+ const response = await skillsService.get(skillId, token);
64
65
  if (!response.success)
65
66
  return { success: false, errorMessage: response.error || '获取详情失败' };
66
67
  // 表明该技能已被使用
67
68
  const detailHubId = response.data?.templateId;
68
69
  if (detailHubId) {
69
- skillHubService.use(detailHubId).catch((err) => logger.warn('技能使用记录失败:', err?.message));
70
+ skillHubService.use(detailHubId, token).catch((err) => logger.warn('技能使用记录失败:', err?.message));
70
71
  }
71
- const apikey = await skillsService.getApiKey(skillId);
72
+ const apikey = await skillsService.getApiKey(skillId, token);
72
73
  return { success: true, output: JSON.stringify({ skill: { ...response.data, apiKey: apikey.apiKey } }) };
73
74
  }
74
75
  case 'setApiKey': {
75
76
  if (!skillId || !apiKey)
76
77
  return { success: false, errorMessage: '设置 API Key 需要提供技能 ID 和 API Key' };
77
- const response = await skillsService.setApiKey(skillId, apiKey);
78
+ const response = await skillsService.setApiKey(skillId, apiKey, token);
78
79
  if (!response)
79
80
  return { success: false, errorMessage: '设置失败' };
80
81
  return { success: true };
@@ -84,7 +85,7 @@ export const skillTool = {
84
85
  return { success: false, errorMessage: '评分需要提供 hubId 和评分(1-5)' };
85
86
  if (rating < 1 || rating > 5)
86
87
  return { success: false, errorMessage: '评分必须是 1-5 之间的整数' };
87
- const response = await skillHubService.rate(hubId, rating);
88
+ const response = await skillHubService.rate(hubId, rating, token);
88
89
  if (!response.success)
89
90
  return { success: false, errorMessage: response.error || '评分失败' };
90
91
  return { success: true };
@@ -1,6 +1,5 @@
1
- import { tasksService } from '../dataService';
2
- import { authStore } from '@/stores';
3
- import { validPlatformApplies, getPlatform } from '@pocketclaw/shared/dist/utils/system.js';
1
+ import { tasksService } from '../dataService.js';
2
+ import { validPlatformApplies, getPlatform } from '@myassis/shared/dist/utils/system.js';
4
3
  export const taskTool = {
5
4
  name: 'task',
6
5
  description: '任务管理工具',
@@ -32,7 +31,7 @@ export const taskTool = {
32
31
  },
33
32
  required: ['action'],
34
33
  },
35
- handler: async (args) => {
34
+ handler: async (args, sessionId, messageId, userId) => {
36
35
  try {
37
36
  const { action, title, description, taskType, recurrenceRule, intervalValue, intervalUnit, startTime, endTime, scheduledAt, platformApply, status, taskId, newTitle, newDescription, newStatus, newRecurrenceRule, newScheduledAt } = args;
38
37
  switch (action) {
@@ -54,7 +53,6 @@ export const taskTool = {
54
53
  createData.endTime = endTime;
55
54
  if (scheduledAt)
56
55
  createData.scheduledAt = scheduledAt;
57
- const userId = authStore.getUserId();
58
56
  if (!userId) {
59
57
  return { success: false, errorMessage: '未登录' };
60
58
  }
@@ -5,14 +5,14 @@
5
5
  import fs from 'fs';
6
6
  import path from 'path';
7
7
  import os from 'os';
8
- import { getLogger } from '@pocketclaw/shared';
9
- import { sessionManager } from '@/services/session';
8
+ import { getLogger } from '@myassis/shared';
9
+ import { getSessionManager } from '@/services/session';
10
10
  const logger = getLogger('authStore');
11
11
  // 认证数据存储文件路径(使用应用数据目录,支持 STORAGE_DIR 环境变量)
12
- const AUTH_STORAGE_DIR = process.env.STORAGE_DIR || path.join(os.homedir(), 'myclaw-gateway-storage');
12
+ const AUTH_STORAGE_DIR = process.env.STORAGE_DIR || path.join(os.homedir(), 'myassis-gateway-storage');
13
13
  const AUTH_STORAGE_FILE = path.join(AUTH_STORAGE_DIR, 'auth', 'auth.json');
14
14
  // 内存中的认证数据
15
- let auth = null;
15
+ let authMap = null;
16
16
  // 确保存储目录存在
17
17
  function ensureStorageDir() {
18
18
  const dir = path.dirname(AUTH_STORAGE_FILE);
@@ -25,15 +25,9 @@ function loadFromFile() {
25
25
  try {
26
26
  if (fs.existsSync(AUTH_STORAGE_FILE)) {
27
27
  const data = fs.readFileSync(AUTH_STORAGE_FILE, 'utf-8');
28
- const authData = JSON.parse(data);
29
- // 检查 token 是否过期
30
- if (authData.expiresAt && authData.expiresAt < Date.now()) {
31
- logger.debug('Token expired, clearing...');
32
- clearAuth();
33
- return null;
34
- }
28
+ const obj = JSON.parse(data);
35
29
  logger.debug('Auth data loaded from file');
36
- return authData;
30
+ return new Map(Object.entries(obj));
37
31
  }
38
32
  }
39
33
  catch (error) {
@@ -42,10 +36,10 @@ function loadFromFile() {
42
36
  return null;
43
37
  }
44
38
  // 保存认证数据到本地文件
45
- function saveAuthToFile(authData) {
39
+ function saveAuthToFile(authMap) {
46
40
  ensureStorageDir();
47
41
  try {
48
- fs.writeFileSync(AUTH_STORAGE_FILE, JSON.stringify(authData, null, 2), 'utf-8');
42
+ fs.writeFileSync(AUTH_STORAGE_FILE, JSON.stringify(Object.fromEntries(authMap)), 'utf-8');
49
43
  logger.debug('Auth data saved to file');
50
44
  }
51
45
  catch (error) {
@@ -66,7 +60,7 @@ function clearAuthFile() {
66
60
  }
67
61
  // 清除认证数据
68
62
  function clearAuth() {
69
- auth = null;
63
+ authMap = null;
70
64
  clearAuthFile();
71
65
  }
72
66
  /**
@@ -78,84 +72,75 @@ export const authStore = {
78
72
  * 应在 Gateway 启动时调用
79
73
  */
80
74
  load() {
81
- auth = loadFromFile();
82
- if (auth) {
83
- logger.debug('Initialized with user:', auth.user.nickname);
84
- }
85
- else {
86
- logger.debug('Initialized, no valid auth data');
75
+ authMap = loadFromFile();
76
+ if (authMap === null) {
77
+ authMap = new Map();
87
78
  }
88
79
  },
89
80
  /**
90
81
  * 保存认证数据(内存 + 本地)
91
82
  */
92
83
  save(authData) {
93
- auth = authData;
94
- saveAuthToFile(authData);
84
+ if (authMap === null) {
85
+ this.load();
86
+ }
87
+ authMap.set(authData.user.id, authData);
88
+ saveAuthToFile(authMap);
95
89
  // 用户登录后初始化 SessionManager
96
- sessionManager.initialize();
90
+ getSessionManager(authData.user.id).initialize();
97
91
  logger.debug('Auth saved for user:', authData.user.nickname);
98
92
  },
93
+ //获取所有认证用户
94
+ getAll() {
95
+ if (authMap === null) {
96
+ this.load();
97
+ }
98
+ return authMap;
99
+ },
99
100
  /**
100
101
  * 获取当前认证数据
101
102
  */
102
- get() {
103
- return auth;
103
+ get(userId) {
104
+ return authMap.get(userId);
104
105
  },
105
106
  /**
106
107
  * 获取用户信息
107
108
  */
108
- getUser() {
109
- return auth?.user || null;
110
- },
111
- /**
112
- * 获取 Token(自动检查过期)
113
- */
114
- getToken() {
115
- if (!auth) {
116
- return null;
117
- }
118
- // 检查是否过期(预留 60 秒缓冲时间)
119
- if (auth.expiresAt && auth.expiresAt < Date.now() + 60000) {
120
- logger.debug('Token expired');
121
- return null;
122
- }
123
- return auth.token;
109
+ getUser(userId) {
110
+ return authMap.get(userId).user || null;
124
111
  },
125
112
  /**
126
113
  * 获取刷新 Token
127
114
  */
128
- getRefreshToken() {
129
- return auth?.refreshToken || null;
115
+ getRefreshToken(userId) {
116
+ return authMap.get(userId)?.refreshToken || null;
130
117
  },
131
118
  /**
132
119
  * 更新 Token
133
120
  */
134
121
  updateToken(token, expiresIn, refreshToken) {
135
- if (auth) {
136
- auth.token = token;
137
- if (expiresIn) {
138
- auth.expiresIn = expiresIn;
139
- auth.expiresAt = Date.now() + expiresIn * 1000;
140
- }
141
- if (refreshToken) {
142
- auth.refreshToken = refreshToken;
122
+ if (authMap) {
123
+ const auth = Array.from(authMap.values()).find(x => x.accessToken === token);
124
+ if (auth) {
125
+ auth.accessToken = token;
126
+ if (refreshToken) {
127
+ auth.refreshToken = refreshToken;
128
+ }
129
+ saveAuthToFile(authMap);
130
+ logger.debug('Token updated');
143
131
  }
144
- saveAuthToFile(auth);
145
- logger.debug('Token updated');
146
132
  }
147
133
  },
148
134
  /**
149
135
  * 获取用户 ID
150
136
  */
151
- getUserId() {
152
- return auth?.user?.id || null;
153
- },
154
- /**
155
- * 是否已认证
156
- */
157
- isAuthenticated() {
158
- return this.getToken() !== null;
137
+ getUserId(token) {
138
+ if (authMap) {
139
+ return Array.from(authMap.values()).find(x => x.accessToken === token)?.user?.id || null;
140
+ }
141
+ else {
142
+ return null;
143
+ }
159
144
  },
160
145
  /**
161
146
  * 清除认证数据(内存 + 本地)
@@ -167,11 +152,12 @@ export const authStore = {
167
152
  /**
168
153
  * 更新用户信息
169
154
  */
170
- updateUser(userData) {
171
- if (auth) {
155
+ updateUser(token, userData) {
156
+ if (authMap) {
157
+ const auth = Array.from(authMap.values()).find(x => x.accessToken === token);
172
158
  auth.user = { ...auth.user, ...userData };
173
- saveAuthToFile(auth);
174
- logger.debug('User updated:', auth.user?.nickname || auth.user?.email);
159
+ saveAuthToFile(authMap);
160
+ logger.debug('User updated:', auth.user?.nickname || auth.user?.account);
175
161
  }
176
162
  },
177
163
  };
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Gateway Stores 统一导出
3
3
  */
4
- export { memoryStore } from './memoryStore';
5
- export { persistStore } from './persistStore';
6
- export { authStore } from './authStore';
4
+ export { memoryStore } from './memoryStore.js';
5
+ export { persistStore } from './persistStore.js';
6
+ export { authStore } from './authStore.js';
@@ -5,10 +5,10 @@
5
5
  import fs from 'fs';
6
6
  import os from 'os';
7
7
  import path from 'path';
8
- import { getLogger } from '@pocketclaw/shared';
8
+ import { getLogger } from '@myassis/shared';
9
9
  const logger = getLogger('persistStore');
10
10
  // 存储文件路径(跨平台兼容)
11
- const STORAGE_DIR = process.env.STORAGE_DIR || path.join(os.homedir(), 'myclaw-gateway-storage');
11
+ const STORAGE_DIR = process.env.STORAGE_DIR || path.join(os.homedir(), 'myassis-gateway-storage');
12
12
  const STORAGE_FILE = path.join(STORAGE_DIR, 'persist.json');
13
13
  // 确保存储目录存在
14
14
  function ensureStorageDir() {
@@ -312,6 +312,40 @@ export const persistStore = {
312
312
  writeStorage(storage);
313
313
  }
314
314
  return cleaned;
315
- }
315
+ },
316
+ // ─── CORS 自定义域名 ────────────────────────────────
317
+ /**
318
+ * 获取所有自定义 CORS 域名
319
+ */
320
+ getCorsDomains() {
321
+ return this.get('corsDomains', []);
322
+ },
323
+ /**
324
+ * 添加自定义 CORS 域名
325
+ */
326
+ addCorsDomain(domain) {
327
+ const domains = this.getCorsDomains();
328
+ const normalized = domain.trim().toLowerCase();
329
+ if (!normalized)
330
+ return false;
331
+ if (domains.some(d => d.toLowerCase() === normalized))
332
+ return false; // 重复
333
+ domains.push(domain.trim());
334
+ this.set('corsDomains', domains);
335
+ return true;
336
+ },
337
+ /**
338
+ * 移除自定义 CORS 域名
339
+ */
340
+ removeCorsDomain(domain) {
341
+ const domains = this.getCorsDomains();
342
+ const normalized = domain.trim().toLowerCase();
343
+ const before = domains.length;
344
+ const filtered = domains.filter(d => d.toLowerCase() !== normalized);
345
+ if (filtered.length === before)
346
+ return false; // 不存在
347
+ this.set('corsDomains', filtered);
348
+ return true;
349
+ },
316
350
  };
317
351
  export default persistStore;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@myassis/gateway",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "我的助手 Gateway Service - 本地 AI 网关服务,支持认证、WebSocket 实时通信和任务调度",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -9,7 +9,10 @@
9
9
  "gateway": "dist/index.js"
10
10
  },
11
11
  "files": [
12
- "dist"
12
+ "dist",
13
+ "scripts",
14
+ "README.md",
15
+ "LICENSE"
13
16
  ],
14
17
  "exports": {
15
18
  ".": {
@@ -21,6 +24,7 @@
21
24
  "dev": "tsx watch src/index.ts",
22
25
  "start": "node dist/index.js",
23
26
  "build": "tsc",
27
+ "postbuild": "node scripts/fix-dist-imports.js",
24
28
  "pkg:win": "pkg . --targets node18-win-x64 --output myassis-gateway-win.exe",
25
29
  "pkg:linux": "pkg . --targets node18-linux-x64 --output myassis-gateway-linux",
26
30
  "pkg:all": "npm run pkg:win && npm run pkg:linux",
@@ -43,7 +47,7 @@
43
47
  },
44
48
  "dependencies": {
45
49
  "@nut-tree-fork/nut-js": "^4.2.0",
46
- "@myassis/shared": "latest",
50
+ "@myassis/shared": "^1.0.0",
47
51
  "axios": "^1.15.2",
48
52
  "bcryptjs": "^2.4.3",
49
53
  "better-sqlite3": "^12.10.0",
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * 修复 dist 目录中的 ES Module 导入
4
+ * 为所有本地导入添加 .js 扩展名
5
+ */
6
+
7
+ import fs from 'fs';
8
+ import path from 'path';
9
+ import { fileURLToPath } from 'url';
10
+
11
+ const __filename = fileURLToPath(import.meta.url);
12
+ const __dirname = path.dirname(__filename);
13
+
14
+ const distDir = path.join(__dirname, '..', 'dist');
15
+
16
+ // 递归处理所有 .js 文件
17
+ function processFile(filePath) {
18
+ let content = fs.readFileSync(filePath, 'utf8');
19
+ let modified = false;
20
+
21
+ // 匹配 import 和 export ... from 语句
22
+ // 匹配模式:from './xxx' 或 from '../xxx'(不包含协议、不为 / 开头、不包含 .js)
23
+ const importRegex = /(?:import|export)\s+(?:[\w*{}\s,]+from\s+)?['"](\.\.?\/[^'"]+)['"]/g;
24
+
25
+ let match;
26
+ while ((match = importRegex.exec(content)) !== null) {
27
+ const importPath = match[1];
28
+
29
+ // 跳过已经有扩展名的
30
+ if (importPath.endsWith('.js') || importPath.endsWith('.json') || importPath.endsWith('.ts')) {
31
+ continue;
32
+ }
33
+
34
+ // 跳过 node_modules 中的包(不包含 . 或 .. 开头的)
35
+ if (!importPath.startsWith('./') && !importPath.startsWith('../')) {
36
+ continue;
37
+ }
38
+
39
+ // 添加 .js 扩展名
40
+ const newImportPath = importPath + '.js';
41
+ content = content.replace(
42
+ new RegExp(`['"]${importPath}['"]`, 'g'),
43
+ `'${newImportPath}'`
44
+ );
45
+ modified = true;
46
+
47
+ console.log(` Fixed: ${importPath} -> ${newImportPath}`);
48
+ }
49
+
50
+ if (modified) {
51
+ fs.writeFileSync(filePath, content, 'utf8');
52
+ return true;
53
+ }
54
+
55
+ return false;
56
+ }
57
+
58
+ // 递归查找并处理所有 .js 文件
59
+ function processDirectory(dir) {
60
+ let fixedCount = 0;
61
+
62
+ const items = fs.readdirSync(dir);
63
+
64
+ items.forEach(item => {
65
+ const itemPath = path.join(dir, item);
66
+ const stat = fs.statSync(itemPath);
67
+
68
+ if (stat.isDirectory()) {
69
+ fixedCount += processDirectory(itemPath);
70
+ } else if (item.endsWith('.js')) {
71
+ if (processFile(itemPath)) {
72
+ fixedCount++;
73
+ }
74
+ }
75
+ });
76
+
77
+ return fixedCount;
78
+ }
79
+
80
+ // 主函数
81
+ function main() {
82
+ console.log('🔧 修复 dist 目录中的 ES Module 导入...');
83
+ console.log(`目录: ${distDir}\n`);
84
+
85
+ if (!fs.existsSync(distDir)) {
86
+ console.error('❌ dist 目录不存在!请先运行 npm run build');
87
+ process.exit(1);
88
+ }
89
+
90
+ const fixedCount = processDirectory(distDir);
91
+
92
+ console.log(`\n✅ 完成!修复了 ${fixedCount} 个文件`);
93
+ }
94
+
95
+ main();
@@ -0,0 +1,90 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * 自动修复 ES Module 导入的扩展名
4
+ * 在 TypeScript 文件中,所有本地导入必须包含 .js 扩展名
5
+ */
6
+
7
+ import fs from 'fs';
8
+ import path from 'path';
9
+ import { fileURLToPath } from 'url';
10
+
11
+ const __filename = fileURLToPath(import.meta.url);
12
+ const __dirname = path.dirname(__filename);
13
+
14
+ // 递归查找所有 .ts 文件(排除 .d.ts)
15
+ function findTsFiles(dir) {
16
+ let results = [];
17
+ const list = fs.readdirSync(dir);
18
+
19
+ list.forEach(file => {
20
+ const filePath = path.join(dir, file);
21
+ const stat = fs.statSync(filePath);
22
+
23
+ if (stat && stat.isDirectory()) {
24
+ // 跳过 node_modules 和 dist
25
+ if (file !== 'node_modules' && file !== 'dist' && file !== '.git') {
26
+ results = results.concat(findTsFiles(filePath));
27
+ }
28
+ } else if (file.endsWith('.ts') && !file.endsWith('.d.ts')) {
29
+ results.push(filePath);
30
+ }
31
+ });
32
+
33
+ return results;
34
+ }
35
+
36
+ // 修复文件中的导入语句
37
+ function fixImports(filePath) {
38
+ let content = fs.readFileSync(filePath, 'utf8');
39
+ let modified = false;
40
+
41
+ // 匹配 import 语句中的本地文件路径(以 ./ 或 ../ 开头,且不以 .js 或 .ts 结尾)
42
+ const importRegex = /from\s+['"](\.\.?\/[^'"]+)['"]/g;
43
+
44
+ content = content.replace(importRegex, (match, importPath) => {
45
+ // 如果已经以 .js 或 .ts 结尾,跳过
46
+ if (importPath.endsWith('.js') || importPath.endsWith('.ts')) {
47
+ return match;
48
+ }
49
+
50
+ // 添加 .js 扩展名
51
+ modified = true;
52
+ return `from '${importPath}.js'`;
53
+ });
54
+
55
+ if (modified) {
56
+ fs.writeFileSync(filePath, content, 'utf8');
57
+ console.log(`✓ Fixed: ${path.relative(process.cwd(), filePath)}`);
58
+ return true;
59
+ }
60
+
61
+ return false;
62
+ }
63
+
64
+ // 主函数
65
+ function main() {
66
+ const srcDir = path.join(__dirname, '..', 'src');
67
+
68
+ console.log('🔍 查找 TypeScript 文件...');
69
+ const tsFiles = findTsFiles(srcDir);
70
+ console.log(`找到 ${tsFiles.length} 个文件\n`);
71
+
72
+ console.log('🔧 修复导入语句...');
73
+ let fixedCount = 0;
74
+
75
+ tsFiles.forEach(file => {
76
+ if (fixImports(file)) {
77
+ fixedCount++;
78
+ }
79
+ });
80
+
81
+ console.log(`\n✅ 完成!修复了 ${fixedCount} 个文件`);
82
+
83
+ if (fixedCount > 0) {
84
+ console.log('\n⚠️ 请运行以下命令验证修改:');
85
+ console.log(' npm run build');
86
+ console.log(' node dist/index.js');
87
+ }
88
+ }
89
+
90
+ main();
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+
5
+ const files = [
6
+ path.join(process.cwd(), 'dist/index.js'),
7
+ // 如果有其他 CLI 入口文件,也添加到这里
8
+ ];
9
+
10
+ files.forEach(filePath => {
11
+ if (!fs.existsSync(filePath)) {
12
+ console.warn(`Warning: ${filePath} does not exist, skipping...`);
13
+ return;
14
+ }
15
+
16
+ let content = fs.readFileSync(filePath, 'utf8');
17
+
18
+ // 检查是否已有 shebang
19
+ if (!content.startsWith('#!/usr/bin/env node')) {
20
+ // 添加 shebang
21
+ content = '#!/usr/bin/env node\n' + content;
22
+ fs.writeFileSync(filePath, content, 'utf8');
23
+ console.log(`✓ Added shebang to ${path.relative(process.cwd(), filePath)}`);
24
+ } else {
25
+ console.log(`✓ Shebang already exists in ${path.relative(process.cwd(), filePath)}`);
26
+ }
27
+
28
+ // 设置可执行权限(Unix/Linux/macOS)
29
+ if (process.platform !== 'win32') {
30
+ try {
31
+ fs.chmodSync(filePath, '755');
32
+ console.log(`✓ Set executable permission for ${path.relative(process.cwd(), filePath)}`);
33
+ } catch (err) {
34
+ console.warn(`Warning: Could not set executable permission for ${filePath}`);
35
+ }
36
+ }
37
+ });
38
+
39
+ console.log('\n✓ Postbuild script completed!');