@adversity/coding-tool-x 2.2.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.
Files changed (125) hide show
  1. package/CHANGELOG.md +333 -0
  2. package/LICENSE +21 -0
  3. package/README.md +404 -0
  4. package/bin/ctx.js +8 -0
  5. package/dist/web/assets/index-D1AYlFLZ.js +3220 -0
  6. package/dist/web/assets/index-aL3cKxSK.css +41 -0
  7. package/dist/web/favicon.ico +0 -0
  8. package/dist/web/index.html +14 -0
  9. package/dist/web/logo.png +0 -0
  10. package/docs/CHANGELOG.md +582 -0
  11. package/docs/DIRECTORY_MIGRATION.md +112 -0
  12. package/docs/PROJECT_STRUCTURE.md +396 -0
  13. package/docs/bannel.png +0 -0
  14. package/docs/home.png +0 -0
  15. package/docs/logo.png +0 -0
  16. package/docs/multi-channel-load-balancing.md +249 -0
  17. package/package.json +73 -0
  18. package/src/commands/channels.js +504 -0
  19. package/src/commands/cli-type.js +99 -0
  20. package/src/commands/daemon.js +286 -0
  21. package/src/commands/doctor.js +332 -0
  22. package/src/commands/list.js +222 -0
  23. package/src/commands/logs.js +259 -0
  24. package/src/commands/port-config.js +115 -0
  25. package/src/commands/proxy-control.js +258 -0
  26. package/src/commands/proxy.js +152 -0
  27. package/src/commands/resume.js +137 -0
  28. package/src/commands/search.js +190 -0
  29. package/src/commands/stats.js +224 -0
  30. package/src/commands/switch.js +48 -0
  31. package/src/commands/toggle-proxy.js +222 -0
  32. package/src/commands/ui.js +92 -0
  33. package/src/commands/workspace.js +454 -0
  34. package/src/config/default.js +40 -0
  35. package/src/config/loader.js +75 -0
  36. package/src/config/paths.js +121 -0
  37. package/src/index.js +373 -0
  38. package/src/reset-config.js +92 -0
  39. package/src/server/api/agents.js +248 -0
  40. package/src/server/api/aliases.js +36 -0
  41. package/src/server/api/channels.js +258 -0
  42. package/src/server/api/claude-hooks.js +480 -0
  43. package/src/server/api/codex-channels.js +312 -0
  44. package/src/server/api/codex-projects.js +91 -0
  45. package/src/server/api/codex-proxy.js +182 -0
  46. package/src/server/api/codex-sessions.js +491 -0
  47. package/src/server/api/codex-statistics.js +57 -0
  48. package/src/server/api/commands.js +245 -0
  49. package/src/server/api/config-templates.js +182 -0
  50. package/src/server/api/config.js +147 -0
  51. package/src/server/api/convert.js +127 -0
  52. package/src/server/api/dashboard.js +125 -0
  53. package/src/server/api/env.js +144 -0
  54. package/src/server/api/favorites.js +77 -0
  55. package/src/server/api/gemini-channels.js +261 -0
  56. package/src/server/api/gemini-projects.js +91 -0
  57. package/src/server/api/gemini-proxy.js +160 -0
  58. package/src/server/api/gemini-sessions.js +397 -0
  59. package/src/server/api/gemini-statistics.js +57 -0
  60. package/src/server/api/health-check.js +118 -0
  61. package/src/server/api/mcp.js +336 -0
  62. package/src/server/api/pm2-autostart.js +269 -0
  63. package/src/server/api/projects.js +124 -0
  64. package/src/server/api/prompts.js +279 -0
  65. package/src/server/api/proxy.js +235 -0
  66. package/src/server/api/rules.js +271 -0
  67. package/src/server/api/sessions.js +595 -0
  68. package/src/server/api/settings.js +61 -0
  69. package/src/server/api/skills.js +305 -0
  70. package/src/server/api/statistics.js +91 -0
  71. package/src/server/api/terminal.js +202 -0
  72. package/src/server/api/ui-config.js +64 -0
  73. package/src/server/api/workspaces.js +407 -0
  74. package/src/server/codex-proxy-server.js +538 -0
  75. package/src/server/dev-server.js +26 -0
  76. package/src/server/gemini-proxy-server.js +518 -0
  77. package/src/server/index.js +305 -0
  78. package/src/server/proxy-server.js +469 -0
  79. package/src/server/services/agents-service.js +354 -0
  80. package/src/server/services/alias.js +71 -0
  81. package/src/server/services/channel-health.js +234 -0
  82. package/src/server/services/channel-scheduler.js +234 -0
  83. package/src/server/services/channels.js +347 -0
  84. package/src/server/services/codex-channels.js +625 -0
  85. package/src/server/services/codex-config.js +90 -0
  86. package/src/server/services/codex-parser.js +322 -0
  87. package/src/server/services/codex-sessions.js +665 -0
  88. package/src/server/services/codex-settings-manager.js +397 -0
  89. package/src/server/services/codex-speed-test-template.json +24 -0
  90. package/src/server/services/codex-statistics-service.js +255 -0
  91. package/src/server/services/commands-service.js +360 -0
  92. package/src/server/services/config-templates-service.js +732 -0
  93. package/src/server/services/env-checker.js +307 -0
  94. package/src/server/services/env-manager.js +300 -0
  95. package/src/server/services/favorites.js +163 -0
  96. package/src/server/services/gemini-channels.js +333 -0
  97. package/src/server/services/gemini-config.js +73 -0
  98. package/src/server/services/gemini-sessions.js +689 -0
  99. package/src/server/services/gemini-settings-manager.js +263 -0
  100. package/src/server/services/gemini-statistics-service.js +253 -0
  101. package/src/server/services/health-check.js +399 -0
  102. package/src/server/services/mcp-service.js +1188 -0
  103. package/src/server/services/prompts-service.js +492 -0
  104. package/src/server/services/proxy-runtime.js +79 -0
  105. package/src/server/services/pty-manager.js +435 -0
  106. package/src/server/services/rules-service.js +401 -0
  107. package/src/server/services/session-cache.js +127 -0
  108. package/src/server/services/session-converter.js +577 -0
  109. package/src/server/services/sessions.js +757 -0
  110. package/src/server/services/settings-manager.js +163 -0
  111. package/src/server/services/skill-service.js +965 -0
  112. package/src/server/services/speed-test.js +545 -0
  113. package/src/server/services/statistics-service.js +386 -0
  114. package/src/server/services/terminal-commands.js +155 -0
  115. package/src/server/services/terminal-config.js +140 -0
  116. package/src/server/services/terminal-detector.js +306 -0
  117. package/src/server/services/ui-config.js +130 -0
  118. package/src/server/services/workspace-service.js +662 -0
  119. package/src/server/utils/pricing.js +41 -0
  120. package/src/server/websocket-server.js +557 -0
  121. package/src/ui/menu.js +129 -0
  122. package/src/ui/prompts.js +100 -0
  123. package/src/utils/format.js +43 -0
  124. package/src/utils/port-helper.js +94 -0
  125. package/src/utils/session.js +239 -0
@@ -0,0 +1,305 @@
1
+ /**
2
+ * Skills API 路由
3
+ */
4
+
5
+ const express = require('express');
6
+ const { SkillService } = require('../services/skill-service');
7
+
8
+ const router = express.Router();
9
+ const skillService = new SkillService();
10
+
11
+ /**
12
+ * 获取技能列表
13
+ * GET /api/skills
14
+ * Query: refresh=1 强制刷新缓存
15
+ */
16
+ router.get('/', async (req, res) => {
17
+ try {
18
+ const forceRefresh = req.query.refresh === '1';
19
+ const skills = await skillService.listSkills(forceRefresh);
20
+ res.json({
21
+ success: true,
22
+ skills,
23
+ total: skills.length,
24
+ installed: skills.filter(s => s.installed).length
25
+ });
26
+ } catch (err) {
27
+ console.error('[Skills API] List skills error:', err);
28
+ res.status(500).json({
29
+ success: false,
30
+ message: err.message
31
+ });
32
+ }
33
+ });
34
+
35
+ /**
36
+ * 获取技能详情(完整内容)
37
+ * GET /api/skills/detail/:directory
38
+ */
39
+ router.get('/detail/*', async (req, res) => {
40
+ try {
41
+ const directory = req.params[0]; // 获取通配符匹配的路径
42
+ if (!directory) {
43
+ return res.status(400).json({
44
+ success: false,
45
+ message: 'Missing directory'
46
+ });
47
+ }
48
+
49
+ const result = await skillService.getSkillDetail(directory);
50
+ res.json({
51
+ success: true,
52
+ ...result
53
+ });
54
+ } catch (err) {
55
+ console.error('[Skills API] Get skill detail error:', err);
56
+ res.status(500).json({
57
+ success: false,
58
+ message: err.message
59
+ });
60
+ }
61
+ });
62
+
63
+ /**
64
+ * 获取已安装的技能
65
+ * GET /api/skills/installed
66
+ */
67
+ router.get('/installed', (req, res) => {
68
+ try {
69
+ const skills = skillService.getInstalledSkills();
70
+ res.json({
71
+ success: true,
72
+ skills
73
+ });
74
+ } catch (err) {
75
+ console.error('[Skills API] Get installed skills error:', err);
76
+ res.status(500).json({
77
+ success: false,
78
+ message: err.message
79
+ });
80
+ }
81
+ });
82
+
83
+ /**
84
+ * 安装技能
85
+ * POST /api/skills/install
86
+ * Body: { directory, repo: { owner, name, branch } }
87
+ */
88
+ router.post('/install', async (req, res) => {
89
+ try {
90
+ const { directory, repo } = req.body;
91
+
92
+ if (!directory) {
93
+ return res.status(400).json({
94
+ success: false,
95
+ message: 'Missing directory'
96
+ });
97
+ }
98
+
99
+ if (!repo || !repo.owner || !repo.name) {
100
+ return res.status(400).json({
101
+ success: false,
102
+ message: 'Missing repo info'
103
+ });
104
+ }
105
+
106
+ const result = await skillService.installSkill(directory, {
107
+ owner: repo.owner,
108
+ name: repo.name,
109
+ branch: repo.branch || 'main'
110
+ });
111
+
112
+ res.json({
113
+ success: true,
114
+ ...result
115
+ });
116
+ } catch (err) {
117
+ console.error('[Skills API] Install skill error:', err);
118
+ res.status(500).json({
119
+ success: false,
120
+ message: err.message
121
+ });
122
+ }
123
+ });
124
+
125
+ /**
126
+ * 创建自定义技能
127
+ * POST /api/skills/create
128
+ * Body: { name, directory, description, content }
129
+ */
130
+ router.post('/create', (req, res) => {
131
+ try {
132
+ const { name, directory, description, content } = req.body;
133
+
134
+ if (!directory) {
135
+ return res.status(400).json({
136
+ success: false,
137
+ message: '请输入目录名称'
138
+ });
139
+ }
140
+
141
+ // 校验目录名:只允许英文、数字、横杠、下划线
142
+ if (!/^[a-zA-Z0-9_-]+$/.test(directory)) {
143
+ return res.status(400).json({
144
+ success: false,
145
+ message: '目录名只能包含英文、数字、横杠和下划线'
146
+ });
147
+ }
148
+
149
+ if (!content) {
150
+ return res.status(400).json({
151
+ success: false,
152
+ message: '请输入技能内容'
153
+ });
154
+ }
155
+
156
+ const result = skillService.createCustomSkill({
157
+ name: name || directory,
158
+ directory,
159
+ description: description || '',
160
+ content
161
+ });
162
+
163
+ res.json({
164
+ success: true,
165
+ ...result
166
+ });
167
+ } catch (err) {
168
+ console.error('[Skills API] Create skill error:', err);
169
+ res.status(500).json({
170
+ success: false,
171
+ message: err.message
172
+ });
173
+ }
174
+ });
175
+
176
+ /**
177
+ * 卸载技能
178
+ * POST /api/skills/uninstall
179
+ * Body: { directory }
180
+ */
181
+ router.post('/uninstall', (req, res) => {
182
+ try {
183
+ const { directory } = req.body;
184
+
185
+ if (!directory) {
186
+ return res.status(400).json({
187
+ success: false,
188
+ message: 'Missing directory'
189
+ });
190
+ }
191
+
192
+ const result = skillService.uninstallSkill(directory);
193
+
194
+ res.json({
195
+ success: true,
196
+ ...result
197
+ });
198
+ } catch (err) {
199
+ console.error('[Skills API] Uninstall skill error:', err);
200
+ res.status(500).json({
201
+ success: false,
202
+ message: err.message
203
+ });
204
+ }
205
+ });
206
+
207
+ /**
208
+ * 获取仓库列表
209
+ * GET /api/skills/repos
210
+ */
211
+ router.get('/repos', (req, res) => {
212
+ try {
213
+ const repos = skillService.loadRepos();
214
+ res.json({
215
+ success: true,
216
+ repos
217
+ });
218
+ } catch (err) {
219
+ console.error('[Skills API] Get repos error:', err);
220
+ res.status(500).json({
221
+ success: false,
222
+ message: err.message
223
+ });
224
+ }
225
+ });
226
+
227
+ /**
228
+ * 添加仓库
229
+ * POST /api/skills/repos
230
+ * Body: { owner, name, branch, enabled }
231
+ */
232
+ router.post('/repos', (req, res) => {
233
+ try {
234
+ const { owner, name, branch = 'main', enabled = true } = req.body;
235
+
236
+ if (!owner || !name) {
237
+ return res.status(400).json({
238
+ success: false,
239
+ message: 'Missing owner or name'
240
+ });
241
+ }
242
+
243
+ const repos = skillService.addRepo({ owner, name, branch, enabled });
244
+
245
+ res.json({
246
+ success: true,
247
+ repos
248
+ });
249
+ } catch (err) {
250
+ console.error('[Skills API] Add repo error:', err);
251
+ res.status(500).json({
252
+ success: false,
253
+ message: err.message
254
+ });
255
+ }
256
+ });
257
+
258
+ /**
259
+ * 删除仓库
260
+ * DELETE /api/skills/repos/:owner/:name
261
+ */
262
+ router.delete('/repos/:owner/:name', (req, res) => {
263
+ try {
264
+ const { owner, name } = req.params;
265
+ const repos = skillService.removeRepo(owner, name);
266
+
267
+ res.json({
268
+ success: true,
269
+ repos
270
+ });
271
+ } catch (err) {
272
+ console.error('[Skills API] Remove repo error:', err);
273
+ res.status(500).json({
274
+ success: false,
275
+ message: err.message
276
+ });
277
+ }
278
+ });
279
+
280
+ /**
281
+ * 切换仓库启用状态
282
+ * PUT /api/skills/repos/:owner/:name/toggle
283
+ * Body: { enabled }
284
+ */
285
+ router.put('/repos/:owner/:name/toggle', (req, res) => {
286
+ try {
287
+ const { owner, name } = req.params;
288
+ const { enabled } = req.body;
289
+
290
+ const repos = skillService.toggleRepo(owner, name, enabled);
291
+
292
+ res.json({
293
+ success: true,
294
+ repos
295
+ });
296
+ } catch (err) {
297
+ console.error('[Skills API] Toggle repo error:', err);
298
+ res.status(500).json({
299
+ success: false,
300
+ message: err.message
301
+ });
302
+ }
303
+ });
304
+
305
+ module.exports = router;
@@ -0,0 +1,91 @@
1
+ const express = require('express');
2
+ const router = express.Router();
3
+ const { getStatistics, getDailyStatistics, getTodayStatistics } = require('../services/statistics-service');
4
+
5
+ /**
6
+ * 获取总体统计数据
7
+ * GET /api/statistics/summary
8
+ */
9
+ router.get('/summary', (req, res) => {
10
+ try {
11
+ const stats = getStatistics();
12
+ res.json(stats);
13
+ } catch (error) {
14
+ console.error('Failed to get statistics:', error);
15
+ res.status(500).json({ error: 'Failed to get statistics' });
16
+ }
17
+ });
18
+
19
+ /**
20
+ * 获取今日统计数据
21
+ * GET /api/statistics/today
22
+ */
23
+ router.get('/today', (req, res) => {
24
+ try {
25
+ const stats = getTodayStatistics();
26
+ res.json(stats);
27
+ } catch (error) {
28
+ console.error('Failed to get today statistics:', error);
29
+ res.status(500).json({ error: 'Failed to get today statistics' });
30
+ }
31
+ });
32
+
33
+ /**
34
+ * 获取指定日期的统计数据
35
+ * GET /api/statistics/daily/:date
36
+ *
37
+ * @param {string} date - 日期,格式:YYYY-MM-DD
38
+ */
39
+ router.get('/daily/:date', (req, res) => {
40
+ try {
41
+ const { date } = req.params;
42
+
43
+ // 验证日期格式
44
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(date)) {
45
+ return res.status(400).json({ error: 'Invalid date format. Expected YYYY-MM-DD' });
46
+ }
47
+
48
+ const stats = getDailyStatistics(date);
49
+ res.json(stats);
50
+ } catch (error) {
51
+ console.error('Failed to get daily statistics:', error);
52
+ res.status(500).json({ error: 'Failed to get daily statistics' });
53
+ }
54
+ });
55
+
56
+ /**
57
+ * 获取最近N天的统计数据
58
+ * GET /api/statistics/recent?days=7
59
+ *
60
+ * @query {number} days - 天数,默认7天
61
+ */
62
+ router.get('/recent', (req, res) => {
63
+ try {
64
+ const days = parseInt(req.query.days) || 7;
65
+
66
+ if (days < 1 || days > 90) {
67
+ return res.status(400).json({ error: 'Days must be between 1 and 90' });
68
+ }
69
+
70
+ const result = [];
71
+ const today = new Date();
72
+
73
+ for (let i = 0; i < days; i++) {
74
+ const date = new Date(today);
75
+ date.setDate(date.getDate() - i);
76
+ const dateStr = date.toISOString().split('T')[0];
77
+ const stats = getDailyStatistics(dateStr);
78
+ result.push({
79
+ date: dateStr,
80
+ ...stats
81
+ });
82
+ }
83
+
84
+ res.json(result);
85
+ } catch (error) {
86
+ console.error('Failed to get recent statistics:', error);
87
+ res.status(500).json({ error: 'Failed to get recent statistics' });
88
+ }
89
+ });
90
+
91
+ module.exports = router;
@@ -0,0 +1,202 @@
1
+ /**
2
+ * Terminal REST API - Web 终端接口
3
+ */
4
+
5
+ const express = require('express');
6
+ const router = express.Router();
7
+ const os = require('os');
8
+ const path = require('path');
9
+ const fs = require('fs');
10
+
11
+ const { ptyManager } = require('../services/pty-manager');
12
+ const {
13
+ loadTerminalCommands,
14
+ saveTerminalCommands,
15
+ getCommandForChannel,
16
+ getDefaultCommands
17
+ } = require('../services/terminal-commands');
18
+
19
+ /**
20
+ * GET /api/terminal/list - 获取所有活跃终端
21
+ */
22
+ router.get('/list', (req, res) => {
23
+ try {
24
+ const terminals = ptyManager.list();
25
+ res.json({ success: true, terminals });
26
+ } catch (err) {
27
+ res.status(500).json({ success: false, error: err.message });
28
+ }
29
+ });
30
+
31
+ /**
32
+ * GET /api/terminal/commands/config - 获取命令配置
33
+ * 注意:此路由必须在 /:id 之前定义,否则会被动态路由捕获
34
+ */
35
+ router.get('/commands/config', (req, res) => {
36
+ try {
37
+ const commands = loadTerminalCommands();
38
+ res.json({ success: true, commands });
39
+ } catch (err) {
40
+ res.status(500).json({ success: false, error: err.message });
41
+ }
42
+ });
43
+
44
+ /**
45
+ * PUT /api/terminal/commands/config - 保存命令配置
46
+ */
47
+ router.put('/commands/config', (req, res) => {
48
+ try {
49
+ const { commands } = req.body;
50
+ if (!commands) {
51
+ return res.status(400).json({ success: false, error: 'Missing commands' });
52
+ }
53
+
54
+ const success = saveTerminalCommands(commands);
55
+ if (!success) {
56
+ return res.status(500).json({ success: false, error: 'Failed to save' });
57
+ }
58
+
59
+ res.json({ success: true, commands: loadTerminalCommands() });
60
+ } catch (err) {
61
+ res.status(500).json({ success: false, error: err.message });
62
+ }
63
+ });
64
+
65
+ /**
66
+ * POST /api/terminal/commands/reset - 重置为默认配置
67
+ */
68
+ router.post('/commands/reset', (req, res) => {
69
+ try {
70
+ const defaults = getDefaultCommands();
71
+ saveTerminalCommands(defaults);
72
+ res.json({ success: true, commands: defaults });
73
+ } catch (err) {
74
+ res.status(500).json({ success: false, error: err.message });
75
+ }
76
+ });
77
+
78
+ /**
79
+ * GET /api/terminal/:id - 获取终端详情
80
+ * 注意:动态路由必须放在静态路由之后
81
+ */
82
+ router.get('/:id', (req, res) => {
83
+ try {
84
+ const terminal = ptyManager.get(req.params.id);
85
+ if (!terminal) {
86
+ return res.status(404).json({ success: false, error: 'Terminal not found' });
87
+ }
88
+ res.json({ success: true, terminal });
89
+ } catch (err) {
90
+ res.status(500).json({ success: false, error: err.message });
91
+ }
92
+ });
93
+
94
+ /**
95
+ * POST /api/terminal/create - 创建新终端
96
+ */
97
+ router.post('/create', (req, res) => {
98
+ try {
99
+ const {
100
+ channel = 'claude',
101
+ sessionId = null,
102
+ projectName = null,
103
+ cwd = null
104
+ } = req.body;
105
+
106
+ // 确定工作目录
107
+ let workDir = cwd || os.homedir();
108
+
109
+ // 如果提供了项目名,尝试解析真实路径
110
+ if (projectName && !cwd) {
111
+ // 尝试从项目名解析路径
112
+ const projectsDir = path.join(os.homedir(), '.claude', 'projects');
113
+ const projectPath = path.join(projectsDir, projectName);
114
+
115
+ if (fs.existsSync(projectPath)) {
116
+ // 尝试读取会话文件获取 cwd
117
+ if (sessionId) {
118
+ const sessionFile = path.join(projectPath, sessionId + '.jsonl');
119
+ if (fs.existsSync(sessionFile)) {
120
+ try {
121
+ const content = fs.readFileSync(sessionFile, 'utf8');
122
+ const firstLine = content.split('\n')[0];
123
+ if (firstLine) {
124
+ const json = JSON.parse(firstLine);
125
+ if (json.cwd && fs.existsSync(json.cwd)) {
126
+ workDir = json.cwd;
127
+ }
128
+ }
129
+ } catch (e) {
130
+ console.warn('Failed to parse session cwd:', e.message);
131
+ }
132
+ }
133
+ }
134
+ }
135
+
136
+ // 尝试从项目名直接解析路径 (URL 编码格式)
137
+ if (workDir === os.homedir()) {
138
+ const decodedPath = decodeURIComponent(projectName).replace(/-/g, '/');
139
+ if (fs.existsSync(decodedPath)) {
140
+ workDir = decodedPath;
141
+ }
142
+ }
143
+ }
144
+
145
+ // 获取启动命令
146
+ const startCommand = getCommandForChannel(channel, sessionId, workDir);
147
+
148
+ // 创建终端
149
+ const terminal = ptyManager.create({
150
+ cwd: workDir,
151
+ channel,
152
+ sessionId,
153
+ projectName,
154
+ startCommand
155
+ });
156
+
157
+ res.json({
158
+ success: true,
159
+ terminal: {
160
+ id: terminal.id,
161
+ pid: terminal.pid,
162
+ metadata: terminal.metadata
163
+ }
164
+ });
165
+ } catch (err) {
166
+ console.error('Failed to create terminal:', err);
167
+ res.status(500).json({ success: false, error: err.message });
168
+ }
169
+ });
170
+
171
+ /**
172
+ * DELETE /api/terminal/:id - 销毁终端
173
+ */
174
+ router.delete('/:id', (req, res) => {
175
+ try {
176
+ const success = ptyManager.destroy(req.params.id);
177
+ if (!success) {
178
+ return res.status(404).json({ success: false, error: 'Terminal not found' });
179
+ }
180
+ res.json({ success: true });
181
+ } catch (err) {
182
+ res.status(500).json({ success: false, error: err.message });
183
+ }
184
+ });
185
+
186
+ /**
187
+ * POST /api/terminal/:id/resize - 调整终端大小
188
+ */
189
+ router.post('/:id/resize', (req, res) => {
190
+ try {
191
+ const { cols, rows } = req.body;
192
+ const success = ptyManager.resize(req.params.id, cols, rows);
193
+ if (!success) {
194
+ return res.status(404).json({ success: false, error: 'Terminal not found' });
195
+ }
196
+ res.json({ success: true });
197
+ } catch (err) {
198
+ res.status(500).json({ success: false, error: err.message });
199
+ }
200
+ });
201
+
202
+ module.exports = router;
@@ -0,0 +1,64 @@
1
+ const express = require('express');
2
+ const router = express.Router();
3
+ const {
4
+ loadUIConfig,
5
+ saveUIConfig,
6
+ updateUIConfig,
7
+ updateNestedUIConfig
8
+ } = require('../services/ui-config');
9
+
10
+ // Get all UI config
11
+ router.get('/', (req, res) => {
12
+ try {
13
+ const config = loadUIConfig();
14
+ res.json({ success: true, config });
15
+ } catch (error) {
16
+ console.error('Error getting UI config:', error);
17
+ res.status(500).json({ error: error.message });
18
+ }
19
+ });
20
+
21
+ // Update entire UI config
22
+ router.post('/', (req, res) => {
23
+ try {
24
+ const { config } = req.body;
25
+ if (!config) {
26
+ return res.status(400).json({ error: 'Missing config' });
27
+ }
28
+ saveUIConfig(config);
29
+ res.json({ success: true, config });
30
+ } catch (error) {
31
+ console.error('Error saving UI config:', error);
32
+ res.status(500).json({ error: error.message });
33
+ }
34
+ });
35
+
36
+ // Update specific config key
37
+ router.put('/:key', (req, res) => {
38
+ try {
39
+ const { key } = req.params;
40
+ const { value } = req.body;
41
+
42
+ const config = updateUIConfig(key, value);
43
+ res.json({ success: true, config });
44
+ } catch (error) {
45
+ console.error('Error updating UI config:', error);
46
+ res.status(500).json({ error: error.message });
47
+ }
48
+ });
49
+
50
+ // Update nested config
51
+ router.put('/:parentKey/:childKey', (req, res) => {
52
+ try {
53
+ const { parentKey, childKey } = req.params;
54
+ const { value } = req.body;
55
+
56
+ const config = updateNestedUIConfig(parentKey, childKey, value);
57
+ res.json({ success: true, config });
58
+ } catch (error) {
59
+ console.error('Error updating nested UI config:', error);
60
+ res.status(500).json({ error: error.message });
61
+ }
62
+ });
63
+
64
+ module.exports = router;