@wrongstack/desktop 0.286.0 → 0.287.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/main.js +361 -124
- package/dist/main/main.js.map +4 -4
- package/dist/renderer/assets/{index-cpNF4AJ5.js → index-Bih3lXdg.js} +166 -166
- package/dist/renderer/assets/index-Bih3lXdg.js.map +1 -0
- package/dist/renderer/index.html +5 -1
- package/package.json +4 -4
- package/dist/renderer/assets/index-cpNF4AJ5.js.map +0 -1
package/dist/main/main.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
// src/main/main.ts
|
|
2
|
-
import * as
|
|
2
|
+
import * as path4 from "node:path";
|
|
3
3
|
import * as fs3 from "node:fs/promises";
|
|
4
4
|
import { wstackGlobalRoot as wstackGlobalRoot3 } from "@wrongstack/core/utils";
|
|
5
5
|
import {
|
|
6
6
|
app,
|
|
7
7
|
BaseWindow,
|
|
8
8
|
dialog,
|
|
9
|
-
ipcMain,
|
|
9
|
+
ipcMain as ipcMain2,
|
|
10
|
+
nativeImage,
|
|
10
11
|
screen,
|
|
11
12
|
shell,
|
|
12
13
|
WebContentsView
|
|
@@ -874,8 +875,8 @@ var DesktopRuntimeManager = class extends EventEmitter2 {
|
|
|
874
875
|
}
|
|
875
876
|
async openProject(projectRoot, options = {}) {
|
|
876
877
|
const resolved = path2.resolve(projectRoot);
|
|
877
|
-
const
|
|
878
|
-
if (!
|
|
878
|
+
const stat3 = await fs2.stat(resolved).catch(() => null);
|
|
879
|
+
if (!stat3?.isDirectory()) throw new Error(`Not a directory: ${resolved}`);
|
|
879
880
|
const kind = options.kind ?? "project";
|
|
880
881
|
const touchRecent = options.touchRecent ?? kind === "project";
|
|
881
882
|
const forceNew = options.forceNew === true;
|
|
@@ -1049,8 +1050,8 @@ var DesktopRuntimeManager = class extends EventEmitter2 {
|
|
|
1049
1050
|
}
|
|
1050
1051
|
async registerProject(projectRoot) {
|
|
1051
1052
|
const resolved = path2.resolve(projectRoot);
|
|
1052
|
-
const
|
|
1053
|
-
if (!
|
|
1053
|
+
const stat3 = await fs2.stat(resolved).catch(() => null);
|
|
1054
|
+
if (!stat3?.isDirectory()) throw new Error(`Not a directory: ${resolved}`);
|
|
1054
1055
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1055
1056
|
const entry = {
|
|
1056
1057
|
name: path2.basename(resolved) || resolved,
|
|
@@ -1389,16 +1390,16 @@ function resolveWebUiEntry() {
|
|
|
1389
1390
|
if (existsSync(candidate)) return candidate;
|
|
1390
1391
|
} catch {
|
|
1391
1392
|
}
|
|
1392
|
-
const serverIndex = require2.resolve("@wrongstack/webui
|
|
1393
|
-
return path2.join(path2.dirname(serverIndex), "entry.js");
|
|
1393
|
+
const serverIndex = require2.resolve("@wrongstack/webui-server");
|
|
1394
|
+
return path2.join(path2.dirname(serverIndex), "server", "entry.js");
|
|
1394
1395
|
}
|
|
1395
1396
|
function resolveWebUiDistDir() {
|
|
1396
1397
|
if (process.env["WRONGSTACK_WEBUI_DIST"]) {
|
|
1397
1398
|
return path2.resolve(process.env["WRONGSTACK_WEBUI_DIST"]);
|
|
1398
1399
|
}
|
|
1399
1400
|
const require2 = createRequire(import.meta.url);
|
|
1400
|
-
const
|
|
1401
|
-
const candidate = path2.
|
|
1401
|
+
const serverEntry = require2.resolve("@wrongstack/webui");
|
|
1402
|
+
const candidate = path2.dirname(serverEntry);
|
|
1402
1403
|
if (existsSync(candidate)) return candidate;
|
|
1403
1404
|
throw new Error(
|
|
1404
1405
|
`WebUI frontend assets not found at ${candidate}. Build @wrongstack/webui or set WRONGSTACK_WEBUI_DIST.`
|
|
@@ -1663,10 +1664,17 @@ function isRecord(value) {
|
|
|
1663
1664
|
}
|
|
1664
1665
|
|
|
1665
1666
|
// src/main/state/constants.ts
|
|
1667
|
+
var OPEN_EXTERNAL_ALLOWED_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:", "mailto:"]);
|
|
1666
1668
|
var SIDEBAR_WIDTH_WIDE = 292;
|
|
1667
1669
|
var SIDEBAR_WIDTH_MEDIUM = 276;
|
|
1668
1670
|
var SIDEBAR_WIDTH_NARROW = 252;
|
|
1669
1671
|
var SIDEBAR_WIDTH_COLLAPSED = 56;
|
|
1672
|
+
var MIN_WINDOW_WIDTH2 = 760;
|
|
1673
|
+
var MIN_WINDOW_HEIGHT2 = 520;
|
|
1674
|
+
var MAX_PENDING_WEBUI_COMMANDS = 50;
|
|
1675
|
+
var MAX_PENDING_FLUSH_ATTEMPTS = 80;
|
|
1676
|
+
var WEBUI_COMMAND_FALLBACK_MS = 350;
|
|
1677
|
+
var WEBUI_COMMAND_ACK_TIMEOUT_MS = 2e3;
|
|
1670
1678
|
|
|
1671
1679
|
// src/main/layout/sidebar.ts
|
|
1672
1680
|
function getSidebarWidth(windowWidth, collapsed) {
|
|
@@ -1680,7 +1688,7 @@ function getSidebarWidth(windowWidth, collapsed) {
|
|
|
1680
1688
|
import { Menu } from "electron";
|
|
1681
1689
|
|
|
1682
1690
|
// src/main/menu/projects-menu.ts
|
|
1683
|
-
|
|
1691
|
+
import path3 from "node:path";
|
|
1684
1692
|
function buildProjectsMenu(runtimes, actions, t) {
|
|
1685
1693
|
const projectGroups = groupProjectRuntimesForMenu(runtimes);
|
|
1686
1694
|
const menu = [
|
|
@@ -1688,9 +1696,11 @@ function buildProjectsMenu(runtimes, actions, t) {
|
|
|
1688
1696
|
label: t("openProjectEllipsis"),
|
|
1689
1697
|
accelerator: "CmdOrCtrl+O",
|
|
1690
1698
|
click: () => actions.newSession("")
|
|
1691
|
-
// Will open dialog
|
|
1692
1699
|
},
|
|
1693
|
-
{
|
|
1700
|
+
{
|
|
1701
|
+
label: t("registerProjectEllipsis"),
|
|
1702
|
+
click: () => actions.registerProject?.()
|
|
1703
|
+
},
|
|
1694
1704
|
{ type: "separator" }
|
|
1695
1705
|
];
|
|
1696
1706
|
if (projectGroups.length === 0) {
|
|
@@ -1812,7 +1822,7 @@ function groupProjectRuntimesForMenu(runtimes) {
|
|
|
1812
1822
|
}
|
|
1813
1823
|
groups.set(key, {
|
|
1814
1824
|
key,
|
|
1815
|
-
name:
|
|
1825
|
+
name: path3.basename(runtime.root) || runtime.name,
|
|
1816
1826
|
root: runtime.root,
|
|
1817
1827
|
sessions: [runtime]
|
|
1818
1828
|
});
|
|
@@ -1983,6 +1993,7 @@ function configureApplicationMenu(ctx) {
|
|
|
1983
1993
|
const actions = {
|
|
1984
1994
|
activate: (runtimeId) => void ctx.activateRuntime(runtimeId),
|
|
1985
1995
|
activateAndNavigate,
|
|
1996
|
+
registerProject: () => void ctx.registerProject(),
|
|
1986
1997
|
newSession: (runtimeId) => {
|
|
1987
1998
|
if (runtimeId) void ctx.openProjectSession(runtimeId);
|
|
1988
1999
|
else void ctx.openProject();
|
|
@@ -2010,16 +2021,295 @@ function configureApplicationMenu(ctx) {
|
|
|
2010
2021
|
Menu.setApplicationMenu(Menu.buildFromTemplate(template));
|
|
2011
2022
|
}
|
|
2012
2023
|
|
|
2024
|
+
// src/main/ipc-handlers/index.ts
|
|
2025
|
+
import { ipcMain } from "electron";
|
|
2026
|
+
|
|
2027
|
+
// src/main/validation/index.ts
|
|
2028
|
+
function validate(schema, data) {
|
|
2029
|
+
try {
|
|
2030
|
+
const result = schema.safeParse(data);
|
|
2031
|
+
if (result.success) {
|
|
2032
|
+
return { success: true, data: result.data };
|
|
2033
|
+
}
|
|
2034
|
+
return { success: false, error: formatZodError(result.error) };
|
|
2035
|
+
} catch (err) {
|
|
2036
|
+
return { success: false, error: String(err) };
|
|
2037
|
+
}
|
|
2038
|
+
}
|
|
2039
|
+
function validateOrDefault(schema, data, defaultValue) {
|
|
2040
|
+
const result = validate(schema, data);
|
|
2041
|
+
return result.success ? result.data : defaultValue;
|
|
2042
|
+
}
|
|
2043
|
+
function formatZodError(error) {
|
|
2044
|
+
return error.issues.map((e) => `${e.path.join(".")}: ${e.message}`).join("; ");
|
|
2045
|
+
}
|
|
2046
|
+
function createValidationLogger(prefix) {
|
|
2047
|
+
const loggedErrors = /* @__PURE__ */ new Set();
|
|
2048
|
+
const maxErrors = 100;
|
|
2049
|
+
return {
|
|
2050
|
+
log(error) {
|
|
2051
|
+
if (loggedErrors.size >= maxErrors) return;
|
|
2052
|
+
const key = `${prefix}:${error}`;
|
|
2053
|
+
if (!loggedErrors.has(key)) {
|
|
2054
|
+
loggedErrors.add(key);
|
|
2055
|
+
console.warn(
|
|
2056
|
+
JSON.stringify({
|
|
2057
|
+
level: "warn",
|
|
2058
|
+
event: "desktop.validation_error",
|
|
2059
|
+
prefix,
|
|
2060
|
+
message: error,
|
|
2061
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
2062
|
+
})
|
|
2063
|
+
);
|
|
2064
|
+
}
|
|
2065
|
+
},
|
|
2066
|
+
reset() {
|
|
2067
|
+
loggedErrors.clear();
|
|
2068
|
+
}
|
|
2069
|
+
};
|
|
2070
|
+
}
|
|
2071
|
+
|
|
2072
|
+
// src/main/validation/schemas.ts
|
|
2073
|
+
import { z } from "zod";
|
|
2074
|
+
var RUNTIME_ID_PATTERN = /^[a-zA-Z0-9._:-]{3,120}$/;
|
|
2075
|
+
var runtimeIdSchema = z.string().regex(RUNTIME_ID_PATTERN, "Invalid runtime ID format");
|
|
2076
|
+
var pathSchema = z.string().min(1).max(1e4);
|
|
2077
|
+
var nonEmptyStringSchema = z.string().min(1);
|
|
2078
|
+
var booleanSchema = z.boolean();
|
|
2079
|
+
var numberSchema = z.number().int().finite();
|
|
2080
|
+
var openProjectSchema = z.object({
|
|
2081
|
+
requestedRoot: pathSchema.optional()
|
|
2082
|
+
});
|
|
2083
|
+
var registerProjectSchema = z.object({
|
|
2084
|
+
requestedRoot: pathSchema.optional()
|
|
2085
|
+
});
|
|
2086
|
+
var unregisterProjectSchema = z.object({
|
|
2087
|
+
root: pathSchema
|
|
2088
|
+
});
|
|
2089
|
+
var openProjectSessionSchema = z.object({
|
|
2090
|
+
runtimeId: runtimeIdSchema.optional()
|
|
2091
|
+
});
|
|
2092
|
+
var activateRuntimeSchema = z.object({
|
|
2093
|
+
id: runtimeIdSchema
|
|
2094
|
+
});
|
|
2095
|
+
var closeRuntimeSchema = z.object({
|
|
2096
|
+
id: runtimeIdSchema
|
|
2097
|
+
});
|
|
2098
|
+
var sendMessageSchema = z.object({
|
|
2099
|
+
id: runtimeIdSchema,
|
|
2100
|
+
content: z.string()
|
|
2101
|
+
});
|
|
2102
|
+
var abortRuntimeSchema = z.object({
|
|
2103
|
+
id: runtimeIdSchema
|
|
2104
|
+
});
|
|
2105
|
+
var openRuntimeInBrowserSchema = z.object({
|
|
2106
|
+
id: runtimeIdSchema
|
|
2107
|
+
});
|
|
2108
|
+
var revealRuntimeRootSchema = z.object({
|
|
2109
|
+
id: runtimeIdSchema
|
|
2110
|
+
});
|
|
2111
|
+
var setShellSidebarCollapsedSchema = z.object({
|
|
2112
|
+
collapsed: booleanSchema
|
|
2113
|
+
});
|
|
2114
|
+
var navigateWebuiSchema = z.object({
|
|
2115
|
+
command: z.unknown()
|
|
2116
|
+
});
|
|
2117
|
+
var getConversationSchema = z.object({
|
|
2118
|
+
runtimeId: runtimeIdSchema
|
|
2119
|
+
});
|
|
2120
|
+
var webuiReadyChangedSchema = z.object({
|
|
2121
|
+
ready: booleanSchema
|
|
2122
|
+
});
|
|
2123
|
+
var webuiPrefsChangedSchema = z.object({
|
|
2124
|
+
prefs: z.record(z.string(), z.unknown())
|
|
2125
|
+
});
|
|
2126
|
+
var webuiCommandAckSchema = z.object({
|
|
2127
|
+
requestId: z.string(),
|
|
2128
|
+
handled: z.boolean(),
|
|
2129
|
+
message: z.string().optional()
|
|
2130
|
+
});
|
|
2131
|
+
var setLocaleSchema = z.object({
|
|
2132
|
+
locale: z.string().min(2).max(10)
|
|
2133
|
+
});
|
|
2134
|
+
|
|
2135
|
+
// src/main/ipc-handlers/index.ts
|
|
2136
|
+
var validationLogger = createValidationLogger("IPC");
|
|
2137
|
+
function registerIpcHandlers(ctx) {
|
|
2138
|
+
ipcMain.handle(IPC.getState, () => ctx.getRuntimeManager().snapshot());
|
|
2139
|
+
ipcMain.handle(IPC.getConversation, (_event, runtimeId) => {
|
|
2140
|
+
const result = validate(runtimeIdSchema, runtimeId);
|
|
2141
|
+
if (!result.success) {
|
|
2142
|
+
validationLogger.log(`getConversation: ${result.error}`);
|
|
2143
|
+
return ctx.getAgentBridge().snapshot("");
|
|
2144
|
+
}
|
|
2145
|
+
return ctx.getAgentBridge().snapshot(result.data);
|
|
2146
|
+
});
|
|
2147
|
+
ipcMain.handle(IPC.getWebuiStatus, () => ctx.getWebuiStatus());
|
|
2148
|
+
ipcMain.handle(IPC.navigateWebui, async (_event, command) => {
|
|
2149
|
+
return ctx.dispatchWebuiCommand(command);
|
|
2150
|
+
});
|
|
2151
|
+
ipcMain.handle(IPC.reloadWebui, async () => ctx.reloadActiveWebuiView());
|
|
2152
|
+
ipcMain.handle(IPC.setShellSidebarCollapsed, (_event, collapsed) => {
|
|
2153
|
+
const result = validateOrDefault(
|
|
2154
|
+
pathSchema.transform(() => collapsed === true),
|
|
2155
|
+
collapsed,
|
|
2156
|
+
true
|
|
2157
|
+
);
|
|
2158
|
+
ctx.setShellSidebarCollapsed(result);
|
|
2159
|
+
return true;
|
|
2160
|
+
});
|
|
2161
|
+
ipcMain.handle(IPC.openSettings, async () => ctx.openSettings());
|
|
2162
|
+
ipcMain.handle(IPC.openProjectSession, async (_event, runtimeId) => {
|
|
2163
|
+
const validated = validateOptional(runtimeIdSchema, runtimeId);
|
|
2164
|
+
return ctx.openProjectSession(validated);
|
|
2165
|
+
});
|
|
2166
|
+
ipcMain.handle(IPC.openProject, async (_event, requestedRoot) => {
|
|
2167
|
+
const validated = validateOptional(pathSchema, requestedRoot);
|
|
2168
|
+
return ctx.openProject(validated);
|
|
2169
|
+
});
|
|
2170
|
+
ipcMain.handle(IPC.registerProject, async (_event, requestedRoot) => {
|
|
2171
|
+
const validated = validateOptional(pathSchema, requestedRoot);
|
|
2172
|
+
return ctx.registerProject(validated);
|
|
2173
|
+
});
|
|
2174
|
+
ipcMain.handle(IPC.unregisterProject, async (_event, root) => {
|
|
2175
|
+
const result = validate(pathSchema, root);
|
|
2176
|
+
if (!result.success) {
|
|
2177
|
+
validationLogger.log(`unregisterProject: ${result.error}`);
|
|
2178
|
+
return ctx.getRuntimeManager().snapshot();
|
|
2179
|
+
}
|
|
2180
|
+
return ctx.unregisterProject(result.data);
|
|
2181
|
+
});
|
|
2182
|
+
ipcMain.handle(IPC.activateRuntime, async (_event, id) => {
|
|
2183
|
+
const result = validate(runtimeIdSchema, id);
|
|
2184
|
+
if (!result.success) {
|
|
2185
|
+
validationLogger.log(`activateRuntime: ${result.error}`);
|
|
2186
|
+
return ctx.getRuntimeManager().snapshot();
|
|
2187
|
+
}
|
|
2188
|
+
return ctx.activateRuntime(result.data);
|
|
2189
|
+
});
|
|
2190
|
+
ipcMain.handle(IPC.closeRuntime, async (_event, id) => {
|
|
2191
|
+
const result = validate(runtimeIdSchema, id);
|
|
2192
|
+
if (!result.success) {
|
|
2193
|
+
validationLogger.log(`closeRuntime: ${result.error}`);
|
|
2194
|
+
return ctx.getRuntimeManager().snapshot();
|
|
2195
|
+
}
|
|
2196
|
+
return ctx.closeRuntime(result.data);
|
|
2197
|
+
});
|
|
2198
|
+
ipcMain.handle(IPC.sendMessage, async (_event, id, content) => {
|
|
2199
|
+
const idResult = validate(runtimeIdSchema, id);
|
|
2200
|
+
if (!idResult.success) {
|
|
2201
|
+
validationLogger.log(`sendMessage (id): ${idResult.error}`);
|
|
2202
|
+
return ctx.sendMessage("", "", "");
|
|
2203
|
+
}
|
|
2204
|
+
const contentResult = validate(pathSchema.transform(() => String(content ?? "")), content);
|
|
2205
|
+
const safeContent = contentResult.success ? contentResult.data : String(content ?? "");
|
|
2206
|
+
return ctx.sendMessage(
|
|
2207
|
+
idResult.data,
|
|
2208
|
+
ctx.getRuntimeManager().getRuntimeWsUrlWithToken(idResult.data) ?? "",
|
|
2209
|
+
safeContent
|
|
2210
|
+
);
|
|
2211
|
+
});
|
|
2212
|
+
ipcMain.handle(IPC.abortRuntime, async (_event, id) => {
|
|
2213
|
+
const result = validate(runtimeIdSchema, id);
|
|
2214
|
+
if (!result.success) {
|
|
2215
|
+
validationLogger.log(`abortRuntime: ${result.error}`);
|
|
2216
|
+
return ctx.abortRuntime("", "");
|
|
2217
|
+
}
|
|
2218
|
+
return ctx.abortRuntime(
|
|
2219
|
+
result.data,
|
|
2220
|
+
ctx.getRuntimeManager().getRuntimeWsUrlWithToken(result.data) ?? ""
|
|
2221
|
+
);
|
|
2222
|
+
});
|
|
2223
|
+
ipcMain.handle(IPC.openRuntimeInBrowser, async (_event, id) => {
|
|
2224
|
+
const result = validate(runtimeIdSchema, id);
|
|
2225
|
+
if (!result.success) {
|
|
2226
|
+
validationLogger.log(`openRuntimeInBrowser: ${result.error}`);
|
|
2227
|
+
return;
|
|
2228
|
+
}
|
|
2229
|
+
const url = ctx.getRuntimeManager().getRuntimeUrlWithToken(result.data);
|
|
2230
|
+
if (url) ctx.openExternal(url);
|
|
2231
|
+
});
|
|
2232
|
+
ipcMain.handle(IPC.revealRuntimeRoot, async (_event, id) => {
|
|
2233
|
+
const result = validate(runtimeIdSchema, id);
|
|
2234
|
+
if (!result.success) {
|
|
2235
|
+
validationLogger.log(`revealRuntimeRoot: ${result.error}`);
|
|
2236
|
+
return;
|
|
2237
|
+
}
|
|
2238
|
+
const runtime = ctx.getRuntimeManager().getRuntime(result.data);
|
|
2239
|
+
if (runtime) ctx.revealInExplorer(runtime.root);
|
|
2240
|
+
});
|
|
2241
|
+
ipcMain.on(IPC.webuiReadyChanged, (event, ready) => {
|
|
2242
|
+
const entry = ctx.findWebuiEntryBySenderId(event.sender.id);
|
|
2243
|
+
if (!entry) return;
|
|
2244
|
+
const isReady = ready === true;
|
|
2245
|
+
entry.bridgeReady = isReady;
|
|
2246
|
+
if (entry.bridgeReady) {
|
|
2247
|
+
ctx.setEntryWebuiStatus(entry, { ...entry.status, status: "ready" });
|
|
2248
|
+
ctx.schedulePendingWebuiFlush(entry);
|
|
2249
|
+
} else if (entry.status.status === "ready") {
|
|
2250
|
+
ctx.setEntryWebuiStatus(entry, { ...entry.status, status: "loading" });
|
|
2251
|
+
}
|
|
2252
|
+
});
|
|
2253
|
+
ipcMain.on(IPC.webuiPrefsChanged, (event, prefs) => {
|
|
2254
|
+
const entry = ctx.findWebuiEntryBySenderId(event.sender.id);
|
|
2255
|
+
if (!entry) return;
|
|
2256
|
+
const sanitized = sanitizeWebuiPrefs(prefs);
|
|
2257
|
+
if (Object.keys(sanitized).length === 0) return;
|
|
2258
|
+
ctx.setEntryWebuiStatus(entry, {
|
|
2259
|
+
...entry.status,
|
|
2260
|
+
prefs: { ...entry.status.prefs ?? {}, ...sanitized }
|
|
2261
|
+
});
|
|
2262
|
+
});
|
|
2263
|
+
ipcMain.on(IPC.webuiCommandAck, (event, requestId, handled, _message) => {
|
|
2264
|
+
const entry = ctx.findWebuiEntryBySenderId(event.sender.id);
|
|
2265
|
+
if (!entry) return;
|
|
2266
|
+
const result = validate(webuiCommandAckSchema, {
|
|
2267
|
+
requestId,
|
|
2268
|
+
handled,
|
|
2269
|
+
message: _message
|
|
2270
|
+
});
|
|
2271
|
+
if (!result.success) {
|
|
2272
|
+
validationLogger.log(`webuiCommandAck: ${result.error}`);
|
|
2273
|
+
return;
|
|
2274
|
+
}
|
|
2275
|
+
const pending = ctx.getPendingWebuiCommandAcks().get(result.data.requestId);
|
|
2276
|
+
if (!pending || pending.runtimeId !== entry.runtimeId) return;
|
|
2277
|
+
ctx.settlePendingWebuiCommandAck(result.data.requestId, result.data.handled);
|
|
2278
|
+
});
|
|
2279
|
+
ipcMain.on(IPC.setLocale, (_event, locale) => {
|
|
2280
|
+
const result = validate(setLocaleSchema, { locale });
|
|
2281
|
+
if (!result.success) {
|
|
2282
|
+
validationLogger.log(`setLocale: ${result.error}`);
|
|
2283
|
+
return;
|
|
2284
|
+
}
|
|
2285
|
+
ctx.getI18n().setMainLocale(result.data.locale);
|
|
2286
|
+
ctx.configureApplicationMenu();
|
|
2287
|
+
ctx.broadcastLocaleToEmbeddedWebuis(result.data.locale);
|
|
2288
|
+
void ctx.getConfigIo().writeUiLocale(result.data.locale);
|
|
2289
|
+
});
|
|
2290
|
+
}
|
|
2291
|
+
function validateOptional(schema, value) {
|
|
2292
|
+
if (value === void 0 || value === null) return void 0;
|
|
2293
|
+
const result = schema.safeParse(value);
|
|
2294
|
+
return result.success ? result.data : void 0;
|
|
2295
|
+
}
|
|
2296
|
+
function sanitizeWebuiPrefs(prefs) {
|
|
2297
|
+
const next = {};
|
|
2298
|
+
if (!isRecord2(prefs)) return next;
|
|
2299
|
+
if (typeof prefs["yolo"] === "boolean") next.yolo = prefs["yolo"];
|
|
2300
|
+
if (typeof prefs["nextPrediction"] === "boolean") next.nextPrediction = prefs["nextPrediction"];
|
|
2301
|
+
if (typeof prefs["contextAutoCompact"] === "boolean") {
|
|
2302
|
+
next.contextAutoCompact = prefs["contextAutoCompact"];
|
|
2303
|
+
}
|
|
2304
|
+
return next;
|
|
2305
|
+
}
|
|
2306
|
+
function isRecord2(value) {
|
|
2307
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
2308
|
+
}
|
|
2309
|
+
|
|
2013
2310
|
// src/main/main.ts
|
|
2014
|
-
var OPEN_EXTERNAL_ALLOWED_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:", "mailto:"]);
|
|
2015
|
-
var MIN_WINDOW_WIDTH2 = 760;
|
|
2016
|
-
var MIN_WINDOW_HEIGHT2 = 520;
|
|
2017
|
-
var MAX_PENDING_WEBUI_COMMANDS = 50;
|
|
2018
|
-
var MAX_PENDING_FLUSH_ATTEMPTS = 80;
|
|
2019
|
-
var WEBUI_COMMAND_FALLBACK_MS = 350;
|
|
2020
|
-
var WEBUI_COMMAND_ACK_TIMEOUT_MS = 2e3;
|
|
2021
2311
|
app.setAppUserModelId("com.wrongstack.desktop");
|
|
2022
|
-
app.setPath("userData",
|
|
2312
|
+
app.setPath("userData", path4.join(wstackGlobalRoot3(), "desktop", "electron-profile"));
|
|
2023
2313
|
var manager = new DesktopRuntimeManager();
|
|
2024
2314
|
var bridge = new DesktopAgentBridge();
|
|
2025
2315
|
var mainWindow = null;
|
|
@@ -2051,14 +2341,6 @@ function sameOrigin(candidate, base) {
|
|
|
2051
2341
|
return false;
|
|
2052
2342
|
}
|
|
2053
2343
|
}
|
|
2054
|
-
function isRecord2(value) {
|
|
2055
|
-
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
2056
|
-
}
|
|
2057
|
-
function runtimeWsUrlOrThrow(runtimeId) {
|
|
2058
|
-
const wsUrl = manager.getRuntimeWsUrlWithToken(runtimeId);
|
|
2059
|
-
if (!wsUrl) throw new Error(`Runtime not found: ${runtimeId}`);
|
|
2060
|
-
return wsUrl;
|
|
2061
|
-
}
|
|
2062
2344
|
function setShellSidebarCollapsed(collapsed) {
|
|
2063
2345
|
shellSidebarCollapsed = collapsed;
|
|
2064
2346
|
layoutWebuiViews();
|
|
@@ -2610,98 +2892,44 @@ function createMenuContext() {
|
|
|
2610
2892
|
function configureApplicationMenu2() {
|
|
2611
2893
|
configureApplicationMenu(createMenuContext());
|
|
2612
2894
|
}
|
|
2613
|
-
function
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
(
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
async (_event, id) => bridge.abort(id, runtimeWsUrlOrThrow(id))
|
|
2652
|
-
);
|
|
2653
|
-
ipcMain.handle(IPC.openRuntimeInBrowser, async (_event, id) => {
|
|
2654
|
-
const url = manager.getRuntimeUrlWithToken(id);
|
|
2655
|
-
if (url) safeOpenExternal(url);
|
|
2656
|
-
});
|
|
2657
|
-
ipcMain.handle(IPC.revealRuntimeRoot, async (_event, id) => {
|
|
2658
|
-
const runtime = manager.getRuntime(id);
|
|
2659
|
-
if (runtime) void shell.openPath(runtime.root);
|
|
2660
|
-
});
|
|
2661
|
-
ipcMain.on(IPC.webuiReadyChanged, (event, ready) => {
|
|
2662
|
-
const entry = findWebuiEntryBySenderId(event.sender.id);
|
|
2663
|
-
if (!entry) return;
|
|
2664
|
-
entry.bridgeReady = ready === true;
|
|
2665
|
-
if (entry.bridgeReady) {
|
|
2666
|
-
setEntryWebuiStatus(entry, { ...entry.status, status: "ready" });
|
|
2667
|
-
schedulePendingWebuiFlush(entry);
|
|
2668
|
-
} else if (entry.status.status === "ready") {
|
|
2669
|
-
setEntryWebuiStatus(entry, { ...entry.status, status: "loading" });
|
|
2670
|
-
}
|
|
2671
|
-
});
|
|
2672
|
-
ipcMain.on(IPC.webuiPrefsChanged, (event, prefs) => {
|
|
2673
|
-
const entry = findWebuiEntryBySenderId(event.sender.id);
|
|
2674
|
-
if (!entry) return;
|
|
2675
|
-
const next = {};
|
|
2676
|
-
if (isRecord2(prefs) && typeof prefs["yolo"] === "boolean") next.yolo = prefs["yolo"];
|
|
2677
|
-
if (isRecord2(prefs) && typeof prefs["nextPrediction"] === "boolean") {
|
|
2678
|
-
next.nextPrediction = prefs["nextPrediction"];
|
|
2679
|
-
}
|
|
2680
|
-
if (isRecord2(prefs) && typeof prefs["contextAutoCompact"] === "boolean") {
|
|
2681
|
-
next.contextAutoCompact = prefs["contextAutoCompact"];
|
|
2682
|
-
}
|
|
2683
|
-
if (Object.keys(next).length === 0) return;
|
|
2684
|
-
setEntryWebuiStatus(entry, {
|
|
2685
|
-
...entry.status,
|
|
2686
|
-
prefs: { ...entry.status.prefs ?? {}, ...next }
|
|
2687
|
-
});
|
|
2688
|
-
});
|
|
2689
|
-
ipcMain.on(
|
|
2690
|
-
IPC.webuiCommandAck,
|
|
2691
|
-
(event, requestId, handled, _message) => {
|
|
2692
|
-
const entry = findWebuiEntryBySenderId(event.sender.id);
|
|
2693
|
-
if (!entry || typeof requestId !== "string") return;
|
|
2694
|
-
const pending = pendingWebuiCommandAcks.get(requestId);
|
|
2695
|
-
if (!pending || pending.runtimeId !== entry.runtimeId) return;
|
|
2696
|
-
settlePendingWebuiCommandAck(requestId, handled === true);
|
|
2697
|
-
}
|
|
2698
|
-
);
|
|
2699
|
-
ipcMain.on(IPC.setLocale, (_event, locale) => {
|
|
2700
|
-
setMainLocale(locale);
|
|
2701
|
-
configureApplicationMenu2();
|
|
2702
|
-
broadcastLocaleToEmbeddedWebuis(locale);
|
|
2703
|
-
void writeUiLocale(locale);
|
|
2704
|
-
});
|
|
2895
|
+
function buildIpcHandlerContext() {
|
|
2896
|
+
return {
|
|
2897
|
+
getMainWindow: () => mainWindow,
|
|
2898
|
+
getShellView: () => shellView,
|
|
2899
|
+
getWebuiViews: () => webuiViews,
|
|
2900
|
+
getWebuiStatus: () => webuiStatus,
|
|
2901
|
+
getRuntimeManager: () => manager,
|
|
2902
|
+
getAgentBridge: () => bridge,
|
|
2903
|
+
getI18n: () => ({ getMainLocale, setMainLocale, tMain }),
|
|
2904
|
+
getConfigIo: () => ({ desktopConfigPaths, readUiLocale, writeUiLocale }),
|
|
2905
|
+
getShellSidebarCollapsed: () => shellSidebarCollapsed,
|
|
2906
|
+
setShellSidebarCollapsed: (collapsed) => setShellSidebarCollapsed(collapsed),
|
|
2907
|
+
broadcastState: () => broadcastState(),
|
|
2908
|
+
publishWebuiStatus: (next) => publishWebuiStatus(next),
|
|
2909
|
+
syncActiveWebuiView: () => syncActiveWebuiView(),
|
|
2910
|
+
configureApplicationMenu: () => configureApplicationMenu2(),
|
|
2911
|
+
broadcastLocaleToEmbeddedWebuis: (locale) => broadcastLocaleToEmbeddedWebuis(locale),
|
|
2912
|
+
dispatchWebuiCommand: (command) => dispatchWebuiCommand(command),
|
|
2913
|
+
reloadActiveWebuiView: () => reloadActiveWebuiView(),
|
|
2914
|
+
openProject: (root) => openProject(root),
|
|
2915
|
+
registerProject: (root) => registerProject(root),
|
|
2916
|
+
unregisterProject: (root) => unregisterProject(root),
|
|
2917
|
+
openProjectSession: (id) => openProjectSession(id),
|
|
2918
|
+
activateRuntime: (id) => activateRuntime(id),
|
|
2919
|
+
closeRuntime: (id) => closeRuntime(id),
|
|
2920
|
+
openSettings: () => openSettings(),
|
|
2921
|
+
sendMessage: (id, wsUrl, content) => bridge.sendMessage(id, wsUrl, content),
|
|
2922
|
+
abortRuntime: (id, wsUrl) => bridge.abort(id, wsUrl),
|
|
2923
|
+
openExternal: (url) => safeOpenExternal(url),
|
|
2924
|
+
revealInExplorer: (root) => {
|
|
2925
|
+
void shell.openPath(root);
|
|
2926
|
+
},
|
|
2927
|
+
findWebuiEntryBySenderId: (senderId) => findWebuiEntryBySenderId(senderId),
|
|
2928
|
+
getPendingWebuiCommandAcks: () => pendingWebuiCommandAcks,
|
|
2929
|
+
settlePendingWebuiCommandAck: (requestId, handled) => settlePendingWebuiCommandAck(requestId, handled),
|
|
2930
|
+
setEntryWebuiStatus: (entry, next) => setEntryWebuiStatus(entry, next),
|
|
2931
|
+
schedulePendingWebuiFlush: (entry) => schedulePendingWebuiFlush(entry)
|
|
2932
|
+
};
|
|
2705
2933
|
}
|
|
2706
2934
|
async function boot() {
|
|
2707
2935
|
const locale = await readUiLocale();
|
|
@@ -2719,18 +2947,27 @@ async function boot() {
|
|
|
2719
2947
|
safeOpenExternal(url);
|
|
2720
2948
|
return { action: "deny" };
|
|
2721
2949
|
});
|
|
2722
|
-
registerIpcHandlers();
|
|
2950
|
+
registerIpcHandlers(buildIpcHandlerContext());
|
|
2723
2951
|
await shellView.webContents.loadURL(shellUrl);
|
|
2724
2952
|
const prevState = validatedWindowState(manager.getWindowState());
|
|
2725
2953
|
const defaultWidth = 1180;
|
|
2726
2954
|
const defaultHeight = 720;
|
|
2955
|
+
let appIcon;
|
|
2956
|
+
try {
|
|
2957
|
+
const iconPath = new URL("../../assets/icon.svg", import.meta.url).pathname;
|
|
2958
|
+
await fs3.stat(iconPath);
|
|
2959
|
+
appIcon = nativeImage.createFromPath(iconPath);
|
|
2960
|
+
if (appIcon.isEmpty()) appIcon = void 0;
|
|
2961
|
+
} catch {
|
|
2962
|
+
}
|
|
2727
2963
|
const winOptions = {
|
|
2728
2964
|
width: prevState?.width ?? defaultWidth,
|
|
2729
2965
|
height: prevState?.height ?? defaultHeight,
|
|
2730
2966
|
show: false,
|
|
2731
2967
|
minWidth: MIN_WINDOW_WIDTH2,
|
|
2732
2968
|
minHeight: MIN_WINDOW_HEIGHT2,
|
|
2733
|
-
title: tMain("windowTitle")
|
|
2969
|
+
title: tMain("windowTitle"),
|
|
2970
|
+
...appIcon ? { icon: appIcon } : {}
|
|
2734
2971
|
};
|
|
2735
2972
|
if (prevState) {
|
|
2736
2973
|
if (prevState.x !== void 0) winOptions.x = prevState.x;
|