remcodex 0.1.0-beta.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/LICENSE +21 -0
- package/README.md +331 -0
- package/dist/server/src/app.js +186 -0
- package/dist/server/src/cli.js +270 -0
- package/dist/server/src/controllers/codex-options.controller.js +199 -0
- package/dist/server/src/controllers/message.controller.js +21 -0
- package/dist/server/src/controllers/project.controller.js +44 -0
- package/dist/server/src/controllers/session.controller.js +175 -0
- package/dist/server/src/db/client.js +10 -0
- package/dist/server/src/db/migrations.js +32 -0
- package/dist/server/src/gateways/ws.gateway.js +60 -0
- package/dist/server/src/services/codex-app-server-runner.js +363 -0
- package/dist/server/src/services/codex-exec-runner.js +147 -0
- package/dist/server/src/services/codex-rollout-sync.js +977 -0
- package/dist/server/src/services/codex-runner.js +11 -0
- package/dist/server/src/services/codex-stream-events.js +478 -0
- package/dist/server/src/services/event-store.js +328 -0
- package/dist/server/src/services/project-manager.js +130 -0
- package/dist/server/src/services/pty-runner.js +72 -0
- package/dist/server/src/services/session-manager.js +1586 -0
- package/dist/server/src/services/session-timeline-service.js +181 -0
- package/dist/server/src/types/codex-launch.js +2 -0
- package/dist/server/src/types/models.js +37 -0
- package/dist/server/src/utils/ansi.js +143 -0
- package/dist/server/src/utils/codex-launch.js +102 -0
- package/dist/server/src/utils/codex-quota.js +179 -0
- package/dist/server/src/utils/codex-status.js +163 -0
- package/dist/server/src/utils/codex-ui-options.js +114 -0
- package/dist/server/src/utils/command.js +46 -0
- package/dist/server/src/utils/errors.js +16 -0
- package/dist/server/src/utils/ids.js +7 -0
- package/dist/server/src/utils/node-pty.js +29 -0
- package/package.json +36 -0
- package/scripts/fix-node-pty-helper.js +36 -0
- package/web/api.js +175 -0
- package/web/app.js +8082 -0
- package/web/components/composer.js +627 -0
- package/web/components/session-workbench.js +173 -0
- package/web/i18n/index.js +171 -0
- package/web/i18n/locales/de.js +50 -0
- package/web/i18n/locales/en.js +320 -0
- package/web/i18n/locales/es.js +50 -0
- package/web/i18n/locales/fr.js +50 -0
- package/web/i18n/locales/ja.js +50 -0
- package/web/i18n/locales/ko.js +50 -0
- package/web/i18n/locales/pt-BR.js +50 -0
- package/web/i18n/locales/ru.js +50 -0
- package/web/i18n/locales/zh-CN.js +320 -0
- package/web/i18n/locales/zh-Hant.js +53 -0
- package/web/index.html +23 -0
- package/web/message-rich-text.js +218 -0
- package/web/session-command-activity.js +980 -0
- package/web/session-event-adapter.js +826 -0
- package/web/session-timeline-reducer.js +728 -0
- package/web/session-timeline-renderer.js +656 -0
- package/web/session-ws.js +31 -0
- package/web/styles.css +5665 -0
- package/web/vendor/markdown-it.js +6969 -0
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SessionTimelineService = void 0;
|
|
4
|
+
const DEFAULT_TIMELINE_LIMIT = 200;
|
|
5
|
+
const MAX_TIMELINE_LIMIT = 400;
|
|
6
|
+
function clampLimit(limit) {
|
|
7
|
+
const numeric = Number(limit || DEFAULT_TIMELINE_LIMIT);
|
|
8
|
+
if (!Number.isFinite(numeric) || numeric <= 0) {
|
|
9
|
+
return DEFAULT_TIMELINE_LIMIT;
|
|
10
|
+
}
|
|
11
|
+
return Math.max(1, Math.min(Math.trunc(numeric), MAX_TIMELINE_LIMIT));
|
|
12
|
+
}
|
|
13
|
+
function normalizeCursor(value) {
|
|
14
|
+
const numeric = Number(value || 0);
|
|
15
|
+
if (!Number.isFinite(numeric) || numeric <= 0) {
|
|
16
|
+
return 0;
|
|
17
|
+
}
|
|
18
|
+
return Math.trunc(numeric);
|
|
19
|
+
}
|
|
20
|
+
function cloneEvent(event) {
|
|
21
|
+
return {
|
|
22
|
+
...event,
|
|
23
|
+
payload: event.payload && typeof event.payload === "object"
|
|
24
|
+
? { ...event.payload }
|
|
25
|
+
: event.payload,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function appendTextDelta(currentValue, nextValue) {
|
|
29
|
+
if (!nextValue) {
|
|
30
|
+
return currentValue || "";
|
|
31
|
+
}
|
|
32
|
+
return `${currentValue || ""}${nextValue}`;
|
|
33
|
+
}
|
|
34
|
+
function compareEvents(left, right) {
|
|
35
|
+
if (left.seq !== right.seq) {
|
|
36
|
+
return left.seq - right.seq;
|
|
37
|
+
}
|
|
38
|
+
return String(left.id || "").localeCompare(String(right.id || ""));
|
|
39
|
+
}
|
|
40
|
+
function upsertTimelineEvent(items, indexById, nextEvent) {
|
|
41
|
+
const existing = indexById.get(nextEvent.id);
|
|
42
|
+
if (existing) {
|
|
43
|
+
Object.assign(existing, nextEvent);
|
|
44
|
+
return existing;
|
|
45
|
+
}
|
|
46
|
+
const cloned = cloneEvent(nextEvent);
|
|
47
|
+
items.push(cloned);
|
|
48
|
+
indexById.set(cloned.id, cloned);
|
|
49
|
+
return cloned;
|
|
50
|
+
}
|
|
51
|
+
function timelineAssistantDeltaId(event) {
|
|
52
|
+
return `timeline:assistant:delta:${event.messageId || event.id}`;
|
|
53
|
+
}
|
|
54
|
+
function timelineReasoningDeltaId(event) {
|
|
55
|
+
return `timeline:reasoning:delta:${event.messageId || event.id}`;
|
|
56
|
+
}
|
|
57
|
+
function timelineCommandOutputId(event) {
|
|
58
|
+
return `timeline:command:output:${event.callId || event.id}:${event.stream || "stdout"}`;
|
|
59
|
+
}
|
|
60
|
+
function timelinePatchOutputId(event) {
|
|
61
|
+
return `timeline:patch:output:${event.callId || event.id}`;
|
|
62
|
+
}
|
|
63
|
+
function aggregateSemanticTimeline(rawEvents) {
|
|
64
|
+
const items = [];
|
|
65
|
+
const indexById = new Map();
|
|
66
|
+
rawEvents.forEach((event) => {
|
|
67
|
+
switch (event.type) {
|
|
68
|
+
case "message.assistant.delta": {
|
|
69
|
+
const syntheticId = timelineAssistantDeltaId(event);
|
|
70
|
+
const existing = indexById.get(syntheticId);
|
|
71
|
+
upsertTimelineEvent(items, indexById, {
|
|
72
|
+
...cloneEvent(event),
|
|
73
|
+
id: syntheticId,
|
|
74
|
+
payload: {
|
|
75
|
+
...(event.payload || {}),
|
|
76
|
+
textDelta: appendTextDelta(existing?.payload?.textDelta || "", String(event.payload?.textDelta || "")),
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
case "reasoning.delta": {
|
|
82
|
+
const syntheticId = timelineReasoningDeltaId(event);
|
|
83
|
+
const existing = indexById.get(syntheticId);
|
|
84
|
+
const nextText = appendTextDelta(existing?.payload?.textDelta || "", String(event.payload?.textDelta || ""));
|
|
85
|
+
upsertTimelineEvent(items, indexById, {
|
|
86
|
+
...cloneEvent(event),
|
|
87
|
+
id: syntheticId,
|
|
88
|
+
payload: {
|
|
89
|
+
...(event.payload || {}),
|
|
90
|
+
textDelta: nextText,
|
|
91
|
+
summary: event.payload?.summary ||
|
|
92
|
+
existing?.payload?.summary ||
|
|
93
|
+
nextText ||
|
|
94
|
+
null,
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
case "command.output.delta": {
|
|
100
|
+
const syntheticId = timelineCommandOutputId(event);
|
|
101
|
+
const existing = indexById.get(syntheticId);
|
|
102
|
+
upsertTimelineEvent(items, indexById, {
|
|
103
|
+
...cloneEvent(event),
|
|
104
|
+
id: syntheticId,
|
|
105
|
+
payload: {
|
|
106
|
+
...(event.payload || {}),
|
|
107
|
+
textDelta: appendTextDelta(existing?.payload?.textDelta || "", String(event.payload?.textDelta || "")),
|
|
108
|
+
stream: event.payload?.stream || event.stream || "stdout",
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
case "patch.output.delta": {
|
|
114
|
+
const syntheticId = timelinePatchOutputId(event);
|
|
115
|
+
const existing = indexById.get(syntheticId);
|
|
116
|
+
upsertTimelineEvent(items, indexById, {
|
|
117
|
+
...cloneEvent(event),
|
|
118
|
+
id: syntheticId,
|
|
119
|
+
payload: {
|
|
120
|
+
...(event.payload || {}),
|
|
121
|
+
textDelta: appendTextDelta(existing?.payload?.textDelta || "", String(event.payload?.textDelta || "")),
|
|
122
|
+
},
|
|
123
|
+
});
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
default:
|
|
127
|
+
upsertTimelineEvent(items, indexById, event);
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
return items.sort(compareEvents);
|
|
132
|
+
}
|
|
133
|
+
function paginateTimelineItems(items, options, lastSeq) {
|
|
134
|
+
const limit = clampLimit(options.limit);
|
|
135
|
+
const after = normalizeCursor(options.after);
|
|
136
|
+
const before = normalizeCursor(options.before);
|
|
137
|
+
if (before > 0) {
|
|
138
|
+
const matches = items.filter((item) => item.seq < before);
|
|
139
|
+
const hasMoreBefore = matches.length > limit;
|
|
140
|
+
const pageItems = matches.slice(Math.max(0, matches.length - limit));
|
|
141
|
+
return {
|
|
142
|
+
items: pageItems,
|
|
143
|
+
nextCursor: pageItems.at(-1)?.seq || after,
|
|
144
|
+
beforeCursor: pageItems[0]?.seq || before,
|
|
145
|
+
hasMoreBefore,
|
|
146
|
+
lastSeq,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
if (after > 0) {
|
|
150
|
+
const pageItems = items.filter((item) => item.seq > after).slice(0, limit);
|
|
151
|
+
return {
|
|
152
|
+
items: pageItems,
|
|
153
|
+
nextCursor: pageItems.at(-1)?.seq || after,
|
|
154
|
+
beforeCursor: pageItems[0]?.seq || 0,
|
|
155
|
+
hasMoreBefore: pageItems.length > 0 ? pageItems[0].seq > 1 : items.length > 0,
|
|
156
|
+
lastSeq,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
const hasMoreBefore = items.length > limit;
|
|
160
|
+
const pageItems = items.slice(Math.max(0, items.length - limit));
|
|
161
|
+
return {
|
|
162
|
+
items: pageItems,
|
|
163
|
+
nextCursor: pageItems.at(-1)?.seq || 0,
|
|
164
|
+
beforeCursor: pageItems[0]?.seq || 0,
|
|
165
|
+
hasMoreBefore,
|
|
166
|
+
lastSeq,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
class SessionTimelineService {
|
|
170
|
+
eventStore;
|
|
171
|
+
constructor(eventStore) {
|
|
172
|
+
this.eventStore = eventStore;
|
|
173
|
+
}
|
|
174
|
+
list(sessionId, options = {}) {
|
|
175
|
+
const rawEvents = this.eventStore.listAll(sessionId);
|
|
176
|
+
const lastSeq = rawEvents.at(-1)?.seq || 0;
|
|
177
|
+
const aggregatedItems = aggregateSemanticTimeline(rawEvents);
|
|
178
|
+
return paginateTimelineItems(aggregatedItems, options, lastSeq);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
exports.SessionTimelineService = SessionTimelineService;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.IO_STREAMS = exports.ASSISTANT_PHASES = exports.EVENT_TYPES = exports.SESSION_SOURCE_KINDS = exports.SESSION_STATUSES = void 0;
|
|
4
|
+
exports.SESSION_STATUSES = [
|
|
5
|
+
"idle",
|
|
6
|
+
"starting",
|
|
7
|
+
"running",
|
|
8
|
+
"waiting_input",
|
|
9
|
+
"stopping",
|
|
10
|
+
"completed",
|
|
11
|
+
"failed",
|
|
12
|
+
];
|
|
13
|
+
exports.SESSION_SOURCE_KINDS = ["native", "imported_rollout"];
|
|
14
|
+
exports.EVENT_TYPES = [
|
|
15
|
+
"message.user",
|
|
16
|
+
"message.assistant.start",
|
|
17
|
+
"message.assistant.delta",
|
|
18
|
+
"message.assistant.end",
|
|
19
|
+
"reasoning.start",
|
|
20
|
+
"reasoning.delta",
|
|
21
|
+
"reasoning.end",
|
|
22
|
+
"command.start",
|
|
23
|
+
"command.output.delta",
|
|
24
|
+
"command.end",
|
|
25
|
+
"patch.start",
|
|
26
|
+
"patch.output.delta",
|
|
27
|
+
"patch.end",
|
|
28
|
+
"approval.requested",
|
|
29
|
+
"approval.resolved",
|
|
30
|
+
"turn.started",
|
|
31
|
+
"turn.completed",
|
|
32
|
+
"turn.aborted",
|
|
33
|
+
"error",
|
|
34
|
+
"token_count",
|
|
35
|
+
];
|
|
36
|
+
exports.ASSISTANT_PHASES = ["commentary", "final_answer"];
|
|
37
|
+
exports.IO_STREAMS = ["stdout", "stderr"];
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.stripAnsi = stripAnsi;
|
|
4
|
+
exports.normalizeTerminalOutput = normalizeTerminalOutput;
|
|
5
|
+
function stripAnsi(value) {
|
|
6
|
+
return normalizeTerminalOutput(value).content;
|
|
7
|
+
}
|
|
8
|
+
function normalizeTerminalOutput(value) {
|
|
9
|
+
const { content, rest } = stripTerminalSequences(value);
|
|
10
|
+
return {
|
|
11
|
+
content: applyTerminalControls(content),
|
|
12
|
+
rest,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
function stripTerminalSequences(value) {
|
|
16
|
+
let index = 0;
|
|
17
|
+
let output = "";
|
|
18
|
+
while (index < value.length) {
|
|
19
|
+
const current = value.charCodeAt(index);
|
|
20
|
+
if (current === 0x1b) {
|
|
21
|
+
const nextIndex = consumeEscapeSequence(value, index);
|
|
22
|
+
if (nextIndex === -1) {
|
|
23
|
+
break;
|
|
24
|
+
}
|
|
25
|
+
index = nextIndex;
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
if (current === 0x9b) {
|
|
29
|
+
const nextIndex = consumeCsi(value, index + 1);
|
|
30
|
+
if (nextIndex === -1) {
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
index = nextIndex;
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
output += value[index];
|
|
37
|
+
index += 1;
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
content: output,
|
|
41
|
+
rest: value.slice(index),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function consumeEscapeSequence(value, start) {
|
|
45
|
+
const next = value[start + 1];
|
|
46
|
+
if (!next) {
|
|
47
|
+
return -1;
|
|
48
|
+
}
|
|
49
|
+
switch (next) {
|
|
50
|
+
case "[":
|
|
51
|
+
return consumeCsi(value, start + 2);
|
|
52
|
+
case "]":
|
|
53
|
+
return consumeOsc(value, start + 2);
|
|
54
|
+
case "P":
|
|
55
|
+
case "^":
|
|
56
|
+
case "_":
|
|
57
|
+
return consumeStTerminated(value, start + 2);
|
|
58
|
+
case "(":
|
|
59
|
+
case ")":
|
|
60
|
+
case "*":
|
|
61
|
+
case "+":
|
|
62
|
+
case "-":
|
|
63
|
+
case ".":
|
|
64
|
+
case "/":
|
|
65
|
+
return start + 3 <= value.length ? start + 3 : -1;
|
|
66
|
+
default:
|
|
67
|
+
return start + 2;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function consumeCsi(value, start) {
|
|
71
|
+
for (let index = start; index < value.length; index += 1) {
|
|
72
|
+
const code = value.charCodeAt(index);
|
|
73
|
+
if (code >= 0x40 && code <= 0x7e) {
|
|
74
|
+
return index + 1;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return -1;
|
|
78
|
+
}
|
|
79
|
+
function consumeOsc(value, start) {
|
|
80
|
+
for (let index = start; index < value.length; index += 1) {
|
|
81
|
+
const code = value.charCodeAt(index);
|
|
82
|
+
if (code === 0x07) {
|
|
83
|
+
return index + 1;
|
|
84
|
+
}
|
|
85
|
+
if (code === 0x1b) {
|
|
86
|
+
if (index + 1 >= value.length) {
|
|
87
|
+
return -1;
|
|
88
|
+
}
|
|
89
|
+
if (value[index + 1] === "\\") {
|
|
90
|
+
return index + 2;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return -1;
|
|
95
|
+
}
|
|
96
|
+
function consumeStTerminated(value, start) {
|
|
97
|
+
for (let index = start; index < value.length; index += 1) {
|
|
98
|
+
const code = value.charCodeAt(index);
|
|
99
|
+
if (code === 0x9c) {
|
|
100
|
+
return index + 1;
|
|
101
|
+
}
|
|
102
|
+
if (code === 0x1b) {
|
|
103
|
+
if (index + 1 >= value.length) {
|
|
104
|
+
return -1;
|
|
105
|
+
}
|
|
106
|
+
if (value[index + 1] === "\\") {
|
|
107
|
+
return index + 2;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return -1;
|
|
112
|
+
}
|
|
113
|
+
function applyTerminalControls(value) {
|
|
114
|
+
const lines = [""];
|
|
115
|
+
for (const char of value.replace(/\r\n/g, "\n")) {
|
|
116
|
+
const lineIndex = lines.length - 1;
|
|
117
|
+
switch (char) {
|
|
118
|
+
case "\n":
|
|
119
|
+
lines.push("");
|
|
120
|
+
break;
|
|
121
|
+
case "\r":
|
|
122
|
+
lines[lineIndex] = "";
|
|
123
|
+
break;
|
|
124
|
+
case "\b":
|
|
125
|
+
lines[lineIndex] = lines[lineIndex].slice(0, -1);
|
|
126
|
+
break;
|
|
127
|
+
case "\t":
|
|
128
|
+
lines[lineIndex] += "\t";
|
|
129
|
+
break;
|
|
130
|
+
default: {
|
|
131
|
+
const code = char.charCodeAt(0);
|
|
132
|
+
if ((code >= 0x20 && code !== 0x7f) || code >= 0xa0) {
|
|
133
|
+
lines[lineIndex] += char;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return lines
|
|
139
|
+
.join("\n")
|
|
140
|
+
.replace(/[ \t]+\n/g, "\n")
|
|
141
|
+
.replace(/\n{3,}/g, "\n\n")
|
|
142
|
+
.trimEnd();
|
|
143
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.normalizeCodexExecLaunchInput = normalizeCodexExecLaunchInput;
|
|
4
|
+
const SANDBOX = new Set([
|
|
5
|
+
"read-only",
|
|
6
|
+
"workspace-write",
|
|
7
|
+
"danger-full-access",
|
|
8
|
+
]);
|
|
9
|
+
const SPEEDS = new Set(["default", "fast", "deep"]);
|
|
10
|
+
const REASONING_EFFORTS = new Set(["low", "medium", "high", "xhigh"]);
|
|
11
|
+
function sanitizeToken(value, maxLen) {
|
|
12
|
+
if (typeof value !== "string") {
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
const t = value.trim();
|
|
16
|
+
if (!t || t.length > maxLen) {
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
if (!/^[\w.+-]+$/i.test(t)) {
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
return t;
|
|
23
|
+
}
|
|
24
|
+
function sanitizeProfile(value) {
|
|
25
|
+
if (typeof value !== "string") {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
const t = value.trim();
|
|
29
|
+
if (!t || t.length > 64) {
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
if (!/^[\w-]+$/i.test(t)) {
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
return t;
|
|
36
|
+
}
|
|
37
|
+
function sanitizeFeatureName(value) {
|
|
38
|
+
if (typeof value !== "string") {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
const t = value.trim();
|
|
42
|
+
if (!t || t.length > 64) {
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
if (!/^[a-z][a-z0-9_]*$/i.test(t)) {
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
return t;
|
|
49
|
+
}
|
|
50
|
+
function normalizeCodexExecLaunchInput(raw) {
|
|
51
|
+
if (!raw || typeof raw !== "object") {
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
const o = raw;
|
|
55
|
+
const model = sanitizeToken(o.model, 96);
|
|
56
|
+
const profile = sanitizeProfile(o.profile);
|
|
57
|
+
const sandbox = typeof o.sandbox === "string" && SANDBOX.has(o.sandbox)
|
|
58
|
+
? o.sandbox
|
|
59
|
+
: undefined;
|
|
60
|
+
const speed = typeof o.speed === "string" && SPEEDS.has(o.speed) ? o.speed : undefined;
|
|
61
|
+
const reasoningEffort = typeof o.reasoningEffort === "string" && REASONING_EFFORTS.has(o.reasoningEffort)
|
|
62
|
+
? o.reasoningEffort
|
|
63
|
+
: undefined;
|
|
64
|
+
const enableFeatures = Array.isArray(o.enableFeatures)
|
|
65
|
+
? o.enableFeatures.map(sanitizeFeatureName).filter(Boolean)
|
|
66
|
+
: [];
|
|
67
|
+
const disableFeatures = Array.isArray(o.disableFeatures)
|
|
68
|
+
? o.disableFeatures.map(sanitizeFeatureName).filter(Boolean)
|
|
69
|
+
: [];
|
|
70
|
+
const out = {};
|
|
71
|
+
if (model) {
|
|
72
|
+
out.model = model;
|
|
73
|
+
}
|
|
74
|
+
if (profile) {
|
|
75
|
+
out.profile = profile;
|
|
76
|
+
}
|
|
77
|
+
if (sandbox) {
|
|
78
|
+
out.sandbox = sandbox;
|
|
79
|
+
}
|
|
80
|
+
if (speed && speed !== "default") {
|
|
81
|
+
out.speed = speed;
|
|
82
|
+
}
|
|
83
|
+
if (reasoningEffort) {
|
|
84
|
+
out.reasoningEffort = reasoningEffort;
|
|
85
|
+
}
|
|
86
|
+
if (enableFeatures.length > 0) {
|
|
87
|
+
out.enableFeatures = enableFeatures;
|
|
88
|
+
}
|
|
89
|
+
if (disableFeatures.length > 0) {
|
|
90
|
+
out.disableFeatures = disableFeatures;
|
|
91
|
+
}
|
|
92
|
+
if (!out.model &&
|
|
93
|
+
!out.profile &&
|
|
94
|
+
!out.sandbox &&
|
|
95
|
+
!out.speed &&
|
|
96
|
+
!out.reasoningEffort &&
|
|
97
|
+
!out.enableFeatures?.length &&
|
|
98
|
+
!out.disableFeatures?.length) {
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
return out;
|
|
102
|
+
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.resolveCodexQuotaSnapshot = resolveCodexQuotaSnapshot;
|
|
7
|
+
const node_fs_1 = require("node:fs");
|
|
8
|
+
const node_os_1 = __importDefault(require("node:os"));
|
|
9
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
10
|
+
const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
|
|
11
|
+
function resolveCodexHomeDir() {
|
|
12
|
+
const override = process.env.CODEX_HOME?.trim();
|
|
13
|
+
if (override) {
|
|
14
|
+
return node_path_1.default.resolve(override);
|
|
15
|
+
}
|
|
16
|
+
return node_path_1.default.join(node_os_1.default.homedir(), ".codex");
|
|
17
|
+
}
|
|
18
|
+
function resolveStateDbPath() {
|
|
19
|
+
const override = process.env.CODEX_STATE_DB_PATH?.trim();
|
|
20
|
+
if (override) {
|
|
21
|
+
return node_path_1.default.resolve(override);
|
|
22
|
+
}
|
|
23
|
+
return node_path_1.default.join(resolveCodexHomeDir(), "state_5.sqlite");
|
|
24
|
+
}
|
|
25
|
+
function readNumberField(input) {
|
|
26
|
+
if (typeof input === "number" && Number.isFinite(input)) {
|
|
27
|
+
return input;
|
|
28
|
+
}
|
|
29
|
+
if (typeof input === "string" && input.trim()) {
|
|
30
|
+
const parsed = Number.parseFloat(input);
|
|
31
|
+
return Number.isFinite(parsed) ? parsed : undefined;
|
|
32
|
+
}
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
function normalizeTokenCountPayload(payload, source, timestamp) {
|
|
36
|
+
const rateLimits = payload.rate_limits && typeof payload.rate_limits === "object"
|
|
37
|
+
? payload.rate_limits
|
|
38
|
+
: payload.rateLimits && typeof payload.rateLimits === "object"
|
|
39
|
+
? payload.rateLimits
|
|
40
|
+
: null;
|
|
41
|
+
if (!rateLimits) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
const info = payload.info && typeof payload.info === "object"
|
|
45
|
+
? payload.info
|
|
46
|
+
: undefined;
|
|
47
|
+
const totalTokenUsage = info?.total_token_usage && typeof info.total_token_usage === "object"
|
|
48
|
+
? info.total_token_usage
|
|
49
|
+
: undefined;
|
|
50
|
+
const lastTokenUsage = info?.last_token_usage && typeof info.last_token_usage === "object"
|
|
51
|
+
? info.last_token_usage
|
|
52
|
+
: undefined;
|
|
53
|
+
const modelContextWindow = readNumberField(info?.model_context_window);
|
|
54
|
+
return {
|
|
55
|
+
rateLimits,
|
|
56
|
+
totalTokenUsage,
|
|
57
|
+
lastTokenUsage,
|
|
58
|
+
modelContextWindow,
|
|
59
|
+
receivedAt: timestamp || new Date().toISOString(),
|
|
60
|
+
rawPayload: payload,
|
|
61
|
+
source,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function parseQuotaLine(line) {
|
|
65
|
+
const trimmed = line.trim();
|
|
66
|
+
if (!trimmed) {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
try {
|
|
70
|
+
const raw = JSON.parse(trimmed);
|
|
71
|
+
const timestamp = typeof raw.timestamp === "string" && raw.timestamp.trim() ? raw.timestamp.trim() : undefined;
|
|
72
|
+
if (raw.type === "event_msg" && raw.payload && typeof raw.payload === "object") {
|
|
73
|
+
const payload = raw.payload;
|
|
74
|
+
if (payload.type === "token_count") {
|
|
75
|
+
return normalizeTokenCountPayload(raw.payload, "rollout", timestamp);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (raw.type === "token_count") {
|
|
79
|
+
return normalizeTokenCountPayload(raw, "rollout", timestamp);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
function findRolloutPathInStateDb(input) {
|
|
88
|
+
const dbPath = resolveStateDbPath();
|
|
89
|
+
if (!(0, node_fs_1.existsSync)(dbPath)) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
const db = new better_sqlite3_1.default(dbPath);
|
|
93
|
+
try {
|
|
94
|
+
const byId = db.prepare("SELECT id, rollout_path, cwd, updated_at FROM threads WHERE id = ? LIMIT 1");
|
|
95
|
+
const byCwd = db.prepare("SELECT id, rollout_path, cwd, updated_at FROM threads WHERE cwd = ? ORDER BY updated_at DESC, id DESC LIMIT 1");
|
|
96
|
+
let row;
|
|
97
|
+
if (input.threadId?.trim()) {
|
|
98
|
+
row = byId.get(input.threadId.trim());
|
|
99
|
+
}
|
|
100
|
+
if (!row && input.cwd?.trim()) {
|
|
101
|
+
row = byCwd.get(input.cwd.trim());
|
|
102
|
+
}
|
|
103
|
+
if (!row?.rollout_path) {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
const resolved = node_path_1.default.isAbsolute(row.rollout_path)
|
|
107
|
+
? row.rollout_path
|
|
108
|
+
: node_path_1.default.resolve(resolveCodexHomeDir(), row.rollout_path);
|
|
109
|
+
return (0, node_fs_1.existsSync)(resolved) ? resolved : null;
|
|
110
|
+
}
|
|
111
|
+
finally {
|
|
112
|
+
db.close?.();
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function scanDirForRollout(rootDir, threadId) {
|
|
116
|
+
const stack = [rootDir];
|
|
117
|
+
while (stack.length > 0) {
|
|
118
|
+
const current = stack.pop();
|
|
119
|
+
if (!current) {
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
let entries;
|
|
123
|
+
try {
|
|
124
|
+
entries = (0, node_fs_1.readdirSync)(current);
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
for (const name of entries) {
|
|
130
|
+
const nextPath = node_path_1.default.join(current, name);
|
|
131
|
+
let stats;
|
|
132
|
+
try {
|
|
133
|
+
stats = (0, node_fs_1.statSync)(nextPath);
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
if (stats.isDirectory()) {
|
|
139
|
+
stack.push(nextPath);
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if (stats.isFile() &&
|
|
143
|
+
name.includes(threadId) &&
|
|
144
|
+
name.endsWith(".jsonl")) {
|
|
145
|
+
return nextPath;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
function findRolloutPathInSessions(threadId) {
|
|
152
|
+
const sessionsDir = node_path_1.default.join(resolveCodexHomeDir(), "sessions");
|
|
153
|
+
if (!(0, node_fs_1.existsSync)(sessionsDir)) {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
return scanDirForRollout(sessionsDir, threadId);
|
|
157
|
+
}
|
|
158
|
+
function readLatestQuotaFromRollout(rolloutPath) {
|
|
159
|
+
if (!(0, node_fs_1.existsSync)(rolloutPath)) {
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
const body = (0, node_fs_1.readFileSync)(rolloutPath, "utf8");
|
|
163
|
+
const lines = body.split(/\r?\n/);
|
|
164
|
+
for (let index = lines.length - 1; index >= 0; index -= 1) {
|
|
165
|
+
const parsed = parseQuotaLine(lines[index] || "");
|
|
166
|
+
if (parsed) {
|
|
167
|
+
return parsed;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
function resolveCodexQuotaSnapshot(input = {}) {
|
|
173
|
+
const rolloutPath = findRolloutPathInStateDb(input) ||
|
|
174
|
+
(input.threadId?.trim() ? findRolloutPathInSessions(input.threadId.trim()) : null);
|
|
175
|
+
if (!rolloutPath) {
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
return readLatestQuotaFromRollout(rolloutPath);
|
|
179
|
+
}
|