@pixelbyte-software/pixcode 1.30.2 → 1.31.1

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 (209) hide show
  1. package/LICENSE +718 -718
  2. package/README.de.md +248 -248
  3. package/README.ja.md +240 -240
  4. package/README.ko.md +240 -240
  5. package/README.md +295 -285
  6. package/README.ru.md +248 -248
  7. package/README.tr.md +250 -250
  8. package/README.zh-CN.md +240 -240
  9. package/dist/api-docs.html +879 -879
  10. package/dist/assets/index-BtOeB3cE.js +837 -0
  11. package/dist/assets/index-CDpePeIN.css +32 -0
  12. package/dist/assets/vendor-codemirror-CzYAOTxS.js +41 -0
  13. package/dist/clear-cache.html +85 -85
  14. package/dist/convert-icons.md +52 -52
  15. package/dist/favicon.png +0 -0
  16. package/dist/favicon.svg +7 -8
  17. package/dist/generate-icons.js +48 -48
  18. package/dist/icons/codex-white.svg +3 -3
  19. package/dist/icons/codex.svg +3 -3
  20. package/dist/icons/cursor-white.svg +11 -11
  21. package/dist/icons/icon-128x128.png +0 -0
  22. package/dist/icons/icon-128x128.svg +9 -12
  23. package/dist/icons/icon-144x144.png +0 -0
  24. package/dist/icons/icon-144x144.svg +9 -12
  25. package/dist/icons/icon-152x152.png +0 -0
  26. package/dist/icons/icon-152x152.svg +9 -12
  27. package/dist/icons/icon-192x192.png +0 -0
  28. package/dist/icons/icon-192x192.svg +9 -12
  29. package/dist/icons/icon-384x384.png +0 -0
  30. package/dist/icons/icon-384x384.svg +9 -12
  31. package/dist/icons/icon-512x512.png +0 -0
  32. package/dist/icons/icon-512x512.svg +9 -12
  33. package/dist/icons/icon-72x72.png +0 -0
  34. package/dist/icons/icon-72x72.svg +9 -12
  35. package/dist/icons/icon-96x96.png +0 -0
  36. package/dist/icons/icon-96x96.svg +9 -12
  37. package/dist/icons/icon-template.svg +9 -12
  38. package/dist/icons/qwen-ai-icon.png +0 -0
  39. package/dist/index.html +60 -50
  40. package/dist/logo.png +0 -0
  41. package/dist/logo.svg +11 -16
  42. package/dist/manifest.json +60 -60
  43. package/dist/sw.js +124 -124
  44. package/dist-server/server/claude-sdk.js +28 -5
  45. package/dist-server/server/claude-sdk.js.map +1 -1
  46. package/dist-server/server/cli.js +100 -97
  47. package/dist-server/server/cli.js.map +1 -1
  48. package/dist-server/server/daemon/manager.js +33 -33
  49. package/dist-server/server/daemon-manager.js +62 -62
  50. package/dist-server/server/database/db.js +114 -22
  51. package/dist-server/server/database/db.js.map +1 -1
  52. package/dist-server/server/database/schema.js +122 -89
  53. package/dist-server/server/database/schema.js.map +1 -1
  54. package/dist-server/server/gemini-cli.js +6 -1
  55. package/dist-server/server/gemini-cli.js.map +1 -1
  56. package/dist-server/server/index.js +346 -61
  57. package/dist-server/server/index.js.map +1 -1
  58. package/dist-server/server/modules/providers/list/claude/claude-auth.provider.js +29 -2
  59. package/dist-server/server/modules/providers/list/claude/claude-auth.provider.js.map +1 -1
  60. package/dist-server/server/modules/providers/list/codex/codex-auth.provider.js +22 -2
  61. package/dist-server/server/modules/providers/list/codex/codex-auth.provider.js.map +1 -1
  62. package/dist-server/server/modules/providers/list/cursor/cursor-auth.provider.js +2 -2
  63. package/dist-server/server/modules/providers/list/cursor/cursor-auth.provider.js.map +1 -1
  64. package/dist-server/server/modules/providers/list/gemini/gemini-auth.provider.js +14 -2
  65. package/dist-server/server/modules/providers/list/gemini/gemini-auth.provider.js.map +1 -1
  66. package/dist-server/server/modules/providers/list/qwen/qwen-auth.provider.js +132 -0
  67. package/dist-server/server/modules/providers/list/qwen/qwen-auth.provider.js.map +1 -0
  68. package/dist-server/server/modules/providers/list/qwen/qwen-mcp.provider.js +87 -0
  69. package/dist-server/server/modules/providers/list/qwen/qwen-mcp.provider.js.map +1 -0
  70. package/dist-server/server/modules/providers/list/qwen/qwen-sessions.provider.js +201 -0
  71. package/dist-server/server/modules/providers/list/qwen/qwen-sessions.provider.js.map +1 -0
  72. package/dist-server/server/modules/providers/list/qwen/qwen.provider.js +19 -0
  73. package/dist-server/server/modules/providers/list/qwen/qwen.provider.js.map +1 -0
  74. package/dist-server/server/modules/providers/provider.registry.js +2 -0
  75. package/dist-server/server/modules/providers/provider.registry.js.map +1 -1
  76. package/dist-server/server/modules/providers/provider.routes.js +478 -1
  77. package/dist-server/server/modules/providers/provider.routes.js.map +1 -1
  78. package/dist-server/server/modules/providers/shared/provider-configs.js +105 -0
  79. package/dist-server/server/modules/providers/shared/provider-configs.js.map +1 -0
  80. package/dist-server/server/projects.js +197 -6
  81. package/dist-server/server/projects.js.map +1 -1
  82. package/dist-server/server/qwen-code-cli.js +350 -0
  83. package/dist-server/server/qwen-code-cli.js.map +1 -0
  84. package/dist-server/server/qwen-response-handler.js +70 -0
  85. package/dist-server/server/qwen-response-handler.js.map +1 -0
  86. package/dist-server/server/routes/commands.js +25 -25
  87. package/dist-server/server/routes/git.js +17 -17
  88. package/dist-server/server/routes/network.js +116 -0
  89. package/dist-server/server/routes/network.js.map +1 -0
  90. package/dist-server/server/routes/projects.js +166 -1
  91. package/dist-server/server/routes/projects.js.map +1 -1
  92. package/dist-server/server/routes/qwen.js +23 -0
  93. package/dist-server/server/routes/qwen.js.map +1 -0
  94. package/dist-server/server/routes/taskmaster.js +419 -419
  95. package/dist-server/server/routes/telegram.js +119 -0
  96. package/dist-server/server/routes/telegram.js.map +1 -0
  97. package/dist-server/server/services/external-access.js +228 -0
  98. package/dist-server/server/services/external-access.js.map +1 -0
  99. package/dist-server/server/services/install-jobs.js +552 -0
  100. package/dist-server/server/services/install-jobs.js.map +1 -0
  101. package/dist-server/server/services/notification-orchestrator.js +19 -5
  102. package/dist-server/server/services/notification-orchestrator.js.map +1 -1
  103. package/dist-server/server/services/provider-credentials.js +154 -0
  104. package/dist-server/server/services/provider-credentials.js.map +1 -0
  105. package/dist-server/server/services/provider-models.js +218 -0
  106. package/dist-server/server/services/provider-models.js.map +1 -0
  107. package/dist-server/server/services/telegram/bot.js +259 -0
  108. package/dist-server/server/services/telegram/bot.js.map +1 -0
  109. package/dist-server/server/services/telegram/translations.js +160 -0
  110. package/dist-server/server/services/telegram/translations.js.map +1 -0
  111. package/dist-server/server/utils/port-access.js +196 -0
  112. package/dist-server/server/utils/port-access.js.map +1 -0
  113. package/dist-server/shared/modelConstants.js +18 -0
  114. package/dist-server/shared/modelConstants.js.map +1 -1
  115. package/package.json +177 -168
  116. package/scripts/fix-node-pty.js +67 -67
  117. package/server/claude-sdk.js +857 -834
  118. package/server/cli.js +940 -937
  119. package/server/constants/config.js +4 -4
  120. package/server/cursor-cli.js +342 -342
  121. package/server/daemon/manager.js +564 -564
  122. package/server/daemon-manager.js +920 -920
  123. package/server/database/db.js +696 -593
  124. package/server/database/schema.js +138 -102
  125. package/server/gemini-cli.js +475 -469
  126. package/server/gemini-response-handler.js +79 -79
  127. package/server/index.js +2854 -2556
  128. package/server/load-env.js +34 -34
  129. package/server/middleware/auth.js +132 -132
  130. package/server/modules/providers/list/claude/claude-auth.provider.ts +145 -123
  131. package/server/modules/providers/list/claude/claude-mcp.provider.ts +135 -135
  132. package/server/modules/providers/list/claude/claude-sessions.provider.ts +306 -306
  133. package/server/modules/providers/list/claude/claude.provider.ts +15 -15
  134. package/server/modules/providers/list/codex/codex-auth.provider.ts +115 -100
  135. package/server/modules/providers/list/codex/codex-mcp.provider.ts +135 -135
  136. package/server/modules/providers/list/codex/codex-sessions.provider.ts +319 -319
  137. package/server/modules/providers/list/codex/codex.provider.ts +15 -15
  138. package/server/modules/providers/list/cursor/cursor-auth.provider.ts +143 -143
  139. package/server/modules/providers/list/cursor/cursor-mcp.provider.ts +108 -108
  140. package/server/modules/providers/list/cursor/cursor-sessions.provider.ts +421 -421
  141. package/server/modules/providers/list/cursor/cursor.provider.ts +15 -15
  142. package/server/modules/providers/list/gemini/gemini-auth.provider.ts +163 -151
  143. package/server/modules/providers/list/gemini/gemini-mcp.provider.ts +110 -110
  144. package/server/modules/providers/list/gemini/gemini-sessions.provider.ts +227 -227
  145. package/server/modules/providers/list/gemini/gemini.provider.ts +15 -15
  146. package/server/modules/providers/list/qwen/qwen-auth.provider.ts +145 -0
  147. package/server/modules/providers/list/qwen/qwen-mcp.provider.ts +114 -0
  148. package/server/modules/providers/list/qwen/qwen-sessions.provider.ts +218 -0
  149. package/server/modules/providers/list/qwen/qwen.provider.ts +21 -0
  150. package/server/modules/providers/provider.registry.ts +38 -36
  151. package/server/modules/providers/provider.routes.ts +781 -217
  152. package/server/modules/providers/services/mcp.service.ts +94 -94
  153. package/server/modules/providers/services/provider-auth.service.ts +26 -26
  154. package/server/modules/providers/services/sessions.service.ts +45 -45
  155. package/server/modules/providers/shared/base/abstract.provider.ts +20 -20
  156. package/server/modules/providers/shared/mcp/mcp.provider.ts +151 -151
  157. package/server/modules/providers/shared/provider-configs.ts +118 -0
  158. package/server/modules/providers/tests/mcp.test.ts +293 -293
  159. package/server/openai-codex.js +426 -426
  160. package/server/projects.js +2993 -2792
  161. package/server/qwen-code-cli.js +392 -0
  162. package/server/qwen-response-handler.js +73 -0
  163. package/server/routes/agent.js +1245 -1245
  164. package/server/routes/auth.js +134 -134
  165. package/server/routes/codex.js +19 -19
  166. package/server/routes/commands.js +554 -554
  167. package/server/routes/cursor.js +52 -52
  168. package/server/routes/gemini.js +24 -24
  169. package/server/routes/git.js +1488 -1488
  170. package/server/routes/mcp-utils.js +31 -31
  171. package/server/routes/messages.js +61 -61
  172. package/server/routes/network.js +128 -0
  173. package/server/routes/plugins.js +307 -307
  174. package/server/routes/projects.js +795 -627
  175. package/server/routes/qwen.js +27 -0
  176. package/server/routes/settings.js +286 -286
  177. package/server/routes/taskmaster.js +1471 -1471
  178. package/server/routes/telegram.js +125 -0
  179. package/server/routes/user.js +123 -123
  180. package/server/services/external-access.js +240 -0
  181. package/server/services/install-jobs.js +569 -0
  182. package/server/services/notification-orchestrator.js +242 -227
  183. package/server/services/provider-credentials.js +151 -0
  184. package/server/services/provider-models.js +225 -0
  185. package/server/services/telegram/bot.js +280 -0
  186. package/server/services/telegram/translations.js +170 -0
  187. package/server/services/vapid-keys.js +35 -35
  188. package/server/sessionManager.js +225 -225
  189. package/server/shared/interfaces.ts +54 -54
  190. package/server/shared/types.ts +172 -172
  191. package/server/shared/utils.ts +193 -193
  192. package/server/tsconfig.json +36 -36
  193. package/server/utils/colors.js +21 -21
  194. package/server/utils/commandParser.js +303 -303
  195. package/server/utils/frontmatter.js +18 -18
  196. package/server/utils/gitConfig.js +34 -34
  197. package/server/utils/mcp-detector.js +147 -147
  198. package/server/utils/plugin-loader.js +457 -457
  199. package/server/utils/plugin-process-manager.js +184 -184
  200. package/server/utils/port-access.js +209 -0
  201. package/server/utils/runtime-paths.js +37 -37
  202. package/server/utils/taskmaster-websocket.js +128 -128
  203. package/server/utils/url-detection.js +71 -71
  204. package/server/vite-daemon.js +78 -78
  205. package/shared/modelConstants.js +117 -97
  206. package/shared/networkHosts.js +22 -22
  207. package/dist/assets/index-C2c9QNwK.css +0 -32
  208. package/dist/assets/index-DyXDZED-.js +0 -1277
  209. package/dist/assets/vendor-codemirror-NA4v81it.js +0 -41
@@ -1,31 +1,31 @@
1
- /**
2
- * MCP UTILITIES API ROUTES
3
- * ========================
4
- *
5
- * API endpoints for MCP server detection and configuration utilities.
6
- * These endpoints expose centralized MCP detection functionality.
7
- */
8
-
9
- import express from 'express';
10
- import { detectTaskMasterMCPServer } from '../utils/mcp-detector.js';
11
-
12
- const router = express.Router();
13
-
14
- /**
15
- * GET /api/mcp-utils/taskmaster-server
16
- * Check if TaskMaster MCP server is configured
17
- */
18
- router.get('/taskmaster-server', async (req, res) => {
19
- try {
20
- const result = await detectTaskMasterMCPServer();
21
- res.json(result);
22
- } catch (error) {
23
- console.error('TaskMaster MCP detection error:', error);
24
- res.status(500).json({
25
- error: 'Failed to detect TaskMaster MCP server',
26
- message: error.message
27
- });
28
- }
29
- });
30
-
31
- export default router;
1
+ /**
2
+ * MCP UTILITIES API ROUTES
3
+ * ========================
4
+ *
5
+ * API endpoints for MCP server detection and configuration utilities.
6
+ * These endpoints expose centralized MCP detection functionality.
7
+ */
8
+
9
+ import express from 'express';
10
+ import { detectTaskMasterMCPServer } from '../utils/mcp-detector.js';
11
+
12
+ const router = express.Router();
13
+
14
+ /**
15
+ * GET /api/mcp-utils/taskmaster-server
16
+ * Check if TaskMaster MCP server is configured
17
+ */
18
+ router.get('/taskmaster-server', async (req, res) => {
19
+ try {
20
+ const result = await detectTaskMasterMCPServer();
21
+ res.json(result);
22
+ } catch (error) {
23
+ console.error('TaskMaster MCP detection error:', error);
24
+ res.status(500).json({
25
+ error: 'Failed to detect TaskMaster MCP server',
26
+ message: error.message
27
+ });
28
+ }
29
+ });
30
+
31
+ export default router;
@@ -1,61 +1,61 @@
1
- /**
2
- * Unified messages endpoint.
3
- *
4
- * GET /api/sessions/:sessionId/messages?provider=claude&projectName=foo&limit=50&offset=0
5
- *
6
- * Replaces the four provider-specific session message endpoints with a single route
7
- * that delegates to the appropriate adapter via the provider registry.
8
- *
9
- * @module routes/messages
10
- */
11
-
12
- import express from 'express';
13
- import { sessionsService } from '../modules/providers/services/sessions.service.js';
14
-
15
- const router = express.Router();
16
-
17
- /**
18
- * GET /api/sessions/:sessionId/messages
19
- *
20
- * Auth: authenticateToken applied at mount level in index.js
21
- *
22
- * Query params:
23
- * provider - 'claude' | 'cursor' | 'codex' | 'gemini' (default: 'claude')
24
- * projectName - required for claude provider
25
- * projectPath - required for cursor provider (absolute path used for cwdId hash)
26
- * limit - page size (omit or null for all)
27
- * offset - pagination offset (default: 0)
28
- */
29
- router.get('/:sessionId/messages', async (req, res) => {
30
- try {
31
- const { sessionId } = req.params;
32
- const provider = String(req.query.provider || 'claude').trim().toLowerCase();
33
- const projectName = req.query.projectName || '';
34
- const projectPath = req.query.projectPath || '';
35
- const limitParam = req.query.limit;
36
- const limit = limitParam !== undefined && limitParam !== null && limitParam !== ''
37
- ? parseInt(limitParam, 10)
38
- : null;
39
- const offset = parseInt(req.query.offset || '0', 10);
40
-
41
- const availableProviders = sessionsService.listProviderIds();
42
- if (!availableProviders.includes(provider)) {
43
- const available = availableProviders.join(', ');
44
- return res.status(400).json({ error: `Unknown provider: ${provider}. Available: ${available}` });
45
- }
46
-
47
- const result = await sessionsService.fetchHistory(provider, sessionId, {
48
- projectName,
49
- projectPath,
50
- limit,
51
- offset,
52
- });
53
-
54
- return res.json(result);
55
- } catch (error) {
56
- console.error('Error fetching unified messages:', error);
57
- return res.status(500).json({ error: 'Failed to fetch messages' });
58
- }
59
- });
60
-
61
- export default router;
1
+ /**
2
+ * Unified messages endpoint.
3
+ *
4
+ * GET /api/sessions/:sessionId/messages?provider=claude&projectName=foo&limit=50&offset=0
5
+ *
6
+ * Replaces the four provider-specific session message endpoints with a single route
7
+ * that delegates to the appropriate adapter via the provider registry.
8
+ *
9
+ * @module routes/messages
10
+ */
11
+
12
+ import express from 'express';
13
+ import { sessionsService } from '../modules/providers/services/sessions.service.js';
14
+
15
+ const router = express.Router();
16
+
17
+ /**
18
+ * GET /api/sessions/:sessionId/messages
19
+ *
20
+ * Auth: authenticateToken applied at mount level in index.js
21
+ *
22
+ * Query params:
23
+ * provider - 'claude' | 'cursor' | 'codex' | 'gemini' (default: 'claude')
24
+ * projectName - required for claude provider
25
+ * projectPath - required for cursor provider (absolute path used for cwdId hash)
26
+ * limit - page size (omit or null for all)
27
+ * offset - pagination offset (default: 0)
28
+ */
29
+ router.get('/:sessionId/messages', async (req, res) => {
30
+ try {
31
+ const { sessionId } = req.params;
32
+ const provider = String(req.query.provider || 'claude').trim().toLowerCase();
33
+ const projectName = req.query.projectName || '';
34
+ const projectPath = req.query.projectPath || '';
35
+ const limitParam = req.query.limit;
36
+ const limit = limitParam !== undefined && limitParam !== null && limitParam !== ''
37
+ ? parseInt(limitParam, 10)
38
+ : null;
39
+ const offset = parseInt(req.query.offset || '0', 10);
40
+
41
+ const availableProviders = sessionsService.listProviderIds();
42
+ if (!availableProviders.includes(provider)) {
43
+ const available = availableProviders.join(', ');
44
+ return res.status(400).json({ error: `Unknown provider: ${provider}. Available: ${available}` });
45
+ }
46
+
47
+ const result = await sessionsService.fetchHistory(provider, sessionId, {
48
+ projectName,
49
+ projectPath,
50
+ limit,
51
+ offset,
52
+ });
53
+
54
+ return res.json(result);
55
+ } catch (error) {
56
+ console.error('Error fetching unified messages:', error);
57
+ return res.status(500).json({ error: 'Failed to fetch messages' });
58
+ }
59
+ });
60
+
61
+ export default router;
@@ -0,0 +1,128 @@
1
+ import express from 'express';
2
+ import os from 'os';
3
+
4
+ import {
5
+ disableUpnp,
6
+ enableUpnp,
7
+ getTunnelState,
8
+ getUpnpState,
9
+ startTunnel,
10
+ stopTunnel,
11
+ } from '../services/external-access.js';
12
+
13
+ const router = express.Router();
14
+
15
+ // Accept any env key the rest of the server honors. Keep this aligned with server/cli.js.
16
+ const resolveServerPort = () => {
17
+ const raw = process.env.SERVER_PORT || process.env.PORT || '3001';
18
+ const port = Number(raw);
19
+ return Number.isFinite(port) && port > 0 ? port : 3001;
20
+ };
21
+
22
+ // Filter to usable LAN addresses. We skip:
23
+ // - internal (loopback) addresses, since QR-ing "127.0.0.1" is useless for a phone
24
+ // - link-local IPv6, since pasting "fe80::…%iface" into a phone browser won't resolve
25
+ // - IPv6 addresses in general for the first pass (most users want the v4 QR)
26
+ const listLanEndpoints = () => {
27
+ const interfaces = os.networkInterfaces();
28
+ const endpoints = [];
29
+
30
+ for (const [ifaceName, addrs] of Object.entries(interfaces)) {
31
+ if (!addrs) continue;
32
+ for (const addr of addrs) {
33
+ if (addr.internal) continue;
34
+ if (addr.family !== 'IPv4' && addr.family !== 4) continue;
35
+ endpoints.push({
36
+ host: addr.address,
37
+ label: ifaceName,
38
+ family: 'IPv4',
39
+ });
40
+ }
41
+ }
42
+
43
+ return endpoints;
44
+ };
45
+
46
+ router.get('/endpoints', (req, res) => {
47
+ try {
48
+ const port = resolveServerPort();
49
+ const endpoints = listLanEndpoints().map((entry) => ({
50
+ ...entry,
51
+ url: `http://${entry.host}:${port}`,
52
+ }));
53
+
54
+ res.json({
55
+ port,
56
+ hostname: os.hostname(),
57
+ endpoints,
58
+ });
59
+ } catch (error) {
60
+ console.error('Error listing network endpoints:', error);
61
+ res.status(500).json({ error: 'Failed to list network endpoints' });
62
+ }
63
+ });
64
+
65
+ // ============================================================================
66
+ // External access (UPnP + tunnel)
67
+ // ============================================================================
68
+
69
+ router.get('/external', (req, res) => {
70
+ try {
71
+ res.json({
72
+ upnp: getUpnpState(),
73
+ tunnel: getTunnelState(),
74
+ });
75
+ } catch (error) {
76
+ console.error('Error reading external-access state:', error);
77
+ res.status(500).json({ error: 'Failed to read external access state' });
78
+ }
79
+ });
80
+
81
+ router.post('/upnp', async (req, res) => {
82
+ const port = resolveServerPort();
83
+ try {
84
+ const state = await enableUpnp({ port });
85
+ res.json({ success: true, upnp: state });
86
+ } catch (error) {
87
+ console.error('UPnP enable failed:', error);
88
+ res.status(502).json({ error: error?.message || 'UPnP enable failed', upnp: getUpnpState() });
89
+ }
90
+ });
91
+
92
+ router.delete('/upnp', async (req, res) => {
93
+ const port = resolveServerPort();
94
+ try {
95
+ const state = await disableUpnp({ port });
96
+ res.json({ success: true, upnp: state });
97
+ } catch (error) {
98
+ console.error('UPnP disable failed:', error);
99
+ res.status(502).json({ error: error?.message || 'UPnP disable failed', upnp: getUpnpState() });
100
+ }
101
+ });
102
+
103
+ router.post('/tunnel', async (req, res) => {
104
+ const port = resolveServerPort();
105
+ try {
106
+ const state = await startTunnel({ port });
107
+ res.json({ success: true, tunnel: state });
108
+ } catch (error) {
109
+ console.error('Tunnel start failed:', error);
110
+ // 424 Failed Dependency is the best match for "we can't start because a
111
+ // required external binary is missing" — it tells the UI to show the
112
+ // "install cloudflared/ngrok" hint rather than a generic server error.
113
+ const status = error?.code === 'ENOENT_TUNNEL' ? 424 : 502;
114
+ res.status(status).json({ error: error?.message || 'Tunnel start failed', tunnel: getTunnelState() });
115
+ }
116
+ });
117
+
118
+ router.delete('/tunnel', async (req, res) => {
119
+ try {
120
+ const state = await stopTunnel();
121
+ res.json({ success: true, tunnel: state });
122
+ } catch (error) {
123
+ console.error('Tunnel stop failed:', error);
124
+ res.status(502).json({ error: error?.message || 'Tunnel stop failed', tunnel: getTunnelState() });
125
+ }
126
+ });
127
+
128
+ export default router;