@evident-ai/cli 3.4.1-dev.8fa4d29 → 3.4.1-dev.b1bf8c5
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 +10 -1
- package/dist/index.js +358 -68
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -746,6 +746,40 @@ async function reportClaudeUsage(agentId, authHeader, snapshot) {
|
|
|
746
746
|
return { ok: false, error: describeBestEffortError(error2) };
|
|
747
747
|
}
|
|
748
748
|
}
|
|
749
|
+
function toReportedOpenAiWindow(window) {
|
|
750
|
+
if (!window) return null;
|
|
751
|
+
return {
|
|
752
|
+
utilization: window.utilization,
|
|
753
|
+
window_minutes: window.windowMinutes,
|
|
754
|
+
resets_at: window.resetsAt
|
|
755
|
+
};
|
|
756
|
+
}
|
|
757
|
+
async function reportOpenAiUsage(agentId, authHeader, snapshot) {
|
|
758
|
+
try {
|
|
759
|
+
const apiUrl = getApiUrlConfig();
|
|
760
|
+
const response = await fetch(`${apiUrl}/runners/${agentId}/openai-usage`, {
|
|
761
|
+
method: "POST",
|
|
762
|
+
headers: { Authorization: authHeader, "Content-Type": "application/json" },
|
|
763
|
+
body: JSON.stringify({
|
|
764
|
+
primary: toReportedOpenAiWindow(snapshot.primary),
|
|
765
|
+
secondary: toReportedOpenAiWindow(snapshot.secondary),
|
|
766
|
+
has_credits: snapshot.hasCredits,
|
|
767
|
+
credits_unlimited: snapshot.creditsUnlimited
|
|
768
|
+
}),
|
|
769
|
+
signal: AbortSignal.timeout(BEST_EFFORT_NOTIFY_TIMEOUT_MS)
|
|
770
|
+
});
|
|
771
|
+
if (!response.ok) {
|
|
772
|
+
const serverMessage = await readErrorMessage(response);
|
|
773
|
+
return {
|
|
774
|
+
ok: false,
|
|
775
|
+
error: `HTTP ${response.status}${serverMessage ? `: ${serverMessage}` : ""}`
|
|
776
|
+
};
|
|
777
|
+
}
|
|
778
|
+
return { ok: true };
|
|
779
|
+
} catch (error2) {
|
|
780
|
+
return { ok: false, error: describeBestEffortError(error2) };
|
|
781
|
+
}
|
|
782
|
+
}
|
|
749
783
|
async function reportResourceUsage(agentId, authHeader, usage) {
|
|
750
784
|
try {
|
|
751
785
|
const apiUrl = getApiUrlConfig();
|
|
@@ -1092,8 +1126,8 @@ async function claudeUsage() {
|
|
|
1092
1126
|
}
|
|
1093
1127
|
|
|
1094
1128
|
// src/commands/run.ts
|
|
1095
|
-
import { homedir as
|
|
1096
|
-
import { isAbsolute as isAbsolute2, join as
|
|
1129
|
+
import { homedir as homedir4 } from "os";
|
|
1130
|
+
import { isAbsolute as isAbsolute2, join as join6, parse, resolve as resolvePath } from "path";
|
|
1097
1131
|
import chalk6 from "chalk";
|
|
1098
1132
|
|
|
1099
1133
|
// ../../packages/types/src/agents/index.ts
|
|
@@ -2991,6 +3025,177 @@ function writeTunnelReadyMarker(path, agentId) {
|
|
|
2991
3025
|
}
|
|
2992
3026
|
}
|
|
2993
3027
|
|
|
3028
|
+
// src/lib/openai-usage.ts
|
|
3029
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
3030
|
+
import { homedir as homedir2 } from "os";
|
|
3031
|
+
import { join as join3 } from "path";
|
|
3032
|
+
var CODEX_RESPONSES_URL = "https://chatgpt.com/backend-api/codex/responses";
|
|
3033
|
+
var OPENCODE_AUTH_SEGMENTS = [".local", "share", "opencode", "auth.json"];
|
|
3034
|
+
var OpenAiUsageError = class extends Error {
|
|
3035
|
+
constructor(message, reason) {
|
|
3036
|
+
super(message);
|
|
3037
|
+
this.reason = reason;
|
|
3038
|
+
}
|
|
3039
|
+
};
|
|
3040
|
+
function isLocalCredentialProblem2(err) {
|
|
3041
|
+
return err instanceof OpenAiUsageError && (err.reason === "no_credentials" || err.reason === "credentials_expired");
|
|
3042
|
+
}
|
|
3043
|
+
function readOpenCodeChatGptCredentials() {
|
|
3044
|
+
try {
|
|
3045
|
+
const raw = readFileSync2(join3(homedir2(), ...OPENCODE_AUTH_SEGMENTS), "utf-8");
|
|
3046
|
+
let parsed;
|
|
3047
|
+
try {
|
|
3048
|
+
parsed = JSON.parse(raw);
|
|
3049
|
+
} catch {
|
|
3050
|
+
return null;
|
|
3051
|
+
}
|
|
3052
|
+
const entry = parsed.openai;
|
|
3053
|
+
if (entry?.type !== "oauth" || typeof entry.access !== "string" || !entry.access || typeof entry.expires !== "number") {
|
|
3054
|
+
return null;
|
|
3055
|
+
}
|
|
3056
|
+
return { accessToken: entry.access, expiresAt: entry.expires };
|
|
3057
|
+
} catch (err) {
|
|
3058
|
+
const code = err.code;
|
|
3059
|
+
if (code !== "ENOENT" && code !== "ENOTDIR") {
|
|
3060
|
+
console.warn(
|
|
3061
|
+
`readOpenCodeChatGptCredentials: reading auth.json failed (${code ?? "unknown"})`
|
|
3062
|
+
);
|
|
3063
|
+
}
|
|
3064
|
+
return null;
|
|
3065
|
+
}
|
|
3066
|
+
}
|
|
3067
|
+
function toWindow2(headers, name) {
|
|
3068
|
+
const utilizationHeader = headers.get(`x-codex-${name}-used-percent`);
|
|
3069
|
+
const windowMinutesHeader = headers.get(`x-codex-${name}-window-minutes`);
|
|
3070
|
+
if (utilizationHeader == null || utilizationHeader === "" || windowMinutesHeader == null) {
|
|
3071
|
+
return null;
|
|
3072
|
+
}
|
|
3073
|
+
const utilization = Number(utilizationHeader);
|
|
3074
|
+
const windowMinutes = Number(windowMinutesHeader);
|
|
3075
|
+
if (!Number.isFinite(utilization) || !Number.isFinite(windowMinutes) || windowMinutes <= 0) {
|
|
3076
|
+
return null;
|
|
3077
|
+
}
|
|
3078
|
+
const resetAtHeader = headers.get(`x-codex-${name}-reset-at`);
|
|
3079
|
+
const resetSeconds = resetAtHeader == null || resetAtHeader === "" ? NaN : Number(resetAtHeader);
|
|
3080
|
+
const resetsAt = Number.isFinite(resetSeconds) ? new Date(resetSeconds * 1e3).toISOString() : null;
|
|
3081
|
+
return { utilization: Math.min(100, Math.max(0, utilization)), windowMinutes, resetsAt };
|
|
3082
|
+
}
|
|
3083
|
+
function parseCodexUsageHeaders(headers) {
|
|
3084
|
+
return {
|
|
3085
|
+
primary: toWindow2(headers, "primary"),
|
|
3086
|
+
secondary: toWindow2(headers, "secondary"),
|
|
3087
|
+
hasCredits: headers.has("x-codex-credits-has-credits") ? headers.get("x-codex-credits-has-credits")?.toLowerCase() === "true" : null,
|
|
3088
|
+
creditsUnlimited: headers.has("x-codex-credits-unlimited") ? headers.get("x-codex-credits-unlimited")?.toLowerCase() === "true" : null
|
|
3089
|
+
};
|
|
3090
|
+
}
|
|
3091
|
+
function normalizeProbeModel(model) {
|
|
3092
|
+
return model.endsWith("-fast") ? model.slice(0, -"-fast".length) : model;
|
|
3093
|
+
}
|
|
3094
|
+
async function resolveProbeModels(port) {
|
|
3095
|
+
try {
|
|
3096
|
+
const res = await withRequestTimeout(
|
|
3097
|
+
fetch,
|
|
3098
|
+
REQUEST_TIMEOUT_MS
|
|
3099
|
+
)(`${opencodeBase(port)}/config/providers`);
|
|
3100
|
+
if (!res.ok) {
|
|
3101
|
+
console.error(
|
|
3102
|
+
`[resolveProbeModels] GET /config/providers returned HTTP ${res.status} (port ${port})`
|
|
3103
|
+
);
|
|
3104
|
+
return [];
|
|
3105
|
+
}
|
|
3106
|
+
const body = await res.json();
|
|
3107
|
+
const provider = body?.providers?.find((candidate) => candidate?.id === "openai");
|
|
3108
|
+
if (!provider || !provider.models || typeof provider.models !== "object") return [];
|
|
3109
|
+
const candidates = [
|
|
3110
|
+
...typeof body?.default?.openai === "string" ? [body.default.openai] : [],
|
|
3111
|
+
...Object.keys(provider.models)
|
|
3112
|
+
].map(normalizeProbeModel);
|
|
3113
|
+
return [...new Set(candidates)].slice(0, 4);
|
|
3114
|
+
} catch (err) {
|
|
3115
|
+
console.error(
|
|
3116
|
+
`[resolveProbeModels] GET /config/providers failed (port ${port}): ${err instanceof Error ? err.message : String(err)}`
|
|
3117
|
+
);
|
|
3118
|
+
return [];
|
|
3119
|
+
}
|
|
3120
|
+
}
|
|
3121
|
+
function hasPrimaryHeaders(headers) {
|
|
3122
|
+
return [
|
|
3123
|
+
"x-codex-primary-used-percent",
|
|
3124
|
+
"x-codex-primary-window-minutes",
|
|
3125
|
+
"x-codex-primary-reset-at"
|
|
3126
|
+
].some((name) => headers.has(name));
|
|
3127
|
+
}
|
|
3128
|
+
async function getOpenAiUsage(port) {
|
|
3129
|
+
const credentials2 = readOpenCodeChatGptCredentials();
|
|
3130
|
+
if (!credentials2) {
|
|
3131
|
+
throw new OpenAiUsageError(
|
|
3132
|
+
"No ChatGPT login found. Connect a ChatGPT account to this runner, or run `opencode auth login`.",
|
|
3133
|
+
"no_credentials"
|
|
3134
|
+
);
|
|
3135
|
+
}
|
|
3136
|
+
if (credentials2.expiresAt < Date.now()) {
|
|
3137
|
+
throw new OpenAiUsageError(
|
|
3138
|
+
"ChatGPT credentials have expired. Run `opencode auth login` to refresh them.",
|
|
3139
|
+
"credentials_expired"
|
|
3140
|
+
);
|
|
3141
|
+
}
|
|
3142
|
+
const models = await resolveProbeModels(port);
|
|
3143
|
+
if (models.length === 0) {
|
|
3144
|
+
throw new OpenAiUsageError("No supported OpenAI probe model is available.", "no_probe_model");
|
|
3145
|
+
}
|
|
3146
|
+
let lastStatus;
|
|
3147
|
+
for (const model of models) {
|
|
3148
|
+
let res;
|
|
3149
|
+
try {
|
|
3150
|
+
res = await withRequestTimeout(fetch, REQUEST_TIMEOUT_MS)(CODEX_RESPONSES_URL, {
|
|
3151
|
+
method: "POST",
|
|
3152
|
+
headers: {
|
|
3153
|
+
Authorization: `Bearer ${credentials2.accessToken}`,
|
|
3154
|
+
"Content-Type": "application/json"
|
|
3155
|
+
},
|
|
3156
|
+
body: JSON.stringify({ model, store: false, stream: true })
|
|
3157
|
+
});
|
|
3158
|
+
} catch (err) {
|
|
3159
|
+
throw new OpenAiUsageError(
|
|
3160
|
+
`OpenAI usage probe request failed: ${err instanceof Error ? err.message : String(err)}.`,
|
|
3161
|
+
"request_failed"
|
|
3162
|
+
);
|
|
3163
|
+
}
|
|
3164
|
+
try {
|
|
3165
|
+
lastStatus = res.status;
|
|
3166
|
+
if (hasPrimaryHeaders(res.headers)) {
|
|
3167
|
+
const usage = parseCodexUsageHeaders(res.headers);
|
|
3168
|
+
if (!usage.primary && !usage.secondary) {
|
|
3169
|
+
throw new OpenAiUsageError(
|
|
3170
|
+
`OpenAI usage probe returned no usable window (HTTP ${res.status}).`,
|
|
3171
|
+
"no_usable_window"
|
|
3172
|
+
);
|
|
3173
|
+
}
|
|
3174
|
+
return usage;
|
|
3175
|
+
}
|
|
3176
|
+
if (res.status === 401) {
|
|
3177
|
+
throw new OpenAiUsageError(
|
|
3178
|
+
"ChatGPT credentials have expired (HTTP 401).",
|
|
3179
|
+
"credentials_expired"
|
|
3180
|
+
);
|
|
3181
|
+
}
|
|
3182
|
+
if (res.status === 403 || res.status === 429) {
|
|
3183
|
+
throw new OpenAiUsageError(
|
|
3184
|
+
`OpenAI usage probe was blocked (HTTP ${res.status}).`,
|
|
3185
|
+
"probe_blocked"
|
|
3186
|
+
);
|
|
3187
|
+
}
|
|
3188
|
+
} finally {
|
|
3189
|
+
await res.body?.cancel().catch(() => {
|
|
3190
|
+
});
|
|
3191
|
+
}
|
|
3192
|
+
}
|
|
3193
|
+
throw new OpenAiUsageError(
|
|
3194
|
+
`OpenAI usage probe failed: HTTP ${lastStatus ?? "unknown"}.`,
|
|
3195
|
+
"request_failed"
|
|
3196
|
+
);
|
|
3197
|
+
}
|
|
3198
|
+
|
|
2994
3199
|
// src/lib/reporting-schedule.ts
|
|
2995
3200
|
function jitteredDelayMs(baseMs, jitterFraction, random = Math.random) {
|
|
2996
3201
|
const jitterRangeMs = baseMs * jitterFraction;
|
|
@@ -2999,6 +3204,31 @@ function jitteredDelayMs(baseMs, jitterFraction, random = Math.random) {
|
|
|
2999
3204
|
function firstReportDelayMs(random = Math.random) {
|
|
3000
3205
|
return 5e3 + random() * 1e4;
|
|
3001
3206
|
}
|
|
3207
|
+
var VALID_USAGE_REPORTING_MODES = ["auto", "on", "off"];
|
|
3208
|
+
function resolveUsageReportingMode(flagValue, env, names) {
|
|
3209
|
+
const raw = flagValue ?? env[names.envVar];
|
|
3210
|
+
if (raw === void 0 || raw === "") return { mode: "auto", warnings: [] };
|
|
3211
|
+
const normalized = raw.trim().toLowerCase();
|
|
3212
|
+
if (VALID_USAGE_REPORTING_MODES.includes(normalized)) {
|
|
3213
|
+
return { mode: normalized, warnings: [] };
|
|
3214
|
+
}
|
|
3215
|
+
const source = flagValue !== void 0 ? names.flagName : names.envVar;
|
|
3216
|
+
return {
|
|
3217
|
+
mode: "auto",
|
|
3218
|
+
warnings: [
|
|
3219
|
+
`Ignoring invalid ${source} "${raw}": expected one of ${VALID_USAGE_REPORTING_MODES.join(", ")}; using auto`
|
|
3220
|
+
]
|
|
3221
|
+
};
|
|
3222
|
+
}
|
|
3223
|
+
var BASE_USAGE_REPORT_DELAY_MS = 10 * 6e4;
|
|
3224
|
+
var USAGE_REPORT_DELAY_JITTER_FRACTION = 0.2;
|
|
3225
|
+
function usageReportDelayMs(random = Math.random) {
|
|
3226
|
+
return jitteredDelayMs(BASE_USAGE_REPORT_DELAY_MS, USAGE_REPORT_DELAY_JITTER_FRACTION, random);
|
|
3227
|
+
}
|
|
3228
|
+
var USAGE_REPORT_FAILURE_REESCALATION_TICKS = 6;
|
|
3229
|
+
function usageReportFailureLogLevel(consecutiveFailures) {
|
|
3230
|
+
return reportFailureLogLevel(consecutiveFailures, USAGE_REPORT_FAILURE_REESCALATION_TICKS);
|
|
3231
|
+
}
|
|
3002
3232
|
function reportFailureLogLevel(consecutiveFailures, reescalationTicks) {
|
|
3003
3233
|
return consecutiveFailures === 1 || consecutiveFailures % reescalationTicks === 0 ? "warn" : "debug";
|
|
3004
3234
|
}
|
|
@@ -3007,33 +3237,26 @@ function failureStreakSuffix(consecutiveFailures) {
|
|
|
3007
3237
|
}
|
|
3008
3238
|
|
|
3009
3239
|
// src/lib/claude-usage-reporting.ts
|
|
3010
|
-
var VALID_MODES = ["auto", "on", "off"];
|
|
3011
3240
|
function resolveClaudeUsageReportingMode(flagValue, env) {
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
}
|
|
3016
|
-
const normalized = raw.trim().toLowerCase();
|
|
3017
|
-
if (VALID_MODES.includes(normalized)) {
|
|
3018
|
-
return { mode: normalized, warnings: [] };
|
|
3019
|
-
}
|
|
3020
|
-
const source = flagValue !== void 0 ? "--claude-usage-reporting" : "EVIDENT_CLAUDE_USAGE_REPORTING";
|
|
3021
|
-
return {
|
|
3022
|
-
mode: "auto",
|
|
3023
|
-
warnings: [
|
|
3024
|
-
`Ignoring invalid ${source} "${raw}": expected one of ${VALID_MODES.join(", ")}; using auto`
|
|
3025
|
-
]
|
|
3026
|
-
};
|
|
3241
|
+
return resolveUsageReportingMode(flagValue, env, {
|
|
3242
|
+
flagName: "--claude-usage-reporting",
|
|
3243
|
+
envVar: "EVIDENT_CLAUDE_USAGE_REPORTING"
|
|
3244
|
+
});
|
|
3027
3245
|
}
|
|
3028
|
-
var BASE_REPORT_DELAY_MS = 10 * 6e4;
|
|
3029
|
-
var REPORT_DELAY_JITTER_FRACTION = 0.2;
|
|
3030
3246
|
function nextReportDelayMs(random = Math.random) {
|
|
3031
|
-
return
|
|
3247
|
+
return usageReportDelayMs(random);
|
|
3032
3248
|
}
|
|
3033
3249
|
var FIRST_REPORT_DELAY_MS = firstReportDelayMs();
|
|
3034
|
-
var CLAUDE_USAGE_FAILURE_REESCALATION_TICKS = 6;
|
|
3035
3250
|
function claudeUsageFailureLogLevel(consecutiveFailures) {
|
|
3036
|
-
return
|
|
3251
|
+
return usageReportFailureLogLevel(consecutiveFailures);
|
|
3252
|
+
}
|
|
3253
|
+
|
|
3254
|
+
// src/lib/openai-usage-reporting.ts
|
|
3255
|
+
function resolveOpenAiUsageReportingMode(flagValue, env) {
|
|
3256
|
+
return resolveUsageReportingMode(flagValue, env, {
|
|
3257
|
+
flagName: "--openai-usage-reporting",
|
|
3258
|
+
envVar: "EVIDENT_OPENAI_USAGE_REPORTING"
|
|
3259
|
+
});
|
|
3037
3260
|
}
|
|
3038
3261
|
|
|
3039
3262
|
// src/lib/resource-usage-reporting.ts
|
|
@@ -3192,15 +3415,15 @@ function createResourceUsageCollector(homeDir) {
|
|
|
3192
3415
|
}
|
|
3193
3416
|
|
|
3194
3417
|
// src/lib/channels/driver.ts
|
|
3195
|
-
import { homedir as
|
|
3418
|
+
import { homedir as homedir3 } from "os";
|
|
3196
3419
|
|
|
3197
3420
|
// src/lib/runner-file-sync.ts
|
|
3198
|
-
import { join as
|
|
3421
|
+
import { join as join5 } from "path";
|
|
3199
3422
|
|
|
3200
3423
|
// src/lib/file-push.ts
|
|
3201
3424
|
import { randomUUID } from "crypto";
|
|
3202
3425
|
import { chmod, mkdir, open as open2, realpath, rename, unlink } from "fs/promises";
|
|
3203
|
-
import { basename, dirname as dirname3, isAbsolute, join as
|
|
3426
|
+
import { basename, dirname as dirname3, isAbsolute, join as join4, relative, resolve as resolve2, sep } from "path";
|
|
3204
3427
|
var FILE_MODE = 384;
|
|
3205
3428
|
var DIRECTORY_MODE = 448;
|
|
3206
3429
|
async function writePushedFile(request) {
|
|
@@ -3233,7 +3456,7 @@ async function writePushedFile(request) {
|
|
|
3233
3456
|
const { existingAncestor, missingSegments } = await resolveNearestExistingAncestor(
|
|
3234
3457
|
dirname3(candidate)
|
|
3235
3458
|
);
|
|
3236
|
-
const realTarget =
|
|
3459
|
+
const realTarget = join4(existingAncestor, ...missingSegments, basename(candidate));
|
|
3237
3460
|
const allowedDirectory = await findContainingAllowedDirectory(allowedDirectories, realTarget);
|
|
3238
3461
|
if (allowedDirectory === null) {
|
|
3239
3462
|
return refuse("path_not_allowed", "The runner does not allow writing to that location.", {
|
|
@@ -3269,7 +3492,7 @@ function expandAndValidate(requestedPath, homeDir) {
|
|
|
3269
3492
|
if (requestedPath.trim() === "" || requestedPath.includes("\0")) {
|
|
3270
3493
|
return null;
|
|
3271
3494
|
}
|
|
3272
|
-
const expanded = requestedPath === "~" ? homeDir : requestedPath.startsWith("~/") ?
|
|
3495
|
+
const expanded = requestedPath === "~" ? homeDir : requestedPath.startsWith("~/") ? join4(homeDir, requestedPath.slice(2)) : requestedPath;
|
|
3273
3496
|
if (expanded.split(/[/\\]/).includes("..")) {
|
|
3274
3497
|
return null;
|
|
3275
3498
|
}
|
|
@@ -3342,13 +3565,13 @@ function contains(realDirectory, realTarget) {
|
|
|
3342
3565
|
async function createMissingDirectories(existingAncestor, missingSegments) {
|
|
3343
3566
|
let current = existingAncestor;
|
|
3344
3567
|
for (const segment of missingSegments) {
|
|
3345
|
-
current =
|
|
3568
|
+
current = join4(current, segment);
|
|
3346
3569
|
await mkdir(current, { recursive: true, mode: DIRECTORY_MODE });
|
|
3347
3570
|
await chmod(current, DIRECTORY_MODE);
|
|
3348
3571
|
}
|
|
3349
3572
|
}
|
|
3350
3573
|
async function writeAtomically(realTarget, content) {
|
|
3351
|
-
const temporaryPath =
|
|
3574
|
+
const temporaryPath = join4(dirname3(realTarget), `.evident-push-${randomUUID()}.tmp`);
|
|
3352
3575
|
let handle;
|
|
3353
3576
|
try {
|
|
3354
3577
|
handle = await open2(temporaryPath, "wx", FILE_MODE);
|
|
@@ -3390,20 +3613,28 @@ async function syncPendingRunnerFiles(options) {
|
|
|
3390
3613
|
for (const id of options.ackFailures.keys()) {
|
|
3391
3614
|
if (!pendingIds.has(id)) options.ackFailures.delete(id);
|
|
3392
3615
|
}
|
|
3393
|
-
if (pending.length === 0)
|
|
3616
|
+
if (pending.length === 0) {
|
|
3617
|
+
return { applied: 0, claudeCredentialApplied: false, opencodeAuthApplied: false };
|
|
3618
|
+
}
|
|
3394
3619
|
options.log({
|
|
3395
3620
|
level: "info",
|
|
3396
3621
|
message: `Runner file sync: ${pending.length} file(s) queued for this runner`
|
|
3397
3622
|
});
|
|
3398
3623
|
let applied = 0;
|
|
3399
3624
|
let claudeCredentialApplied = false;
|
|
3625
|
+
let opencodeAuthApplied = false;
|
|
3400
3626
|
for (const file of pending) {
|
|
3401
3627
|
if ((options.ackFailures.get(file.id) ?? 0) >= MAX_ACK_ATTEMPTS) continue;
|
|
3402
3628
|
const outcome = await applyOne(options, file);
|
|
3403
3629
|
if (outcome.applied) applied += 1;
|
|
3404
3630
|
if (outcome.claudeCredentialApplied) claudeCredentialApplied = true;
|
|
3631
|
+
if (outcome.opencodeAuthApplied) opencodeAuthApplied = true;
|
|
3405
3632
|
}
|
|
3406
|
-
return {
|
|
3633
|
+
return {
|
|
3634
|
+
applied,
|
|
3635
|
+
claudeCredentialApplied,
|
|
3636
|
+
opencodeAuthApplied
|
|
3637
|
+
};
|
|
3407
3638
|
}
|
|
3408
3639
|
async function listPendingFiles(options) {
|
|
3409
3640
|
let res;
|
|
@@ -3464,10 +3695,18 @@ function asPendingFile(entry) {
|
|
|
3464
3695
|
if (typeof size !== "number" || !Number.isFinite(size) || size < 0) return null;
|
|
3465
3696
|
return { id, path, size };
|
|
3466
3697
|
}
|
|
3467
|
-
var NOT_APPLIED = {
|
|
3698
|
+
var NOT_APPLIED = {
|
|
3699
|
+
applied: false,
|
|
3700
|
+
claudeCredentialApplied: false,
|
|
3701
|
+
opencodeAuthApplied: false
|
|
3702
|
+
};
|
|
3468
3703
|
function isClaudeCredentialPath(requestedPath, homeDir) {
|
|
3469
|
-
const expanded = requestedPath === "~" ? homeDir : requestedPath.startsWith("~/") ?
|
|
3470
|
-
return expanded ===
|
|
3704
|
+
const expanded = requestedPath === "~" ? homeDir : requestedPath.startsWith("~/") ? join5(homeDir, requestedPath.slice(2)) : requestedPath;
|
|
3705
|
+
return expanded === join5(homeDir, ...CLAUDE_CREDENTIALS_SEGMENTS);
|
|
3706
|
+
}
|
|
3707
|
+
function isOpenCodeAuthPath(requestedPath, homeDir) {
|
|
3708
|
+
const expanded = requestedPath === "~" ? homeDir : requestedPath.startsWith("~/") ? join5(homeDir, requestedPath.slice(2)) : requestedPath;
|
|
3709
|
+
return expanded === join5(homeDir, ...OPENCODE_AUTH_SEGMENTS);
|
|
3471
3710
|
}
|
|
3472
3711
|
async function applyOne(options, file) {
|
|
3473
3712
|
const label = `${file.id.slice(0, 8)} (${file.path})`;
|
|
@@ -3523,7 +3762,8 @@ async function applyOne(options, file) {
|
|
|
3523
3762
|
await ack(options, file, "applied");
|
|
3524
3763
|
return {
|
|
3525
3764
|
applied: true,
|
|
3526
|
-
claudeCredentialApplied: isClaudeCredentialPath(file.path, options.homeDir)
|
|
3765
|
+
claudeCredentialApplied: isClaudeCredentialPath(file.path, options.homeDir),
|
|
3766
|
+
opencodeAuthApplied: isOpenCodeAuthPath(file.path, options.homeDir)
|
|
3527
3767
|
};
|
|
3528
3768
|
}
|
|
3529
3769
|
function durableDownloadCode(status2) {
|
|
@@ -3977,6 +4217,7 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
3977
4217
|
* that way rather than "fixing" it into a count.
|
|
3978
4218
|
*/
|
|
3979
4219
|
claudeCredentialApplyCount = 0;
|
|
4220
|
+
opencodeAuthApplyCount = 0;
|
|
3980
4221
|
/**
|
|
3981
4222
|
* The currently-executing `drainPending()` promise, or null when idle. Lets a
|
|
3982
4223
|
* graceful shutdown (`waitForInFlight`) await an in-progress drain so a turn it
|
|
@@ -4011,7 +4252,7 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
4011
4252
|
this.stuckQueuedMs = config.stuckQueuedMs ?? DEFAULT_STUCK_QUEUED_MS;
|
|
4012
4253
|
this.now = config.now ?? (() => Date.now());
|
|
4013
4254
|
this.fileSyncDirectories = config.fileSyncDirectories ?? [];
|
|
4014
|
-
this.homeDir = config.homeDir ??
|
|
4255
|
+
this.homeDir = config.homeDir ?? homedir3();
|
|
4015
4256
|
this.maxActiveSessions = config.maxActiveSessions;
|
|
4016
4257
|
this.watcherStallMs = config.watcherStallMs ?? WATCHER_STALL_MS;
|
|
4017
4258
|
this.wedgeWarningIntervalMs = config.wedgeWarningIntervalMs ?? WEDGE_WARNING_INTERVAL_MS;
|
|
@@ -4081,6 +4322,7 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
4081
4322
|
});
|
|
4082
4323
|
this.appliedFileCount += result.applied;
|
|
4083
4324
|
if (result.claudeCredentialApplied) this.claudeCredentialApplyCount += 1;
|
|
4325
|
+
if (result.opencodeAuthApplied) this.opencodeAuthApplyCount += 1;
|
|
4084
4326
|
return result.applied;
|
|
4085
4327
|
} catch (err) {
|
|
4086
4328
|
this.log({
|
|
@@ -4188,7 +4430,8 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
4188
4430
|
return {
|
|
4189
4431
|
appliedFiles: this.appliedFileCount,
|
|
4190
4432
|
inFlight: this.syncingFiles,
|
|
4191
|
-
claudeCredentialApplies: this.claudeCredentialApplyCount
|
|
4433
|
+
claudeCredentialApplies: this.claudeCredentialApplyCount,
|
|
4434
|
+
opencodeAuthApplyCount: this.opencodeAuthApplyCount
|
|
4192
4435
|
};
|
|
4193
4436
|
}
|
|
4194
4437
|
/**
|
|
@@ -7525,7 +7768,7 @@ function resolveFileSyncDirectories(raw, homeDir) {
|
|
|
7525
7768
|
if (trimmed === "") {
|
|
7526
7769
|
throw new Error("--enable-file-sync-to requires a directory path (got an empty value)");
|
|
7527
7770
|
}
|
|
7528
|
-
const expanded = trimmed === "~" ? homeDir : trimmed.startsWith("~/") ?
|
|
7771
|
+
const expanded = trimmed === "~" ? homeDir : trimmed.startsWith("~/") ? join6(homeDir, trimmed.slice(2)) : trimmed;
|
|
7529
7772
|
if (!isAbsolute2(expanded)) {
|
|
7530
7773
|
throw new Error(`--enable-file-sync-to requires an absolute directory path; got "${entry}"`);
|
|
7531
7774
|
}
|
|
@@ -7733,6 +7976,7 @@ async function driveChannels(state, driver) {
|
|
|
7733
7976
|
let lastSeenProxiedActivityAt = state.lastProxiedActivityAt;
|
|
7734
7977
|
let lastSeenAppliedFiles = driver.fileSyncActivity().appliedFiles;
|
|
7735
7978
|
let lastSeenClaudeApplies = driver.fileSyncActivity().claudeCredentialApplies;
|
|
7979
|
+
let lastSeenOpencodeAuthApplies = driver.fileSyncActivity().opencodeAuthApplyCount;
|
|
7736
7980
|
while (state.running) {
|
|
7737
7981
|
const cycleStartedAtMs = performance.now();
|
|
7738
7982
|
let idleThisCycle = false;
|
|
@@ -7765,6 +8009,10 @@ async function driveChannels(state, driver) {
|
|
|
7765
8009
|
const claudeCredentialApplied = claudeCredentialApplies !== lastSeenClaudeApplies;
|
|
7766
8010
|
lastSeenClaudeApplies = claudeCredentialApplies;
|
|
7767
8011
|
if (claudeCredentialApplied) state.claudeUsageRearm?.();
|
|
8012
|
+
const opencodeAuthApplies = fileActivitySnapshot.opencodeAuthApplyCount;
|
|
8013
|
+
const opencodeAuthApplied = opencodeAuthApplies !== lastSeenOpencodeAuthApplies;
|
|
8014
|
+
lastSeenOpencodeAuthApplies = opencodeAuthApplies;
|
|
8015
|
+
if (opencodeAuthApplied) state.openaiUsageRearm?.();
|
|
7768
8016
|
if (processed > 0 || driver.hasInFlightWatchers() || proxiedActivity || fileActivity) {
|
|
7769
8017
|
idlePolls = 0;
|
|
7770
8018
|
idleMs = 0;
|
|
@@ -7833,7 +8081,7 @@ async function driveChannels(state, driver) {
|
|
|
7833
8081
|
var SESSION_CLEANUP_FIRST_SWEEP_MS = 1e4;
|
|
7834
8082
|
var SESSION_DB_RECLAIM_MAX_PAGES = 2e3;
|
|
7835
8083
|
function sessionDbPath() {
|
|
7836
|
-
return
|
|
8084
|
+
return join6(homedir4(), ".local", "share", "opencode", "opencode.db");
|
|
7837
8085
|
}
|
|
7838
8086
|
async function runSweep(state, driver, config) {
|
|
7839
8087
|
const mode = `age=${config.maxAgeMs ?? "\u2014"} count=${config.maxCount ?? "\u2014"}`;
|
|
@@ -7916,7 +8164,7 @@ function scheduleSessionCleanup(state, driver, options) {
|
|
|
7916
8164
|
for (const warning2 of config.warnings) {
|
|
7917
8165
|
logActivity(state, { type: "info", level: "warn", message: `Session cleanup: ${warning2}` });
|
|
7918
8166
|
}
|
|
7919
|
-
const dbBytes = statSessionDbBytes(
|
|
8167
|
+
const dbBytes = statSessionDbBytes(homedir4());
|
|
7920
8168
|
void (async () => {
|
|
7921
8169
|
const reclaimSkipReason = dbBytes !== null && config.enabled ? await probeReclaimAvailability({ dbPath: sessionDbPath(), requiredBytes: dbBytes }) : null;
|
|
7922
8170
|
const sizeWarning = buildSessionStoreSizeWarning({
|
|
@@ -7944,23 +8192,20 @@ function scheduleSessionCleanup(state, driver, options) {
|
|
|
7944
8192
|
);
|
|
7945
8193
|
state.sessionCleanupTimers.push(interval, firstSweep);
|
|
7946
8194
|
}
|
|
7947
|
-
function
|
|
7948
|
-
const { mode, warnings } =
|
|
7949
|
-
options.claudeUsageReporting,
|
|
7950
|
-
process.env
|
|
7951
|
-
);
|
|
8195
|
+
function scheduleUsageReporting(state, params) {
|
|
8196
|
+
const { mode, warnings } = params.resolved;
|
|
7952
8197
|
for (const warning2 of warnings) {
|
|
7953
8198
|
logActivity(state, {
|
|
7954
8199
|
type: "info",
|
|
7955
8200
|
level: "warn",
|
|
7956
|
-
message:
|
|
8201
|
+
message: `${params.label} usage reporting: ${warning2}`
|
|
7957
8202
|
});
|
|
7958
8203
|
}
|
|
7959
8204
|
if (mode === "off") {
|
|
7960
8205
|
logActivity(state, {
|
|
7961
8206
|
type: "info",
|
|
7962
8207
|
level: "debug",
|
|
7963
|
-
message:
|
|
8208
|
+
message: `${params.label} usage reporting is off (${params.offFlagHint})`
|
|
7964
8209
|
});
|
|
7965
8210
|
return null;
|
|
7966
8211
|
}
|
|
@@ -7969,7 +8214,7 @@ function scheduleClaudeUsageReporting(state, options) {
|
|
|
7969
8214
|
let rearmRequested = false;
|
|
7970
8215
|
const armProbe = () => {
|
|
7971
8216
|
phase = "probe-pending";
|
|
7972
|
-
|
|
8217
|
+
params.setTimer(setTimeout(() => void tick(true), params.firstDelayMs()));
|
|
7973
8218
|
};
|
|
7974
8219
|
const scheduleNextTick = () => {
|
|
7975
8220
|
if (rearmRequested) {
|
|
@@ -7978,7 +8223,7 @@ function scheduleClaudeUsageReporting(state, options) {
|
|
|
7978
8223
|
return;
|
|
7979
8224
|
}
|
|
7980
8225
|
phase = "steady-pending";
|
|
7981
|
-
|
|
8226
|
+
params.setTimer(setTimeout(() => void tick(false), params.nextDelayMs()));
|
|
7982
8227
|
};
|
|
7983
8228
|
const rearm = () => {
|
|
7984
8229
|
switch (phase) {
|
|
@@ -7988,9 +8233,9 @@ function scheduleClaudeUsageReporting(state, options) {
|
|
|
7988
8233
|
case "probe-pending":
|
|
7989
8234
|
return;
|
|
7990
8235
|
case "steady-pending":
|
|
7991
|
-
if (
|
|
7992
|
-
clearTimeout(
|
|
7993
|
-
|
|
8236
|
+
if (params.getTimer()) {
|
|
8237
|
+
clearTimeout(params.getTimer());
|
|
8238
|
+
params.setTimer(null);
|
|
7994
8239
|
}
|
|
7995
8240
|
rearmRequested = false;
|
|
7996
8241
|
armProbe();
|
|
@@ -8004,45 +8249,45 @@ function scheduleClaudeUsageReporting(state, options) {
|
|
|
8004
8249
|
const tick = async (isProbe) => {
|
|
8005
8250
|
phase = "tick-in-flight";
|
|
8006
8251
|
try {
|
|
8007
|
-
const usage = await
|
|
8008
|
-
const result = await
|
|
8252
|
+
const usage = await params.fetchUsage();
|
|
8253
|
+
const result = await params.report(usage);
|
|
8009
8254
|
if (result.ok) {
|
|
8010
8255
|
if (consecutiveFailures > 0) {
|
|
8011
8256
|
logActivity(state, {
|
|
8012
8257
|
type: "info",
|
|
8013
8258
|
level: "info",
|
|
8014
|
-
message:
|
|
8259
|
+
message: `${params.label} usage reporting recovered`
|
|
8015
8260
|
});
|
|
8016
8261
|
}
|
|
8017
8262
|
consecutiveFailures = 0;
|
|
8018
8263
|
logActivity(state, {
|
|
8019
8264
|
type: "info",
|
|
8020
8265
|
level: "debug",
|
|
8021
|
-
message:
|
|
8266
|
+
message: `Reported ${params.label} usage to Evident`
|
|
8022
8267
|
});
|
|
8023
8268
|
} else {
|
|
8024
8269
|
consecutiveFailures++;
|
|
8025
8270
|
logActivity(state, {
|
|
8026
8271
|
type: "info",
|
|
8027
|
-
level:
|
|
8028
|
-
message: `Failed to report
|
|
8272
|
+
level: params.failureLogLevel(consecutiveFailures),
|
|
8273
|
+
message: `Failed to report ${params.label} usage: ${result.error}${failureStreakSuffix(consecutiveFailures)}`
|
|
8029
8274
|
});
|
|
8030
8275
|
}
|
|
8031
8276
|
scheduleNextTick();
|
|
8032
8277
|
} catch (error2) {
|
|
8033
|
-
if (
|
|
8278
|
+
if (params.isLocalCredentialProblem(error2)) {
|
|
8034
8279
|
if (mode === "on") {
|
|
8035
8280
|
logActivity(state, {
|
|
8036
8281
|
type: "info",
|
|
8037
8282
|
level: "warn",
|
|
8038
|
-
message:
|
|
8283
|
+
message: `${params.label} usage reporting is forced on but no usable login was found \u2014 ${params.forcedOnHint}; reporting will keep retrying`
|
|
8039
8284
|
});
|
|
8040
8285
|
scheduleNextTick();
|
|
8041
8286
|
} else if (isProbe) {
|
|
8042
8287
|
logActivity(state, {
|
|
8043
8288
|
type: "info",
|
|
8044
8289
|
level: "debug",
|
|
8045
|
-
message:
|
|
8290
|
+
message: `${params.label} usage reporting: ${error2 instanceof Error ? error2.message : String(error2)}`
|
|
8046
8291
|
});
|
|
8047
8292
|
phase = "dormant";
|
|
8048
8293
|
if (rearmRequested) rearm();
|
|
@@ -8050,7 +8295,7 @@ function scheduleClaudeUsageReporting(state, options) {
|
|
|
8050
8295
|
logActivity(state, {
|
|
8051
8296
|
type: "info",
|
|
8052
8297
|
level: "debug",
|
|
8053
|
-
message:
|
|
8298
|
+
message: `${params.label} usage reporting: ${error2 instanceof Error ? error2.message : String(error2)}`
|
|
8054
8299
|
});
|
|
8055
8300
|
scheduleNextTick();
|
|
8056
8301
|
}
|
|
@@ -8059,8 +8304,8 @@ function scheduleClaudeUsageReporting(state, options) {
|
|
|
8059
8304
|
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
8060
8305
|
logActivity(state, {
|
|
8061
8306
|
type: "info",
|
|
8062
|
-
level:
|
|
8063
|
-
message:
|
|
8307
|
+
level: params.failureLogLevel(consecutiveFailures),
|
|
8308
|
+
message: `${params.label} usage reporting failed: ${message}${failureStreakSuffix(consecutiveFailures)}`
|
|
8064
8309
|
});
|
|
8065
8310
|
scheduleNextTick();
|
|
8066
8311
|
}
|
|
@@ -8069,6 +8314,24 @@ function scheduleClaudeUsageReporting(state, options) {
|
|
|
8069
8314
|
armProbe();
|
|
8070
8315
|
return rearm;
|
|
8071
8316
|
}
|
|
8317
|
+
function scheduleClaudeUsageReporting(state, options) {
|
|
8318
|
+
return scheduleUsageReporting(state, {
|
|
8319
|
+
label: "Claude",
|
|
8320
|
+
resolved: resolveClaudeUsageReportingMode(options.claudeUsageReporting, process.env),
|
|
8321
|
+
offFlagHint: "--claude-usage-reporting off",
|
|
8322
|
+
getTimer: () => state.claudeUsageTimer,
|
|
8323
|
+
setTimer: (timer) => {
|
|
8324
|
+
state.claudeUsageTimer = timer;
|
|
8325
|
+
},
|
|
8326
|
+
fetchUsage: getClaudeUsage,
|
|
8327
|
+
report: (usage) => reportClaudeUsage(state.agentId, state.authHeader, usage),
|
|
8328
|
+
isLocalCredentialProblem,
|
|
8329
|
+
forcedOnHint: "run `claude` to sign in",
|
|
8330
|
+
firstDelayMs: () => FIRST_REPORT_DELAY_MS,
|
|
8331
|
+
nextDelayMs: nextReportDelayMs,
|
|
8332
|
+
failureLogLevel: claudeUsageFailureLogLevel
|
|
8333
|
+
});
|
|
8334
|
+
}
|
|
8072
8335
|
var RESOURCE_USAGE_BASE_REPORT_DELAY_MS = 10 * 6e4;
|
|
8073
8336
|
var RESOURCE_USAGE_REPORT_DELAY_JITTER_FRACTION = 0.2;
|
|
8074
8337
|
var RESOURCE_USAGE_FAILURE_REESCALATION_TICKS = 6;
|
|
@@ -8092,7 +8355,7 @@ function scheduleResourceUsageReporting(state, options) {
|
|
|
8092
8355
|
});
|
|
8093
8356
|
return;
|
|
8094
8357
|
}
|
|
8095
|
-
const collect = createResourceUsageCollector(
|
|
8358
|
+
const collect = createResourceUsageCollector(homedir4());
|
|
8096
8359
|
let consecutiveFailures = 0;
|
|
8097
8360
|
const tick = async () => {
|
|
8098
8361
|
try {
|
|
@@ -8193,6 +8456,11 @@ async function cleanup(state, opts = {}) {
|
|
|
8193
8456
|
state.claudeUsageTimer = null;
|
|
8194
8457
|
}
|
|
8195
8458
|
state.claudeUsageRearm = null;
|
|
8459
|
+
if (state.openaiUsageTimer) {
|
|
8460
|
+
clearTimeout(state.openaiUsageTimer);
|
|
8461
|
+
state.openaiUsageTimer = null;
|
|
8462
|
+
}
|
|
8463
|
+
state.openaiUsageRearm = null;
|
|
8196
8464
|
if (state.resourceUsageTimer) {
|
|
8197
8465
|
clearTimeout(state.resourceUsageTimer);
|
|
8198
8466
|
state.resourceUsageTimer = null;
|
|
@@ -8244,7 +8512,7 @@ async function run(options) {
|
|
|
8244
8512
|
let fileSyncDirectories;
|
|
8245
8513
|
try {
|
|
8246
8514
|
logLevel = resolveLogLevel(options);
|
|
8247
|
-
fileSyncDirectories = resolveFileSyncDirectories(options.enableFileSyncTo,
|
|
8515
|
+
fileSyncDirectories = resolveFileSyncDirectories(options.enableFileSyncTo, homedir4());
|
|
8248
8516
|
} catch (error2) {
|
|
8249
8517
|
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
8250
8518
|
if (options.json) {
|
|
@@ -8279,6 +8547,8 @@ async function run(options) {
|
|
|
8279
8547
|
sessionCleanupTimers: [],
|
|
8280
8548
|
claudeUsageTimer: null,
|
|
8281
8549
|
claudeUsageRearm: null,
|
|
8550
|
+
openaiUsageTimer: null,
|
|
8551
|
+
openaiUsageRearm: null,
|
|
8282
8552
|
resourceUsageTimer: null,
|
|
8283
8553
|
authHeader: ""
|
|
8284
8554
|
};
|
|
@@ -8541,7 +8811,7 @@ async function run(options) {
|
|
|
8541
8811
|
// #559: the `--enable-file-sync-to` allow-list. Empty ⇒ pending files are
|
|
8542
8812
|
// REJECTED with `file_sync_disabled` on the ack, not silently ignored.
|
|
8543
8813
|
fileSyncDirectories,
|
|
8544
|
-
homeDir:
|
|
8814
|
+
homeDir: homedir4(),
|
|
8545
8815
|
maxActiveSessions,
|
|
8546
8816
|
log: (entry) => (
|
|
8547
8817
|
// Thread the driver's real level straight through so `debug`/`warn`
|
|
@@ -8679,6 +8949,22 @@ async function run(options) {
|
|
|
8679
8949
|
}
|
|
8680
8950
|
scheduleSessionCleanup(state, channelDriver, options);
|
|
8681
8951
|
state.claudeUsageRearm = scheduleClaudeUsageReporting(state, options);
|
|
8952
|
+
state.openaiUsageRearm = scheduleUsageReporting(state, {
|
|
8953
|
+
label: "OpenAI",
|
|
8954
|
+
resolved: resolveOpenAiUsageReportingMode(options.openaiUsageReporting, process.env),
|
|
8955
|
+
offFlagHint: "--openai-usage-reporting off",
|
|
8956
|
+
getTimer: () => state.openaiUsageTimer,
|
|
8957
|
+
setTimer: (timer) => {
|
|
8958
|
+
state.openaiUsageTimer = timer;
|
|
8959
|
+
},
|
|
8960
|
+
fetchUsage: () => getOpenAiUsage(state.port),
|
|
8961
|
+
report: (usage) => reportOpenAiUsage(state.agentId, state.authHeader, usage),
|
|
8962
|
+
isLocalCredentialProblem: isLocalCredentialProblem2,
|
|
8963
|
+
forcedOnHint: "connect a ChatGPT account to this runner, or run `opencode auth login`",
|
|
8964
|
+
firstDelayMs: firstReportDelayMs,
|
|
8965
|
+
nextDelayMs: usageReportDelayMs,
|
|
8966
|
+
failureLogLevel: usageReportFailureLogLevel
|
|
8967
|
+
});
|
|
8682
8968
|
scheduleResourceUsageReporting(state, options);
|
|
8683
8969
|
if (!interactive || state.json) {
|
|
8684
8970
|
log2(state, "Driving channel messages...");
|
|
@@ -8760,6 +9046,9 @@ program.command("run").description("Connect to Evident and process messages").op
|
|
|
8760
9046
|
).option(
|
|
8761
9047
|
"--claude-usage-reporting <mode>",
|
|
8762
9048
|
"Report Claude subscription usage to Evident: auto | on | off (default: auto). Env: EVIDENT_CLAUDE_USAGE_REPORTING"
|
|
9049
|
+
).option(
|
|
9050
|
+
"--openai-usage-reporting <mode>",
|
|
9051
|
+
"Report OpenAI plan usage to Evident: auto | on | off (default: auto). Env: EVIDENT_OPENAI_USAGE_REPORTING"
|
|
8763
9052
|
).option(
|
|
8764
9053
|
"--no-resource-usage-reporting",
|
|
8765
9054
|
"Don't report this machine's CPU and memory usage to Evident (reporting is on by default). Env: EVIDENT_RESOURCE_USAGE_REPORTING=off"
|
|
@@ -8795,6 +9084,7 @@ program.command("run").description("Connect to Evident and process messages").op
|
|
|
8795
9084
|
// Raw string — the resolver in run.ts single-sources parsing
|
|
8796
9085
|
// (resolveClaudeUsageReportingMode).
|
|
8797
9086
|
claudeUsageReporting: options.claudeUsageReporting,
|
|
9087
|
+
openaiUsageReporting: options.openaiUsageReporting,
|
|
8798
9088
|
// Raw value — resolution is single-sourced in run.ts's
|
|
8799
9089
|
// resolveResourceUsageReportingEnabled.
|
|
8800
9090
|
resourceUsageReporting: options.resourceUsageReporting,
|