@wrongstack/desktop 0.284.1 → 0.286.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main/agent-bridge.js +353 -4
- package/dist/main/agent-bridge.js.map +7 -1
- package/dist/main/main.js +371 -22
- package/dist/main/main.js.map +7 -1
- package/dist/preload/preload.cjs +1 -1
- package/dist/preload/preload.cjs.map +7 -1
- package/dist/preload/webui-preload.cjs +1 -1
- package/dist/preload/webui-preload.cjs.map +7 -1
- package/package.json +6 -7
- package/dist/main/chunk-AMPSW3AD.js +0 -357
- package/dist/main/chunk-AMPSW3AD.js.map +0 -1
package/dist/main/main.js
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
DesktopAgentBridge
|
|
3
|
-
} from "./chunk-AMPSW3AD.js";
|
|
4
|
-
|
|
5
1
|
// src/main/main.ts
|
|
6
|
-
import * as path3 from "path";
|
|
7
|
-
import * as fs3 from "fs/promises";
|
|
2
|
+
import * as path3 from "node:path";
|
|
3
|
+
import * as fs3 from "node:fs/promises";
|
|
8
4
|
import { wstackGlobalRoot as wstackGlobalRoot3 } from "@wrongstack/core/utils";
|
|
9
5
|
import {
|
|
10
6
|
app,
|
|
@@ -16,6 +12,359 @@ import {
|
|
|
16
12
|
WebContentsView
|
|
17
13
|
} from "electron";
|
|
18
14
|
|
|
15
|
+
// src/main/agent-bridge.ts
|
|
16
|
+
import { randomUUID } from "node:crypto";
|
|
17
|
+
import { EventEmitter } from "node:events";
|
|
18
|
+
import WebSocket from "ws";
|
|
19
|
+
var MAX_MESSAGES = 300;
|
|
20
|
+
var RECONNECT_CONFIG = {
|
|
21
|
+
/** Maximum number of reconnection attempts (0 = disabled) */
|
|
22
|
+
maxAttempts: 5,
|
|
23
|
+
/** Initial delay in ms before first reconnection */
|
|
24
|
+
initialDelayMs: 1e3,
|
|
25
|
+
/** Maximum delay in ms between reconnection attempts */
|
|
26
|
+
maxDelayMs: 3e4,
|
|
27
|
+
/** Multiplier for exponential backoff */
|
|
28
|
+
backoffMultiplier: 2,
|
|
29
|
+
/** Jitter factor (0-1) to add randomness to delays */
|
|
30
|
+
jitterFactor: 0.1
|
|
31
|
+
};
|
|
32
|
+
var DesktopAgentBridge = class extends EventEmitter {
|
|
33
|
+
conversations = /* @__PURE__ */ new Map();
|
|
34
|
+
snapshot(runtimeId) {
|
|
35
|
+
return publicConversation(this.getOrCreate(runtimeId));
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Get reconnection status for a runtime.
|
|
39
|
+
*/
|
|
40
|
+
getReconnectStatus(runtimeId) {
|
|
41
|
+
const conversation = this.conversations.get(runtimeId);
|
|
42
|
+
if (!conversation) return null;
|
|
43
|
+
return {
|
|
44
|
+
attempt: conversation.reconnectAttempt,
|
|
45
|
+
maxAttempts: RECONNECT_CONFIG.maxAttempts
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Force reconnection for a runtime (resets reconnection state).
|
|
50
|
+
*/
|
|
51
|
+
forceReconnect(runtimeId, wsUrl) {
|
|
52
|
+
const conversation = this.getOrCreate(runtimeId);
|
|
53
|
+
this.cancelReconnect(conversation);
|
|
54
|
+
conversation.reconnectAttempt = 0;
|
|
55
|
+
void this.ensureConnected(runtimeId, wsUrl);
|
|
56
|
+
}
|
|
57
|
+
async ensureConnected(runtimeId, wsUrl) {
|
|
58
|
+
const conversation = this.getOrCreate(runtimeId);
|
|
59
|
+
if (conversation.ws?.readyState === WebSocket.OPEN) return publicConversation(conversation);
|
|
60
|
+
if (conversation.connectPromise) {
|
|
61
|
+
await conversation.connectPromise;
|
|
62
|
+
return publicConversation(conversation);
|
|
63
|
+
}
|
|
64
|
+
if (conversation.reconnectTimer) {
|
|
65
|
+
return publicConversation(conversation);
|
|
66
|
+
}
|
|
67
|
+
conversation.reconnectUrl = wsUrl;
|
|
68
|
+
conversation.reconnectAttempt = 0;
|
|
69
|
+
await this.connect(runtimeId, wsUrl);
|
|
70
|
+
return publicConversation(conversation);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Internal connect method that actually establishes the WebSocket.
|
|
74
|
+
*/
|
|
75
|
+
async connect(runtimeId, wsUrl) {
|
|
76
|
+
const conversation = this.getOrCreate(runtimeId);
|
|
77
|
+
this.cancelReconnect(conversation);
|
|
78
|
+
conversation.status = "connecting";
|
|
79
|
+
conversation.error = void 0;
|
|
80
|
+
this.emitChanged(conversation);
|
|
81
|
+
this.emitReconnectEvent(conversation, "connecting");
|
|
82
|
+
return new Promise((resolve2, reject) => {
|
|
83
|
+
const ws = new WebSocket(wsUrl);
|
|
84
|
+
conversation.ws = ws;
|
|
85
|
+
const timeout = setTimeout(() => {
|
|
86
|
+
if (conversation.ws === ws) {
|
|
87
|
+
ws.close();
|
|
88
|
+
reject(new Error("Connection timeout"));
|
|
89
|
+
}
|
|
90
|
+
}, 1e4);
|
|
91
|
+
ws.once("open", () => {
|
|
92
|
+
clearTimeout(timeout);
|
|
93
|
+
conversation.status = "connected";
|
|
94
|
+
conversation.error = void 0;
|
|
95
|
+
conversation.connectPromise = null;
|
|
96
|
+
conversation.reconnectAttempt = 0;
|
|
97
|
+
conversation.reconnectUrl = null;
|
|
98
|
+
this.emitChanged(conversation);
|
|
99
|
+
this.emitReconnectEvent(conversation, "connected");
|
|
100
|
+
resolve2();
|
|
101
|
+
});
|
|
102
|
+
ws.on("message", (data) => {
|
|
103
|
+
this.handleServerMessage(conversation, data.toString());
|
|
104
|
+
});
|
|
105
|
+
ws.once("error", (err) => {
|
|
106
|
+
clearTimeout(timeout);
|
|
107
|
+
conversation.status = "error";
|
|
108
|
+
conversation.error = err instanceof Error ? err.message : String(err);
|
|
109
|
+
conversation.connectPromise = null;
|
|
110
|
+
this.appendMessage(conversation, {
|
|
111
|
+
role: "system",
|
|
112
|
+
text: `Connection error: ${conversation.error}`
|
|
113
|
+
});
|
|
114
|
+
this.emitChanged(conversation);
|
|
115
|
+
this.emitReconnectEvent(conversation, "error");
|
|
116
|
+
reject(err);
|
|
117
|
+
});
|
|
118
|
+
ws.once("close", () => {
|
|
119
|
+
clearTimeout(timeout);
|
|
120
|
+
if (conversation.ws === ws) conversation.ws = null;
|
|
121
|
+
conversation.connectPromise = null;
|
|
122
|
+
if (conversation.status !== "error") {
|
|
123
|
+
conversation.status = "disconnected";
|
|
124
|
+
}
|
|
125
|
+
conversation.activeAssistantMessageId = null;
|
|
126
|
+
this.emitChanged(conversation);
|
|
127
|
+
if (conversation.reconnectUrl && conversation.status === "disconnected") {
|
|
128
|
+
this.scheduleReconnect(conversation);
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Schedule a reconnection attempt with exponential backoff.
|
|
135
|
+
*/
|
|
136
|
+
scheduleReconnect(conversation) {
|
|
137
|
+
if (RECONNECT_CONFIG.maxAttempts === 0) return;
|
|
138
|
+
if (conversation.reconnectAttempt >= RECONNECT_CONFIG.maxAttempts) {
|
|
139
|
+
this.emitReconnectEvent(conversation, "exhausted");
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
const baseDelay = Math.min(
|
|
143
|
+
RECONNECT_CONFIG.initialDelayMs * RECONNECT_CONFIG.backoffMultiplier ** conversation.reconnectAttempt,
|
|
144
|
+
RECONNECT_CONFIG.maxDelayMs
|
|
145
|
+
);
|
|
146
|
+
const jitter = baseDelay * RECONNECT_CONFIG.jitterFactor * Math.random();
|
|
147
|
+
const delay = Math.floor(baseDelay + jitter);
|
|
148
|
+
conversation.reconnectAttempt++;
|
|
149
|
+
this.emitReconnectEvent(conversation, "scheduled", { delay, attempt: conversation.reconnectAttempt });
|
|
150
|
+
conversation.reconnectTimer = setTimeout(() => {
|
|
151
|
+
conversation.reconnectTimer = null;
|
|
152
|
+
if (!conversation.reconnectUrl) return;
|
|
153
|
+
if (conversation.ws?.readyState !== WebSocket.OPEN) {
|
|
154
|
+
void this.connect(conversation.runtimeId, conversation.reconnectUrl);
|
|
155
|
+
}
|
|
156
|
+
}, delay);
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Cancel pending reconnection.
|
|
160
|
+
*/
|
|
161
|
+
cancelReconnect(conversation) {
|
|
162
|
+
if (conversation.reconnectTimer) {
|
|
163
|
+
clearTimeout(conversation.reconnectTimer);
|
|
164
|
+
conversation.reconnectTimer = null;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Emit reconnection event for UI feedback.
|
|
169
|
+
*/
|
|
170
|
+
emitReconnectEvent(conversation, status, data) {
|
|
171
|
+
this.emit("reconnect", {
|
|
172
|
+
runtimeId: conversation.runtimeId,
|
|
173
|
+
status,
|
|
174
|
+
attempt: conversation.reconnectAttempt,
|
|
175
|
+
maxAttempts: RECONNECT_CONFIG.maxAttempts,
|
|
176
|
+
...data
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
async sendMessage(runtimeId, wsUrl, content) {
|
|
180
|
+
const trimmed = content.trim();
|
|
181
|
+
if (!trimmed) return this.snapshot(runtimeId);
|
|
182
|
+
const conversation = this.getOrCreate(runtimeId);
|
|
183
|
+
conversation.reconnectAttempt = 0;
|
|
184
|
+
await this.ensureConnected(runtimeId, wsUrl);
|
|
185
|
+
const conv = this.getOrCreate(runtimeId);
|
|
186
|
+
this.appendMessage(conv, {
|
|
187
|
+
id: `user_${randomUUID()}`,
|
|
188
|
+
role: "user",
|
|
189
|
+
text: trimmed
|
|
190
|
+
});
|
|
191
|
+
conv.status = "running";
|
|
192
|
+
conv.activeAssistantMessageId = null;
|
|
193
|
+
this.emitChanged(conv);
|
|
194
|
+
this.send(conv, {
|
|
195
|
+
type: "user_message",
|
|
196
|
+
payload: {
|
|
197
|
+
id: `msg_${Date.now()}_${randomUUID().slice(0, 8)}`,
|
|
198
|
+
content: trimmed,
|
|
199
|
+
timestamp: Date.now(),
|
|
200
|
+
...conv.sessionId ? { sessionId: conv.sessionId } : {}
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
return publicConversation(conv);
|
|
204
|
+
}
|
|
205
|
+
async abort(runtimeId, wsUrl) {
|
|
206
|
+
const conversation = this.getOrCreate(runtimeId);
|
|
207
|
+
conversation.reconnectAttempt = 0;
|
|
208
|
+
await this.ensureConnected(runtimeId, wsUrl);
|
|
209
|
+
const conv = this.getOrCreate(runtimeId);
|
|
210
|
+
this.send(conv, {
|
|
211
|
+
type: "abort",
|
|
212
|
+
payload: conv.sessionId ? { sessionId: conv.sessionId } : {}
|
|
213
|
+
});
|
|
214
|
+
conv.status = "connected";
|
|
215
|
+
this.appendMessage(conv, { role: "system", text: "Abort requested." });
|
|
216
|
+
return publicConversation(conv);
|
|
217
|
+
}
|
|
218
|
+
close(runtimeId) {
|
|
219
|
+
const conversation = this.conversations.get(runtimeId);
|
|
220
|
+
if (!conversation) return;
|
|
221
|
+
this.cancelReconnect(conversation);
|
|
222
|
+
conversation.reconnectAttempt = 0;
|
|
223
|
+
conversation.reconnectUrl = null;
|
|
224
|
+
conversation.ws?.close();
|
|
225
|
+
conversation.ws = null;
|
|
226
|
+
conversation.connectPromise = null;
|
|
227
|
+
conversation.status = "disconnected";
|
|
228
|
+
conversation.activeAssistantMessageId = null;
|
|
229
|
+
this.emitChanged(conversation);
|
|
230
|
+
}
|
|
231
|
+
closeAll() {
|
|
232
|
+
for (const runtimeId of this.conversations.keys()) {
|
|
233
|
+
this.close(runtimeId);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
handleServerMessage(conversation, raw) {
|
|
237
|
+
let message;
|
|
238
|
+
try {
|
|
239
|
+
message = JSON.parse(raw);
|
|
240
|
+
} catch {
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
const payload = message.payload ?? {};
|
|
244
|
+
switch (message.type) {
|
|
245
|
+
case "session.start": {
|
|
246
|
+
const sessionId = stringValue(payload["sessionId"]);
|
|
247
|
+
if (sessionId) conversation.sessionId = sessionId;
|
|
248
|
+
conversation.status = conversation.status === "running" ? "running" : "connected";
|
|
249
|
+
this.emitChanged(conversation);
|
|
250
|
+
break;
|
|
251
|
+
}
|
|
252
|
+
case "provider.text_delta": {
|
|
253
|
+
conversation.status = "running";
|
|
254
|
+
this.appendAssistantDelta(conversation, stringValue(payload["text"]) ?? "");
|
|
255
|
+
break;
|
|
256
|
+
}
|
|
257
|
+
case "tool.started": {
|
|
258
|
+
conversation.status = "running";
|
|
259
|
+
const name = stringValue(payload["name"]) ?? "tool";
|
|
260
|
+
this.appendMessage(conversation, { role: "tool", text: `Started ${name}` });
|
|
261
|
+
break;
|
|
262
|
+
}
|
|
263
|
+
case "tool.executed": {
|
|
264
|
+
const name = stringValue(payload["name"]) ?? "tool";
|
|
265
|
+
const ok = payload["ok"] === true;
|
|
266
|
+
this.appendMessage(conversation, {
|
|
267
|
+
role: "tool",
|
|
268
|
+
text: `${name} ${ok ? "completed" : "failed"}`
|
|
269
|
+
});
|
|
270
|
+
break;
|
|
271
|
+
}
|
|
272
|
+
case "provider.error":
|
|
273
|
+
case "provider.stream_error":
|
|
274
|
+
case "error": {
|
|
275
|
+
const text = stringValue(payload["message"]) ?? stringValue(payload["description"]) ?? `${message.type} received`;
|
|
276
|
+
conversation.status = "error";
|
|
277
|
+
conversation.error = text;
|
|
278
|
+
this.appendMessage(conversation, { role: "system", text });
|
|
279
|
+
break;
|
|
280
|
+
}
|
|
281
|
+
case "run.result": {
|
|
282
|
+
const finalText = stringValue(payload["finalText"]);
|
|
283
|
+
if (finalText && !this.lastAssistantHasText(conversation)) {
|
|
284
|
+
this.appendMessage(conversation, { role: "assistant", text: finalText });
|
|
285
|
+
}
|
|
286
|
+
conversation.status = payload["status"] === "failed" ? "error" : "connected";
|
|
287
|
+
conversation.activeAssistantMessageId = null;
|
|
288
|
+
this.emitChanged(conversation);
|
|
289
|
+
break;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
send(conversation, message) {
|
|
294
|
+
if (conversation.ws?.readyState !== WebSocket.OPEN) {
|
|
295
|
+
throw new Error("Runtime socket is not connected");
|
|
296
|
+
}
|
|
297
|
+
conversation.ws.send(JSON.stringify(message));
|
|
298
|
+
}
|
|
299
|
+
appendAssistantDelta(conversation, text) {
|
|
300
|
+
if (!text) return;
|
|
301
|
+
let message = conversation.messages.find((m) => m.id === conversation.activeAssistantMessageId);
|
|
302
|
+
if (!message) {
|
|
303
|
+
message = {
|
|
304
|
+
id: `assistant_${randomUUID()}`,
|
|
305
|
+
role: "assistant",
|
|
306
|
+
text: "",
|
|
307
|
+
timestamp: Date.now()
|
|
308
|
+
};
|
|
309
|
+
conversation.activeAssistantMessageId = message.id;
|
|
310
|
+
conversation.messages.push(message);
|
|
311
|
+
}
|
|
312
|
+
message.text += text;
|
|
313
|
+
this.trimMessages(conversation);
|
|
314
|
+
this.emitChanged(conversation);
|
|
315
|
+
}
|
|
316
|
+
appendMessage(conversation, input) {
|
|
317
|
+
conversation.messages.push({
|
|
318
|
+
id: input.id ?? `${input.role}_${randomUUID()}`,
|
|
319
|
+
role: input.role,
|
|
320
|
+
text: input.text,
|
|
321
|
+
timestamp: input.timestamp ?? Date.now()
|
|
322
|
+
});
|
|
323
|
+
this.trimMessages(conversation);
|
|
324
|
+
this.emitChanged(conversation);
|
|
325
|
+
}
|
|
326
|
+
lastAssistantHasText(conversation) {
|
|
327
|
+
const lastAssistant = [...conversation.messages].reverse().find((m) => m.role === "assistant");
|
|
328
|
+
return Boolean(lastAssistant?.text.trim());
|
|
329
|
+
}
|
|
330
|
+
trimMessages(conversation) {
|
|
331
|
+
if (conversation.messages.length <= MAX_MESSAGES) return;
|
|
332
|
+
conversation.messages.splice(0, conversation.messages.length - MAX_MESSAGES);
|
|
333
|
+
}
|
|
334
|
+
getOrCreate(runtimeId) {
|
|
335
|
+
let conversation = this.conversations.get(runtimeId);
|
|
336
|
+
if (conversation) return conversation;
|
|
337
|
+
conversation = {
|
|
338
|
+
runtimeId,
|
|
339
|
+
status: "disconnected",
|
|
340
|
+
messages: [],
|
|
341
|
+
ws: null,
|
|
342
|
+
connectPromise: null,
|
|
343
|
+
activeAssistantMessageId: null,
|
|
344
|
+
reconnectAttempt: 0,
|
|
345
|
+
reconnectTimer: null,
|
|
346
|
+
reconnectUrl: null
|
|
347
|
+
};
|
|
348
|
+
this.conversations.set(runtimeId, conversation);
|
|
349
|
+
return conversation;
|
|
350
|
+
}
|
|
351
|
+
emitChanged(conversation) {
|
|
352
|
+
this.emit("changed", publicConversation(conversation));
|
|
353
|
+
}
|
|
354
|
+
};
|
|
355
|
+
function publicConversation(conversation) {
|
|
356
|
+
return {
|
|
357
|
+
runtimeId: conversation.runtimeId,
|
|
358
|
+
status: conversation.status,
|
|
359
|
+
sessionId: conversation.sessionId,
|
|
360
|
+
error: conversation.error,
|
|
361
|
+
messages: conversation.messages.map((message) => ({ ...message }))
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
function stringValue(value) {
|
|
365
|
+
return typeof value === "string" ? value : void 0;
|
|
366
|
+
}
|
|
367
|
+
|
|
19
368
|
// src/main/ipc.ts
|
|
20
369
|
var IPC = {
|
|
21
370
|
getState: "desktop:get-state",
|
|
@@ -352,8 +701,8 @@ function tMain(key) {
|
|
|
352
701
|
}
|
|
353
702
|
|
|
354
703
|
// src/main/desktop-config-io.ts
|
|
355
|
-
import * as fs from "fs/promises";
|
|
356
|
-
import * as path from "path";
|
|
704
|
+
import * as fs from "node:fs/promises";
|
|
705
|
+
import * as path from "node:path";
|
|
357
706
|
import { DefaultSecretVault } from "@wrongstack/core";
|
|
358
707
|
import { decryptConfigSecrets, encryptConfigSecrets } from "@wrongstack/core/security";
|
|
359
708
|
import { atomicWrite, wstackGlobalRoot } from "@wrongstack/core/utils";
|
|
@@ -400,24 +749,24 @@ async function writeUiLocale(code) {
|
|
|
400
749
|
}
|
|
401
750
|
|
|
402
751
|
// src/main/runtime-manager.ts
|
|
403
|
-
import { spawn } from "child_process";
|
|
404
|
-
import { randomBytes } from "crypto";
|
|
405
|
-
import { EventEmitter } from "events";
|
|
406
|
-
import * as fs2 from "fs/promises";
|
|
407
|
-
import { existsSync } from "fs";
|
|
408
|
-
import * as http from "http";
|
|
409
|
-
import { createRequire } from "module";
|
|
410
|
-
import * as net from "net";
|
|
411
|
-
import * as os from "os";
|
|
412
|
-
import * as path2 from "path";
|
|
413
|
-
import { fileURLToPath } from "url";
|
|
752
|
+
import { spawn } from "node:child_process";
|
|
753
|
+
import { randomBytes } from "node:crypto";
|
|
754
|
+
import { EventEmitter as EventEmitter2 } from "node:events";
|
|
755
|
+
import * as fs2 from "node:fs/promises";
|
|
756
|
+
import { existsSync } from "node:fs";
|
|
757
|
+
import * as http from "node:http";
|
|
758
|
+
import { createRequire } from "node:module";
|
|
759
|
+
import * as net from "node:net";
|
|
760
|
+
import * as os from "node:os";
|
|
761
|
+
import * as path2 from "node:path";
|
|
762
|
+
import { fileURLToPath } from "node:url";
|
|
414
763
|
import { atomicWrite as atomicWrite2, projectSlug, toErrorMessage, wstackGlobalRoot as wstackGlobalRoot2 } from "@wrongstack/core/utils";
|
|
415
764
|
var HTTP_PORT_START = 34560;
|
|
416
765
|
var WS_PORT_START = 34660;
|
|
417
766
|
var START_TIMEOUT_MS = 3e4;
|
|
418
767
|
var MIN_WINDOW_WIDTH = 760;
|
|
419
768
|
var MIN_WINDOW_HEIGHT = 520;
|
|
420
|
-
var DesktopRuntimeManager = class extends
|
|
769
|
+
var DesktopRuntimeManager = class extends EventEmitter2 {
|
|
421
770
|
runtimes = /* @__PURE__ */ new Map();
|
|
422
771
|
stateFile = path2.join(wstackGlobalRoot2(), "desktop.json");
|
|
423
772
|
recentProjects = [];
|
|
@@ -1331,7 +1680,7 @@ function getSidebarWidth(windowWidth, collapsed) {
|
|
|
1331
1680
|
import { Menu } from "electron";
|
|
1332
1681
|
|
|
1333
1682
|
// src/main/menu/projects-menu.ts
|
|
1334
|
-
var posix = (await import("path")).posix;
|
|
1683
|
+
var posix = (await import("node:path")).posix;
|
|
1335
1684
|
function buildProjectsMenu(runtimes, actions, t) {
|
|
1336
1685
|
const projectGroups = groupProjectRuntimesForMenu(runtimes);
|
|
1337
1686
|
const menu = [
|
|
@@ -2454,4 +2803,4 @@ app.on("activate", () => {
|
|
|
2454
2803
|
mainWindow.show();
|
|
2455
2804
|
shellView?.webContents.focus();
|
|
2456
2805
|
});
|
|
2457
|
-
//# sourceMappingURL=main.js.map
|
|
2806
|
+
//# sourceMappingURL=main.js.map
|