codex-copilot-dx 0.6.18 → 0.6.20
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 +2 -0
- package/bin/cli.mjs +2 -1
- package/package.json +1 -1
- package/src/adapter.mjs +19 -3
- package/src/anthropic-handler.mjs +9 -4
- package/src/anthropic.mjs +10 -3
- package/src/auth-profile.mjs +3 -3
- package/src/auth.mjs +44 -10
- package/src/auto-review-model.mjs +2 -2
- package/src/cli-auth.mjs +98 -25
- package/src/cli-error.mjs +50 -0
- package/src/cli-models.mjs +2 -2
- package/src/cli-options.mjs +10 -8
- package/src/copilot-token-session.mjs +17 -2
- package/src/copilot.mjs +22 -3
- package/src/doctor.mjs +2 -2
- package/src/http-transport.mjs +56 -4
- package/src/lock.mjs +24 -4
- package/src/pm-studio-bundle-content.mjs +165 -0
- package/src/pm-studio-relay.mjs +26 -5
- package/src/pm-studio-setup.mjs +42 -162
- package/src/responses-handler.mjs +7 -2
- package/src/responses-proxy.mjs +6 -1
- package/src/runtime-config.mjs +33 -7
- package/src/token-counter-service.mjs +211 -0
- package/src/token-counter-worker.mjs +16 -0
- package/src/usage.mjs +80 -13
package/src/usage.mjs
CHANGED
|
@@ -4,11 +4,17 @@ import path from "node:path";
|
|
|
4
4
|
import readline from "node:readline";
|
|
5
5
|
import { cliOutputFormat, cliOutputWidth, formatResponsiveCliTable, terminalCell } from "./cli-table.mjs";
|
|
6
6
|
import { parseByteLimit, rotateFileIfNeededSync, rotatedFilePath } from "./file-rotation.mjs";
|
|
7
|
+
import { withFileLock } from "./lock.mjs";
|
|
7
8
|
|
|
8
9
|
const DEFAULT_USAGE_PATH = path.join(os.homedir(), ".local", "share", "codex-copilot-dx", "usage.jsonl");
|
|
9
10
|
const DEFAULT_USAGE_MAX_BYTES = 32 * 1024 * 1024;
|
|
11
|
+
const USAGE_WRITE_LOCK_TIMEOUT_MS = 1_000;
|
|
12
|
+
const USAGE_WRITE_LOCK_STALE_MS = 1_000;
|
|
13
|
+
const USAGE_WRITE_LOCK_POLL_MS = 5;
|
|
14
|
+
const USAGE_FLUSH_TIMEOUT_MS = 1_500;
|
|
10
15
|
|
|
11
|
-
|
|
16
|
+
const pendingWrites = [];
|
|
17
|
+
let writeDrain = null;
|
|
12
18
|
|
|
13
19
|
export function usageLogPath() {
|
|
14
20
|
return process.env.CCDX_USAGE_PATH || DEFAULT_USAGE_PATH;
|
|
@@ -119,19 +125,63 @@ export function buildAnthropicUsageRecord({ surface = "messages", mode, model, r
|
|
|
119
125
|
});
|
|
120
126
|
}
|
|
121
127
|
|
|
128
|
+
function takePendingWrites(filePath, maxBytes) {
|
|
129
|
+
const batch = [];
|
|
130
|
+
let retained = 0;
|
|
131
|
+
for (const entry of pendingWrites) {
|
|
132
|
+
if (entry.filePath === filePath && entry.maxBytes === maxBytes) batch.push(entry);
|
|
133
|
+
else pendingWrites[retained++] = entry;
|
|
134
|
+
}
|
|
135
|
+
pendingWrites.length = retained;
|
|
136
|
+
return batch;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
async function writeUsageBatch(batch) {
|
|
140
|
+
const { filePath, maxBytes } = batch[0];
|
|
141
|
+
await fs.promises.mkdir(path.dirname(filePath), { recursive: true });
|
|
142
|
+
await withFileLock(`${path.resolve(filePath)}.lock`, async () => {
|
|
143
|
+
batch.push(...takePendingWrites(filePath, maxBytes));
|
|
144
|
+
for (const { line } of batch) {
|
|
145
|
+
rotateFileIfNeededSync(filePath, Buffer.byteLength(line), maxBytes);
|
|
146
|
+
await fs.promises.appendFile(filePath, line, { encoding: "utf8", mode: 0o600 });
|
|
147
|
+
}
|
|
148
|
+
}, {
|
|
149
|
+
timeoutMs: USAGE_WRITE_LOCK_TIMEOUT_MS,
|
|
150
|
+
staleMs: USAGE_WRITE_LOCK_STALE_MS,
|
|
151
|
+
pollMs: USAGE_WRITE_LOCK_POLL_MS,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function scheduleUsageWriteDrain() {
|
|
156
|
+
if (writeDrain) return;
|
|
157
|
+
writeDrain = Promise.resolve().then(async () => {
|
|
158
|
+
while (pendingWrites.length > 0) {
|
|
159
|
+
const first = pendingWrites.shift();
|
|
160
|
+
const batch = [first, ...takePendingWrites(first.filePath, first.maxBytes)];
|
|
161
|
+
try {
|
|
162
|
+
await writeUsageBatch(batch);
|
|
163
|
+
} catch (error) {
|
|
164
|
+
batch.push(...takePendingWrites(first.filePath, first.maxBytes));
|
|
165
|
+
console.error(`codex-copilot-dx usage log write failed: ${error.message}`);
|
|
166
|
+
} finally {
|
|
167
|
+
for (const entry of batch) entry.resolve();
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}).finally(() => {
|
|
171
|
+
writeDrain = null;
|
|
172
|
+
if (pendingWrites.length > 0) scheduleUsageWriteDrain();
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
122
176
|
export function recordUsage(record) {
|
|
123
177
|
if (!record || process.env.CCDX_DISABLE_USAGE === "1") return Promise.resolve();
|
|
124
178
|
const filePath = usageLogPath();
|
|
125
179
|
const line = `${JSON.stringify(record)}\n`;
|
|
126
|
-
|
|
127
|
-
.
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
await fs.promises.appendFile(filePath, line, { encoding: "utf8", mode: 0o600 });
|
|
132
|
-
})
|
|
133
|
-
.catch((e) => console.error(`codex-copilot-dx usage log write failed: ${e.message}`));
|
|
134
|
-
return writeQueue;
|
|
180
|
+
const completed = new Promise((resolve) => {
|
|
181
|
+
pendingWrites.push({ filePath, line, maxBytes: usageLogMaxBytes(), resolve });
|
|
182
|
+
});
|
|
183
|
+
scheduleUsageWriteDrain();
|
|
184
|
+
return completed;
|
|
135
185
|
}
|
|
136
186
|
|
|
137
187
|
export function recordResponsesUsage(args) {
|
|
@@ -143,11 +193,28 @@ export function recordAnthropicUsage(args) {
|
|
|
143
193
|
}
|
|
144
194
|
|
|
145
195
|
export async function flushUsageWritesForTests() {
|
|
146
|
-
|
|
196
|
+
while (writeDrain || pendingWrites.length > 0) {
|
|
197
|
+
if (writeDrain) await writeDrain;
|
|
198
|
+
else scheduleUsageWriteDrain();
|
|
199
|
+
}
|
|
147
200
|
}
|
|
148
201
|
|
|
149
|
-
export async function flushUsageWrites(
|
|
150
|
-
|
|
202
|
+
export async function flushUsageWrites({
|
|
203
|
+
timeoutMs = USAGE_FLUSH_TIMEOUT_MS,
|
|
204
|
+
warn = console.error,
|
|
205
|
+
} = {}) {
|
|
206
|
+
const timeout = Number.isFinite(timeoutMs) && timeoutMs >= 0
|
|
207
|
+
? Math.floor(timeoutMs)
|
|
208
|
+
: USAGE_FLUSH_TIMEOUT_MS;
|
|
209
|
+
let timer;
|
|
210
|
+
const completed = await Promise.race([
|
|
211
|
+
flushUsageWritesForTests().then(() => true),
|
|
212
|
+
new Promise((resolve) => { timer = setTimeout(() => resolve(false), timeout); }),
|
|
213
|
+
]);
|
|
214
|
+
if (timer) clearTimeout(timer);
|
|
215
|
+
if (!completed) {
|
|
216
|
+
try { warn(`codex-copilot-dx usage log flush timed out after ${timeout}ms`); } catch {}
|
|
217
|
+
}
|
|
151
218
|
}
|
|
152
219
|
|
|
153
220
|
async function* iterateUsageRecords(filePath, { warn = console.error } = {}) {
|