@cloudcli-ai/cloudcli 1.34.0 → 1.35.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 (247) hide show
  1. package/README.de.md +9 -1
  2. package/README.ja.md +8 -0
  3. package/README.ko.md +9 -1
  4. package/README.md +23 -5
  5. package/README.ru.md +9 -1
  6. package/README.tr.md +8 -1
  7. package/README.zh-CN.md +9 -1
  8. package/README.zh-TW.md +9 -1
  9. package/dist/api-docs.html +36 -18
  10. package/dist/assets/index-BvX30NW5.css +32 -0
  11. package/dist/assets/index-D1IdCTT6.js +1418 -0
  12. package/dist/index.html +12 -4
  13. package/dist-server/server/browser-use-mcp.js +350 -0
  14. package/dist-server/server/browser-use-mcp.js.map +1 -0
  15. package/dist-server/server/claude-sdk.js +25 -5
  16. package/dist-server/server/claude-sdk.js.map +1 -1
  17. package/dist-server/server/cli.js +14 -6
  18. package/dist-server/server/cli.js.map +1 -1
  19. package/dist-server/server/cursor-cli.js +27 -11
  20. package/dist-server/server/cursor-cli.js.map +1 -1
  21. package/dist-server/server/gemini-cli.js +17 -2
  22. package/dist-server/server/gemini-cli.js.map +1 -1
  23. package/dist-server/server/index.js +136 -40
  24. package/dist-server/server/index.js.map +1 -1
  25. package/dist-server/server/load-env.js +1 -1
  26. package/dist-server/server/load-env.js.map +1 -1
  27. package/dist-server/server/modules/browser-use/browser-use-mcp.routes.js +107 -0
  28. package/dist-server/server/modules/browser-use/browser-use-mcp.routes.js.map +1 -0
  29. package/dist-server/server/modules/browser-use/browser-use.routes.js +93 -0
  30. package/dist-server/server/modules/browser-use/browser-use.routes.js.map +1 -0
  31. package/dist-server/server/modules/browser-use/browser-use.service.js +688 -0
  32. package/dist-server/server/modules/browser-use/browser-use.service.js.map +1 -0
  33. package/dist-server/server/modules/browser-use/tests/browser-use.service.test.js +8 -0
  34. package/dist-server/server/modules/browser-use/tests/browser-use.service.test.js.map +1 -0
  35. package/dist-server/server/modules/database/index.js +1 -0
  36. package/dist-server/server/modules/database/index.js.map +1 -1
  37. package/dist-server/server/modules/database/migrations.js +23 -1
  38. package/dist-server/server/modules/database/migrations.js.map +1 -1
  39. package/dist-server/server/modules/database/repositories/notification-channel-endpoints.js +100 -0
  40. package/dist-server/server/modules/database/repositories/notification-channel-endpoints.js.map +1 -0
  41. package/dist-server/server/modules/database/repositories/notification-preferences.js +8 -0
  42. package/dist-server/server/modules/database/repositories/notification-preferences.js.map +1 -1
  43. package/dist-server/server/modules/database/repositories/sessions.db.js +173 -17
  44. package/dist-server/server/modules/database/repositories/sessions.db.js.map +1 -1
  45. package/dist-server/server/modules/database/schema.js +26 -0
  46. package/dist-server/server/modules/database/schema.js.map +1 -1
  47. package/dist-server/server/modules/database/tests/projects.db.integration.test.js.map +1 -0
  48. package/dist-server/server/modules/database/tests/sessions-provider-mapping.test.js +78 -0
  49. package/dist-server/server/modules/database/tests/sessions-provider-mapping.test.js.map +1 -0
  50. package/dist-server/server/modules/database/{repositories → tests}/sessions.db.integration.test.js +10 -0
  51. package/dist-server/server/modules/database/tests/sessions.db.integration.test.js.map +1 -0
  52. package/dist-server/server/modules/notifications/index.js +4 -0
  53. package/dist-server/server/modules/notifications/index.js.map +1 -0
  54. package/dist-server/server/modules/notifications/notifications.routes.js +115 -0
  55. package/dist-server/server/modules/notifications/notifications.routes.js.map +1 -0
  56. package/dist-server/server/modules/notifications/services/desktop-notification-clients.service.js +97 -0
  57. package/dist-server/server/modules/notifications/services/desktop-notification-clients.service.js.map +1 -0
  58. package/dist-server/server/modules/notifications/services/notification-orchestrator.service.js +229 -0
  59. package/dist-server/server/modules/notifications/services/notification-orchestrator.service.js.map +1 -0
  60. package/dist-server/server/modules/notifications/websocket/desktop-notifications-websocket.service.js +79 -0
  61. package/dist-server/server/modules/notifications/websocket/desktop-notifications-websocket.service.js.map +1 -0
  62. package/dist-server/server/modules/projects/services/project-management.service.js +0 -4
  63. package/dist-server/server/modules/projects/services/project-management.service.js.map +1 -1
  64. package/dist-server/server/modules/projects/services/projects-with-sessions-fetch.service.js +10 -38
  65. package/dist-server/server/modules/projects/services/projects-with-sessions-fetch.service.js.map +1 -1
  66. package/dist-server/server/modules/providers/index.js +1 -0
  67. package/dist-server/server/modules/providers/index.js.map +1 -1
  68. package/dist-server/server/modules/providers/list/claude/claude-session-synchronizer.provider.js +24 -1
  69. package/dist-server/server/modules/providers/list/claude/claude-session-synchronizer.provider.js.map +1 -1
  70. package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js +9 -12
  71. package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js.map +1 -1
  72. package/dist-server/server/modules/providers/list/claude/claude-skills.provider.js +7 -0
  73. package/dist-server/server/modules/providers/list/claude/claude-skills.provider.js.map +1 -1
  74. package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js +7 -3
  75. package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js.map +1 -1
  76. package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js +3 -9
  77. package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js.map +1 -1
  78. package/dist-server/server/modules/providers/list/codex/codex-skills.provider.js +7 -0
  79. package/dist-server/server/modules/providers/list/codex/codex-skills.provider.js.map +1 -1
  80. package/dist-server/server/modules/providers/list/cursor/cursor-sessions.provider.js +13 -22
  81. package/dist-server/server/modules/providers/list/cursor/cursor-sessions.provider.js.map +1 -1
  82. package/dist-server/server/modules/providers/list/cursor/cursor-skills.provider.js +7 -0
  83. package/dist-server/server/modules/providers/list/cursor/cursor-skills.provider.js.map +1 -1
  84. package/dist-server/server/modules/providers/list/gemini/gemini-models.provider.js +5 -8
  85. package/dist-server/server/modules/providers/list/gemini/gemini-models.provider.js.map +1 -1
  86. package/dist-server/server/modules/providers/list/gemini/gemini-sessions.provider.js +6 -6
  87. package/dist-server/server/modules/providers/list/gemini/gemini-sessions.provider.js.map +1 -1
  88. package/dist-server/server/modules/providers/list/gemini/gemini-skills.provider.js +7 -0
  89. package/dist-server/server/modules/providers/list/gemini/gemini-skills.provider.js.map +1 -1
  90. package/dist-server/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.js +17 -3
  91. package/dist-server/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.js.map +1 -1
  92. package/dist-server/server/modules/providers/list/opencode/opencode-sessions.provider.js +9 -10
  93. package/dist-server/server/modules/providers/list/opencode/opencode-sessions.provider.js.map +1 -1
  94. package/dist-server/server/modules/providers/provider.routes.js +123 -1
  95. package/dist-server/server/modules/providers/provider.routes.js.map +1 -1
  96. package/dist-server/server/modules/providers/services/mcp.service.js +23 -0
  97. package/dist-server/server/modules/providers/services/mcp.service.js.map +1 -1
  98. package/dist-server/server/modules/providers/services/provider-capabilities.service.js +65 -0
  99. package/dist-server/server/modules/providers/services/provider-capabilities.service.js.map +1 -0
  100. package/dist-server/server/modules/providers/services/provider-models.service.js +1 -1
  101. package/dist-server/server/modules/providers/services/provider-models.service.js.map +1 -1
  102. package/dist-server/server/modules/providers/services/sessions-watcher.service.js +62 -23
  103. package/dist-server/server/modules/providers/services/sessions-watcher.service.js.map +1 -1
  104. package/dist-server/server/modules/providers/services/sessions.service.js +62 -4
  105. package/dist-server/server/modules/providers/services/sessions.service.js.map +1 -1
  106. package/dist-server/server/modules/providers/services/skills.service.js +11 -0
  107. package/dist-server/server/modules/providers/services/skills.service.js.map +1 -1
  108. package/dist-server/server/modules/providers/shared/skills/skills.provider.js +163 -1
  109. package/dist-server/server/modules/providers/shared/skills/skills.provider.js.map +1 -1
  110. package/dist-server/server/modules/providers/tests/opencode-sessions.test.js +45 -0
  111. package/dist-server/server/modules/providers/tests/opencode-sessions.test.js.map +1 -1
  112. package/dist-server/server/modules/providers/tests/skills.test.js +160 -0
  113. package/dist-server/server/modules/providers/tests/skills.test.js.map +1 -1
  114. package/dist-server/server/modules/websocket/index.js +1 -0
  115. package/dist-server/server/modules/websocket/index.js.map +1 -1
  116. package/dist-server/server/modules/websocket/services/chat-run-registry.service.js +252 -0
  117. package/dist-server/server/modules/websocket/services/chat-run-registry.service.js.map +1 -0
  118. package/dist-server/server/modules/websocket/services/chat-session-writer.service.js +105 -0
  119. package/dist-server/server/modules/websocket/services/chat-session-writer.service.js.map +1 -0
  120. package/dist-server/server/modules/websocket/services/chat-websocket.service.js +218 -156
  121. package/dist-server/server/modules/websocket/services/chat-websocket.service.js.map +1 -1
  122. package/dist-server/server/modules/websocket/services/shell-websocket.service.js +83 -31
  123. package/dist-server/server/modules/websocket/services/shell-websocket.service.js.map +1 -1
  124. package/dist-server/server/modules/websocket/services/websocket-server.service.js +5 -0
  125. package/dist-server/server/modules/websocket/services/websocket-server.service.js.map +1 -1
  126. package/dist-server/server/modules/websocket/tests/chat-run-registry.test.js +216 -0
  127. package/dist-server/server/modules/websocket/tests/chat-run-registry.test.js.map +1 -0
  128. package/dist-server/server/openai-codex.js +24 -14
  129. package/dist-server/server/openai-codex.js.map +1 -1
  130. package/dist-server/server/opencode-cli.js +21 -8
  131. package/dist-server/server/opencode-cli.js.map +1 -1
  132. package/dist-server/server/opencode-cli.test.js +19 -1
  133. package/dist-server/server/opencode-cli.test.js.map +1 -1
  134. package/dist-server/server/routes/settings.js +1 -1
  135. package/dist-server/server/routes/settings.js.map +1 -1
  136. package/dist-server/server/services/notification-orchestrator.js +1 -176
  137. package/dist-server/server/services/notification-orchestrator.js.map +1 -1
  138. package/dist-server/server/services/tests/notification-orchestrator.test.js +64 -0
  139. package/dist-server/server/services/tests/notification-orchestrator.test.js.map +1 -0
  140. package/dist-server/server/shared/{claude-cli-path.test.js → tests/claude-cli-path.test.js} +1 -1
  141. package/dist-server/server/shared/tests/claude-cli-path.test.js.map +1 -0
  142. package/dist-server/server/shared/tests/slice-tail-page.test.js +35 -0
  143. package/dist-server/server/shared/tests/slice-tail-page.test.js.map +1 -0
  144. package/dist-server/server/shared/utils.js +73 -1
  145. package/dist-server/server/shared/utils.js.map +1 -1
  146. package/dist-server/server/voice-proxy.js +226 -0
  147. package/dist-server/server/voice-proxy.js.map +1 -0
  148. package/electron/assets/logo-macos.icns +0 -0
  149. package/electron/assets/logo-macos.png +0 -0
  150. package/electron/assets/logo-windows.ico +0 -0
  151. package/electron/cloud.js +260 -0
  152. package/electron/desktopNotifications.js +378 -0
  153. package/electron/desktopWindow.js +766 -0
  154. package/electron/launcher/index.html +14 -0
  155. package/electron/launcher/launcher.css +801 -0
  156. package/electron/launcher/launcher.js +687 -0
  157. package/electron/localServer.js +550 -0
  158. package/electron/main.js +944 -0
  159. package/electron/preload.cjs +60 -0
  160. package/electron/scripts/generate-macos-icon.js +62 -0
  161. package/electron/serverInstaller.js +277 -0
  162. package/electron/tabs.js +87 -0
  163. package/electron/viewHost.js +331 -0
  164. package/package.json +79 -3
  165. package/public/api-docs.html +36 -18
  166. package/scripts/release/build-server-bundle.js +176 -0
  167. package/scripts/release/prepare-desktop-app.js +152 -0
  168. package/server/browser-use-mcp.ts +384 -0
  169. package/server/claude-sdk.js +27 -5
  170. package/server/cli.js +15 -6
  171. package/server/cursor-cli.js +27 -11
  172. package/server/gemini-cli.js +17 -2
  173. package/server/index.js +137 -47
  174. package/server/load-env.js +1 -1
  175. package/server/modules/browser-use/browser-use-mcp.routes.ts +120 -0
  176. package/server/modules/browser-use/browser-use.routes.ts +96 -0
  177. package/server/modules/browser-use/browser-use.service.ts +836 -0
  178. package/server/modules/browser-use/tests/browser-use.service.test.ts +10 -0
  179. package/server/modules/database/index.ts +1 -0
  180. package/server/modules/database/migrations.ts +25 -0
  181. package/server/modules/database/repositories/notification-channel-endpoints.ts +153 -0
  182. package/server/modules/database/repositories/notification-preferences.ts +12 -1
  183. package/server/modules/database/repositories/sessions.db.ts +227 -23
  184. package/server/modules/database/schema.ts +27 -0
  185. package/server/modules/database/tests/sessions-provider-mapping.test.ts +108 -0
  186. package/server/modules/database/{repositories → tests}/sessions.db.integration.test.ts +12 -0
  187. package/server/modules/notifications/index.ts +13 -0
  188. package/server/modules/notifications/notifications.routes.ts +127 -0
  189. package/server/modules/notifications/services/desktop-notification-clients.service.ts +124 -0
  190. package/server/modules/notifications/services/notification-orchestrator.service.js +288 -0
  191. package/server/modules/notifications/websocket/desktop-notifications-websocket.service.ts +109 -0
  192. package/server/modules/projects/services/project-management.service.ts +0 -8
  193. package/server/modules/projects/services/projects-with-sessions-fetch.service.ts +12 -53
  194. package/server/modules/providers/README.md +1 -1
  195. package/server/modules/providers/index.ts +1 -0
  196. package/server/modules/providers/list/claude/claude-session-synchronizer.provider.ts +26 -1
  197. package/server/modules/providers/list/claude/claude-sessions.provider.ts +9 -14
  198. package/server/modules/providers/list/claude/claude-skills.provider.ts +8 -0
  199. package/server/modules/providers/list/codex/codex-session-synchronizer.provider.ts +7 -3
  200. package/server/modules/providers/list/codex/codex-sessions.provider.ts +3 -12
  201. package/server/modules/providers/list/codex/codex-skills.provider.ts +8 -0
  202. package/server/modules/providers/list/cursor/cursor-sessions.provider.ts +13 -22
  203. package/server/modules/providers/list/cursor/cursor-skills.provider.ts +8 -0
  204. package/server/modules/providers/list/gemini/gemini-models.provider.ts +5 -8
  205. package/server/modules/providers/list/gemini/gemini-sessions.provider.ts +6 -6
  206. package/server/modules/providers/list/gemini/gemini-skills.provider.ts +8 -0
  207. package/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.ts +18 -4
  208. package/server/modules/providers/list/opencode/opencode-sessions.provider.ts +9 -12
  209. package/server/modules/providers/provider.routes.ts +167 -1
  210. package/server/modules/providers/services/mcp.service.ts +26 -0
  211. package/server/modules/providers/services/provider-capabilities.service.ts +91 -0
  212. package/server/modules/providers/services/provider-models.service.ts +1 -1
  213. package/server/modules/providers/services/sessions-watcher.service.ts +71 -25
  214. package/server/modules/providers/services/sessions.service.ts +79 -4
  215. package/server/modules/providers/services/skills.service.ts +25 -1
  216. package/server/modules/providers/shared/skills/skills.provider.ts +223 -0
  217. package/server/modules/providers/tests/opencode-sessions.test.ts +49 -0
  218. package/server/modules/providers/tests/skills.test.ts +212 -0
  219. package/server/modules/websocket/README.md +33 -27
  220. package/server/modules/websocket/index.ts +1 -0
  221. package/server/modules/websocket/services/chat-run-registry.service.ts +327 -0
  222. package/server/modules/websocket/services/chat-session-writer.service.ts +145 -0
  223. package/server/modules/websocket/services/chat-websocket.service.ts +292 -207
  224. package/server/modules/websocket/services/shell-websocket.service.ts +108 -32
  225. package/server/modules/websocket/services/websocket-server.service.ts +6 -0
  226. package/server/modules/websocket/tests/chat-run-registry.test.ts +244 -0
  227. package/server/openai-codex.js +24 -14
  228. package/server/opencode-cli.js +21 -8
  229. package/server/opencode-cli.test.js +20 -1
  230. package/server/routes/settings.js +6 -1
  231. package/server/services/notification-orchestrator.js +3 -224
  232. package/server/services/tests/notification-orchestrator.test.js +80 -0
  233. package/server/shared/interfaces.ts +15 -0
  234. package/server/shared/tests/slice-tail-page.test.ts +42 -0
  235. package/server/shared/types.ts +84 -1
  236. package/server/shared/utils.ts +95 -1
  237. package/server/voice-proxy.js +224 -0
  238. package/dist/assets/index-73Xw3i9v.css +0 -32
  239. package/dist/assets/index-BIHaviaA.js +0 -1361
  240. package/dist/modelConstants.js +0 -854
  241. package/dist-server/server/modules/database/repositories/projects.db.integration.test.js.map +0 -1
  242. package/dist-server/server/modules/database/repositories/sessions.db.integration.test.js.map +0 -1
  243. package/dist-server/server/shared/claude-cli-path.test.js.map +0 -1
  244. package/public/modelConstants.js +0 -854
  245. /package/dist-server/server/modules/database/{repositories → tests}/projects.db.integration.test.js +0 -0
  246. /package/server/modules/database/{repositories → tests}/projects.db.integration.test.ts +0 -0
  247. /package/server/shared/{claude-cli-path.test.ts → tests/claude-cli-path.test.ts} +0 -0
@@ -0,0 +1,331 @@
1
+ import { BrowserView } from 'electron';
2
+
3
+ const TARGET_LOAD_TIMEOUT_MS = 20000;
4
+
5
+ function escapeHtml(value) {
6
+ return String(value == null ? '' : value)
7
+ .replace(/&/g, '&')
8
+ .replace(/</g, '&lt;')
9
+ .replace(/>/g, '&gt;')
10
+ .replace(/"/g, '&quot;');
11
+ }
12
+
13
+ function buildPlaceholderHtml(title, message, logs = []) {
14
+ const logHtml = logs.length
15
+ ? `<pre>${logs.map(escapeHtml).join('\n')}</pre>`
16
+ : '<pre>Waiting for process output...</pre>';
17
+ return [
18
+ '<!doctype html><meta charset="utf-8">',
19
+ '<style>',
20
+ 'html,body{margin:0;height:100%;background:#0a0a0a;color:#fafafa;font:14px -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}',
21
+ 'body{padding:28px;overflow:hidden}',
22
+ '.shell{height:100%;display:flex;flex-direction:column;gap:16px}',
23
+ '.box{display:flex;align-items:center;gap:10px;color:#d4d4d4;flex:0 0 auto}',
24
+ '.dot{width:8px;height:8px;border-radius:50%;background:#0b60ea;box-shadow:0 0 0 6px rgba(11,96,234,.15)}',
25
+ 'pre{margin:0;flex:1;overflow:auto;border:1px solid #262626;border-radius:10px;background:#050505;color:#d4d4d4;padding:14px;font:12px/1.55 ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;white-space:pre-wrap;user-select:text}',
26
+ '</style>',
27
+ '<div class="shell">',
28
+ `<div class="box"><span class="dot"></span><span>${escapeHtml(message || `Opening ${title}...`)}</span></div>`,
29
+ logHtml,
30
+ '</div>',
31
+ ].join('');
32
+ }
33
+
34
+ function isHttpUrl(url) {
35
+ try {
36
+ const parsed = new URL(url);
37
+ return parsed.protocol === 'http:' || parsed.protocol === 'https:';
38
+ } catch {
39
+ return false;
40
+ }
41
+ }
42
+
43
+ async function loadUrlWithTimeout(webContents, url, timeoutMs = TARGET_LOAD_TIMEOUT_MS) {
44
+ let timedOut = false;
45
+ let timeout = null;
46
+ const loadPromise = webContents.loadURL(url);
47
+ const timeoutPromise = new Promise((_, reject) => {
48
+ timeout = setTimeout(() => {
49
+ timedOut = true;
50
+ try {
51
+ webContents.stop();
52
+ } catch {
53
+ // Ignore teardown races while reporting the original timeout.
54
+ }
55
+ reject(new Error(`Timed out loading ${url} after ${Math.round(timeoutMs / 1000)} seconds.`));
56
+ }, timeoutMs);
57
+ });
58
+
59
+ try {
60
+ await Promise.race([loadPromise, timeoutPromise]);
61
+ } catch (error) {
62
+ if (timedOut) {
63
+ loadPromise.catch(() => {});
64
+ }
65
+ throw error;
66
+ } finally {
67
+ if (timeout) clearTimeout(timeout);
68
+ }
69
+ }
70
+
71
+ export class ViewHost {
72
+ constructor({ appName, getMainWindow, getContentViewBounds, getPreloadPath, openExternalUrl, showError }) {
73
+ this.appName = appName;
74
+ this.getMainWindow = getMainWindow;
75
+ this.getContentViewBounds = getContentViewBounds;
76
+ this.getPreloadPath = getPreloadPath;
77
+ this.openExternalUrl = openExternalUrl;
78
+ this.showError = showError;
79
+ this.activeContentView = null;
80
+ this.tabViews = new Map();
81
+ }
82
+
83
+ configureChildWebContents(webContents) {
84
+ webContents.setWindowOpenHandler(({ url }) => {
85
+ void this.openExternalUrl(url).catch((error) => this.showError('Could not open external link', error));
86
+ return { action: 'deny' };
87
+ });
88
+ }
89
+
90
+ detachAll() {
91
+ const mainWindow = this.getMainWindow();
92
+ if (!mainWindow || mainWindow.isDestroyed()) return;
93
+ try {
94
+ for (const view of mainWindow.getBrowserViews()) {
95
+ mainWindow.removeBrowserView(view);
96
+ }
97
+ } catch {
98
+ // BrowserViews may already be gone during BrowserWindow teardown.
99
+ }
100
+ this.activeContentView = null;
101
+ }
102
+
103
+ detachActiveView() {
104
+ const mainWindow = this.getMainWindow();
105
+ const view = this.activeContentView;
106
+ if (!mainWindow || mainWindow.isDestroyed() || !view) return false;
107
+ try {
108
+ if (mainWindow.getBrowserViews().includes(view)) {
109
+ mainWindow.removeBrowserView(view);
110
+ }
111
+ } catch {
112
+ return false;
113
+ }
114
+ this.activeContentView = null;
115
+ return true;
116
+ }
117
+
118
+ getActiveView() {
119
+ const view = this.activeContentView;
120
+ if (!view || view.webContents.isDestroyed()) return null;
121
+ return view;
122
+ }
123
+
124
+ openActiveViewDevTools() {
125
+ const view = this.getActiveView();
126
+ if (!view) return false;
127
+ view.webContents.openDevTools({ mode: 'detach' });
128
+ return true;
129
+ }
130
+
131
+ reloadActiveView() {
132
+ const view = this.getActiveView();
133
+ if (!view) return false;
134
+ view.webContents.reloadIgnoringCache();
135
+ return true;
136
+ }
137
+
138
+ async readLocalStorageValueForOrigin(originUrl, key) {
139
+ let targetOrigin;
140
+ try {
141
+ targetOrigin = new URL(originUrl).origin;
142
+ } catch {
143
+ return null;
144
+ }
145
+
146
+ for (const view of this.tabViews.values()) {
147
+ if (!view || view.webContents.isDestroyed()) continue;
148
+ let viewOrigin;
149
+ try {
150
+ viewOrigin = new URL(view.webContents.getURL()).origin;
151
+ } catch {
152
+ continue;
153
+ }
154
+ if (viewOrigin !== targetOrigin) continue;
155
+
156
+ try {
157
+ const value = await view.webContents.executeJavaScript(
158
+ `window.localStorage.getItem(${JSON.stringify(key)})`,
159
+ true
160
+ );
161
+ return typeof value === 'string' && value ? value : null;
162
+ } catch {
163
+ return null;
164
+ }
165
+ }
166
+
167
+ return null;
168
+ }
169
+
170
+ getTabViewDiagnostics() {
171
+ const mainWindow = this.getMainWindow();
172
+ const attachedViews = new Set();
173
+ if (mainWindow && !mainWindow.isDestroyed()) {
174
+ try {
175
+ for (const view of mainWindow.getBrowserViews()) {
176
+ attachedViews.add(view);
177
+ }
178
+ } catch {
179
+ // Ignore teardown races while gathering best-effort diagnostics.
180
+ }
181
+ }
182
+
183
+ return Array.from(this.tabViews.entries()).map(([tabId, view]) => {
184
+ const { webContents } = view;
185
+ const destroyed = webContents.isDestroyed();
186
+ return {
187
+ tabId,
188
+ webContentsId: destroyed ? null : webContents.id,
189
+ url: destroyed ? null : webContents.getURL(),
190
+ title: destroyed ? null : webContents.getTitle(),
191
+ osProcessId: destroyed || typeof webContents.getOSProcessId !== 'function' ? null : webContents.getOSProcessId(),
192
+ processId: destroyed || typeof webContents.getProcessId !== 'function' ? null : webContents.getProcessId(),
193
+ attached: attachedViews.has(view),
194
+ active: this.activeContentView === view,
195
+ destroyed,
196
+ };
197
+ });
198
+ }
199
+
200
+ getOrCreateTabView(tabId) {
201
+ let view = this.tabViews.get(tabId);
202
+ if (view) return view;
203
+
204
+ view = new BrowserView({
205
+ webPreferences: {
206
+ contextIsolation: true,
207
+ nodeIntegration: false,
208
+ sandbox: true,
209
+ preload: this.getPreloadPath(),
210
+ },
211
+ });
212
+ this.configureChildWebContents(view.webContents);
213
+ this.tabViews.set(tabId, view);
214
+ return view;
215
+ }
216
+
217
+ attach(view) {
218
+ const mainWindow = this.getMainWindow();
219
+ if (!mainWindow || mainWindow.isDestroyed()) return;
220
+ if (this.activeContentView && this.activeContentView !== view) {
221
+ this.detachAll();
222
+ }
223
+ this.activeContentView = view;
224
+ try {
225
+ if (!mainWindow.getBrowserViews().includes(view)) {
226
+ mainWindow.addBrowserView(view);
227
+ }
228
+ } catch {
229
+ return;
230
+ }
231
+ view.setBounds(this.getContentViewBounds());
232
+ view.setAutoResize({ width: true, height: true });
233
+ }
234
+
235
+ resizeActiveView() {
236
+ if (this.activeContentView) {
237
+ this.activeContentView.setBounds(this.getContentViewBounds());
238
+ }
239
+ }
240
+
241
+ async showTabPlaceholder(tabId, target, message) {
242
+ const view = this.getOrCreateTabView(tabId);
243
+ this.attach(view);
244
+ const html = buildPlaceholderHtml(target.name || this.appName, message);
245
+ await view.webContents.loadURL(`data:text/html;charset=utf-8,${encodeURIComponent(html)}`);
246
+ view.__cloudcliStartupHtml = html;
247
+ view.__cloudcliLoadedUrl = null;
248
+ }
249
+
250
+ async showLocalStartupTarget(tabId, target, logs) {
251
+ const view = this.getOrCreateTabView(tabId);
252
+ if (view.__cloudcliLoadingUrl) return;
253
+ this.attach(view);
254
+ const html = buildPlaceholderHtml(target.name || this.appName, 'Starting Local CloudCLI...', logs);
255
+ if (view.__cloudcliStartupHtml === html) return;
256
+ await view.webContents.loadURL(`data:text/html;charset=utf-8,${encodeURIComponent(html)}`);
257
+ view.__cloudcliStartupHtml = html;
258
+ view.__cloudcliLoadedUrl = null;
259
+ }
260
+
261
+ async showContentTarget(tabId, target) {
262
+ const loadUrl = target.loadUrl || target.url;
263
+ if (!isHttpUrl(loadUrl)) {
264
+ throw new Error(`Refusing to load unsupported app URL: ${loadUrl}`);
265
+ }
266
+ const view = this.getOrCreateTabView(tabId);
267
+ this.attach(view);
268
+ if (target.forceLoad || view.__cloudcliLoadedUrl !== target.url) {
269
+ view.__cloudcliLoadingUrl = loadUrl;
270
+ try {
271
+ await loadUrlWithTimeout(view.webContents, loadUrl);
272
+ view.__cloudcliLoadedUrl = target.url;
273
+ view.__cloudcliStartupHtml = null;
274
+ delete target.loadUrl;
275
+ delete target.forceLoad;
276
+ } finally {
277
+ if (view.__cloudcliLoadingUrl === loadUrl) {
278
+ view.__cloudcliLoadingUrl = null;
279
+ }
280
+ }
281
+ }
282
+ return view.webContents.getURL();
283
+ }
284
+
285
+ reloadTab(tabId) {
286
+ const view = this.tabViews.get(tabId);
287
+ if (!view || view.webContents.isDestroyed()) return false;
288
+ view.webContents.reloadIgnoringCache();
289
+ return true;
290
+ }
291
+
292
+ async navigateActiveView(url) {
293
+ const view = this.getActiveView();
294
+ if (!view) return false;
295
+ await loadUrlWithTimeout(view.webContents, url);
296
+ view.__cloudcliLoadedUrl = url;
297
+ view.__cloudcliStartupHtml = null;
298
+ return true;
299
+ }
300
+
301
+ destroyTabView(tabId) {
302
+ const view = this.tabViews.get(tabId);
303
+ if (!view) return;
304
+ const mainWindow = this.getMainWindow();
305
+ if (mainWindow && !mainWindow.isDestroyed()) {
306
+ try {
307
+ if (mainWindow.getBrowserViews().includes(view)) {
308
+ mainWindow.removeBrowserView(view);
309
+ }
310
+ } catch {
311
+ // Ignore teardown races; Electron owns final destruction during quit.
312
+ }
313
+ }
314
+ if (this.activeContentView === view) {
315
+ this.activeContentView = null;
316
+ }
317
+ try {
318
+ if (!view.webContents.isDestroyed()) {
319
+ view.webContents.destroy();
320
+ }
321
+ } catch {
322
+ // The view may already be destroyed by its parent BrowserWindow.
323
+ }
324
+ this.tabViews.delete(tabId);
325
+ }
326
+
327
+ clear() {
328
+ this.tabViews.clear();
329
+ this.activeContentView = null;
330
+ }
331
+ }
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "@cloudcli-ai/cloudcli",
3
- "version": "1.34.0",
3
+ "version": "1.35.1",
4
+ "productName": "CloudCLI",
4
5
  "description": "A web-based UI for Claude Code CLI",
5
6
  "type": "module",
6
7
  "main": "dist-server/server/index.js",
@@ -8,10 +9,10 @@
8
9
  "cloudcli": "dist-server/server/cli.js"
9
10
  },
10
11
  "files": [
12
+ "electron/",
11
13
  "server/",
12
14
  "shared/",
13
15
  "public/api-docs.html",
14
- "public/modelConstants.js",
15
16
  "dist/",
16
17
  "dist-server/",
17
18
  "scripts/",
@@ -31,6 +32,14 @@
31
32
  "server:dev": "tsx --tsconfig server/tsconfig.json server/index.js",
32
33
  "server:dev-watch": "tsx watch --tsconfig server/tsconfig.json server/index.js",
33
34
  "client": "vite",
35
+ "desktop": "electron electron/main.js",
36
+ "desktop:dev": "cross-env ELECTRON_DEV_URL=http://127.0.0.1:5173 electron electron/main.js",
37
+ "desktop:stage": "node scripts/release/prepare-desktop-app.js",
38
+ "desktop:pack": "npm run build && npm run desktop:stage && electron-builder --projectDir .desktop-build/desktop-app --dir",
39
+ "desktop:dist:mac": "npm run build && npm run desktop:stage && electron-builder --projectDir .desktop-build/desktop-app --mac dmg",
40
+ "desktop:dist:win": "npm run build && npm run desktop:stage && electron-builder --projectDir .desktop-build/desktop-app --win nsis",
41
+ "server:bundle": "npm run build && node scripts/release/build-server-bundle.js",
42
+ "desktop:icon:mac": "node electron/scripts/generate-macos-icon.js",
34
43
  "build": "npm run build:client && npm run build:server",
35
44
  "build:client": "vite build",
36
45
  "prebuild:server": "node -e \"require('node:fs').rmSync('dist-server', { recursive: true, force: true })\"",
@@ -46,6 +55,66 @@
46
55
  "prepare": "husky",
47
56
  "update:platform": "./update-platform.sh"
48
57
  },
58
+ "build": {
59
+ "appId": "ai.cloudcli.desktop",
60
+ "productName": "CloudCLI",
61
+ "asar": false,
62
+ "artifactName": "cloudcli-desktop-${version}-${os}-${arch}.${ext}",
63
+ "directories": {
64
+ "output": "release/desktop"
65
+ },
66
+ "extraMetadata": {
67
+ "main": "electron/main.js"
68
+ },
69
+ "files": [
70
+ "electron/",
71
+ "public/",
72
+ "dist/",
73
+ "dist-server/",
74
+ "shared/",
75
+ "server/",
76
+ "package.json",
77
+ "!**/node_modules/@anthropic-ai/claude-agent-sdk-{darwin,linux,win32}-*/**"
78
+ ],
79
+ "protocols": [
80
+ {
81
+ "name": "CloudCLI",
82
+ "schemes": [
83
+ "cloudcli"
84
+ ]
85
+ }
86
+ ],
87
+ "mac": {
88
+ "category": "public.app-category.developer-tools",
89
+ "icon": "electron/assets/logo-macos.icns",
90
+ "notarize": true,
91
+ "target": [
92
+ "dmg"
93
+ ],
94
+ "extendInfo": {
95
+ "CFBundleName": "CloudCLI",
96
+ "CFBundleDisplayName": "CloudCLI",
97
+ "CFBundleURLTypes": [
98
+ {
99
+ "CFBundleURLName": "CloudCLI",
100
+ "CFBundleURLSchemes": [
101
+ "cloudcli"
102
+ ]
103
+ }
104
+ ]
105
+ }
106
+ },
107
+ "win": {
108
+ "icon": "electron/assets/logo-windows.ico",
109
+ "target": [
110
+ "nsis"
111
+ ]
112
+ },
113
+ "nsis": {
114
+ "installerIcon": "electron/assets/logo-windows.ico",
115
+ "uninstallerIcon": "electron/assets/logo-windows.ico"
116
+ }
117
+ },
49
118
  "keywords": [
50
119
  "claude code",
51
120
  "claude-code",
@@ -78,7 +147,7 @@
78
147
  "@codemirror/theme-one-dark": "^6.1.2",
79
148
  "@iarna/toml": "^2.2.5",
80
149
  "@octokit/rest": "^22.0.0",
81
- "@openai/codex-sdk": "^0.125.0",
150
+ "@openai/codex-sdk": "^0.141.0",
82
151
  "@replit/codemirror-minimap": "^0.5.2",
83
152
  "@tailwindcss/typography": "^0.5.16",
84
153
  "@uiw/react-codemirror": "^4.23.13",
@@ -142,6 +211,9 @@
142
211
  "auto-changelog": "^2.5.0",
143
212
  "autoprefixer": "^10.4.16",
144
213
  "concurrently": "^8.2.2",
214
+ "cross-env": "^10.1.0",
215
+ "electron": "^38.0.0",
216
+ "electron-builder": "^26.15.3",
145
217
  "eslint": "^9.39.3",
146
218
  "eslint-import-resolver-typescript": "^4.4.4",
147
219
  "eslint-plugin-boundaries": "^6.0.2",
@@ -168,5 +240,9 @@
168
240
  "lint-staged": {
169
241
  "src/**/*.{ts,tsx,js,jsx}": "eslint",
170
242
  "server/**/*.{js,ts}": "eslint"
243
+ },
244
+ "optionalDependencies": {
245
+ "@nut-tree-fork/nut-js": "^4.2.6",
246
+ "screenshot-desktop": "^1.15.4"
171
247
  }
172
248
  }
@@ -820,31 +820,49 @@ data: {"type":"done"}</code></pre>
820
820
  </div>
821
821
  </div>
822
822
 
823
- <script type="module">
824
- // Import model constants
825
- import { PROVIDERS } from './modelConstants.js';
826
-
823
+ <script>
827
824
  // Dynamic URL replacement
828
825
  const apiUrl = window.location.origin;
829
826
  document.querySelectorAll('.api-url').forEach(el => {
830
827
  el.textContent = apiUrl;
831
828
  });
832
829
 
833
- // Dynamically populate model documentation
834
- window.addEventListener('DOMContentLoaded', () => {
830
+ // Populate model documentation from the live provider API
831
+ const PROVIDER_ORDER = [
832
+ { id: 'claude', name: 'Anthropic' },
833
+ { id: 'codex', name: 'OpenAI' },
834
+ { id: 'gemini', name: 'Google' },
835
+ { id: 'cursor', name: 'Cursor' },
836
+ { id: 'opencode', name: 'OpenCode' },
837
+ ];
838
+
839
+ async function populateModels() {
835
840
  const modelCell = document.getElementById('model-options-cell');
836
- if (modelCell) {
837
- const providerModels = PROVIDERS.map(provider => {
838
- const models = provider.models.OPTIONS.map(m => `<code>${m.value}</code>`).join(', ');
839
- return `<strong>${provider.name}:</strong> ${models} (default: <code>${provider.models.DEFAULT}</code>)`;
840
- }).join('<br><br>');
841
-
842
- modelCell.innerHTML = `
843
- Model identifier for the AI provider:<br><br>
844
- ${providerModels}
845
- `;
846
- }
847
- });
841
+ if (!modelCell) return;
842
+
843
+ const token = localStorage.getItem('auth-token');
844
+ const headers = token ? { Authorization: `Bearer ${token}` } : {};
845
+
846
+ const results = await Promise.allSettled(
847
+ PROVIDER_ORDER.map(({ id }) =>
848
+ fetch(`/api/providers/${id}/models`, { headers }).then(r => r.json())
849
+ )
850
+ );
851
+
852
+ const providerModels = results.map((result, i) => {
853
+ const { name } = PROVIDER_ORDER[i];
854
+ if (result.status === 'rejected' || !result.value?.data?.models) {
855
+ return `<strong>${name}:</strong> <em>unavailable</em>`;
856
+ }
857
+ const { OPTIONS, DEFAULT } = result.value.data.models;
858
+ const models = OPTIONS.map(m => `<code>${m.value}</code>`).join(', ');
859
+ return `<strong>${name}:</strong> ${models} (default: <code>${DEFAULT}</code>)`;
860
+ }).join('<br><br>');
861
+
862
+ modelCell.innerHTML = `Model identifier for the AI provider:<br><br>${providerModels}`;
863
+ }
864
+
865
+ document.addEventListener('DOMContentLoaded', populateModels);
848
866
 
849
867
  // Tab switching
850
868
  window.showTab = function(tabName) {