@researai/deepscientist 1.5.15 → 1.5.17

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 (202) hide show
  1. package/README.md +385 -104
  2. package/bin/ds.js +1241 -110
  3. package/docs/en/00_QUICK_START.md +100 -19
  4. package/docs/en/01_SETTINGS_REFERENCE.md +34 -1
  5. package/docs/en/02_START_RESEARCH_GUIDE.md +7 -0
  6. package/docs/en/05_TUI_GUIDE.md +6 -0
  7. package/docs/en/06_RUNTIME_AND_CANVAS.md +4 -3
  8. package/docs/en/09_DOCTOR.md +25 -8
  9. package/docs/en/14_PROMPT_SKILLS_AND_MCP_GUIDE.md +63 -13
  10. package/docs/en/15_CODEX_PROVIDER_SETUP.md +37 -11
  11. package/docs/en/19_EXTERNAL_CONTROLLER_GUIDE.md +226 -0
  12. package/docs/en/19_LOCAL_BROWSER_AUTH.md +70 -0
  13. package/docs/en/20_WORKSPACE_MODES_GUIDE.md +250 -0
  14. package/docs/en/21_LOCAL_MODEL_BACKENDS_GUIDE.md +283 -0
  15. package/docs/en/91_DEVELOPMENT.md +237 -0
  16. package/docs/en/README.md +24 -2
  17. package/docs/zh/00_QUICK_START.md +89 -19
  18. package/docs/zh/01_SETTINGS_REFERENCE.md +34 -1
  19. package/docs/zh/02_START_RESEARCH_GUIDE.md +7 -0
  20. package/docs/zh/05_TUI_GUIDE.md +6 -0
  21. package/docs/zh/09_DOCTOR.md +26 -9
  22. package/docs/zh/14_PROMPT_SKILLS_AND_MCP_GUIDE.md +63 -13
  23. package/docs/zh/15_CODEX_PROVIDER_SETUP.md +37 -11
  24. package/docs/zh/19_EXTERNAL_CONTROLLER_GUIDE.md +226 -0
  25. package/docs/zh/19_LOCAL_BROWSER_AUTH.md +68 -0
  26. package/docs/zh/20_WORKSPACE_MODES_GUIDE.md +251 -0
  27. package/docs/zh/21_LOCAL_MODEL_BACKENDS_GUIDE.md +281 -0
  28. package/docs/zh/README.md +24 -2
  29. package/install.sh +46 -4
  30. package/package.json +2 -1
  31. package/pyproject.toml +1 -1
  32. package/src/deepscientist/__init__.py +1 -1
  33. package/src/deepscientist/acp/envelope.py +6 -0
  34. package/src/deepscientist/artifact/service.py +647 -22
  35. package/src/deepscientist/bash_exec/service.py +234 -9
  36. package/src/deepscientist/bridges/connectors.py +8 -2
  37. package/src/deepscientist/cli.py +115 -19
  38. package/src/deepscientist/codex_cli_compat.py +367 -22
  39. package/src/deepscientist/config/models.py +2 -1
  40. package/src/deepscientist/config/service.py +183 -13
  41. package/src/deepscientist/daemon/api/handlers.py +255 -31
  42. package/src/deepscientist/daemon/api/router.py +9 -0
  43. package/src/deepscientist/daemon/app.py +1146 -105
  44. package/src/deepscientist/diagnostics/__init__.py +6 -0
  45. package/src/deepscientist/diagnostics/runner_failures.py +130 -0
  46. package/src/deepscientist/doctor.py +207 -3
  47. package/src/deepscientist/gitops/__init__.py +10 -1
  48. package/src/deepscientist/gitops/diff.py +129 -0
  49. package/src/deepscientist/gitops/service.py +4 -1
  50. package/src/deepscientist/mcp/server.py +39 -0
  51. package/src/deepscientist/prompts/builder.py +275 -34
  52. package/src/deepscientist/quest/layout.py +15 -2
  53. package/src/deepscientist/quest/service.py +707 -55
  54. package/src/deepscientist/quest/stage_views.py +6 -1
  55. package/src/deepscientist/runners/codex.py +143 -43
  56. package/src/deepscientist/shared.py +19 -0
  57. package/src/deepscientist/skills/__init__.py +2 -2
  58. package/src/deepscientist/skills/installer.py +196 -5
  59. package/src/deepscientist/skills/registry.py +66 -0
  60. package/src/prompts/connectors/qq.md +18 -8
  61. package/src/prompts/connectors/weixin.md +16 -6
  62. package/src/prompts/contracts/shared_interaction.md +14 -2
  63. package/src/prompts/system.md +23 -5
  64. package/src/prompts/system_copilot.md +56 -0
  65. package/src/skills/analysis-campaign/SKILL.md +1 -0
  66. package/src/skills/baseline/SKILL.md +8 -0
  67. package/src/skills/decision/SKILL.md +8 -0
  68. package/src/skills/experiment/SKILL.md +8 -0
  69. package/src/skills/figure-polish/SKILL.md +1 -0
  70. package/src/skills/finalize/SKILL.md +1 -0
  71. package/src/skills/idea/SKILL.md +1 -0
  72. package/src/skills/intake-audit/SKILL.md +8 -0
  73. package/src/skills/mentor/SKILL.md +217 -0
  74. package/src/skills/mentor/references/correction-rules.md +210 -0
  75. package/src/skills/mentor/references/knowledge-profile.md +91 -0
  76. package/src/skills/mentor/references/persona-profile.md +138 -0
  77. package/src/skills/mentor/references/taste-profile.md +128 -0
  78. package/src/skills/mentor/references/thought-style-profile.md +138 -0
  79. package/src/skills/mentor/references/work-profile.md +289 -0
  80. package/src/skills/mentor/references/workflow-profile.md +240 -0
  81. package/src/skills/optimize/SKILL.md +1 -0
  82. package/src/skills/rebuttal/SKILL.md +1 -0
  83. package/src/skills/review/SKILL.md +1 -0
  84. package/src/skills/scout/SKILL.md +8 -0
  85. package/src/skills/write/SKILL.md +1 -0
  86. package/src/tui/dist/app/AppContainer.js +19 -11
  87. package/src/tui/dist/index.js +4 -1
  88. package/src/tui/dist/lib/api.js +33 -3
  89. package/src/tui/package.json +1 -1
  90. package/src/ui/dist/assets/AiManusChatView-Bv-Z8YpU.js +204 -0
  91. package/src/ui/dist/assets/AnalysisPlugin-BCKAfjba.js +1 -0
  92. package/src/ui/dist/assets/CliPlugin-BCKcpc35.js +109 -0
  93. package/src/ui/dist/assets/CodeEditorPlugin-DbOfSJ8K.js +2 -0
  94. package/src/ui/dist/assets/CodeViewerPlugin-CbaFRrUU.js +270 -0
  95. package/src/ui/dist/assets/DocViewerPlugin-DAjLVeQD.js +7 -0
  96. package/src/ui/dist/assets/GitCommitViewerPlugin-CIUqbUDO.js +1 -0
  97. package/src/ui/dist/assets/GitDiffViewerPlugin-CQACjoAA.js +6 -0
  98. package/src/ui/dist/assets/GitSnapshotViewer-0r4nLPke.js +30 -0
  99. package/src/ui/dist/assets/ImageViewerPlugin-nBOmI2v_.js +26 -0
  100. package/src/ui/dist/assets/LabCopilotPanel-BHxOxF4z.js +14 -0
  101. package/src/ui/dist/assets/LabPlugin-BKoZGs95.js +22 -0
  102. package/src/ui/dist/assets/LatexPlugin-ZwtV8pIp.js +25 -0
  103. package/src/ui/dist/assets/MarkdownViewerPlugin-DKqVfKyW.js +128 -0
  104. package/src/ui/dist/assets/MarketplacePlugin-BwxStZ9D.js +13 -0
  105. package/src/ui/dist/assets/NotebookEditor-BEQhaQbt.js +81 -0
  106. package/src/ui/dist/assets/{NotebookEditor-CccQYZjX.css → NotebookEditor-BHH8rdGj.css} +1 -1
  107. package/src/ui/dist/assets/NotebookEditor-BOr3x3Ej.css +1 -0
  108. package/src/ui/dist/assets/NotebookEditor-DB9N_T9q.js +361 -0
  109. package/src/ui/dist/assets/PdfLoader-Cy5jtWrr.css +1 -0
  110. package/src/ui/dist/assets/PdfLoader-eWBONbQP.js +16 -0
  111. package/src/ui/dist/assets/PdfMarkdownPlugin-D22YOZL3.js +1 -0
  112. package/src/ui/dist/assets/PdfViewerPlugin-c-RK9DLM.js +17 -0
  113. package/src/ui/dist/assets/PdfViewerPlugin-nwwE-fjJ.css +1 -0
  114. package/src/ui/dist/assets/SearchPlugin-CxF9ytAx.js +16 -0
  115. package/src/ui/dist/assets/SearchPlugin-DA4en4hK.css +1 -0
  116. package/src/ui/dist/assets/TextViewerPlugin-C5xqeeUH.js +54 -0
  117. package/src/ui/dist/assets/VNCViewer-BoLGLnHz.js +11 -0
  118. package/src/ui/dist/assets/bot-DREQOxzP.js +6 -0
  119. package/src/ui/dist/assets/browser-CTB2jwNe.js +8 -0
  120. package/src/ui/dist/assets/chevron-up-C9Qpx4DE.js +6 -0
  121. package/src/ui/dist/assets/code-WlFHE7z_.js +6 -0
  122. package/src/ui/dist/assets/file-content-BZMz3RYp.js +1 -0
  123. package/src/ui/dist/assets/file-diff-panel-CQhw0jS2.js +1 -0
  124. package/src/ui/dist/assets/file-jump-queue-DA-SdG__.js +1 -0
  125. package/src/ui/dist/assets/file-socket-CfQPKQKj.js +1 -0
  126. package/src/ui/dist/assets/git-commit-horizontal-DxZ8DCZh.js +6 -0
  127. package/src/ui/dist/assets/image-Bgl4VIyx.js +6 -0
  128. package/src/ui/dist/assets/index-BpV6lusQ.css +33 -0
  129. package/src/ui/dist/assets/index-CBNVuWcP.js +2496 -0
  130. package/src/ui/dist/assets/index-CwNu1aH4.js +11 -0
  131. package/src/ui/dist/assets/index-DrUnlf6K.js +1 -0
  132. package/src/ui/dist/assets/index-NW-h8VzN.js +1 -0
  133. package/src/ui/dist/assets/monaco-CiHMMNH_.js +1 -0
  134. package/src/ui/dist/assets/pdf-effect-queue-J8OnM0jE.js +6 -0
  135. package/src/ui/dist/assets/plugin-monaco-C8UgLomw.js +19 -0
  136. package/src/ui/dist/assets/plugin-notebook-HbW2K-1c.js +169 -0
  137. package/src/ui/dist/assets/plugin-pdf-CR8hgQBV.js +357 -0
  138. package/src/ui/dist/assets/plugin-terminal-MXFIPun8.js +227 -0
  139. package/src/ui/dist/assets/popover-CLc0pPP8.js +1 -0
  140. package/src/ui/dist/assets/project-sync-C9IdzdZW.js +1 -0
  141. package/src/ui/dist/assets/select-Cs2PmzwL.js +11 -0
  142. package/src/ui/dist/assets/sigma-ClKcHAXm.js +6 -0
  143. package/src/ui/dist/assets/trash-DwpbFr3w.js +11 -0
  144. package/src/ui/dist/assets/useCliAccess-NQ8m0Let.js +1 -0
  145. package/src/ui/dist/assets/useFileDiffOverlay-FuhcnKiw.js +1 -0
  146. package/src/ui/dist/assets/wrap-text-BC-Hltpd.js +11 -0
  147. package/src/ui/dist/assets/zoom-out-E_gaeAxL.js +11 -0
  148. package/src/ui/dist/index.html +5 -2
  149. package/src/ui/dist/assets/AiManusChatView-DDjbFnbt.js +0 -26597
  150. package/src/ui/dist/assets/AnalysisPlugin-Yb5IdmaU.js +0 -123
  151. package/src/ui/dist/assets/CliPlugin-e64sreyu.js +0 -31037
  152. package/src/ui/dist/assets/CodeEditorPlugin-C4D2TIkU.js +0 -427
  153. package/src/ui/dist/assets/CodeViewerPlugin-BVoNZIvC.js +0 -905
  154. package/src/ui/dist/assets/DocViewerPlugin-CLChbllo.js +0 -278
  155. package/src/ui/dist/assets/GitDiffViewerPlugin-C4xeFyFQ.js +0 -2661
  156. package/src/ui/dist/assets/ImageViewerPlugin-OiMUAcLi.js +0 -500
  157. package/src/ui/dist/assets/LabCopilotPanel-BjD2ThQF.js +0 -4104
  158. package/src/ui/dist/assets/LabPlugin-DQPg-NrB.js +0 -2677
  159. package/src/ui/dist/assets/LatexPlugin-CI05XAV9.js +0 -1792
  160. package/src/ui/dist/assets/MarkdownViewerPlugin-DpeBLYZf.js +0 -308
  161. package/src/ui/dist/assets/MarketplacePlugin-DolE58Q2.js +0 -413
  162. package/src/ui/dist/assets/NotebookEditor-7Qm2rSWD.js +0 -4214
  163. package/src/ui/dist/assets/NotebookEditor-C1kWaxKi.js +0 -84873
  164. package/src/ui/dist/assets/NotebookEditor-C3VQ7ylN.css +0 -1405
  165. package/src/ui/dist/assets/PdfLoader-BfOHw8Zw.js +0 -25468
  166. package/src/ui/dist/assets/PdfLoader-C-Y707R3.css +0 -49
  167. package/src/ui/dist/assets/PdfMarkdownPlugin-BulDREv1.js +0 -409
  168. package/src/ui/dist/assets/PdfViewerPlugin-C-daaOaL.js +0 -3095
  169. package/src/ui/dist/assets/PdfViewerPlugin-DQ11QcSf.css +0 -3627
  170. package/src/ui/dist/assets/SearchPlugin-CjpaiJ3A.js +0 -741
  171. package/src/ui/dist/assets/SearchPlugin-DDMrGDkh.css +0 -379
  172. package/src/ui/dist/assets/TextViewerPlugin-BxIyqPQC.js +0 -472
  173. package/src/ui/dist/assets/VNCViewer-HAg9mF7M.js +0 -18821
  174. package/src/ui/dist/assets/awareness-C0NPR2Dj.js +0 -292
  175. package/src/ui/dist/assets/bot-0DYntytV.js +0 -21
  176. package/src/ui/dist/assets/browser-BAcuE0Xj.js +0 -2895
  177. package/src/ui/dist/assets/code-B20Slj_w.js +0 -17
  178. package/src/ui/dist/assets/file-content-DT24KFma.js +0 -377
  179. package/src/ui/dist/assets/file-diff-panel-DK13YPql.js +0 -92
  180. package/src/ui/dist/assets/file-jump-queue-r5XKgJEV.js +0 -16
  181. package/src/ui/dist/assets/file-socket-B4T2o4nR.js +0 -58
  182. package/src/ui/dist/assets/function-B5QZkkHC.js +0 -1895
  183. package/src/ui/dist/assets/image-DSeR_sDS.js +0 -18
  184. package/src/ui/dist/assets/index-BrFje2Uk.js +0 -120
  185. package/src/ui/dist/assets/index-BwRJaoTl.js +0 -25
  186. package/src/ui/dist/assets/index-D_E4281X.js +0 -221322
  187. package/src/ui/dist/assets/index-DnYB3xb1.js +0 -159
  188. package/src/ui/dist/assets/index-G7AcWcMu.css +0 -12594
  189. package/src/ui/dist/assets/monaco-LExaAN3Y.js +0 -623
  190. package/src/ui/dist/assets/pdf-effect-queue-BJk5okWJ.js +0 -47
  191. package/src/ui/dist/assets/pdf_viewer-e0g1is2C.js +0 -8206
  192. package/src/ui/dist/assets/popover-D3Gg_FoV.js +0 -476
  193. package/src/ui/dist/assets/project-sync-C_ygLlVU.js +0 -297
  194. package/src/ui/dist/assets/select-CpAK6uWm.js +0 -1690
  195. package/src/ui/dist/assets/sigma-DEccaSgk.js +0 -22
  196. package/src/ui/dist/assets/square-check-big-uUfyVsbD.js +0 -17
  197. package/src/ui/dist/assets/trash-CXvwwSe8.js +0 -32
  198. package/src/ui/dist/assets/useCliAccess-Bnop4mgR.js +0 -957
  199. package/src/ui/dist/assets/useFileDiffOverlay-B8eUAX0I.js +0 -53
  200. package/src/ui/dist/assets/wrap-text-9vbOBpkW.js +0 -35
  201. package/src/ui/dist/assets/yjs-DncrqiZ8.js +0 -11243
  202. package/src/ui/dist/assets/zoom-out-BgVMmOW4.js +0 -34
@@ -1,957 +0,0 @@
1
- import { x as supportsSocketIo, D as resolveApiBaseUrl, y as lookup, z as useAuthStore, O as ConnectionState, r as reactExports, Y as checkProjectAccess } from './index-D_E4281X.js';
2
-
3
- const SOCKET_CACHE = /* @__PURE__ */ new Map();
4
- const CLIENT_ID_KEY = "ds_cli_client_id";
5
- let cachedClientId = null;
6
- function createNoopCliSocket() {
7
- const socket = {
8
- connected: false,
9
- connect: () => socket,
10
- disconnect: () => socket,
11
- on: () => socket,
12
- off: () => socket,
13
- emit: () => true
14
- };
15
- return socket;
16
- }
17
- function getCliClientId() {
18
- if (cachedClientId) return cachedClientId;
19
- let clientId = "";
20
- if (typeof window !== "undefined") {
21
- try {
22
- clientId = window.localStorage.getItem(CLIENT_ID_KEY) || "";
23
- } catch {
24
- clientId = "";
25
- }
26
- }
27
- if (!clientId) {
28
- try {
29
- clientId = crypto.randomUUID();
30
- } catch {
31
- clientId = `cli-${Date.now()}-${Math.random().toString(16).slice(2, 10)}`;
32
- }
33
- if (typeof window !== "undefined") {
34
- try {
35
- window.localStorage.setItem(CLIENT_ID_KEY, clientId);
36
- } catch {
37
- }
38
- }
39
- }
40
- cachedClientId = clientId;
41
- return clientId;
42
- }
43
- function acquireCliSocket(options = {}) {
44
- if (!supportsSocketIo()) {
45
- return {
46
- socket: createNoopCliSocket(),
47
- release: () => {
48
- }
49
- };
50
- }
51
- const endpoint = resolveApiBaseUrl();
52
- const cacheKey = `${endpoint}::user`;
53
- let entry = SOCKET_CACHE.get(cacheKey);
54
- if (!entry) {
55
- const socket = lookup(`${endpoint}/cli`, {
56
- path: "/ws/socket.io",
57
- autoConnect: false,
58
- transports: ["websocket", "polling"],
59
- auth: (cb) => {
60
- const client_id = getCliClientId();
61
- const protocol_version = "cli.v1";
62
- const token = useAuthStore.getState().accessToken || (typeof window !== "undefined" ? window.localStorage.getItem("ds_access_token") : null);
63
- cb({ token: token || null, client_id, protocol_version });
64
- }
65
- });
66
- entry = { socket, refCount: 0 };
67
- SOCKET_CACHE.set(cacheKey, entry);
68
- }
69
- entry.refCount += 1;
70
- if (!entry.socket.connected) {
71
- entry.socket.connect();
72
- }
73
- return {
74
- socket: entry.socket,
75
- release: () => {
76
- const current = SOCKET_CACHE.get(cacheKey);
77
- if (!current) return;
78
- current.refCount -= 1;
79
- if (current.refCount <= 0) {
80
- current.socket.disconnect();
81
- SOCKET_CACHE.delete(cacheKey);
82
- }
83
- }
84
- };
85
- }
86
- function unwrapPayload(data) {
87
- if (data && typeof data === "object" && "payload" in data) {
88
- return data.payload;
89
- }
90
- return data;
91
- }
92
-
93
- let seqCounter = 0;
94
- function nextSeq() {
95
- seqCounter += 1;
96
- return seqCounter;
97
- }
98
- function wrapEnvelope(payload, meta = {}) {
99
- return {
100
- protocol_version: "cli.v1",
101
- ts: (/* @__PURE__ */ new Date()).toISOString(),
102
- payload,
103
- ...meta
104
- };
105
- }
106
-
107
- const connectionTransitions = {
108
- [ConnectionState.DISCONNECTED]: {
109
- CONNECT: ConnectionState.CONNECTING
110
- },
111
- [ConnectionState.CONNECTING]: {
112
- AUTH_SUCCESS: ConnectionState.AUTHENTICATING,
113
- DISCONNECT: ConnectionState.DISCONNECTED,
114
- CONNECTION_LOST: ConnectionState.RECONNECTING
115
- },
116
- [ConnectionState.AUTHENTICATING]: {
117
- AUTH_SUCCESS: ConnectionState.CONNECTED,
118
- AUTH_FAILURE: ConnectionState.FAILED,
119
- CONNECTION_LOST: ConnectionState.RECONNECTING
120
- },
121
- [ConnectionState.CONNECTED]: {
122
- DISCONNECT: ConnectionState.DISCONNECTED,
123
- CONNECTION_LOST: ConnectionState.RECONNECTING,
124
- SUSPEND: ConnectionState.SUSPENDED
125
- },
126
- [ConnectionState.RECONNECTING]: {
127
- RECONNECT_SUCCESS: ConnectionState.CONNECTED,
128
- MAX_RETRIES_EXCEEDED: ConnectionState.FAILED,
129
- DISCONNECT: ConnectionState.DISCONNECTED
130
- },
131
- [ConnectionState.SUSPENDED]: {
132
- RESUME: ConnectionState.CONNECTING,
133
- DISCONNECT: ConnectionState.DISCONNECTED
134
- },
135
- [ConnectionState.FAILED]: {
136
- CONNECT: ConnectionState.CONNECTING,
137
- DISCONNECT: ConnectionState.DISCONNECTED
138
- }
139
- };
140
- class ConnectionStateMachine {
141
- state = ConnectionState.DISCONNECTED;
142
- listeners = /* @__PURE__ */ new Set();
143
- getState() {
144
- return this.state;
145
- }
146
- sendEvent(event) {
147
- const nextState = connectionTransitions[this.state]?.[event.type];
148
- if (!nextState) {
149
- console.warn(`Invalid connection transition: ${this.state} -> ${event.type}`);
150
- return false;
151
- }
152
- const prevState = this.state;
153
- this.state = nextState;
154
- this.notifyListeners(nextState, event);
155
- if (prevState !== nextState) {
156
- console.debug(`[CLI] Connection state: ${prevState} -> ${nextState} (${event.type})`);
157
- }
158
- return true;
159
- }
160
- subscribe(listener) {
161
- this.listeners.add(listener);
162
- return () => this.listeners.delete(listener);
163
- }
164
- canSendEvent(eventType) {
165
- return Boolean(connectionTransitions[this.state]?.[eventType]);
166
- }
167
- reset() {
168
- this.state = ConnectionState.DISCONNECTED;
169
- }
170
- notifyListeners(state, event) {
171
- this.listeners.forEach((listener) => listener(state, event));
172
- }
173
- }
174
-
175
- class ConnectionMetricsCollector {
176
- metrics = {
177
- totalConnections: 0,
178
- successfulConnections: 0,
179
- failedConnections: 0,
180
- reconnectionAttempts: 0,
181
- averageReconnectTime: 0,
182
- messagesBuffered: 0,
183
- messagesDropped: 0
184
- };
185
- recordConnection() {
186
- this.metrics.totalConnections += 1;
187
- }
188
- recordConnectionSuccess() {
189
- this.metrics.successfulConnections += 1;
190
- }
191
- recordConnectionFailure() {
192
- this.metrics.failedConnections += 1;
193
- }
194
- recordReconnectionAttempt(durationMs) {
195
- this.metrics.reconnectionAttempts += 1;
196
- const attempts = this.metrics.reconnectionAttempts;
197
- this.metrics.averageReconnectTime = (this.metrics.averageReconnectTime * (attempts - 1) + durationMs) / attempts;
198
- }
199
- recordMessageBuffered(count = 1) {
200
- this.metrics.messagesBuffered += count;
201
- }
202
- recordMessageDropped(count = 1) {
203
- this.metrics.messagesDropped += count;
204
- }
205
- getMetrics() {
206
- return { ...this.metrics };
207
- }
208
- }
209
-
210
- class MessageBuffer {
211
- buffer = [];
212
- maxSize;
213
- onOverflow;
214
- constructor(options = {}) {
215
- this.maxSize = options.maxSize ?? 1e3;
216
- this.onOverflow = options.onOverflow ?? "drop_oldest";
217
- }
218
- enqueue(message) {
219
- this.pruneExpired();
220
- if (this.buffer.length >= this.maxSize) {
221
- switch (this.onOverflow) {
222
- case "drop_oldest": {
223
- this.buffer.sort((a, b) => {
224
- const priorityOrder = { high: 0, normal: 1, low: 2 };
225
- return priorityOrder[b.priority] - priorityOrder[a.priority] || a.timestamp - b.timestamp;
226
- });
227
- this.buffer.pop();
228
- break;
229
- }
230
- case "drop_newest":
231
- return false;
232
- case "reject":
233
- throw new Error("Message buffer overflow");
234
- }
235
- }
236
- const bufferedMessage = {
237
- ...message,
238
- id: crypto.randomUUID(),
239
- timestamp: Date.now()
240
- };
241
- this.buffer.push(bufferedMessage);
242
- return true;
243
- }
244
- flush() {
245
- this.pruneExpired();
246
- const messages = [...this.buffer].sort((a, b) => {
247
- const priorityOrder = { high: 0, normal: 1, low: 2 };
248
- const priorityDiff = priorityOrder[a.priority] - priorityOrder[b.priority];
249
- return priorityDiff !== 0 ? priorityDiff : a.timestamp - b.timestamp;
250
- });
251
- this.buffer = [];
252
- return messages;
253
- }
254
- getStatus() {
255
- return {
256
- size: this.buffer.length,
257
- maxSize: this.maxSize,
258
- oldestTimestamp: this.buffer.length > 0 ? Math.min(...this.buffer.map((message) => message.timestamp)) : null
259
- };
260
- }
261
- clear() {
262
- this.buffer = [];
263
- }
264
- countByPriority(priority) {
265
- return this.buffer.filter((message) => message.priority === priority).length;
266
- }
267
- pruneExpired() {
268
- const now = Date.now();
269
- this.buffer = this.buffer.filter((message) => {
270
- if (!message.maxAge) return true;
271
- return now - message.timestamp < message.maxAge;
272
- });
273
- }
274
- }
275
-
276
- const DEFAULT_RECONNECTION_CONFIG = {
277
- initialDelay: 1e3,
278
- maxDelay: 3e5,
279
- maxRetries: 30,
280
- backoffMultiplier: 1.5,
281
- jitterFactor: 0.2
282
- };
283
- class ReconnectionManager {
284
- config;
285
- retryCount = 0;
286
- currentTimeout = null;
287
- listeners = /* @__PURE__ */ new Set();
288
- constructor(config = {}) {
289
- this.config = { ...DEFAULT_RECONNECTION_CONFIG, ...config };
290
- }
291
- calculateDelay() {
292
- const baseDelay = Math.min(
293
- this.config.initialDelay * Math.pow(this.config.backoffMultiplier, this.retryCount),
294
- this.config.maxDelay
295
- );
296
- const jitter = baseDelay * this.config.jitterFactor * (Math.random() * 2 - 1);
297
- return Math.max(0, Math.floor(baseDelay + jitter));
298
- }
299
- scheduleReconnect(onReconnect) {
300
- if (this.retryCount >= this.config.maxRetries) {
301
- this.notifyListeners({
302
- status: "failed",
303
- message: `Max retries reached (${this.config.maxRetries})`
304
- });
305
- return;
306
- }
307
- const delay = this.calculateDelay();
308
- this.retryCount += 1;
309
- this.notifyListeners({
310
- status: "waiting",
311
- retryCount: this.retryCount,
312
- maxRetries: this.config.maxRetries,
313
- nextRetryIn: delay
314
- });
315
- this.currentTimeout = setTimeout(async () => {
316
- this.notifyListeners({ status: "reconnecting", retryCount: this.retryCount });
317
- try {
318
- const success = await onReconnect();
319
- if (success) {
320
- this.reset();
321
- this.notifyListeners({ status: "connected" });
322
- return;
323
- }
324
- } catch (error) {
325
- console.error("[CLI] Reconnect failed:", error);
326
- }
327
- this.scheduleReconnect(onReconnect);
328
- }, delay);
329
- }
330
- reset() {
331
- this.retryCount = 0;
332
- if (this.currentTimeout) {
333
- clearTimeout(this.currentTimeout);
334
- this.currentTimeout = null;
335
- }
336
- }
337
- cancel() {
338
- this.reset();
339
- this.notifyListeners({ status: "cancelled" });
340
- }
341
- retryNow(onReconnect) {
342
- this.cancel();
343
- this.retryCount = 0;
344
- this.scheduleReconnect(onReconnect);
345
- }
346
- subscribe(listener) {
347
- this.listeners.add(listener);
348
- return () => this.listeners.delete(listener);
349
- }
350
- getRetryCount() {
351
- return this.retryCount;
352
- }
353
- notifyListeners(state) {
354
- this.listeners.forEach((listener) => listener(state));
355
- }
356
- }
357
-
358
- const CLI_OFFLINE_MESSAGE = "CLI server offline. Please ensure the CLI is running.";
359
- const normalizeGatewayError = (message) => {
360
- const lower = message.toLowerCase();
361
- if (lower.includes("cli server") && (lower.includes("not connected") || lower.includes("offline")) || lower.includes("cli_server_not_connected") || lower.includes("cli_server_offline")) {
362
- return CLI_OFFLINE_MESSAGE;
363
- }
364
- return message;
365
- };
366
- function useCliSocket(options) {
367
- const { projectId, serverId, handlers } = options;
368
- const clientIdRef = reactExports.useRef(getCliClientId());
369
- const reconnectRef = reactExports.useRef(new ReconnectionManager());
370
- const reconnectingRef = reactExports.useRef(false);
371
- const bufferRef = reactExports.useRef(new MessageBuffer());
372
- const stateMachineRef = reactExports.useRef(new ConnectionStateMachine());
373
- const metricsRef = reactExports.useRef(new ConnectionMetricsCollector());
374
- const reconnectStartedAtRef = reactExports.useRef(null);
375
- const subscribedRef = reactExports.useRef(false);
376
- const subscribeInFlightRef = reactExports.useRef(null);
377
- const scheduleReconnectRef = reactExports.useRef(() => {
378
- });
379
- const [status, setStatus] = reactExports.useState(() => ({
380
- state: stateMachineRef.current.getState(),
381
- bufferedMessages: 0,
382
- metrics: metricsRef.current.getMetrics()
383
- }));
384
- const { socket, release } = reactExports.useMemo(() => acquireCliSocket(), []);
385
- const syncMetrics = reactExports.useCallback(() => {
386
- setStatus((prev) => ({
387
- ...prev,
388
- metrics: metricsRef.current.getMetrics()
389
- }));
390
- }, []);
391
- const updateBufferedStatus = reactExports.useCallback(() => {
392
- const bufferStatus = bufferRef.current.getStatus();
393
- setStatus((prev) => ({
394
- ...prev,
395
- bufferedMessages: bufferStatus.size,
396
- metrics: metricsRef.current.getMetrics()
397
- }));
398
- }, []);
399
- const emitEnvelope = reactExports.useCallback(
400
- (event, payload, meta = {}, options2) => {
401
- if (!projectId || !serverId) return;
402
- const envelope = wrapEnvelope(payload, {
403
- project_id: projectId,
404
- server_id: serverId,
405
- client_id: clientIdRef.current,
406
- ...meta
407
- });
408
- if (!socket.connected) {
409
- try {
410
- const accepted = bufferRef.current.enqueue({
411
- type: event,
412
- payload: envelope,
413
- priority: options2?.priority ?? "normal",
414
- maxAge: options2?.maxAge
415
- });
416
- if (!accepted) {
417
- metricsRef.current.recordMessageDropped();
418
- syncMetrics();
419
- handlers?.onError?.("Message buffer full; dropping newest message");
420
- } else {
421
- metricsRef.current.recordMessageBuffered();
422
- }
423
- } catch (error) {
424
- metricsRef.current.recordMessageDropped();
425
- syncMetrics();
426
- handlers?.onError?.(error instanceof Error ? error.message : "Message buffer error");
427
- }
428
- updateBufferedStatus();
429
- return;
430
- }
431
- socket.emit(event, envelope);
432
- },
433
- [projectId, serverId, socket, handlers, updateBufferedStatus, syncMetrics]
434
- );
435
- const flushBuffer = reactExports.useCallback(() => {
436
- if (!socket.connected) return;
437
- const messages = bufferRef.current.flush();
438
- messages.forEach((message) => socket.emit(message.type, message.payload));
439
- updateBufferedStatus();
440
- }, [socket, updateBufferedStatus]);
441
- const subscribe = reactExports.useCallback(async () => {
442
- if (!projectId || !serverId) return false;
443
- if (subscribeInFlightRef.current) {
444
- return subscribeInFlightRef.current;
445
- }
446
- const startedAt = Date.now();
447
- const envelope = wrapEnvelope({}, {
448
- project_id: projectId,
449
- server_id: serverId,
450
- client_id: clientIdRef.current
451
- });
452
- metricsRef.current.recordConnection();
453
- syncMetrics();
454
- const promise = new Promise((resolve) => {
455
- socket.timeout(3e3).emit("cli:subscribe", envelope, (err) => {
456
- subscribeInFlightRef.current = null;
457
- if (err) {
458
- subscribedRef.current = false;
459
- metricsRef.current.recordConnectionFailure();
460
- syncMetrics();
461
- const currentState2 = stateMachineRef.current.getState();
462
- if (currentState2 === ConnectionState.AUTHENTICATING && stateMachineRef.current.canSendEvent("AUTH_FAILURE")) {
463
- stateMachineRef.current.sendEvent({ type: "AUTH_FAILURE", error: err.message });
464
- }
465
- if (currentState2 === ConnectionState.RECONNECTING) {
466
- scheduleReconnectRef.current();
467
- }
468
- setStatus((prev) => ({ ...prev, lastError: err.message }));
469
- resolve(false);
470
- return;
471
- }
472
- const latencyMs = Date.now() - startedAt;
473
- const currentState = stateMachineRef.current.getState();
474
- if (currentState === ConnectionState.RECONNECTING) {
475
- stateMachineRef.current.sendEvent({ type: "RECONNECT_SUCCESS" });
476
- } else if (currentState === ConnectionState.AUTHENTICATING) {
477
- stateMachineRef.current.sendEvent({ type: "AUTH_SUCCESS" });
478
- }
479
- subscribedRef.current = true;
480
- metricsRef.current.recordConnectionSuccess();
481
- syncMetrics();
482
- reconnectRef.current.reset();
483
- reconnectingRef.current = false;
484
- setStatus((prev) => ({
485
- ...prev,
486
- latencyMs,
487
- lastConnectedAt: Date.now()
488
- }));
489
- resolve(true);
490
- });
491
- });
492
- subscribeInFlightRef.current = promise;
493
- return promise;
494
- }, [projectId, serverId, socket, syncMetrics]);
495
- const attemptReconnect = reactExports.useCallback(async () => {
496
- if (socket.connected && subscribedRef.current) return true;
497
- if (!socket.connected) {
498
- const connected = await new Promise((resolve) => {
499
- let settled = false;
500
- const timeout = window.setTimeout(() => {
501
- if (settled) return;
502
- settled = true;
503
- cleanup();
504
- resolve(false);
505
- }, 4e3);
506
- const handleConnect = () => {
507
- if (settled) return;
508
- settled = true;
509
- cleanup();
510
- resolve(true);
511
- };
512
- const handleError = () => {
513
- if (settled) return;
514
- settled = true;
515
- cleanup();
516
- resolve(false);
517
- };
518
- const cleanup = () => {
519
- window.clearTimeout(timeout);
520
- socket.off("connect", handleConnect);
521
- socket.off("connect_error", handleError);
522
- };
523
- socket.once("connect", handleConnect);
524
- socket.once("connect_error", handleError);
525
- socket.connect();
526
- });
527
- if (!connected) return false;
528
- }
529
- const subscribed = await subscribe();
530
- if (subscribed) {
531
- flushBuffer();
532
- }
533
- return subscribed;
534
- }, [socket, subscribe, flushBuffer]);
535
- const scheduleReconnect = reactExports.useCallback(() => {
536
- if (reconnectingRef.current) return;
537
- reconnectingRef.current = true;
538
- reconnectRef.current.scheduleReconnect(attemptReconnect);
539
- }, [attemptReconnect]);
540
- scheduleReconnectRef.current = scheduleReconnect;
541
- reactExports.useEffect(() => {
542
- const stateMachine = stateMachineRef.current;
543
- const unsubscribeState = stateMachine.subscribe((nextState, event) => {
544
- setStatus((prev) => ({
545
- ...prev,
546
- state: nextState,
547
- lastError: event.type === "AUTH_FAILURE" ? event.error : prev.lastError
548
- }));
549
- });
550
- const unsubscribeReconnect = reconnectRef.current.subscribe((reconnectState) => {
551
- if (reconnectState.status === "waiting") {
552
- reconnectStartedAtRef.current = null;
553
- setStatus((prev) => ({
554
- ...prev,
555
- reconnectAttempts: reconnectState.retryCount,
556
- nextRetryIn: reconnectState.nextRetryIn
557
- }));
558
- return;
559
- }
560
- if (reconnectState.status === "reconnecting") {
561
- if (!reconnectStartedAtRef.current) {
562
- reconnectStartedAtRef.current = Date.now();
563
- }
564
- setStatus((prev) => ({
565
- ...prev,
566
- reconnectAttempts: reconnectState.retryCount,
567
- nextRetryIn: void 0
568
- }));
569
- return;
570
- }
571
- if (reconnectState.status === "failed") {
572
- reconnectingRef.current = false;
573
- metricsRef.current.recordConnectionFailure();
574
- syncMetrics();
575
- if (stateMachine.canSendEvent("MAX_RETRIES_EXCEEDED")) {
576
- stateMachine.sendEvent({ type: "MAX_RETRIES_EXCEEDED" });
577
- }
578
- setStatus((prev) => ({
579
- ...prev,
580
- lastError: reconnectState.message
581
- }));
582
- return;
583
- }
584
- if (reconnectState.status === "connected") {
585
- reconnectingRef.current = false;
586
- if (reconnectStartedAtRef.current) {
587
- metricsRef.current.recordReconnectionAttempt(
588
- Date.now() - reconnectStartedAtRef.current
589
- );
590
- syncMetrics();
591
- reconnectStartedAtRef.current = null;
592
- }
593
- setStatus((prev) => ({
594
- ...prev,
595
- reconnectAttempts: 0,
596
- nextRetryIn: void 0
597
- }));
598
- return;
599
- }
600
- if (reconnectState.status === "cancelled") {
601
- reconnectingRef.current = false;
602
- reconnectStartedAtRef.current = null;
603
- setStatus((prev) => ({
604
- ...prev,
605
- reconnectAttempts: 0,
606
- nextRetryIn: void 0
607
- }));
608
- }
609
- });
610
- return () => {
611
- unsubscribeState();
612
- unsubscribeReconnect();
613
- };
614
- }, [syncMetrics]);
615
- reactExports.useEffect(() => {
616
- if (!projectId || !serverId) return;
617
- const stateMachine = stateMachineRef.current;
618
- if (stateMachine.canSendEvent("CONNECT")) {
619
- stateMachine.sendEvent({ type: "CONNECT" });
620
- }
621
- const handleConnect = async () => {
622
- const currentState = stateMachine.getState();
623
- if (currentState === ConnectionState.CONNECTING && stateMachine.canSendEvent("AUTH_SUCCESS")) {
624
- stateMachine.sendEvent({ type: "AUTH_SUCCESS" });
625
- }
626
- if (currentState !== ConnectionState.RECONNECTING) {
627
- const subscribed = await subscribe();
628
- if (subscribed) {
629
- flushBuffer();
630
- }
631
- }
632
- };
633
- const handleDisconnect = (reason) => {
634
- subscribedRef.current = false;
635
- if (stateMachine.canSendEvent("CONNECTION_LOST")) {
636
- stateMachine.sendEvent({ type: "CONNECTION_LOST" });
637
- }
638
- if (reason) {
639
- setStatus((prev) => ({ ...prev, lastError: `Disconnected: ${reason}` }));
640
- }
641
- scheduleReconnect();
642
- };
643
- const handleError = (error) => {
644
- subscribedRef.current = false;
645
- const message = error instanceof Error ? error.message : "Connection error";
646
- setStatus((prev) => ({ ...prev, lastError: message }));
647
- handlers?.onError?.(message);
648
- metricsRef.current.recordConnectionFailure();
649
- syncMetrics();
650
- if (stateMachine.canSendEvent("CONNECTION_LOST")) {
651
- stateMachine.sendEvent({ type: "CONNECTION_LOST" });
652
- }
653
- scheduleReconnect();
654
- };
655
- const handleTerminalOutput = (payload) => {
656
- handlers?.onTerminalOutput?.(payload);
657
- };
658
- const handleStatusUpdate = (payload) => {
659
- handlers?.onStatusUpdate?.(payload);
660
- };
661
- const handleFileResponse = (payload) => {
662
- handlers?.onFileResponse?.(payload);
663
- };
664
- const handleConfirm = (payload) => {
665
- handlers?.onConfirmRequired?.(payload);
666
- };
667
- const handleBlocked = (payload) => {
668
- handlers?.onBlocked?.(payload);
669
- };
670
- const handleSessionCreated = (payload) => {
671
- handlers?.onSessionCreated?.(payload);
672
- };
673
- const handleSessionAttached = (payload) => {
674
- handlers?.onSessionAttached?.(payload);
675
- };
676
- const handleSessionDetached = (payload) => {
677
- handlers?.onSessionDetached?.(payload);
678
- };
679
- const handleSessionClosed = (payload) => {
680
- handlers?.onSessionClosed?.(payload);
681
- };
682
- const handleSessionError = (payload) => {
683
- handlers?.onSessionError?.(payload);
684
- };
685
- const handleMethodStatus = (payload) => {
686
- handlers?.onMethodStatus?.(payload);
687
- };
688
- const handleMethodDone = (payload) => {
689
- handlers?.onMethodDone?.(payload);
690
- };
691
- const handleMethodError = (payload) => {
692
- handlers?.onMethodError?.(payload);
693
- };
694
- const handleMethodListResponse = (payload) => {
695
- handlers?.onMethodListResponse?.(payload);
696
- };
697
- const handleMethodGetResponse = (payload) => {
698
- handlers?.onMethodGetResponse?.(payload);
699
- };
700
- const handleGatewayError = (payload) => {
701
- const message = typeof payload?.message === "string" && payload.message ? payload.message : "CLI server error";
702
- const eventLabel = typeof payload?.event === "string" && payload.event ? ` (${payload.event})` : "";
703
- const normalized = normalizeGatewayError(message);
704
- handlers?.onError?.(`${normalized}${eventLabel}`);
705
- };
706
- socket.on("connect", handleConnect);
707
- socket.on("disconnect", handleDisconnect);
708
- socket.on("connect_error", handleError);
709
- socket.on("cli:terminal:output", handleTerminalOutput);
710
- socket.on("cli:status:update", handleStatusUpdate);
711
- socket.on("cli:file:list:response", handleFileResponse);
712
- socket.on("cli:file:content:response", handleFileResponse);
713
- socket.on("cli:file:download:response", handleFileResponse);
714
- socket.on("cli:file:write:response", handleFileResponse);
715
- socket.on("cli:file:delete:response", handleFileResponse);
716
- socket.on("cli:terminal:confirm_required", handleConfirm);
717
- socket.on("cli:terminal:blocked", handleBlocked);
718
- socket.on("cli:session:created", handleSessionCreated);
719
- socket.on("cli:session:attached", handleSessionAttached);
720
- socket.on("cli:session:detached", handleSessionDetached);
721
- socket.on("cli:session:closed", handleSessionClosed);
722
- socket.on("cli:session:error", handleSessionError);
723
- socket.on("cli:method:status", handleMethodStatus);
724
- socket.on("cli:method:done", handleMethodDone);
725
- socket.on("cli:method:error", handleMethodError);
726
- socket.on("cli:method:list:response", handleMethodListResponse);
727
- socket.on("cli:method:get:response", handleMethodGetResponse);
728
- socket.on("cli:error", handleGatewayError);
729
- if (!socket.connected) {
730
- socket.connect();
731
- }
732
- return () => {
733
- socket.off("connect", handleConnect);
734
- socket.off("disconnect", handleDisconnect);
735
- socket.off("connect_error", handleError);
736
- socket.off("cli:terminal:output", handleTerminalOutput);
737
- socket.off("cli:status:update", handleStatusUpdate);
738
- socket.off("cli:file:list:response", handleFileResponse);
739
- socket.off("cli:file:content:response", handleFileResponse);
740
- socket.off("cli:file:download:response", handleFileResponse);
741
- socket.off("cli:file:write:response", handleFileResponse);
742
- socket.off("cli:file:delete:response", handleFileResponse);
743
- socket.off("cli:terminal:confirm_required", handleConfirm);
744
- socket.off("cli:terminal:blocked", handleBlocked);
745
- socket.off("cli:session:created", handleSessionCreated);
746
- socket.off("cli:session:attached", handleSessionAttached);
747
- socket.off("cli:session:detached", handleSessionDetached);
748
- socket.off("cli:session:closed", handleSessionClosed);
749
- socket.off("cli:session:error", handleSessionError);
750
- socket.off("cli:method:status", handleMethodStatus);
751
- socket.off("cli:method:done", handleMethodDone);
752
- socket.off("cli:method:error", handleMethodError);
753
- socket.off("cli:method:list:response", handleMethodListResponse);
754
- socket.off("cli:method:get:response", handleMethodGetResponse);
755
- socket.off("cli:error", handleGatewayError);
756
- reconnectRef.current.cancel();
757
- release();
758
- };
759
- }, [projectId, serverId, socket, release, subscribe, flushBuffer, handlers, scheduleReconnect, syncMetrics]);
760
- const sendTerminalInput = reactExports.useCallback(
761
- (payload) => {
762
- const inputPayload = {
763
- data: payload.data,
764
- confirmed: payload.confirmed
765
- };
766
- if (payload.mode) {
767
- inputPayload.mode = payload.mode;
768
- }
769
- emitEnvelope(
770
- "cli:terminal:input",
771
- inputPayload,
772
- {
773
- session_id: payload.sessionId,
774
- seq: payload.seq,
775
- operation_id: payload.operationId
776
- },
777
- { priority: "high" }
778
- );
779
- },
780
- [emitEnvelope]
781
- );
782
- const sendTerminalResize = reactExports.useCallback(
783
- (payload) => {
784
- emitEnvelope(
785
- "cli:terminal:resize",
786
- { cols: payload.cols, rows: payload.rows },
787
- { session_id: payload.sessionId },
788
- { priority: "low", maxAge: 2e3 }
789
- );
790
- },
791
- [emitEnvelope]
792
- );
793
- const sendFileRequest = reactExports.useCallback(
794
- (event, payload, meta) => {
795
- emitEnvelope(event, payload, meta);
796
- },
797
- [emitEnvelope]
798
- );
799
- return {
800
- socket,
801
- status,
802
- emitEnvelope,
803
- sendTerminalInput,
804
- sendTerminalResize,
805
- sendFileRequest,
806
- unwrapPayload
807
- };
808
- }
809
-
810
- const CLI_SESSION_NAMESPACE = "6f07d9a4-0e31-4f69-9ad4-b6295b6e4d2e";
811
- function uuidToBytes(uuid) {
812
- const hex = uuid.replace(/-/g, "");
813
- const bytes = new Uint8Array(16);
814
- for (let i = 0; i < 16; i += 1) {
815
- bytes[i] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);
816
- }
817
- return bytes;
818
- }
819
- function bytesToUuid(bytes) {
820
- const hex = Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
821
- return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
822
- }
823
- function fnv1a(input, seed) {
824
- let hash = 2166136261 ^ seed;
825
- for (let i = 0; i < input.length; i += 1) {
826
- hash ^= input.charCodeAt(i);
827
- hash = Math.imul(hash, 16777619);
828
- }
829
- return hash >>> 0;
830
- }
831
- function fallbackUuid(input) {
832
- const h1 = fnv1a(input, 1).toString(16).padStart(8, "0");
833
- const h2 = fnv1a(input, 2).toString(16).padStart(8, "0");
834
- const h3 = fnv1a(input, 3).toString(16).padStart(8, "0");
835
- const h4 = fnv1a(input, 4).toString(16).padStart(8, "0");
836
- const raw = `${h1}${h2}${h3}${h4}`.slice(0, 32);
837
- return `${raw.slice(0, 8)}-${raw.slice(8, 12)}-5${raw.slice(13, 16)}-a${raw.slice(17, 20)}-${raw.slice(20, 32)}`;
838
- }
839
- async function uuidV5(name, namespace = CLI_SESSION_NAMESPACE) {
840
- const nsBytes = uuidToBytes(namespace);
841
- const nameBytes = new TextEncoder().encode(name);
842
- const data = new Uint8Array(nsBytes.length + nameBytes.length);
843
- data.set(nsBytes, 0);
844
- data.set(nameBytes, nsBytes.length);
845
- if (typeof crypto !== "undefined" && crypto.subtle?.digest) {
846
- const hash = new Uint8Array(await crypto.subtle.digest("SHA-1", data));
847
- const bytes = hash.slice(0, 16);
848
- bytes[6] = bytes[6] & 15 | 80;
849
- bytes[8] = bytes[8] & 63 | 128;
850
- return bytesToUuid(bytes);
851
- }
852
- return fallbackUuid(`${namespace}:${name}`);
853
- }
854
- async function buildDefaultSessionId(projectId, serverId) {
855
- return uuidV5(`${projectId}:${serverId}:default`);
856
- }
857
- async function buildConversationSessionId(projectId, serverId, conversationId) {
858
- return uuidV5(`${projectId}:${serverId}:${conversationId}`);
859
- }
860
- async function buildChatSessionId(projectId, serverId, chatSessionId) {
861
- return uuidV5(`${projectId}:${serverId}:${chatSessionId}`);
862
- }
863
-
864
- function mapProjectRoleToPermission(role) {
865
- if (!role) return "none";
866
- if (role === "owner") return "owner";
867
- if (role === "admin") return "admin";
868
- if (role === "editor") return "edit";
869
- if (role === "viewer") return "view";
870
- return "none";
871
- }
872
- function resolveCliCapabilities(level, granularity) {
873
- const allowTerminalInput = granularity?.allowTerminalInput ?? true;
874
- const allowFileEdit = granularity?.allowFileEdit ?? true;
875
- const isEdit = level === "edit";
876
- const isAdmin = level === "admin" || level === "owner";
877
- const baseView = {
878
- view_server_status: level !== "none",
879
- view_terminal_output: level !== "none",
880
- view_operation_logs: level !== "none",
881
- view_files: level !== "none",
882
- view_tasks: level !== "none",
883
- view_findings: level !== "none"
884
- };
885
- const editCaps = {
886
- terminal_input: level !== "none" && level !== "view" && allowTerminalInput,
887
- file_upload: level !== "none" && level !== "view" && allowFileEdit,
888
- file_download: isAdmin || isEdit && allowFileEdit,
889
- file_edit: level !== "none" && level !== "view" && allowFileEdit,
890
- file_delete: level !== "none" && level !== "view" && allowFileEdit
891
- };
892
- const adminCaps = {
893
- manage_sessions: level === "admin" || level === "owner",
894
- manage_permissions: level === "admin" || level === "owner",
895
- disconnect_server: level === "admin" || level === "owner",
896
- view_all_user_logs: level === "admin" || level === "owner",
897
- delete_server: level === "owner"
898
- };
899
- return {
900
- ...baseView,
901
- ...editCaps,
902
- ...adminCaps
903
- };
904
- }
905
-
906
- function useCliAccess(options) {
907
- const { projectId, serverId, readOnly } = options;
908
- const [permission, setPermission] = reactExports.useState("none");
909
- const [granularity, setGranularity] = reactExports.useState(null);
910
- const [isLoading, setIsLoading] = reactExports.useState(false);
911
- const [error, setError] = reactExports.useState(null);
912
- reactExports.useEffect(() => {
913
- if (!projectId || !serverId) {
914
- setPermission("none");
915
- setGranularity(null);
916
- return;
917
- }
918
- if (readOnly) {
919
- setPermission("view");
920
- setGranularity(null);
921
- return;
922
- }
923
- let cancelled = false;
924
- setIsLoading(true);
925
- setError(null);
926
- const resolveAccess = async () => {
927
- try {
928
- const access = await checkProjectAccess(projectId);
929
- if (cancelled) return;
930
- const rolePermission = mapProjectRoleToPermission(access?.role);
931
- setPermission(rolePermission);
932
- setGranularity(null);
933
- } catch (err) {
934
- if (cancelled) return;
935
- setPermission("none");
936
- setGranularity(null);
937
- setError("Failed to resolve CLI permissions");
938
- } finally {
939
- if (!cancelled) setIsLoading(false);
940
- }
941
- };
942
- void resolveAccess();
943
- return () => {
944
- cancelled = true;
945
- };
946
- }, [projectId, readOnly, serverId]);
947
- const capabilities = reactExports.useMemo(() => resolveCliCapabilities(permission, granularity), [permission, granularity]);
948
- return {
949
- permission,
950
- granularity,
951
- capabilities,
952
- isLoading,
953
- error
954
- };
955
- }
956
-
957
- export { useCliSocket as a, buildChatSessionId as b, acquireCliSocket as c, unwrapPayload as d, buildDefaultSessionId as e, buildConversationSessionId as f, getCliClientId as g, nextSeq as n, useCliAccess as u, wrapEnvelope as w };