codex-devtools 0.1.10 → 0.2.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/bin/codex-devtools.cjs +1 -0
- package/dist-electron/main/chunks/{CodexServiceContext-CRkTP14W.cjs → CodexServiceContext-BYe2UXME.cjs} +788 -56
- package/dist-electron/main/index.cjs +46 -2
- package/dist-electron/main/standalone.cjs +76 -15
- package/dist-electron/preload/index.cjs +2 -0
- package/out/renderer/assets/{index-C1DUQHyp.js → index-C-iGxog-.js} +467 -23
- package/out/renderer/assets/{index-BTmVA30y.css → index-D3FYKy1U.css} +230 -16
- package/out/renderer/index.html +2 -2
- package/package.json +1 -1
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
const electron = require("electron");
|
|
3
3
|
const fs = require("node:fs");
|
|
4
4
|
const path = require("node:path");
|
|
5
|
-
const CodexServiceContext = require("./chunks/CodexServiceContext-
|
|
6
|
-
require("node:os");
|
|
5
|
+
const CodexServiceContext = require("./chunks/CodexServiceContext-BYe2UXME.cjs");
|
|
7
6
|
require("node:events");
|
|
7
|
+
require("node:os");
|
|
8
|
+
require("node:crypto");
|
|
8
9
|
require("node:buffer");
|
|
9
10
|
require("node:readline");
|
|
10
11
|
const IPC_CHANNELS = {
|
|
@@ -12,6 +13,7 @@ const IPC_CHANNELS = {
|
|
|
12
13
|
SESSIONS_GET_SESSIONS: "get-sessions",
|
|
13
14
|
SESSIONS_GET_DETAIL: "get-session-detail",
|
|
14
15
|
SESSIONS_GET_CHUNKS: "get-session-chunks",
|
|
16
|
+
SESSIONS_GET_STATS: "get-session-stats",
|
|
15
17
|
SEARCH_SESSIONS: "search-sessions",
|
|
16
18
|
CONFIG_GET: "config:get",
|
|
17
19
|
CONFIG_UPDATE: "config:update",
|
|
@@ -80,6 +82,7 @@ function registerSessionHandlers(ipcMain) {
|
|
|
80
82
|
ipcMain.handle(IPC_CHANNELS.SESSIONS_GET_SESSIONS, handleGetSessions);
|
|
81
83
|
ipcMain.handle(IPC_CHANNELS.SESSIONS_GET_DETAIL, handleGetSessionDetail);
|
|
82
84
|
ipcMain.handle(IPC_CHANNELS.SESSIONS_GET_CHUNKS, handleGetSessionChunks);
|
|
85
|
+
ipcMain.handle(IPC_CHANNELS.SESSIONS_GET_STATS, handleGetSessionStats);
|
|
83
86
|
logger$3.info("Session handlers registered");
|
|
84
87
|
}
|
|
85
88
|
function removeSessionHandlers(ipcMain) {
|
|
@@ -87,6 +90,7 @@ function removeSessionHandlers(ipcMain) {
|
|
|
87
90
|
ipcMain.removeHandler(IPC_CHANNELS.SESSIONS_GET_SESSIONS);
|
|
88
91
|
ipcMain.removeHandler(IPC_CHANNELS.SESSIONS_GET_DETAIL);
|
|
89
92
|
ipcMain.removeHandler(IPC_CHANNELS.SESSIONS_GET_CHUNKS);
|
|
93
|
+
ipcMain.removeHandler(IPC_CHANNELS.SESSIONS_GET_STATS);
|
|
90
94
|
logger$3.info("Session handlers removed");
|
|
91
95
|
}
|
|
92
96
|
async function handleGetProjects(_event) {
|
|
@@ -121,6 +125,45 @@ async function handleGetSessionChunks(_event, sessionId) {
|
|
|
121
125
|
return null;
|
|
122
126
|
}
|
|
123
127
|
}
|
|
128
|
+
async function handleGetSessionStats(_event, scope) {
|
|
129
|
+
try {
|
|
130
|
+
return await serviceContext$1.getStats(scope);
|
|
131
|
+
} catch (error) {
|
|
132
|
+
logger$3.error("Error in get-session-stats", error);
|
|
133
|
+
return {
|
|
134
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
135
|
+
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone || "local",
|
|
136
|
+
scope: { type: "all" },
|
|
137
|
+
totals: {
|
|
138
|
+
sessions: 0,
|
|
139
|
+
archivedSessions: 0,
|
|
140
|
+
eventCount: 0,
|
|
141
|
+
durationMs: 0,
|
|
142
|
+
estimatedCostUsd: 0,
|
|
143
|
+
totalTokens: 0,
|
|
144
|
+
inputTokens: 0,
|
|
145
|
+
outputTokens: 0,
|
|
146
|
+
cachedTokens: 0,
|
|
147
|
+
reasoningTokens: 0
|
|
148
|
+
},
|
|
149
|
+
daily: [],
|
|
150
|
+
hourly: [],
|
|
151
|
+
topDays: [],
|
|
152
|
+
topHours: [],
|
|
153
|
+
models: [],
|
|
154
|
+
reasoningEfforts: [],
|
|
155
|
+
costCoverage: {
|
|
156
|
+
pricedTokens: 0,
|
|
157
|
+
unpricedTokens: 0,
|
|
158
|
+
unpricedModels: []
|
|
159
|
+
},
|
|
160
|
+
rates: {
|
|
161
|
+
updatedAt: null,
|
|
162
|
+
source: null
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
}
|
|
124
167
|
const logger$2 = CodexServiceContext.createLogger("IPC:utility");
|
|
125
168
|
let getAppVersion = () => "0.0.0";
|
|
126
169
|
function initializeUtilityHandlers(options = {}) {
|
|
@@ -240,6 +283,7 @@ function disposeServices() {
|
|
|
240
283
|
}
|
|
241
284
|
}
|
|
242
285
|
electron.app.setName(APP_DISPLAY_NAME);
|
|
286
|
+
process.title = APP_DISPLAY_NAME;
|
|
243
287
|
void electron.app.whenReady().then(() => {
|
|
244
288
|
const iconPath = resolveAppIconPath();
|
|
245
289
|
if (iconPath && process.platform === "darwin" && electron.app.dock) {
|
|
@@ -5,10 +5,11 @@ const fastifyStatic = require("@fastify/static");
|
|
|
5
5
|
const Fastify = require("fastify");
|
|
6
6
|
const path = require("node:path");
|
|
7
7
|
const node_url = require("node:url");
|
|
8
|
-
const CodexServiceContext = require("./chunks/CodexServiceContext-
|
|
8
|
+
const CodexServiceContext = require("./chunks/CodexServiceContext-BYe2UXME.cjs");
|
|
9
9
|
const fs = require("node:fs");
|
|
10
|
-
require("node:os");
|
|
11
10
|
require("node:events");
|
|
11
|
+
require("node:os");
|
|
12
|
+
require("node:crypto");
|
|
12
13
|
require("node:buffer");
|
|
13
14
|
require("node:readline");
|
|
14
15
|
function _interopNamespaceDefault(e) {
|
|
@@ -29,7 +30,7 @@ function _interopNamespaceDefault(e) {
|
|
|
29
30
|
}
|
|
30
31
|
const path__namespace = /* @__PURE__ */ _interopNamespaceDefault(path);
|
|
31
32
|
const fs__namespace = /* @__PURE__ */ _interopNamespaceDefault(fs);
|
|
32
|
-
const logger$
|
|
33
|
+
const logger$7 = CodexServiceContext.createLogger("HTTP:config");
|
|
33
34
|
const registerConfigRoutes = (app, services) => {
|
|
34
35
|
app.get("/config", async () => {
|
|
35
36
|
return services.getConfig();
|
|
@@ -38,12 +39,12 @@ const registerConfigRoutes = (app, services) => {
|
|
|
38
39
|
try {
|
|
39
40
|
return services.updateConfig(request.body.key, request.body.value);
|
|
40
41
|
} catch (error) {
|
|
41
|
-
logger$
|
|
42
|
+
logger$7.error("Error in PUT /config", error);
|
|
42
43
|
return null;
|
|
43
44
|
}
|
|
44
45
|
});
|
|
45
46
|
};
|
|
46
|
-
const logger$
|
|
47
|
+
const logger$6 = CodexServiceContext.createLogger("HTTP:events");
|
|
47
48
|
const KEEPALIVE_INTERVAL_MS = 3e4;
|
|
48
49
|
const sseClients = /* @__PURE__ */ new Set();
|
|
49
50
|
const registerEventRoutes = (app) => {
|
|
@@ -54,14 +55,14 @@ const registerEventRoutes = (app) => {
|
|
|
54
55
|
Connection: "keep-alive"
|
|
55
56
|
});
|
|
56
57
|
sseClients.add(reply);
|
|
57
|
-
logger$
|
|
58
|
+
logger$6.info(`SSE client connected (total: ${sseClients.size})`);
|
|
58
59
|
const timer = setInterval(() => {
|
|
59
60
|
reply.raw.write(":ping\n\n");
|
|
60
61
|
}, KEEPALIVE_INTERVAL_MS);
|
|
61
62
|
request.raw.on("close", () => {
|
|
62
63
|
clearInterval(timer);
|
|
63
64
|
sseClients.delete(reply);
|
|
64
|
-
logger$
|
|
65
|
+
logger$6.info(`SSE client disconnected (total: ${sseClients.size})`);
|
|
65
66
|
});
|
|
66
67
|
await reply;
|
|
67
68
|
});
|
|
@@ -82,7 +83,7 @@ data: ${JSON.stringify(data)}
|
|
|
82
83
|
const broadcastFileChangeEvent = (event) => {
|
|
83
84
|
broadcastEvent("file-change", event);
|
|
84
85
|
};
|
|
85
|
-
const logger$
|
|
86
|
+
const logger$5 = CodexServiceContext.createLogger("HTTP:projects");
|
|
86
87
|
function decodeCwd(value) {
|
|
87
88
|
try {
|
|
88
89
|
return decodeURIComponent(value);
|
|
@@ -95,7 +96,7 @@ const registerProjectRoutes = (app, services) => {
|
|
|
95
96
|
try {
|
|
96
97
|
return await services.getProjects();
|
|
97
98
|
} catch (error) {
|
|
98
|
-
logger$
|
|
99
|
+
logger$5.error("Error in GET /projects", error);
|
|
99
100
|
return [];
|
|
100
101
|
}
|
|
101
102
|
});
|
|
@@ -104,19 +105,19 @@ const registerProjectRoutes = (app, services) => {
|
|
|
104
105
|
try {
|
|
105
106
|
return await services.getSessions(cwd);
|
|
106
107
|
} catch (error) {
|
|
107
|
-
logger$
|
|
108
|
+
logger$5.error(`Error in GET /projects/${cwd}/sessions`, error);
|
|
108
109
|
return [];
|
|
109
110
|
}
|
|
110
111
|
});
|
|
111
112
|
};
|
|
112
|
-
const logger$
|
|
113
|
+
const logger$4 = CodexServiceContext.createLogger("HTTP:search");
|
|
113
114
|
const registerSearchRoutes = (app, services) => {
|
|
114
115
|
app.get("/search", async (request) => {
|
|
115
116
|
const query = request.query.q ?? "";
|
|
116
117
|
try {
|
|
117
118
|
return await services.searchSessions(query);
|
|
118
119
|
} catch (error) {
|
|
119
|
-
logger$
|
|
120
|
+
logger$4.error("Error in GET /search", error);
|
|
120
121
|
return {
|
|
121
122
|
query,
|
|
122
123
|
totalMatches: 0,
|
|
@@ -126,13 +127,13 @@ const registerSearchRoutes = (app, services) => {
|
|
|
126
127
|
}
|
|
127
128
|
});
|
|
128
129
|
};
|
|
129
|
-
const logger$
|
|
130
|
+
const logger$3 = CodexServiceContext.createLogger("HTTP:sessions");
|
|
130
131
|
const registerSessionRoutes = (app, services) => {
|
|
131
132
|
app.get("/sessions/:id", async (request) => {
|
|
132
133
|
try {
|
|
133
134
|
return await services.getSessionDetail(request.params.id);
|
|
134
135
|
} catch (error) {
|
|
135
|
-
logger$
|
|
136
|
+
logger$3.error(`Error in GET /sessions/${request.params.id}`, error);
|
|
136
137
|
return null;
|
|
137
138
|
}
|
|
138
139
|
});
|
|
@@ -140,11 +141,70 @@ const registerSessionRoutes = (app, services) => {
|
|
|
140
141
|
try {
|
|
141
142
|
return await services.getSessionChunks(request.params.id);
|
|
142
143
|
} catch (error) {
|
|
143
|
-
logger$
|
|
144
|
+
logger$3.error(`Error in GET /sessions/${request.params.id}/chunks`, error);
|
|
144
145
|
return null;
|
|
145
146
|
}
|
|
146
147
|
});
|
|
147
148
|
};
|
|
149
|
+
const logger$2 = CodexServiceContext.createLogger("HTTP:stats");
|
|
150
|
+
function decodeMaybeUri(value) {
|
|
151
|
+
try {
|
|
152
|
+
return decodeURIComponent(value);
|
|
153
|
+
} catch {
|
|
154
|
+
return value;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
function parseStatsScope(scope, cwd) {
|
|
158
|
+
if (scope === "project" && cwd && cwd.trim().length > 0) {
|
|
159
|
+
return {
|
|
160
|
+
type: "project",
|
|
161
|
+
cwd: decodeMaybeUri(cwd)
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
return { type: "all" };
|
|
165
|
+
}
|
|
166
|
+
const registerStatsRoutes = (app, services) => {
|
|
167
|
+
app.get("/stats", async (request) => {
|
|
168
|
+
try {
|
|
169
|
+
const scope = parseStatsScope(request.query.scope, request.query.cwd);
|
|
170
|
+
return await services.getStats(scope);
|
|
171
|
+
} catch (error) {
|
|
172
|
+
logger$2.error("Error in GET /stats", error);
|
|
173
|
+
return {
|
|
174
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
175
|
+
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone || "local",
|
|
176
|
+
scope: { type: "all" },
|
|
177
|
+
totals: {
|
|
178
|
+
sessions: 0,
|
|
179
|
+
archivedSessions: 0,
|
|
180
|
+
eventCount: 0,
|
|
181
|
+
durationMs: 0,
|
|
182
|
+
estimatedCostUsd: 0,
|
|
183
|
+
totalTokens: 0,
|
|
184
|
+
inputTokens: 0,
|
|
185
|
+
outputTokens: 0,
|
|
186
|
+
cachedTokens: 0,
|
|
187
|
+
reasoningTokens: 0
|
|
188
|
+
},
|
|
189
|
+
daily: [],
|
|
190
|
+
hourly: [],
|
|
191
|
+
topDays: [],
|
|
192
|
+
topHours: [],
|
|
193
|
+
models: [],
|
|
194
|
+
reasoningEfforts: [],
|
|
195
|
+
costCoverage: {
|
|
196
|
+
pricedTokens: 0,
|
|
197
|
+
unpricedTokens: 0,
|
|
198
|
+
unpricedModels: []
|
|
199
|
+
},
|
|
200
|
+
rates: {
|
|
201
|
+
updatedAt: null,
|
|
202
|
+
source: null
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
};
|
|
148
208
|
const readVersionFromPackageJson = () => {
|
|
149
209
|
const candidatePaths = [
|
|
150
210
|
path__namespace.resolve(__dirname, "../../package.json"),
|
|
@@ -177,6 +237,7 @@ const logger$1 = CodexServiceContext.createLogger("HTTP:routes");
|
|
|
177
237
|
const registerHttpRoutes = (app, services) => {
|
|
178
238
|
registerProjectRoutes(app, services.serviceContext);
|
|
179
239
|
registerSessionRoutes(app, services.serviceContext);
|
|
240
|
+
registerStatsRoutes(app, services.serviceContext);
|
|
180
241
|
registerSearchRoutes(app, services.serviceContext);
|
|
181
242
|
registerConfigRoutes(app, services.serviceContext);
|
|
182
243
|
registerUtilityRoutes(app, services.getVersion);
|
|
@@ -5,6 +5,7 @@ const IPC_CHANNELS = {
|
|
|
5
5
|
SESSIONS_GET_SESSIONS: "get-sessions",
|
|
6
6
|
SESSIONS_GET_DETAIL: "get-session-detail",
|
|
7
7
|
SESSIONS_GET_CHUNKS: "get-session-chunks",
|
|
8
|
+
SESSIONS_GET_STATS: "get-session-stats",
|
|
8
9
|
SEARCH_SESSIONS: "search-sessions",
|
|
9
10
|
CONFIG_GET: "config:get",
|
|
10
11
|
CONFIG_UPDATE: "config:update",
|
|
@@ -18,6 +19,7 @@ function createCodexDevtoolsApi(renderer = electron.ipcRenderer) {
|
|
|
18
19
|
getSessions: (projectCwd) => renderer.invoke(IPC_CHANNELS.SESSIONS_GET_SESSIONS, projectCwd),
|
|
19
20
|
getSessionDetail: (sessionId) => renderer.invoke(IPC_CHANNELS.SESSIONS_GET_DETAIL, sessionId),
|
|
20
21
|
getSessionChunks: (sessionId) => renderer.invoke(IPC_CHANNELS.SESSIONS_GET_CHUNKS, sessionId),
|
|
22
|
+
getStats: (scope) => renderer.invoke(IPC_CHANNELS.SESSIONS_GET_STATS, scope),
|
|
21
23
|
searchSessions: (query) => renderer.invoke(IPC_CHANNELS.SEARCH_SESSIONS, query),
|
|
22
24
|
getConfig: () => renderer.invoke(IPC_CHANNELS.CONFIG_GET),
|
|
23
25
|
updateConfig: (key, value) => renderer.invoke(IPC_CHANNELS.CONFIG_UPDATE, key, value),
|