codex-devtools 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +117 -0
- package/dist-electron/main/chunks/CodexServiceContext-CRkTP14W.cjs +2027 -0
- package/dist-electron/main/index.cjs +250 -0
- package/dist-electron/main/standalone.cjs +231 -0
- package/dist-electron/preload/index.cjs +37 -0
- package/out/renderer/assets/index-CR8-JZrV.js +11905 -0
- package/out/renderer/assets/index-r_pK0Xle.css +1916 -0
- package/out/renderer/index.html +22 -0
- package/package.json +121 -0
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const electron = require("electron");
|
|
3
|
+
const fs = require("node:fs");
|
|
4
|
+
const path = require("node:path");
|
|
5
|
+
const CodexServiceContext = require("./chunks/CodexServiceContext-CRkTP14W.cjs");
|
|
6
|
+
require("node:os");
|
|
7
|
+
require("node:events");
|
|
8
|
+
require("node:buffer");
|
|
9
|
+
require("node:readline");
|
|
10
|
+
const IPC_CHANNELS = {
|
|
11
|
+
SESSIONS_GET_PROJECTS: "get-projects",
|
|
12
|
+
SESSIONS_GET_SESSIONS: "get-sessions",
|
|
13
|
+
SESSIONS_GET_DETAIL: "get-session-detail",
|
|
14
|
+
SESSIONS_GET_CHUNKS: "get-session-chunks",
|
|
15
|
+
SEARCH_SESSIONS: "search-sessions",
|
|
16
|
+
CONFIG_GET: "config:get",
|
|
17
|
+
CONFIG_UPDATE: "config:update",
|
|
18
|
+
UTILITY_GET_APP_VERSION: "get-app-version",
|
|
19
|
+
EVENTS_FILE_CHANGE: "file-change"
|
|
20
|
+
};
|
|
21
|
+
const logger$5 = CodexServiceContext.createLogger("IPC:config");
|
|
22
|
+
let serviceContext$3;
|
|
23
|
+
function initializeConfigHandlers(context) {
|
|
24
|
+
serviceContext$3 = context;
|
|
25
|
+
}
|
|
26
|
+
function registerConfigHandlers(ipcMain) {
|
|
27
|
+
ipcMain.handle(IPC_CHANNELS.CONFIG_GET, handleGetConfig);
|
|
28
|
+
ipcMain.handle(IPC_CHANNELS.CONFIG_UPDATE, handleUpdateConfig);
|
|
29
|
+
logger$5.info("Config handlers registered");
|
|
30
|
+
}
|
|
31
|
+
function removeConfigHandlers(ipcMain) {
|
|
32
|
+
ipcMain.removeHandler(IPC_CHANNELS.CONFIG_GET);
|
|
33
|
+
ipcMain.removeHandler(IPC_CHANNELS.CONFIG_UPDATE);
|
|
34
|
+
logger$5.info("Config handlers removed");
|
|
35
|
+
}
|
|
36
|
+
async function handleGetConfig(_event) {
|
|
37
|
+
return serviceContext$3.getConfig();
|
|
38
|
+
}
|
|
39
|
+
async function handleUpdateConfig(_event, key, value) {
|
|
40
|
+
try {
|
|
41
|
+
return serviceContext$3.updateConfig(key, value);
|
|
42
|
+
} catch (error) {
|
|
43
|
+
logger$5.error(`Error in config:update for key ${String(key)}`, error);
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
const logger$4 = CodexServiceContext.createLogger("IPC:search");
|
|
48
|
+
let serviceContext$2;
|
|
49
|
+
function initializeSearchHandlers(context) {
|
|
50
|
+
serviceContext$2 = context;
|
|
51
|
+
}
|
|
52
|
+
function registerSearchHandlers(ipcMain) {
|
|
53
|
+
ipcMain.handle(IPC_CHANNELS.SEARCH_SESSIONS, handleSearchSessions);
|
|
54
|
+
logger$4.info("Search handlers registered");
|
|
55
|
+
}
|
|
56
|
+
function removeSearchHandlers(ipcMain) {
|
|
57
|
+
ipcMain.removeHandler(IPC_CHANNELS.SEARCH_SESSIONS);
|
|
58
|
+
logger$4.info("Search handlers removed");
|
|
59
|
+
}
|
|
60
|
+
async function handleSearchSessions(_event, query) {
|
|
61
|
+
try {
|
|
62
|
+
return await serviceContext$2.searchSessions(query);
|
|
63
|
+
} catch (error) {
|
|
64
|
+
logger$4.error("Error in search-sessions", error);
|
|
65
|
+
return {
|
|
66
|
+
query,
|
|
67
|
+
totalMatches: 0,
|
|
68
|
+
sessionsSearched: 0,
|
|
69
|
+
results: []
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const logger$3 = CodexServiceContext.createLogger("IPC:sessions");
|
|
74
|
+
let serviceContext$1;
|
|
75
|
+
function initializeSessionHandlers(context) {
|
|
76
|
+
serviceContext$1 = context;
|
|
77
|
+
}
|
|
78
|
+
function registerSessionHandlers(ipcMain) {
|
|
79
|
+
ipcMain.handle(IPC_CHANNELS.SESSIONS_GET_PROJECTS, handleGetProjects);
|
|
80
|
+
ipcMain.handle(IPC_CHANNELS.SESSIONS_GET_SESSIONS, handleGetSessions);
|
|
81
|
+
ipcMain.handle(IPC_CHANNELS.SESSIONS_GET_DETAIL, handleGetSessionDetail);
|
|
82
|
+
ipcMain.handle(IPC_CHANNELS.SESSIONS_GET_CHUNKS, handleGetSessionChunks);
|
|
83
|
+
logger$3.info("Session handlers registered");
|
|
84
|
+
}
|
|
85
|
+
function removeSessionHandlers(ipcMain) {
|
|
86
|
+
ipcMain.removeHandler(IPC_CHANNELS.SESSIONS_GET_PROJECTS);
|
|
87
|
+
ipcMain.removeHandler(IPC_CHANNELS.SESSIONS_GET_SESSIONS);
|
|
88
|
+
ipcMain.removeHandler(IPC_CHANNELS.SESSIONS_GET_DETAIL);
|
|
89
|
+
ipcMain.removeHandler(IPC_CHANNELS.SESSIONS_GET_CHUNKS);
|
|
90
|
+
logger$3.info("Session handlers removed");
|
|
91
|
+
}
|
|
92
|
+
async function handleGetProjects(_event) {
|
|
93
|
+
try {
|
|
94
|
+
return await serviceContext$1.getProjects();
|
|
95
|
+
} catch (error) {
|
|
96
|
+
logger$3.error("Error in get-projects", error);
|
|
97
|
+
return [];
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
async function handleGetSessions(_event, projectCwd) {
|
|
101
|
+
try {
|
|
102
|
+
return await serviceContext$1.getSessions(projectCwd);
|
|
103
|
+
} catch (error) {
|
|
104
|
+
logger$3.error(`Error in get-sessions for ${projectCwd}`, error);
|
|
105
|
+
return [];
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
async function handleGetSessionDetail(_event, sessionId) {
|
|
109
|
+
try {
|
|
110
|
+
return await serviceContext$1.getSessionDetail(sessionId);
|
|
111
|
+
} catch (error) {
|
|
112
|
+
logger$3.error(`Error in get-session-detail for ${sessionId}`, error);
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
async function handleGetSessionChunks(_event, sessionId) {
|
|
117
|
+
try {
|
|
118
|
+
return await serviceContext$1.getSessionChunks(sessionId);
|
|
119
|
+
} catch (error) {
|
|
120
|
+
logger$3.error(`Error in get-session-chunks for ${sessionId}`, error);
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
const logger$2 = CodexServiceContext.createLogger("IPC:utility");
|
|
125
|
+
let getAppVersion = () => "0.0.0";
|
|
126
|
+
function initializeUtilityHandlers(options = {}) {
|
|
127
|
+
if (options.getVersion) {
|
|
128
|
+
getAppVersion = options.getVersion;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
function registerUtilityHandlers(ipcMain) {
|
|
132
|
+
ipcMain.handle(IPC_CHANNELS.UTILITY_GET_APP_VERSION, handleGetAppVersion);
|
|
133
|
+
logger$2.info("Utility handlers registered");
|
|
134
|
+
}
|
|
135
|
+
function removeUtilityHandlers(ipcMain) {
|
|
136
|
+
ipcMain.removeHandler(IPC_CHANNELS.UTILITY_GET_APP_VERSION);
|
|
137
|
+
logger$2.info("Utility handlers removed");
|
|
138
|
+
}
|
|
139
|
+
function handleGetAppVersion(_event) {
|
|
140
|
+
try {
|
|
141
|
+
return getAppVersion();
|
|
142
|
+
} catch (error) {
|
|
143
|
+
logger$2.error("Error in get-app-version", error);
|
|
144
|
+
return "0.0.0";
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
const logger$1 = CodexServiceContext.createLogger("IPC:handlers");
|
|
148
|
+
const initializeIpcHandlers = (serviceContext2, targetIpcMain, options = {}) => {
|
|
149
|
+
initializeSessionHandlers(serviceContext2);
|
|
150
|
+
initializeSearchHandlers(serviceContext2);
|
|
151
|
+
initializeConfigHandlers(serviceContext2);
|
|
152
|
+
initializeUtilityHandlers({ getVersion: options.getVersion });
|
|
153
|
+
registerSessionHandlers(targetIpcMain);
|
|
154
|
+
registerSearchHandlers(targetIpcMain);
|
|
155
|
+
registerConfigHandlers(targetIpcMain);
|
|
156
|
+
registerUtilityHandlers(targetIpcMain);
|
|
157
|
+
logger$1.info("All handlers registered");
|
|
158
|
+
};
|
|
159
|
+
const removeIpcHandlers = (targetIpcMain) => {
|
|
160
|
+
removeSessionHandlers(targetIpcMain);
|
|
161
|
+
removeSearchHandlers(targetIpcMain);
|
|
162
|
+
removeConfigHandlers(targetIpcMain);
|
|
163
|
+
removeUtilityHandlers(targetIpcMain);
|
|
164
|
+
logger$1.info("All handlers removed");
|
|
165
|
+
};
|
|
166
|
+
const logger = CodexServiceContext.createLogger("Main");
|
|
167
|
+
let mainWindow = null;
|
|
168
|
+
let serviceContext = null;
|
|
169
|
+
let removeFileChangeListener = null;
|
|
170
|
+
function getRendererIndexPath() {
|
|
171
|
+
const candidates = [
|
|
172
|
+
path.join(__dirname, "../../out/renderer/index.html"),
|
|
173
|
+
path.join(__dirname, "../renderer/index.html")
|
|
174
|
+
];
|
|
175
|
+
return candidates.find((candidate) => fs.existsSync(candidate)) ?? candidates[0];
|
|
176
|
+
}
|
|
177
|
+
const createWindow = () => {
|
|
178
|
+
const window = new electron.BrowserWindow({
|
|
179
|
+
width: 1200,
|
|
180
|
+
height: 800,
|
|
181
|
+
webPreferences: {
|
|
182
|
+
preload: path.join(__dirname, "../preload/index.cjs"),
|
|
183
|
+
contextIsolation: true,
|
|
184
|
+
nodeIntegration: false
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
if (process.env.ELECTRON_RENDERER_URL) {
|
|
188
|
+
void window.loadURL(process.env.ELECTRON_RENDERER_URL);
|
|
189
|
+
} else {
|
|
190
|
+
void window.loadFile(getRendererIndexPath());
|
|
191
|
+
}
|
|
192
|
+
return window;
|
|
193
|
+
};
|
|
194
|
+
function wireFileWatcherEvents() {
|
|
195
|
+
if (!serviceContext) {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
if (removeFileChangeListener) {
|
|
199
|
+
removeFileChangeListener();
|
|
200
|
+
removeFileChangeListener = null;
|
|
201
|
+
}
|
|
202
|
+
removeFileChangeListener = serviceContext.onFileChange((event) => {
|
|
203
|
+
if (mainWindow && !mainWindow.isDestroyed()) {
|
|
204
|
+
mainWindow.webContents.send(IPC_CHANNELS.EVENTS_FILE_CHANGE, event);
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
function initializeServices() {
|
|
209
|
+
serviceContext = new CodexServiceContext.CodexServiceContext({
|
|
210
|
+
sessionsPath: process.env.CODEX_SESSIONS_PATH
|
|
211
|
+
});
|
|
212
|
+
serviceContext.start();
|
|
213
|
+
wireFileWatcherEvents();
|
|
214
|
+
initializeIpcHandlers(serviceContext, electron.ipcMain, { getVersion: () => electron.app.getVersion() });
|
|
215
|
+
}
|
|
216
|
+
function disposeServices() {
|
|
217
|
+
if (removeFileChangeListener) {
|
|
218
|
+
removeFileChangeListener();
|
|
219
|
+
removeFileChangeListener = null;
|
|
220
|
+
}
|
|
221
|
+
removeIpcHandlers(electron.ipcMain);
|
|
222
|
+
if (serviceContext) {
|
|
223
|
+
serviceContext.dispose();
|
|
224
|
+
serviceContext = null;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
void electron.app.whenReady().then(() => {
|
|
228
|
+
initializeServices();
|
|
229
|
+
mainWindow = createWindow();
|
|
230
|
+
electron.app.on("activate", () => {
|
|
231
|
+
if (electron.BrowserWindow.getAllWindows().length === 0) {
|
|
232
|
+
mainWindow = createWindow();
|
|
233
|
+
wireFileWatcherEvents();
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
electron.app.on("before-quit", () => {
|
|
238
|
+
disposeServices();
|
|
239
|
+
});
|
|
240
|
+
electron.app.on("window-all-closed", () => {
|
|
241
|
+
if (process.platform !== "darwin") {
|
|
242
|
+
electron.app.quit();
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
process.on("unhandledRejection", (reason) => {
|
|
246
|
+
logger.error("Unhandled promise rejection in main process", reason);
|
|
247
|
+
});
|
|
248
|
+
process.on("uncaughtException", (error) => {
|
|
249
|
+
logger.error("Uncaught exception in main process", error);
|
|
250
|
+
});
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const fastifyCors = require("@fastify/cors");
|
|
4
|
+
const fastifyStatic = require("@fastify/static");
|
|
5
|
+
const Fastify = require("fastify");
|
|
6
|
+
const path = require("node:path");
|
|
7
|
+
const node_url = require("node:url");
|
|
8
|
+
const CodexServiceContext = require("./chunks/CodexServiceContext-CRkTP14W.cjs");
|
|
9
|
+
const fs = require("node:fs");
|
|
10
|
+
require("node:os");
|
|
11
|
+
require("node:events");
|
|
12
|
+
require("node:buffer");
|
|
13
|
+
require("node:readline");
|
|
14
|
+
function _interopNamespaceDefault(e) {
|
|
15
|
+
const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
|
|
16
|
+
if (e) {
|
|
17
|
+
for (const k in e) {
|
|
18
|
+
if (k !== "default") {
|
|
19
|
+
const d = Object.getOwnPropertyDescriptor(e, k);
|
|
20
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
21
|
+
enumerable: true,
|
|
22
|
+
get: () => e[k]
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
n.default = e;
|
|
28
|
+
return Object.freeze(n);
|
|
29
|
+
}
|
|
30
|
+
const path__namespace = /* @__PURE__ */ _interopNamespaceDefault(path);
|
|
31
|
+
const fs__namespace = /* @__PURE__ */ _interopNamespaceDefault(fs);
|
|
32
|
+
const logger$6 = CodexServiceContext.createLogger("HTTP:config");
|
|
33
|
+
const registerConfigRoutes = (app, services) => {
|
|
34
|
+
app.get("/config", async () => {
|
|
35
|
+
return services.getConfig();
|
|
36
|
+
});
|
|
37
|
+
app.put("/config", async (request) => {
|
|
38
|
+
try {
|
|
39
|
+
return services.updateConfig(request.body.key, request.body.value);
|
|
40
|
+
} catch (error) {
|
|
41
|
+
logger$6.error("Error in PUT /config", error);
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
|
+
const logger$5 = CodexServiceContext.createLogger("HTTP:events");
|
|
47
|
+
const KEEPALIVE_INTERVAL_MS = 3e4;
|
|
48
|
+
const sseClients = /* @__PURE__ */ new Set();
|
|
49
|
+
const registerEventRoutes = (app) => {
|
|
50
|
+
app.get("/events", async (request, reply) => {
|
|
51
|
+
reply.raw.writeHead(200, {
|
|
52
|
+
"Content-Type": "text/event-stream",
|
|
53
|
+
"Cache-Control": "no-cache",
|
|
54
|
+
Connection: "keep-alive"
|
|
55
|
+
});
|
|
56
|
+
sseClients.add(reply);
|
|
57
|
+
logger$5.info(`SSE client connected (total: ${sseClients.size})`);
|
|
58
|
+
const timer = setInterval(() => {
|
|
59
|
+
reply.raw.write(":ping\n\n");
|
|
60
|
+
}, KEEPALIVE_INTERVAL_MS);
|
|
61
|
+
request.raw.on("close", () => {
|
|
62
|
+
clearInterval(timer);
|
|
63
|
+
sseClients.delete(reply);
|
|
64
|
+
logger$5.info(`SSE client disconnected (total: ${sseClients.size})`);
|
|
65
|
+
});
|
|
66
|
+
await reply;
|
|
67
|
+
});
|
|
68
|
+
};
|
|
69
|
+
const broadcastEvent = (channel, data) => {
|
|
70
|
+
const payload = `event: ${channel}
|
|
71
|
+
data: ${JSON.stringify(data)}
|
|
72
|
+
|
|
73
|
+
`;
|
|
74
|
+
for (const client of sseClients) {
|
|
75
|
+
try {
|
|
76
|
+
client.raw.write(payload);
|
|
77
|
+
} catch {
|
|
78
|
+
sseClients.delete(client);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
const broadcastFileChangeEvent = (event) => {
|
|
83
|
+
broadcastEvent("file-change", event);
|
|
84
|
+
};
|
|
85
|
+
const logger$4 = CodexServiceContext.createLogger("HTTP:projects");
|
|
86
|
+
function decodeCwd(value) {
|
|
87
|
+
try {
|
|
88
|
+
return decodeURIComponent(value);
|
|
89
|
+
} catch {
|
|
90
|
+
return value;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const registerProjectRoutes = (app, services) => {
|
|
94
|
+
app.get("/projects", async () => {
|
|
95
|
+
try {
|
|
96
|
+
return await services.getProjects();
|
|
97
|
+
} catch (error) {
|
|
98
|
+
logger$4.error("Error in GET /projects", error);
|
|
99
|
+
return [];
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
app.get("/projects/:cwd/sessions", async (request) => {
|
|
103
|
+
const cwd = decodeCwd(request.params.cwd);
|
|
104
|
+
try {
|
|
105
|
+
return await services.getSessions(cwd);
|
|
106
|
+
} catch (error) {
|
|
107
|
+
logger$4.error(`Error in GET /projects/${cwd}/sessions`, error);
|
|
108
|
+
return [];
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
};
|
|
112
|
+
const logger$3 = CodexServiceContext.createLogger("HTTP:search");
|
|
113
|
+
const registerSearchRoutes = (app, services) => {
|
|
114
|
+
app.get("/search", async (request) => {
|
|
115
|
+
const query = request.query.q ?? "";
|
|
116
|
+
try {
|
|
117
|
+
return await services.searchSessions(query);
|
|
118
|
+
} catch (error) {
|
|
119
|
+
logger$3.error("Error in GET /search", error);
|
|
120
|
+
return {
|
|
121
|
+
query,
|
|
122
|
+
totalMatches: 0,
|
|
123
|
+
sessionsSearched: 0,
|
|
124
|
+
results: []
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
};
|
|
129
|
+
const logger$2 = CodexServiceContext.createLogger("HTTP:sessions");
|
|
130
|
+
const registerSessionRoutes = (app, services) => {
|
|
131
|
+
app.get("/sessions/:id", async (request) => {
|
|
132
|
+
try {
|
|
133
|
+
return await services.getSessionDetail(request.params.id);
|
|
134
|
+
} catch (error) {
|
|
135
|
+
logger$2.error(`Error in GET /sessions/${request.params.id}`, error);
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
app.get("/sessions/:id/chunks", async (request) => {
|
|
140
|
+
try {
|
|
141
|
+
return await services.getSessionChunks(request.params.id);
|
|
142
|
+
} catch (error) {
|
|
143
|
+
logger$2.error(`Error in GET /sessions/${request.params.id}/chunks`, error);
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
};
|
|
148
|
+
const readVersionFromPackageJson = () => {
|
|
149
|
+
const candidatePaths = [
|
|
150
|
+
path__namespace.resolve(__dirname, "../../package.json"),
|
|
151
|
+
path__namespace.resolve(__dirname, "../../../package.json"),
|
|
152
|
+
path__namespace.resolve(process.cwd(), "package.json")
|
|
153
|
+
];
|
|
154
|
+
try {
|
|
155
|
+
for (const candidate of candidatePaths) {
|
|
156
|
+
if (!fs__namespace.existsSync(candidate)) {
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
const packageJson = JSON.parse(fs__namespace.readFileSync(candidate, "utf8"));
|
|
160
|
+
if (packageJson.version) {
|
|
161
|
+
return packageJson.version;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
} catch {
|
|
165
|
+
}
|
|
166
|
+
return "0.0.0";
|
|
167
|
+
};
|
|
168
|
+
const registerUtilityRoutes = (app, getVersion) => {
|
|
169
|
+
app.get("/version", async () => {
|
|
170
|
+
if (getVersion) {
|
|
171
|
+
return getVersion();
|
|
172
|
+
}
|
|
173
|
+
return readVersionFromPackageJson();
|
|
174
|
+
});
|
|
175
|
+
};
|
|
176
|
+
const logger$1 = CodexServiceContext.createLogger("HTTP:routes");
|
|
177
|
+
const registerHttpRoutes = (app, services) => {
|
|
178
|
+
registerProjectRoutes(app, services.serviceContext);
|
|
179
|
+
registerSessionRoutes(app, services.serviceContext);
|
|
180
|
+
registerSearchRoutes(app, services.serviceContext);
|
|
181
|
+
registerConfigRoutes(app, services.serviceContext);
|
|
182
|
+
registerUtilityRoutes(app, services.getVersion);
|
|
183
|
+
registerEventRoutes(app);
|
|
184
|
+
logger$1.info("All HTTP routes registered");
|
|
185
|
+
};
|
|
186
|
+
const logger = CodexServiceContext.createLogger("Standalone");
|
|
187
|
+
const __filename$1 = node_url.fileURLToPath(require("url").pathToFileURL(__filename).href);
|
|
188
|
+
const __dirname$1 = path.dirname(__filename$1);
|
|
189
|
+
const createStandaloneServer = async (options = {}) => {
|
|
190
|
+
const app = Fastify({ logger: true });
|
|
191
|
+
const serviceContext = new CodexServiceContext.CodexServiceContext({
|
|
192
|
+
sessionsPath: options.sessionsPath ?? process.env.CODEX_SESSIONS_PATH,
|
|
193
|
+
configPath: options.configPath
|
|
194
|
+
});
|
|
195
|
+
serviceContext.start();
|
|
196
|
+
const removeFileChangeListener = serviceContext.onFileChange((event) => {
|
|
197
|
+
broadcastFileChangeEvent(event);
|
|
198
|
+
});
|
|
199
|
+
app.addHook("onClose", async () => {
|
|
200
|
+
removeFileChangeListener();
|
|
201
|
+
serviceContext.dispose();
|
|
202
|
+
});
|
|
203
|
+
await app.register(fastifyCors, { origin: true });
|
|
204
|
+
await app.register(fastifyStatic, {
|
|
205
|
+
root: path.join(__dirname$1, "../../out/renderer"),
|
|
206
|
+
prefix: "/"
|
|
207
|
+
});
|
|
208
|
+
registerHttpRoutes(app, {
|
|
209
|
+
serviceContext,
|
|
210
|
+
getVersion: readVersionFromPackageJson
|
|
211
|
+
});
|
|
212
|
+
return { app, serviceContext };
|
|
213
|
+
};
|
|
214
|
+
const startStandalone = async () => {
|
|
215
|
+
const { app } = await createStandaloneServer();
|
|
216
|
+
const host = process.env.HOST ?? "0.0.0.0";
|
|
217
|
+
const port = Number(process.env.PORT ?? "3456");
|
|
218
|
+
await app.listen({ host, port });
|
|
219
|
+
logger.info(`Standalone server listening on http://${host}:${port}`);
|
|
220
|
+
};
|
|
221
|
+
const startStandaloneCli = (start = startStandalone) => {
|
|
222
|
+
void start().catch((error) => {
|
|
223
|
+
logger.error("Standalone startup failed", error);
|
|
224
|
+
process.exitCode = 1;
|
|
225
|
+
});
|
|
226
|
+
};
|
|
227
|
+
if (process.argv[1] && node_url.fileURLToPath(require("url").pathToFileURL(__filename).href) === process.argv[1]) {
|
|
228
|
+
startStandaloneCli();
|
|
229
|
+
}
|
|
230
|
+
exports.createStandaloneServer = createStandaloneServer;
|
|
231
|
+
exports.startStandaloneCli = startStandaloneCli;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const electron = require("electron");
|
|
3
|
+
const IPC_CHANNELS = {
|
|
4
|
+
SESSIONS_GET_PROJECTS: "get-projects",
|
|
5
|
+
SESSIONS_GET_SESSIONS: "get-sessions",
|
|
6
|
+
SESSIONS_GET_DETAIL: "get-session-detail",
|
|
7
|
+
SESSIONS_GET_CHUNKS: "get-session-chunks",
|
|
8
|
+
SEARCH_SESSIONS: "search-sessions",
|
|
9
|
+
CONFIG_GET: "config:get",
|
|
10
|
+
CONFIG_UPDATE: "config:update",
|
|
11
|
+
UTILITY_GET_APP_VERSION: "get-app-version",
|
|
12
|
+
EVENTS_FILE_CHANGE: "file-change"
|
|
13
|
+
};
|
|
14
|
+
function createCodexDevtoolsApi(renderer = electron.ipcRenderer) {
|
|
15
|
+
return {
|
|
16
|
+
channels: IPC_CHANNELS,
|
|
17
|
+
getProjects: () => renderer.invoke(IPC_CHANNELS.SESSIONS_GET_PROJECTS),
|
|
18
|
+
getSessions: (projectCwd) => renderer.invoke(IPC_CHANNELS.SESSIONS_GET_SESSIONS, projectCwd),
|
|
19
|
+
getSessionDetail: (sessionId) => renderer.invoke(IPC_CHANNELS.SESSIONS_GET_DETAIL, sessionId),
|
|
20
|
+
getSessionChunks: (sessionId) => renderer.invoke(IPC_CHANNELS.SESSIONS_GET_CHUNKS, sessionId),
|
|
21
|
+
searchSessions: (query) => renderer.invoke(IPC_CHANNELS.SEARCH_SESSIONS, query),
|
|
22
|
+
getConfig: () => renderer.invoke(IPC_CHANNELS.CONFIG_GET),
|
|
23
|
+
updateConfig: (key, value) => renderer.invoke(IPC_CHANNELS.CONFIG_UPDATE, key, value),
|
|
24
|
+
getAppVersion: () => renderer.invoke(IPC_CHANNELS.UTILITY_GET_APP_VERSION),
|
|
25
|
+
onFileChange: (callback) => {
|
|
26
|
+
const listener = (_event, payload) => {
|
|
27
|
+
callback(payload);
|
|
28
|
+
};
|
|
29
|
+
renderer.on(IPC_CHANNELS.EVENTS_FILE_CHANGE, listener);
|
|
30
|
+
return () => {
|
|
31
|
+
renderer.removeListener(IPC_CHANNELS.EVENTS_FILE_CHANGE, listener);
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
const api = createCodexDevtoolsApi();
|
|
37
|
+
electron.contextBridge.exposeInMainWorld("codexDevtools", api);
|