@omnicross/daemon 0.4.4 → 0.4.5
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/dist/cli.cjs +2015 -993
- package/dist/cli.js +1705 -667
- package/dist/index.cjs +2017 -992
- package/dist/index.d.cts +143 -2
- package/dist/index.d.ts +143 -2
- package/dist/index.js +1712 -674
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/bootstrap.ts
|
|
2
|
-
import { accessSync, constants as fsConstants, existsSync as
|
|
2
|
+
import { accessSync, constants as fsConstants, existsSync as existsSync31, mkdirSync as mkdirSync9 } from "fs";
|
|
3
3
|
import { dirname as dirname17 } from "path";
|
|
4
4
|
import { DEFAULT_AUDIT_CONFIG } from "@omnicross/contracts/audit-types";
|
|
5
5
|
import { DEFAULT_BILLING_CONFIG } from "@omnicross/contracts/billing-types";
|
|
@@ -196,14 +196,14 @@ async function runKimiDevicePoll(sessionId, deviceCode, deviceId, fingerprint, s
|
|
|
196
196
|
{
|
|
197
197
|
fingerprint,
|
|
198
198
|
deadlineMs: DEFAULT_KIMI_OAUTH_TTL_MS,
|
|
199
|
-
sleep: (ms) => new Promise((
|
|
199
|
+
sleep: (ms) => new Promise((resolve11, reject) => {
|
|
200
200
|
const onAbort = () => {
|
|
201
201
|
clearTimeout(timer);
|
|
202
202
|
reject(new Error("login: cancelled"));
|
|
203
203
|
};
|
|
204
204
|
const timer = setTimeout(() => {
|
|
205
205
|
signal.removeEventListener("abort", onAbort);
|
|
206
|
-
|
|
206
|
+
resolve11();
|
|
207
207
|
}, ms);
|
|
208
208
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
209
209
|
})
|
|
@@ -279,14 +279,14 @@ async function runGrokDevicePoll(sessionId, tokenEndpoint, deviceCode, signal, d
|
|
|
279
279
|
fetchImpl,
|
|
280
280
|
{
|
|
281
281
|
deadlineMs: DEFAULT_GROK_OAUTH_TTL_MS,
|
|
282
|
-
sleep: (ms) => new Promise((
|
|
282
|
+
sleep: (ms) => new Promise((resolve11, reject) => {
|
|
283
283
|
const onAbort = () => {
|
|
284
284
|
clearTimeout(timer);
|
|
285
285
|
reject(new Error("login: cancelled"));
|
|
286
286
|
};
|
|
287
287
|
const timer = setTimeout(() => {
|
|
288
288
|
signal.removeEventListener("abort", onAbort);
|
|
289
|
-
|
|
289
|
+
resolve11();
|
|
290
290
|
}, ms);
|
|
291
291
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
292
292
|
})
|
|
@@ -364,14 +364,14 @@ async function runCopilotDevicePoll(sessionId, deviceCode, signal, deps, enterpr
|
|
|
364
364
|
{
|
|
365
365
|
deadlineMs: DEFAULT_COPILOT_OAUTH_TTL_MS,
|
|
366
366
|
...enterpriseUrl ? { enterpriseUrl } : {},
|
|
367
|
-
sleep: (ms) => new Promise((
|
|
367
|
+
sleep: (ms) => new Promise((resolve11, reject) => {
|
|
368
368
|
const onAbort = () => {
|
|
369
369
|
clearTimeout(timer);
|
|
370
370
|
reject(new Error("login: cancelled"));
|
|
371
371
|
};
|
|
372
372
|
const timer = setTimeout(() => {
|
|
373
373
|
signal.removeEventListener("abort", onAbort);
|
|
374
|
-
|
|
374
|
+
resolve11();
|
|
375
375
|
}, ms);
|
|
376
376
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
377
377
|
})
|
|
@@ -2764,19 +2764,19 @@ function resetWebhookRuntimeForTests() {
|
|
|
2764
2764
|
|
|
2765
2765
|
// src/admin/webhookTestApi.ts
|
|
2766
2766
|
function readJsonBody(req) {
|
|
2767
|
-
return new Promise((
|
|
2767
|
+
return new Promise((resolve11) => {
|
|
2768
2768
|
const chunks = [];
|
|
2769
2769
|
req.on("data", (c) => chunks.push(c));
|
|
2770
2770
|
req.on("end", () => {
|
|
2771
2771
|
try {
|
|
2772
2772
|
const raw = Buffer.concat(chunks).toString("utf8");
|
|
2773
2773
|
const parsed = raw ? JSON.parse(raw) : {};
|
|
2774
|
-
|
|
2774
|
+
resolve11(parsed && typeof parsed === "object" ? parsed : {});
|
|
2775
2775
|
} catch {
|
|
2776
|
-
|
|
2776
|
+
resolve11({});
|
|
2777
2777
|
}
|
|
2778
2778
|
});
|
|
2779
|
-
req.on("error", () =>
|
|
2779
|
+
req.on("error", () => resolve11({}));
|
|
2780
2780
|
});
|
|
2781
2781
|
}
|
|
2782
2782
|
async function handleWebhookTest(req, res) {
|
|
@@ -2879,13 +2879,771 @@ async function handleRouteLeaseApi(req, res, path2, deps) {
|
|
|
2879
2879
|
}
|
|
2880
2880
|
}
|
|
2881
2881
|
|
|
2882
|
+
// src/admin/codexSessionManager.ts
|
|
2883
|
+
import { homedir } from "os";
|
|
2884
|
+
import { basename, join, resolve, win32 } from "path";
|
|
2885
|
+
import {
|
|
2886
|
+
copyFile,
|
|
2887
|
+
open,
|
|
2888
|
+
readdir,
|
|
2889
|
+
readFile,
|
|
2890
|
+
rename,
|
|
2891
|
+
stat,
|
|
2892
|
+
unlink,
|
|
2893
|
+
writeFile
|
|
2894
|
+
} from "fs/promises";
|
|
2895
|
+
import { existsSync as existsSync2 } from "fs";
|
|
2896
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
2897
|
+
import { TextDecoder as TextDecoder2 } from "util";
|
|
2898
|
+
var PROVIDER_PROPERTY_NAMES = /* @__PURE__ */ new Set(["model_provider", "model_provider_id"]);
|
|
2899
|
+
var FIRST_LINE_LIMIT = 4 * 1024 * 1024;
|
|
2900
|
+
var PROVIDER_ID_LIMIT = 256;
|
|
2901
|
+
var CodexSessionManagerError = class extends Error {
|
|
2902
|
+
constructor(message) {
|
|
2903
|
+
super(message);
|
|
2904
|
+
this.name = "CodexSessionManagerError";
|
|
2905
|
+
}
|
|
2906
|
+
};
|
|
2907
|
+
var CodexSessionManager = class {
|
|
2908
|
+
codexHome;
|
|
2909
|
+
stateDatabasePath;
|
|
2910
|
+
mutationTail = Promise.resolve();
|
|
2911
|
+
constructor(options = {}) {
|
|
2912
|
+
const configuredHome = options.codexHome?.trim() || process.env["CODEX_HOME"]?.trim();
|
|
2913
|
+
this.codexHome = configuredHome || join(homedir(), ".codex");
|
|
2914
|
+
this.stateDatabasePath = options.stateDatabasePath?.trim() || join(this.codexHome, "state_5.sqlite");
|
|
2915
|
+
}
|
|
2916
|
+
async list(projectPath) {
|
|
2917
|
+
const project = await validateProjectPath(projectPath);
|
|
2918
|
+
const [files, state] = await Promise.all([
|
|
2919
|
+
scanSessionFiles(join(this.codexHome, "sessions")),
|
|
2920
|
+
readStateThreads(this.stateDatabasePath)
|
|
2921
|
+
]);
|
|
2922
|
+
return mergeSessionMetadata(project.displayPath, this.codexHome, files, state);
|
|
2923
|
+
}
|
|
2924
|
+
async preview(input) {
|
|
2925
|
+
const normalized2 = normalizeApplyInput(input);
|
|
2926
|
+
const project = await validateProjectPath(normalized2.projectPath);
|
|
2927
|
+
const [files, state] = await Promise.all([
|
|
2928
|
+
scanSessionFiles(join(this.codexHome, "sessions")),
|
|
2929
|
+
readStateThreads(this.stateDatabasePath)
|
|
2930
|
+
]);
|
|
2931
|
+
const snapshot = mergeSessionMetadata(project.displayPath, this.codexHome, files, state);
|
|
2932
|
+
const plans = await buildProviderPlans(normalized2, snapshot, state.rows);
|
|
2933
|
+
return {
|
|
2934
|
+
projectPath: project.displayPath,
|
|
2935
|
+
fromProvider: normalized2.fromProvider ?? null,
|
|
2936
|
+
toProvider: normalized2.toProvider,
|
|
2937
|
+
stateDatabase: state.status,
|
|
2938
|
+
sessions: plans,
|
|
2939
|
+
warnings: snapshot.warnings
|
|
2940
|
+
};
|
|
2941
|
+
}
|
|
2942
|
+
async apply(input) {
|
|
2943
|
+
return this.withMutationLock(() => this.applyLocked(input));
|
|
2944
|
+
}
|
|
2945
|
+
async applyLocked(input) {
|
|
2946
|
+
const normalized2 = normalizeApplyInput(input);
|
|
2947
|
+
const project = await validateProjectPath(normalized2.projectPath);
|
|
2948
|
+
const [files, state] = await Promise.all([
|
|
2949
|
+
scanSessionFiles(join(this.codexHome, "sessions")),
|
|
2950
|
+
readStateThreads(this.stateDatabasePath)
|
|
2951
|
+
]);
|
|
2952
|
+
if (!state.status.available) {
|
|
2953
|
+
throw new CodexSessionManagerError(
|
|
2954
|
+
state.status.reason || `Codex state database is unavailable: ${state.status.path}`
|
|
2955
|
+
);
|
|
2956
|
+
}
|
|
2957
|
+
const snapshot = mergeSessionMetadata(project.displayPath, this.codexHome, files, state);
|
|
2958
|
+
const plans = await buildProviderPlans(normalized2, snapshot, state.rows);
|
|
2959
|
+
const selected = plans.filter((plan) => plan.action !== "no_change");
|
|
2960
|
+
const blocked = selected.filter((plan) => plan.action === "blocked");
|
|
2961
|
+
if (blocked.length > 0) {
|
|
2962
|
+
const details = blocked.map((plan) => `${plan.id}: ${plan.reason || "blocked"}`).join("; ");
|
|
2963
|
+
throw new CodexSessionManagerError(`cannot update selected Codex sessions: ${details}`);
|
|
2964
|
+
}
|
|
2965
|
+
if (selected.length === 0) {
|
|
2966
|
+
return {
|
|
2967
|
+
ok: true,
|
|
2968
|
+
projectPath: project.displayPath,
|
|
2969
|
+
fromProvider: normalized2.fromProvider ?? null,
|
|
2970
|
+
toProvider: normalized2.toProvider,
|
|
2971
|
+
updatedSessions: 0,
|
|
2972
|
+
jsonlFiles: 0,
|
|
2973
|
+
jsonlFields: 0,
|
|
2974
|
+
sqliteRows: 0,
|
|
2975
|
+
backups: []
|
|
2976
|
+
};
|
|
2977
|
+
}
|
|
2978
|
+
const fileChanges = [];
|
|
2979
|
+
for (const plan of selected) {
|
|
2980
|
+
const file = files.get(plan.id);
|
|
2981
|
+
if (!file || file.status !== "ready") {
|
|
2982
|
+
throw new CodexSessionManagerError(
|
|
2983
|
+
`rollout for session '${plan.id}' is unavailable; no files were changed`
|
|
2984
|
+
);
|
|
2985
|
+
}
|
|
2986
|
+
const before = await snapshotFile(file.filePath);
|
|
2987
|
+
const inspection = await inspectAndTransformFile(file.filePath, normalized2.fromProvider, normalized2.toProvider);
|
|
2988
|
+
const after = await snapshotFile(file.filePath);
|
|
2989
|
+
if (!sameFileSnapshot(before, after)) {
|
|
2990
|
+
throw new CodexSessionManagerError(
|
|
2991
|
+
`rollout '${file.filePath}' changed while it was being read; no files were changed`
|
|
2992
|
+
);
|
|
2993
|
+
}
|
|
2994
|
+
if (inspection.changedFields > 0) {
|
|
2995
|
+
fileChanges.push({
|
|
2996
|
+
id: plan.id,
|
|
2997
|
+
filePath: file.filePath,
|
|
2998
|
+
before,
|
|
2999
|
+
transformedText: inspection.transformedText,
|
|
3000
|
+
changedFields: inspection.changedFields
|
|
3001
|
+
});
|
|
3002
|
+
}
|
|
3003
|
+
}
|
|
3004
|
+
const stateRowsById = new Map(state.rows.map((row) => [row.id, row]));
|
|
3005
|
+
const dbChanges = plans.filter((plan) => plan.action === "update").map((plan) => stateRowsById.get(plan.id)).filter((row) => {
|
|
3006
|
+
if (!row) return false;
|
|
3007
|
+
if (normalized2.fromProvider && row.modelProvider !== normalized2.fromProvider) return false;
|
|
3008
|
+
return row.modelProvider !== normalized2.toProvider;
|
|
3009
|
+
});
|
|
3010
|
+
const temporaryFiles = [];
|
|
3011
|
+
const backups = [];
|
|
3012
|
+
let database;
|
|
3013
|
+
let committed = false;
|
|
3014
|
+
try {
|
|
3015
|
+
for (const change of fileChanges) {
|
|
3016
|
+
const tempPath = `${change.filePath}.provider-switch-${randomUUID2()}.tmp`;
|
|
3017
|
+
await writeFile(tempPath, change.transformedText, "utf8");
|
|
3018
|
+
temporaryFiles.push(tempPath);
|
|
3019
|
+
}
|
|
3020
|
+
if (dbChanges.length > 0) {
|
|
3021
|
+
const sqlite = await loadSqlite();
|
|
3022
|
+
database = openDatabase(sqlite, this.stateDatabasePath, false);
|
|
3023
|
+
const databaseBackup = await createDatabaseBackup(sqlite, database, this.stateDatabasePath);
|
|
3024
|
+
backups.push(databaseBackup);
|
|
3025
|
+
database.exec("BEGIN IMMEDIATE");
|
|
3026
|
+
assertDatabaseRowsUnchanged(database, dbChanges);
|
|
3027
|
+
}
|
|
3028
|
+
for (let index = 0; index < fileChanges.length; index += 1) {
|
|
3029
|
+
const change = fileChanges[index];
|
|
3030
|
+
const tempPath = temporaryFiles[index];
|
|
3031
|
+
const current = await snapshotFile(change.filePath);
|
|
3032
|
+
if (!sameFileSnapshot(change.before, current)) {
|
|
3033
|
+
throw new CodexSessionManagerError(
|
|
3034
|
+
`rollout '${change.filePath}' changed before replacement; no files were changed`
|
|
3035
|
+
);
|
|
3036
|
+
}
|
|
3037
|
+
const backupPath = await createUniqueBackupPath(change.filePath);
|
|
3038
|
+
await copyFile(change.filePath, backupPath);
|
|
3039
|
+
backups.push(backupPath);
|
|
3040
|
+
await replaceFileAtomically(tempPath, change.filePath);
|
|
3041
|
+
temporaryFiles[index] = "";
|
|
3042
|
+
}
|
|
3043
|
+
if (database && dbChanges.length > 0) {
|
|
3044
|
+
const update = database.prepare("UPDATE threads SET model_provider = ? WHERE id = ?");
|
|
3045
|
+
for (const row of dbChanges) update.run(normalized2.toProvider, row.id);
|
|
3046
|
+
database.exec("COMMIT");
|
|
3047
|
+
committed = true;
|
|
3048
|
+
} else {
|
|
3049
|
+
committed = true;
|
|
3050
|
+
}
|
|
3051
|
+
return {
|
|
3052
|
+
ok: true,
|
|
3053
|
+
projectPath: project.displayPath,
|
|
3054
|
+
fromProvider: normalized2.fromProvider ?? null,
|
|
3055
|
+
toProvider: normalized2.toProvider,
|
|
3056
|
+
updatedSessions: (/* @__PURE__ */ new Set([...fileChanges.map((change) => change.id), ...dbChanges.map((row) => row.id)])).size,
|
|
3057
|
+
jsonlFiles: fileChanges.length,
|
|
3058
|
+
jsonlFields: fileChanges.reduce((total, change) => total + change.changedFields, 0),
|
|
3059
|
+
sqliteRows: dbChanges.length,
|
|
3060
|
+
backups
|
|
3061
|
+
};
|
|
3062
|
+
} catch (error) {
|
|
3063
|
+
if (database?.isTransaction) {
|
|
3064
|
+
try {
|
|
3065
|
+
database.exec("ROLLBACK");
|
|
3066
|
+
} catch {
|
|
3067
|
+
}
|
|
3068
|
+
}
|
|
3069
|
+
if (!committed) {
|
|
3070
|
+
try {
|
|
3071
|
+
await restoreBackups(backups.filter((path2) => /\.jsonl\.provider-switch-[^/\\]+\.bak$/iu.test(path2)));
|
|
3072
|
+
} catch (restoreError) {
|
|
3073
|
+
const original = error instanceof Error ? error.message : String(error);
|
|
3074
|
+
const restoration = restoreError instanceof Error ? restoreError.message : String(restoreError);
|
|
3075
|
+
throw new CodexSessionManagerError(
|
|
3076
|
+
`${original}; JSONL restoration also failed: ${restoration}`
|
|
3077
|
+
);
|
|
3078
|
+
}
|
|
3079
|
+
}
|
|
3080
|
+
throw error;
|
|
3081
|
+
} finally {
|
|
3082
|
+
if (database?.isOpen) database.close();
|
|
3083
|
+
for (const tempPath of temporaryFiles) {
|
|
3084
|
+
if (!tempPath) continue;
|
|
3085
|
+
await unlinkIfPresent(tempPath);
|
|
3086
|
+
}
|
|
3087
|
+
}
|
|
3088
|
+
}
|
|
3089
|
+
async withMutationLock(operation) {
|
|
3090
|
+
const previous = this.mutationTail;
|
|
3091
|
+
let release;
|
|
3092
|
+
this.mutationTail = new Promise((resolve11) => {
|
|
3093
|
+
release = resolve11;
|
|
3094
|
+
});
|
|
3095
|
+
await previous;
|
|
3096
|
+
try {
|
|
3097
|
+
return await operation();
|
|
3098
|
+
} finally {
|
|
3099
|
+
release();
|
|
3100
|
+
}
|
|
3101
|
+
}
|
|
3102
|
+
};
|
|
3103
|
+
function normalizeApplyInput(input) {
|
|
3104
|
+
const projectPath = typeof input.projectPath === "string" ? input.projectPath.trim() : "";
|
|
3105
|
+
const toProvider = typeof input.toProvider === "string" ? input.toProvider.trim() : "";
|
|
3106
|
+
const fromProvider = typeof input.fromProvider === "string" ? input.fromProvider.trim() : void 0;
|
|
3107
|
+
if (!projectPath) throw new CodexSessionManagerError("projectPath must be a non-empty string");
|
|
3108
|
+
validateProviderId(toProvider, "toProvider");
|
|
3109
|
+
if (fromProvider) validateProviderId(fromProvider, "fromProvider");
|
|
3110
|
+
if (!Array.isArray(input.sessionIds) || input.sessionIds.length === 0) {
|
|
3111
|
+
throw new CodexSessionManagerError("sessionIds must contain at least one session id");
|
|
3112
|
+
}
|
|
3113
|
+
const sessionIds = [
|
|
3114
|
+
...new Set(
|
|
3115
|
+
input.sessionIds.filter((id) => typeof id === "string" && id.trim().length > 0).map((id) => id.trim())
|
|
3116
|
+
)
|
|
3117
|
+
];
|
|
3118
|
+
if (sessionIds.length === 0) throw new CodexSessionManagerError("sessionIds must contain at least one session id");
|
|
3119
|
+
if (sessionIds.length > 1e4) throw new CodexSessionManagerError("too many sessionIds");
|
|
3120
|
+
return { projectPath, sessionIds, toProvider, ...fromProvider ? { fromProvider } : {} };
|
|
3121
|
+
}
|
|
3122
|
+
function validateProviderId(value, name) {
|
|
3123
|
+
if (!value) throw new CodexSessionManagerError(`${name} must be a non-empty string`);
|
|
3124
|
+
if (value.length > PROVIDER_ID_LIMIT || /[\u0000-\u001f\u007f]/u.test(value)) {
|
|
3125
|
+
throw new CodexSessionManagerError(`${name} is invalid`);
|
|
3126
|
+
}
|
|
3127
|
+
}
|
|
3128
|
+
async function validateProjectPath(projectPath) {
|
|
3129
|
+
if (typeof projectPath !== "string" || !projectPath.trim()) {
|
|
3130
|
+
throw new CodexSessionManagerError("projectPath must be a non-empty string");
|
|
3131
|
+
}
|
|
3132
|
+
const displayPath = resolve(projectPath.trim());
|
|
3133
|
+
let info;
|
|
3134
|
+
try {
|
|
3135
|
+
info = await stat(displayPath);
|
|
3136
|
+
} catch {
|
|
3137
|
+
throw new CodexSessionManagerError(`project path does not exist: ${displayPath}`);
|
|
3138
|
+
}
|
|
3139
|
+
if (!info.isDirectory()) throw new CodexSessionManagerError(`project path is not a directory: ${displayPath}`);
|
|
3140
|
+
return { displayPath };
|
|
3141
|
+
}
|
|
3142
|
+
async function scanSessionFiles(sessionsRoot) {
|
|
3143
|
+
const files = await collectJsonlFiles(sessionsRoot);
|
|
3144
|
+
const records = /* @__PURE__ */ new Map();
|
|
3145
|
+
for (const filePath of files) {
|
|
3146
|
+
let fileStat;
|
|
3147
|
+
try {
|
|
3148
|
+
fileStat = await stat(filePath);
|
|
3149
|
+
const firstLine = await readFirstLine(filePath);
|
|
3150
|
+
const parsed = parseSessionMetadata(firstLine, filePath);
|
|
3151
|
+
if (!parsed.id) continue;
|
|
3152
|
+
const record = {
|
|
3153
|
+
id: parsed.id,
|
|
3154
|
+
filePath,
|
|
3155
|
+
cwd: parsed.cwd,
|
|
3156
|
+
jsonlProvider: parsed.provider,
|
|
3157
|
+
timestamp: parsed.timestamp,
|
|
3158
|
+
size: fileStat.size,
|
|
3159
|
+
mtimeMs: fileStat.mtimeMs,
|
|
3160
|
+
status: "ready"
|
|
3161
|
+
};
|
|
3162
|
+
const existing = records.get(record.id);
|
|
3163
|
+
if (!existing || record.mtimeMs > existing.mtimeMs) records.set(record.id, record);
|
|
3164
|
+
} catch {
|
|
3165
|
+
const id = sessionIdFromRolloutPath(filePath);
|
|
3166
|
+
if (!id) continue;
|
|
3167
|
+
let fallbackStat;
|
|
3168
|
+
try {
|
|
3169
|
+
fallbackStat = await stat(filePath);
|
|
3170
|
+
} catch {
|
|
3171
|
+
continue;
|
|
3172
|
+
}
|
|
3173
|
+
const existing = records.get(id);
|
|
3174
|
+
if (!existing || fallbackStat.mtimeMs > existing.mtimeMs) {
|
|
3175
|
+
records.set(id, {
|
|
3176
|
+
id,
|
|
3177
|
+
filePath,
|
|
3178
|
+
cwd: null,
|
|
3179
|
+
jsonlProvider: null,
|
|
3180
|
+
timestamp: null,
|
|
3181
|
+
size: fallbackStat.size,
|
|
3182
|
+
mtimeMs: fallbackStat.mtimeMs,
|
|
3183
|
+
status: "unreadable_rollout"
|
|
3184
|
+
});
|
|
3185
|
+
}
|
|
3186
|
+
}
|
|
3187
|
+
}
|
|
3188
|
+
return records;
|
|
3189
|
+
}
|
|
3190
|
+
async function collectJsonlFiles(root) {
|
|
3191
|
+
if (!existsSync2(root)) return [];
|
|
3192
|
+
const result = [];
|
|
3193
|
+
const pending = [root];
|
|
3194
|
+
while (pending.length > 0) {
|
|
3195
|
+
const current = pending.pop();
|
|
3196
|
+
let entries;
|
|
3197
|
+
try {
|
|
3198
|
+
entries = await readdir(current, { withFileTypes: true });
|
|
3199
|
+
} catch {
|
|
3200
|
+
continue;
|
|
3201
|
+
}
|
|
3202
|
+
for (const entry of entries) {
|
|
3203
|
+
const fullPath = join(current, entry.name);
|
|
3204
|
+
if (entry.isDirectory()) pending.push(fullPath);
|
|
3205
|
+
else if (entry.isFile() && entry.name.toLowerCase().endsWith(".jsonl")) result.push(fullPath);
|
|
3206
|
+
}
|
|
3207
|
+
}
|
|
3208
|
+
return result.sort((a, b) => a.localeCompare(b));
|
|
3209
|
+
}
|
|
3210
|
+
async function readFirstLine(filePath) {
|
|
3211
|
+
const handle = await open(filePath, "r");
|
|
3212
|
+
const chunks = [];
|
|
3213
|
+
let total = 0;
|
|
3214
|
+
try {
|
|
3215
|
+
while (total < FIRST_LINE_LIMIT) {
|
|
3216
|
+
const chunk = Buffer.allocUnsafe(Math.min(128 * 1024, FIRST_LINE_LIMIT - total));
|
|
3217
|
+
const result = await handle.read(chunk, 0, chunk.length, null);
|
|
3218
|
+
if (result.bytesRead === 0) break;
|
|
3219
|
+
const used = chunk.subarray(0, result.bytesRead);
|
|
3220
|
+
const lineEnd = findLineEnd(used);
|
|
3221
|
+
if (lineEnd >= 0) {
|
|
3222
|
+
chunks.push(used.subarray(0, lineEnd));
|
|
3223
|
+
return decodeUtf8(Buffer.concat(chunks));
|
|
3224
|
+
}
|
|
3225
|
+
chunks.push(used);
|
|
3226
|
+
total += result.bytesRead;
|
|
3227
|
+
}
|
|
3228
|
+
} finally {
|
|
3229
|
+
await handle.close();
|
|
3230
|
+
}
|
|
3231
|
+
if (total > 0 && total < FIRST_LINE_LIMIT) return decodeUtf8(Buffer.concat(chunks));
|
|
3232
|
+
throw new CodexSessionManagerError(`session metadata line is too large or incomplete: ${filePath}`);
|
|
3233
|
+
}
|
|
3234
|
+
function findLineEnd(buffer) {
|
|
3235
|
+
for (let index = 0; index < buffer.length; index += 1) {
|
|
3236
|
+
if (buffer[index] === 10 || buffer[index] === 13) return index;
|
|
3237
|
+
}
|
|
3238
|
+
return -1;
|
|
3239
|
+
}
|
|
3240
|
+
function parseSessionMetadata(line, filePath) {
|
|
3241
|
+
const value = JSON.parse(line.replace(/^\uFEFF/u, ""));
|
|
3242
|
+
if (!isRecord6(value)) throw new Error("metadata is not an object");
|
|
3243
|
+
const payload = isRecord6(value["payload"]) ? value["payload"] : {};
|
|
3244
|
+
const id = sessionIdFromRolloutPath(filePath) || firstString(payload["session_id"], payload["id"]);
|
|
3245
|
+
return {
|
|
3246
|
+
id,
|
|
3247
|
+
cwd: firstString(payload["cwd"]),
|
|
3248
|
+
provider: firstString(payload["model_provider"]),
|
|
3249
|
+
timestamp: firstString(payload["timestamp"]) || firstString(value["timestamp"])
|
|
3250
|
+
};
|
|
3251
|
+
}
|
|
3252
|
+
function sessionIdFromRolloutPath(filePath) {
|
|
3253
|
+
const match = basename(filePath).match(/([0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12})\.jsonl$/iu);
|
|
3254
|
+
return match?.[1] ?? null;
|
|
3255
|
+
}
|
|
3256
|
+
async function readStateThreads(databasePath) {
|
|
3257
|
+
const unavailable = (reason) => ({
|
|
3258
|
+
status: { path: databasePath, available: false, reason },
|
|
3259
|
+
rows: []
|
|
3260
|
+
});
|
|
3261
|
+
if (!existsSync2(databasePath)) return unavailable(`Codex state database not found: ${databasePath}`);
|
|
3262
|
+
let sqlite;
|
|
3263
|
+
try {
|
|
3264
|
+
sqlite = await loadSqlite();
|
|
3265
|
+
} catch (error) {
|
|
3266
|
+
return unavailable(error instanceof Error ? error.message : String(error));
|
|
3267
|
+
}
|
|
3268
|
+
let database;
|
|
3269
|
+
try {
|
|
3270
|
+
database = openDatabase(sqlite, databasePath, true);
|
|
3271
|
+
const columns = new Set(
|
|
3272
|
+
database.prepare("PRAGMA table_info('threads')").all().map((row) => String(row["name"] ?? ""))
|
|
3273
|
+
);
|
|
3274
|
+
for (const required of ["id", "rollout_path", "cwd", "model_provider"]) {
|
|
3275
|
+
if (!columns.has(required)) return unavailable(`state_5.sqlite is missing threads.${required}`);
|
|
3276
|
+
}
|
|
3277
|
+
const selected = ["id", "rollout_path", "cwd", "model_provider", "model", "created_at", "updated_at", "created_at_ms", "updated_at_ms"].filter((column) => columns.has(column));
|
|
3278
|
+
const rows = database.prepare(`SELECT ${selected.join(", ")} FROM threads`).all();
|
|
3279
|
+
return {
|
|
3280
|
+
status: { path: databasePath, available: true },
|
|
3281
|
+
rows: rows.map(toStateThreadRow)
|
|
3282
|
+
};
|
|
3283
|
+
} catch (error) {
|
|
3284
|
+
return unavailable(`could not read Codex state database: ${error instanceof Error ? error.message : String(error)}`);
|
|
3285
|
+
} finally {
|
|
3286
|
+
if (database?.isOpen) database.close();
|
|
3287
|
+
}
|
|
3288
|
+
}
|
|
3289
|
+
async function loadSqlite() {
|
|
3290
|
+
const specifier = "node:sqlite";
|
|
3291
|
+
try {
|
|
3292
|
+
return await import(specifier);
|
|
3293
|
+
} catch (error) {
|
|
3294
|
+
const cause = error instanceof Error ? error.message : String(error);
|
|
3295
|
+
throw new CodexSessionManagerError(
|
|
3296
|
+
`Codex session SQLite support requires Node.js 22.16 or newer (node:sqlite); this daemon runs ${process.version} (import failed: ${cause})`
|
|
3297
|
+
);
|
|
3298
|
+
}
|
|
3299
|
+
}
|
|
3300
|
+
function openDatabase(sqlite, databasePath, readOnly) {
|
|
3301
|
+
const database = new sqlite.DatabaseSync(databasePath, { readOnly, timeout: 5e3 });
|
|
3302
|
+
database.exec("PRAGMA busy_timeout = 5000");
|
|
3303
|
+
return database;
|
|
3304
|
+
}
|
|
3305
|
+
function toStateThreadRow(row) {
|
|
3306
|
+
return {
|
|
3307
|
+
id: String(row["id"] ?? ""),
|
|
3308
|
+
rolloutPath: String(row["rollout_path"] ?? ""),
|
|
3309
|
+
cwd: String(row["cwd"] ?? ""),
|
|
3310
|
+
modelProvider: String(row["model_provider"] ?? ""),
|
|
3311
|
+
model: row["model"] == null ? null : String(row["model"]),
|
|
3312
|
+
createdAt: timestampFromSqlite(row["created_at_ms"] ?? row["created_at"]),
|
|
3313
|
+
updatedAt: timestampFromSqlite(row["updated_at_ms"] ?? row["updated_at"])
|
|
3314
|
+
};
|
|
3315
|
+
}
|
|
3316
|
+
function timestampFromSqlite(value) {
|
|
3317
|
+
const numeric = typeof value === "bigint" ? Number(value) : typeof value === "number" ? value : Number(value);
|
|
3318
|
+
if (!Number.isFinite(numeric) || numeric <= 0) return null;
|
|
3319
|
+
const milliseconds = numeric < 1e11 ? numeric * 1e3 : numeric;
|
|
3320
|
+
const date = new Date(milliseconds);
|
|
3321
|
+
return Number.isNaN(date.getTime()) ? null : date.toISOString();
|
|
3322
|
+
}
|
|
3323
|
+
function mergeSessionMetadata(projectPath, codexHome, files, state) {
|
|
3324
|
+
const stateRows = new Map(state.rows.map((row) => [row.id, row]));
|
|
3325
|
+
const ids = /* @__PURE__ */ new Set();
|
|
3326
|
+
for (const [id, file] of files) {
|
|
3327
|
+
if (file.cwd && isPathInside(projectPath, file.cwd)) ids.add(id);
|
|
3328
|
+
}
|
|
3329
|
+
for (const row of state.rows) {
|
|
3330
|
+
if (isPathInside(projectPath, row.cwd)) ids.add(row.id);
|
|
3331
|
+
}
|
|
3332
|
+
const sessions2 = [];
|
|
3333
|
+
for (const id of ids) {
|
|
3334
|
+
const file = files.get(id);
|
|
3335
|
+
const row = stateRows.get(id);
|
|
3336
|
+
const cwd = row?.cwd || file?.cwd || projectPath;
|
|
3337
|
+
if (!isPathInside(projectPath, cwd)) continue;
|
|
3338
|
+
const rolloutPath = file?.filePath || row?.rolloutPath || "";
|
|
3339
|
+
const provider = row?.modelProvider || file?.jsonlProvider || null;
|
|
3340
|
+
sessions2.push({
|
|
3341
|
+
id,
|
|
3342
|
+
cwd,
|
|
3343
|
+
rolloutPath,
|
|
3344
|
+
provider,
|
|
3345
|
+
jsonlProvider: file?.jsonlProvider ?? null,
|
|
3346
|
+
model: row?.model ?? null,
|
|
3347
|
+
createdAt: row?.createdAt ?? file?.timestamp ?? null,
|
|
3348
|
+
updatedAt: row?.updatedAt ?? (file ? new Date(file.mtimeMs).toISOString() : null),
|
|
3349
|
+
fileSize: file?.size ?? null,
|
|
3350
|
+
fileModifiedAt: file ? new Date(file.mtimeMs).toISOString() : null,
|
|
3351
|
+
status: file?.status ?? "missing_rollout",
|
|
3352
|
+
inStateDatabase: Boolean(row)
|
|
3353
|
+
});
|
|
3354
|
+
}
|
|
3355
|
+
sessions2.sort((a, b) => (b.updatedAt ?? "").localeCompare(a.updatedAt ?? "") || a.id.localeCompare(b.id));
|
|
3356
|
+
const warnings = [];
|
|
3357
|
+
if (!state.status.available) warnings.push(state.status.reason || "state database unavailable");
|
|
3358
|
+
return { projectPath, codexHome, stateDatabase: state.status, sessions: sessions2, warnings };
|
|
3359
|
+
}
|
|
3360
|
+
async function buildProviderPlans(input, snapshot, stateRows) {
|
|
3361
|
+
const byId = new Map(snapshot.sessions.map((session) => [session.id, session]));
|
|
3362
|
+
const stateById = new Map(stateRows.map((row) => [row.id, row]));
|
|
3363
|
+
const plans = [];
|
|
3364
|
+
for (const id of input.sessionIds) {
|
|
3365
|
+
const session = byId.get(id);
|
|
3366
|
+
const row = stateById.get(id);
|
|
3367
|
+
if (!session) {
|
|
3368
|
+
plans.push({
|
|
3369
|
+
id,
|
|
3370
|
+
provider: null,
|
|
3371
|
+
model: null,
|
|
3372
|
+
rolloutPath: "",
|
|
3373
|
+
status: "blocked",
|
|
3374
|
+
providers: [],
|
|
3375
|
+
matchingFields: 0,
|
|
3376
|
+
changedFields: 0,
|
|
3377
|
+
sqliteWillUpdate: false,
|
|
3378
|
+
action: "blocked",
|
|
3379
|
+
reason: "session is not associated with the requested project"
|
|
3380
|
+
});
|
|
3381
|
+
continue;
|
|
3382
|
+
}
|
|
3383
|
+
if (session.status !== "ready") {
|
|
3384
|
+
plans.push({
|
|
3385
|
+
id,
|
|
3386
|
+
provider: session.provider,
|
|
3387
|
+
model: session.model,
|
|
3388
|
+
rolloutPath: session.rolloutPath,
|
|
3389
|
+
status: session.status,
|
|
3390
|
+
providers: [],
|
|
3391
|
+
matchingFields: 0,
|
|
3392
|
+
changedFields: 0,
|
|
3393
|
+
sqliteWillUpdate: false,
|
|
3394
|
+
action: "blocked",
|
|
3395
|
+
reason: "rollout file is missing or unreadable"
|
|
3396
|
+
});
|
|
3397
|
+
continue;
|
|
3398
|
+
}
|
|
3399
|
+
const inspection = await inspectAndTransformFile(session.rolloutPath, input.fromProvider, input.toProvider);
|
|
3400
|
+
const sqliteWillUpdate = Boolean(
|
|
3401
|
+
row && (!input.fromProvider || row.modelProvider === input.fromProvider) && row.modelProvider !== input.toProvider
|
|
3402
|
+
);
|
|
3403
|
+
const changed = inspection.changedFields > 0 || sqliteWillUpdate;
|
|
3404
|
+
plans.push({
|
|
3405
|
+
id,
|
|
3406
|
+
provider: session.provider,
|
|
3407
|
+
model: session.model,
|
|
3408
|
+
rolloutPath: session.rolloutPath,
|
|
3409
|
+
status: "ready",
|
|
3410
|
+
providers: inspection.providers,
|
|
3411
|
+
matchingFields: inspection.matchingFields,
|
|
3412
|
+
changedFields: inspection.changedFields,
|
|
3413
|
+
sqliteWillUpdate,
|
|
3414
|
+
action: changed ? "update" : "no_change"
|
|
3415
|
+
});
|
|
3416
|
+
}
|
|
3417
|
+
return plans;
|
|
3418
|
+
}
|
|
3419
|
+
async function inspectAndTransformFile(filePath, fromProvider, toProvider) {
|
|
3420
|
+
const bytes = await readFile(filePath);
|
|
3421
|
+
const originalText = decodeUtf8(bytes);
|
|
3422
|
+
const parts = originalText.split(/(\r\n|\n|\r)/u);
|
|
3423
|
+
const providers = /* @__PURE__ */ new Set();
|
|
3424
|
+
let matchingFields = 0;
|
|
3425
|
+
let changedFields = 0;
|
|
3426
|
+
const hasBom = parts[0].startsWith("\uFEFF");
|
|
3427
|
+
for (let index = 0; index < parts.length; index += 2) {
|
|
3428
|
+
const line = parts[index];
|
|
3429
|
+
if (!line.trim()) continue;
|
|
3430
|
+
let value;
|
|
3431
|
+
try {
|
|
3432
|
+
value = JSON.parse(line.replace(index === 0 ? /^\uFEFF/u : /^/u, ""));
|
|
3433
|
+
} catch (error) {
|
|
3434
|
+
throw new CodexSessionManagerError(
|
|
3435
|
+
`invalid JSONL at ${filePath}:${Math.floor(index / 2) + 1}: ${error instanceof Error ? error.message : String(error)}`
|
|
3436
|
+
);
|
|
3437
|
+
}
|
|
3438
|
+
const result = replaceStructuredProviderFields(value, fromProvider, toProvider, providers);
|
|
3439
|
+
matchingFields += result.matchingFields;
|
|
3440
|
+
changedFields += result.changedFields;
|
|
3441
|
+
if (result.changedFields > 0) {
|
|
3442
|
+
parts[index] = `${index === 0 && hasBom ? "\uFEFF" : ""}${JSON.stringify(value)}`;
|
|
3443
|
+
}
|
|
3444
|
+
}
|
|
3445
|
+
return {
|
|
3446
|
+
transformedText: parts.join(""),
|
|
3447
|
+
providers: [...providers].sort(),
|
|
3448
|
+
matchingFields,
|
|
3449
|
+
changedFields
|
|
3450
|
+
};
|
|
3451
|
+
}
|
|
3452
|
+
function replaceStructuredProviderFields(value, fromProvider, toProvider, providers = /* @__PURE__ */ new Set()) {
|
|
3453
|
+
let matchingFields = 0;
|
|
3454
|
+
let changedFields = 0;
|
|
3455
|
+
const visit = (node) => {
|
|
3456
|
+
if (Array.isArray(node)) {
|
|
3457
|
+
for (const child of node) visit(child);
|
|
3458
|
+
return;
|
|
3459
|
+
}
|
|
3460
|
+
if (!isRecord6(node)) return;
|
|
3461
|
+
for (const [key, child] of Object.entries(node)) {
|
|
3462
|
+
if (PROVIDER_PROPERTY_NAMES.has(key) && typeof child === "string") {
|
|
3463
|
+
providers.add(child);
|
|
3464
|
+
if (!fromProvider || child === fromProvider) {
|
|
3465
|
+
matchingFields += 1;
|
|
3466
|
+
if (child !== toProvider) {
|
|
3467
|
+
node[key] = toProvider;
|
|
3468
|
+
changedFields += 1;
|
|
3469
|
+
}
|
|
3470
|
+
}
|
|
3471
|
+
}
|
|
3472
|
+
visit(node[key]);
|
|
3473
|
+
}
|
|
3474
|
+
};
|
|
3475
|
+
visit(value);
|
|
3476
|
+
return { matchingFields, changedFields };
|
|
3477
|
+
}
|
|
3478
|
+
async function snapshotFile(filePath) {
|
|
3479
|
+
const info = await stat(filePath);
|
|
3480
|
+
return { size: info.size, mtimeMs: info.mtimeMs };
|
|
3481
|
+
}
|
|
3482
|
+
function sameFileSnapshot(a, b) {
|
|
3483
|
+
return a.size === b.size && a.mtimeMs === b.mtimeMs;
|
|
3484
|
+
}
|
|
3485
|
+
async function createUniqueBackupPath(filePath) {
|
|
3486
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[.:]/gu, "-");
|
|
3487
|
+
let candidate = `${filePath}.provider-switch-${stamp}.bak`;
|
|
3488
|
+
let suffix = 1;
|
|
3489
|
+
while (existsSync2(candidate)) {
|
|
3490
|
+
candidate = `${filePath}.provider-switch-${stamp}-${suffix}.bak`;
|
|
3491
|
+
suffix += 1;
|
|
3492
|
+
}
|
|
3493
|
+
return candidate;
|
|
3494
|
+
}
|
|
3495
|
+
async function createDatabaseBackup(sqlite, database, databasePath) {
|
|
3496
|
+
const backupPath = await createUniqueBackupPath(databasePath);
|
|
3497
|
+
if (typeof sqlite.backup !== "function") {
|
|
3498
|
+
throw new CodexSessionManagerError(
|
|
3499
|
+
"Codex session updates require the node:sqlite backup API (Node.js 22.16 or newer)"
|
|
3500
|
+
);
|
|
3501
|
+
}
|
|
3502
|
+
await sqlite.backup(database, backupPath);
|
|
3503
|
+
return backupPath;
|
|
3504
|
+
}
|
|
3505
|
+
function assertDatabaseRowsUnchanged(database, rows) {
|
|
3506
|
+
const read = database.prepare("SELECT model_provider FROM threads WHERE id = ?");
|
|
3507
|
+
for (const row of rows) {
|
|
3508
|
+
const current = read.get(row.id);
|
|
3509
|
+
if (!current || String(current["model_provider"] ?? "") !== row.modelProvider) {
|
|
3510
|
+
throw new CodexSessionManagerError(
|
|
3511
|
+
`SQLite session '${row.id}' changed while the update was prepared; no files were changed`
|
|
3512
|
+
);
|
|
3513
|
+
}
|
|
3514
|
+
}
|
|
3515
|
+
}
|
|
3516
|
+
async function replaceFileAtomically(tempPath, targetPath) {
|
|
3517
|
+
try {
|
|
3518
|
+
await rename(tempPath, targetPath);
|
|
3519
|
+
} catch (error) {
|
|
3520
|
+
if (process.platform !== "win32") throw error;
|
|
3521
|
+
await copyFile(tempPath, targetPath);
|
|
3522
|
+
await unlinkIfPresent(tempPath);
|
|
3523
|
+
}
|
|
3524
|
+
}
|
|
3525
|
+
async function restoreBackups(backupPaths) {
|
|
3526
|
+
for (const backupPath of backupPaths.reverse()) {
|
|
3527
|
+
if (!existsSync2(backupPath)) continue;
|
|
3528
|
+
const targetPath = backupPath.replace(/\.provider-switch-[^.]+(?:-[0-9]+)?\.bak$/u, "");
|
|
3529
|
+
if (targetPath === backupPath) continue;
|
|
3530
|
+
const tempPath = `${targetPath}.provider-restore-${randomUUID2()}.tmp`;
|
|
3531
|
+
try {
|
|
3532
|
+
await copyFile(backupPath, tempPath);
|
|
3533
|
+
await replaceFileAtomically(tempPath, targetPath);
|
|
3534
|
+
} finally {
|
|
3535
|
+
await unlinkIfPresent(tempPath);
|
|
3536
|
+
}
|
|
3537
|
+
}
|
|
3538
|
+
}
|
|
3539
|
+
async function unlinkIfPresent(filePath) {
|
|
3540
|
+
try {
|
|
3541
|
+
await unlink(filePath);
|
|
3542
|
+
} catch {
|
|
3543
|
+
}
|
|
3544
|
+
}
|
|
3545
|
+
function decodeUtf8(bytes) {
|
|
3546
|
+
try {
|
|
3547
|
+
return new TextDecoder2("utf-8", { fatal: true }).decode(bytes);
|
|
3548
|
+
} catch {
|
|
3549
|
+
throw new CodexSessionManagerError("Codex session contains invalid UTF-8");
|
|
3550
|
+
}
|
|
3551
|
+
}
|
|
3552
|
+
function isRecord6(value) {
|
|
3553
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
3554
|
+
}
|
|
3555
|
+
function firstString(...values) {
|
|
3556
|
+
for (const value of values) if (typeof value === "string" && value.trim()) return value;
|
|
3557
|
+
return null;
|
|
3558
|
+
}
|
|
3559
|
+
function normalizeComparablePath(value) {
|
|
3560
|
+
const stripped = value.trim().replace(/^\\\\\?\\/u, "");
|
|
3561
|
+
if (process.platform === "win32") {
|
|
3562
|
+
const normalized2 = win32.normalize(stripped).replace(/[\\/]+$/u, "");
|
|
3563
|
+
return normalized2.toLowerCase();
|
|
3564
|
+
}
|
|
3565
|
+
return stripped.replace(/\/+$/u, "") || "/";
|
|
3566
|
+
}
|
|
3567
|
+
function isPathInside(projectPath, candidatePath) {
|
|
3568
|
+
const project = normalizeComparablePath(projectPath);
|
|
3569
|
+
const candidate = normalizeComparablePath(candidatePath);
|
|
3570
|
+
if (project === candidate) return true;
|
|
3571
|
+
const separator = process.platform === "win32" ? "\\" : "/";
|
|
3572
|
+
return candidate.startsWith(`${project}${separator}`);
|
|
3573
|
+
}
|
|
3574
|
+
|
|
3575
|
+
// src/admin/codexSessionApi.ts
|
|
3576
|
+
async function handleCodexSessionApi(req, res, path2, manager) {
|
|
3577
|
+
if (!manager) return writeJsonError(res, 501, "Codex session management is not available");
|
|
3578
|
+
try {
|
|
3579
|
+
const method = (req.method ?? "GET").toUpperCase();
|
|
3580
|
+
if (path2 === "/admin/api/codex-sessions" && (method === "GET" || method === "HEAD")) {
|
|
3581
|
+
const projectPath = new URL(req.url ?? "/", "http://localhost").searchParams.get("projectPath") ?? "";
|
|
3582
|
+
return writeJson(res, 200, await manager.list(projectPath));
|
|
3583
|
+
}
|
|
3584
|
+
if (path2 === "/admin/api/codex-sessions/preview" && method === "POST") {
|
|
3585
|
+
const body = await readJsonBody2(req);
|
|
3586
|
+
return writeJson(res, 200, await manager.preview(parseApplyInput(body)));
|
|
3587
|
+
}
|
|
3588
|
+
if (path2 === "/admin/api/codex-sessions/apply" && method === "POST") {
|
|
3589
|
+
const body = await readJsonBody2(req);
|
|
3590
|
+
return writeJson(res, 200, await manager.apply(parseApplyInput(body)));
|
|
3591
|
+
}
|
|
3592
|
+
return writeJsonError(res, 404, "unknown Codex session admin route");
|
|
3593
|
+
} catch (error) {
|
|
3594
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3595
|
+
return writeJsonError(res, error instanceof CodexSessionManagerError ? 400 : 500, message);
|
|
3596
|
+
}
|
|
3597
|
+
}
|
|
3598
|
+
async function readJsonBody2(req) {
|
|
3599
|
+
const chunks = [];
|
|
3600
|
+
for await (const chunk of req) chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
3601
|
+
if (chunks.length === 0) return {};
|
|
3602
|
+
try {
|
|
3603
|
+
const value = JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
3604
|
+
return isRecord7(value) ? value : {};
|
|
3605
|
+
} catch {
|
|
3606
|
+
throw new CodexSessionManagerError("request body must be a JSON object");
|
|
3607
|
+
}
|
|
3608
|
+
}
|
|
3609
|
+
function parseApplyInput(body) {
|
|
3610
|
+
const projectPath = body["projectPath"];
|
|
3611
|
+
const sessionIds = body["sessionIds"];
|
|
3612
|
+
const toProvider = body["toProvider"];
|
|
3613
|
+
const fromProvider = body["fromProvider"];
|
|
3614
|
+
if (typeof projectPath !== "string") throw new CodexSessionManagerError("projectPath must be a string");
|
|
3615
|
+
if (!Array.isArray(sessionIds) || !sessionIds.every((value) => typeof value === "string")) {
|
|
3616
|
+
throw new CodexSessionManagerError("sessionIds must be an array of strings");
|
|
3617
|
+
}
|
|
3618
|
+
if (typeof toProvider !== "string") throw new CodexSessionManagerError("toProvider must be a string");
|
|
3619
|
+
if (fromProvider !== void 0 && typeof fromProvider !== "string") {
|
|
3620
|
+
throw new CodexSessionManagerError("fromProvider must be a string when provided");
|
|
3621
|
+
}
|
|
3622
|
+
return {
|
|
3623
|
+
projectPath,
|
|
3624
|
+
sessionIds,
|
|
3625
|
+
toProvider,
|
|
3626
|
+
...fromProvider === void 0 ? {} : { fromProvider }
|
|
3627
|
+
};
|
|
3628
|
+
}
|
|
3629
|
+
function writeJson(res, status, body) {
|
|
3630
|
+
res.writeHead(status, { "Content-Type": "application/json" });
|
|
3631
|
+
res.end(JSON.stringify(body));
|
|
3632
|
+
}
|
|
3633
|
+
function writeJsonError(res, status, message) {
|
|
3634
|
+
writeJson(res, status, { error: { type: "admin_api_error", message } });
|
|
3635
|
+
}
|
|
3636
|
+
function isRecord7(value) {
|
|
3637
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
3638
|
+
}
|
|
3639
|
+
|
|
2882
3640
|
// src/admin/adminApi.ts
|
|
2883
3641
|
import http from "http";
|
|
2884
3642
|
import {
|
|
2885
3643
|
createNamedKey,
|
|
2886
3644
|
DEFAULT_IMAGES_SERVER_CONFIG,
|
|
2887
3645
|
DEFAULT_SEARCH_SERVER_CONFIG as DEFAULT_SEARCH_SERVER_CONFIG2,
|
|
2888
|
-
effectiveOutboundPermissions as
|
|
3646
|
+
effectiveOutboundPermissions as effectiveOutboundPermissions3,
|
|
2889
3647
|
gatewayBindingToEndpointConfig,
|
|
2890
3648
|
isKindMappedEndpoint,
|
|
2891
3649
|
loadServerConfig as loadServerConfig3,
|
|
@@ -2903,6 +3661,34 @@ import { getSharedAccountHealth } from "@omnicross/core/pipeline/SubscriptionAcc
|
|
|
2903
3661
|
import { fetchUpstream as fetchUpstream10 } from "@omnicross/core/pipeline/upstreamFetch";
|
|
2904
3662
|
import { mergeExtraHeaders } from "@omnicross/core";
|
|
2905
3663
|
|
|
3664
|
+
// src/admin/testEgressIdentity.ts
|
|
3665
|
+
import {
|
|
3666
|
+
getOpenCodeGoUserAgent as getOpenCodeGoUserAgent2,
|
|
3667
|
+
OPENCODE_SESSION_HEADER
|
|
3668
|
+
} from "@omnicross/core/provider-proxy/identity/openCodeGoHeaders";
|
|
3669
|
+
var ADMIN_PROBE_OPENCODE_SESSION = "omnicross-admin-probe";
|
|
3670
|
+
function isOpenCodeUpstream(baseUrl) {
|
|
3671
|
+
if (!baseUrl) return false;
|
|
3672
|
+
try {
|
|
3673
|
+
const host = new URL(baseUrl).hostname.toLowerCase();
|
|
3674
|
+
return host === "opencode.ai" || host.endsWith(".opencode.ai");
|
|
3675
|
+
} catch {
|
|
3676
|
+
return false;
|
|
3677
|
+
}
|
|
3678
|
+
}
|
|
3679
|
+
function hasHeader2(headers, name) {
|
|
3680
|
+
const lower = name.toLowerCase();
|
|
3681
|
+
return Object.keys(headers).some((key) => key.toLowerCase() === lower);
|
|
3682
|
+
}
|
|
3683
|
+
function applyAdminProbeIdentity(headers, row) {
|
|
3684
|
+
if (!hasHeader2(headers, "user-agent")) {
|
|
3685
|
+
headers["user-agent"] = getOpenCodeGoUserAgent2();
|
|
3686
|
+
}
|
|
3687
|
+
if (isOpenCodeUpstream(row.baseUrl) && !hasHeader2(headers, OPENCODE_SESSION_HEADER)) {
|
|
3688
|
+
headers[OPENCODE_SESSION_HEADER] = ADMIN_PROBE_OPENCODE_SESSION;
|
|
3689
|
+
}
|
|
3690
|
+
}
|
|
3691
|
+
|
|
2906
3692
|
// src/image-generation/imagesConfigValidation.ts
|
|
2907
3693
|
import { validateImagesServerConfig } from "@omnicross/core/outbound-api";
|
|
2908
3694
|
|
|
@@ -2910,7 +3696,7 @@ import { validateImagesServerConfig } from "@omnicross/core/outbound-api";
|
|
|
2910
3696
|
import { randomBytes } from "crypto";
|
|
2911
3697
|
import {
|
|
2912
3698
|
chmodSync,
|
|
2913
|
-
existsSync as
|
|
3699
|
+
existsSync as existsSync3,
|
|
2914
3700
|
lstatSync,
|
|
2915
3701
|
mkdirSync as mkdirSync2,
|
|
2916
3702
|
realpathSync,
|
|
@@ -2918,20 +3704,20 @@ import {
|
|
|
2918
3704
|
statSync as statSync2,
|
|
2919
3705
|
unlinkSync
|
|
2920
3706
|
} from "fs";
|
|
2921
|
-
import { homedir, tmpdir } from "os";
|
|
3707
|
+
import { homedir as homedir2, tmpdir } from "os";
|
|
2922
3708
|
import {
|
|
2923
|
-
basename,
|
|
3709
|
+
basename as basename2,
|
|
2924
3710
|
dirname as dirname2,
|
|
2925
3711
|
isAbsolute,
|
|
2926
|
-
join,
|
|
3712
|
+
join as join2,
|
|
2927
3713
|
parse,
|
|
2928
3714
|
relative,
|
|
2929
|
-
resolve
|
|
3715
|
+
resolve as resolve2
|
|
2930
3716
|
} from "path";
|
|
2931
3717
|
var OPAQUE_NAME = /^(?:file|directory)-[a-f0-9]{32}(?:\.(?:bin|json|tmp))?$/u;
|
|
2932
3718
|
var MOUNT_MANIFEST_NAME = "catalog.v1.json";
|
|
2933
3719
|
function normalized(path2) {
|
|
2934
|
-
const canonical =
|
|
3720
|
+
const canonical = resolve2(path2);
|
|
2935
3721
|
return process.platform === "win32" ? canonical.toLowerCase() : canonical;
|
|
2936
3722
|
}
|
|
2937
3723
|
function samePath(left, right) {
|
|
@@ -2942,26 +3728,26 @@ function isSameOrDescendant(candidate, parent) {
|
|
|
2942
3728
|
return rel === "" || !rel.startsWith("..") && !isAbsolute(rel);
|
|
2943
3729
|
}
|
|
2944
3730
|
function assertNoSymlinkComponents(target) {
|
|
2945
|
-
const absolute =
|
|
3731
|
+
const absolute = resolve2(target);
|
|
2946
3732
|
const filesystemRoot = parse(absolute).root;
|
|
2947
3733
|
let cursor = filesystemRoot;
|
|
2948
3734
|
for (const segment of relative(filesystemRoot, absolute).split(/[\\/]+/u).filter(Boolean)) {
|
|
2949
|
-
cursor =
|
|
2950
|
-
if (!
|
|
3735
|
+
cursor = join2(cursor, segment);
|
|
3736
|
+
if (!existsSync3(cursor)) break;
|
|
2951
3737
|
if (lstatSync(cursor).isSymbolicLink()) {
|
|
2952
3738
|
throw new TypeError("image storage paths must not traverse a symbolic link");
|
|
2953
3739
|
}
|
|
2954
3740
|
}
|
|
2955
3741
|
}
|
|
2956
3742
|
function isInsideDetectedWorktree(target) {
|
|
2957
|
-
let cursor =
|
|
2958
|
-
while (!
|
|
3743
|
+
let cursor = resolve2(target);
|
|
3744
|
+
while (!existsSync3(cursor)) {
|
|
2959
3745
|
const parent = dirname2(cursor);
|
|
2960
3746
|
if (parent === cursor) break;
|
|
2961
3747
|
cursor = parent;
|
|
2962
3748
|
}
|
|
2963
3749
|
while (true) {
|
|
2964
|
-
if (
|
|
3750
|
+
if (existsSync3(join2(cursor, ".git"))) return true;
|
|
2965
3751
|
const parent = dirname2(cursor);
|
|
2966
3752
|
if (parent === cursor) return false;
|
|
2967
3753
|
cursor = parent;
|
|
@@ -2969,15 +3755,15 @@ function isInsideDetectedWorktree(target) {
|
|
|
2969
3755
|
}
|
|
2970
3756
|
function isBroadRoot(target, options) {
|
|
2971
3757
|
const filesystemRoot = parse(target).root;
|
|
2972
|
-
const processDirectory =
|
|
2973
|
-
const userHome =
|
|
2974
|
-
const temporaryDirectory =
|
|
3758
|
+
const processDirectory = resolve2(options.processDirectory ?? process.cwd());
|
|
3759
|
+
const userHome = resolve2(options.userHome ?? homedir2());
|
|
3760
|
+
const temporaryDirectory = resolve2(options.temporaryDirectory ?? tmpdir());
|
|
2975
3761
|
return samePath(target, filesystemRoot) || samePath(target, userHome) || samePath(target, temporaryDirectory) || isSameOrDescendant(processDirectory, target) || isSameOrDescendant(userHome, target) || isSameOrDescendant(temporaryDirectory, target);
|
|
2976
3762
|
}
|
|
2977
3763
|
function validateImageRootCandidate(candidate, options = {}) {
|
|
2978
3764
|
const label = options.label ?? "image storage root";
|
|
2979
3765
|
if (!candidate.trim() || !isAbsolute(candidate)) return [`${label} must be an absolute path`];
|
|
2980
|
-
const target =
|
|
3766
|
+
const target = resolve2(candidate);
|
|
2981
3767
|
const errors = [];
|
|
2982
3768
|
if (isBroadRoot(target, options)) errors.push(`${label} is too broad`);
|
|
2983
3769
|
try {
|
|
@@ -3015,22 +3801,22 @@ var DaemonImagePathResolver = class {
|
|
|
3015
3801
|
#issued = /* @__PURE__ */ new WeakSet();
|
|
3016
3802
|
constructor(options) {
|
|
3017
3803
|
if (!isAbsolute(options.configPath)) throw new TypeError("daemon configPath must be absolute");
|
|
3018
|
-
const applicationDataRoot =
|
|
3804
|
+
const applicationDataRoot = resolve2(dirname2(options.configPath));
|
|
3019
3805
|
const applicationErrors = validateImageRootCandidate(applicationDataRoot, {
|
|
3020
3806
|
...options,
|
|
3021
3807
|
label: "daemon application data root"
|
|
3022
3808
|
});
|
|
3023
3809
|
if (applicationErrors.length > 0) throw new TypeError(applicationErrors.join("; "));
|
|
3024
|
-
const imagesRoot =
|
|
3025
|
-
const durableRoot =
|
|
3810
|
+
const imagesRoot = join2(applicationDataRoot, "images");
|
|
3811
|
+
const durableRoot = resolve2(options.storageRoot ?? join2(imagesRoot, "storage"));
|
|
3026
3812
|
const durableErrors = validateImageRootCandidate(durableRoot, {
|
|
3027
3813
|
...options,
|
|
3028
3814
|
label: "image durable storage root"
|
|
3029
3815
|
});
|
|
3030
3816
|
const reservedRoots = [
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3817
|
+
join2(imagesRoot, "temporary"),
|
|
3818
|
+
join2(imagesRoot, "evidence"),
|
|
3819
|
+
join2(imagesRoot, "mount-catalog")
|
|
3034
3820
|
];
|
|
3035
3821
|
if (reservedRoots.some((reserved) => isSameOrDescendant(durableRoot, reserved) || isSameOrDescendant(reserved, durableRoot))) {
|
|
3036
3822
|
durableErrors.push("image durable storage root must not overlap daemon image control roots");
|
|
@@ -3039,13 +3825,13 @@ var DaemonImagePathResolver = class {
|
|
|
3039
3825
|
const paths = Object.freeze({
|
|
3040
3826
|
applicationDataRoot,
|
|
3041
3827
|
imagesRoot,
|
|
3042
|
-
temporaryRoot:
|
|
3828
|
+
temporaryRoot: join2(imagesRoot, "temporary"),
|
|
3043
3829
|
durableRoot,
|
|
3044
|
-
artifactsRoot:
|
|
3045
|
-
stateRoot:
|
|
3046
|
-
evidenceRoot:
|
|
3047
|
-
mountManifestRoot:
|
|
3048
|
-
mountManifestPath:
|
|
3830
|
+
artifactsRoot: join2(durableRoot, "artifacts"),
|
|
3831
|
+
stateRoot: join2(durableRoot, "state"),
|
|
3832
|
+
evidenceRoot: join2(imagesRoot, "evidence"),
|
|
3833
|
+
mountManifestRoot: join2(imagesRoot, "mount-catalog"),
|
|
3834
|
+
mountManifestPath: join2(imagesRoot, "mount-catalog", MOUNT_MANIFEST_NAME)
|
|
3049
3835
|
});
|
|
3050
3836
|
const rootEntries = [
|
|
3051
3837
|
["temporary", paths.temporaryRoot],
|
|
@@ -3077,7 +3863,7 @@ var DaemonImagePathResolver = class {
|
|
|
3077
3863
|
/** Revalidate immediately before unlinking a resolver-issued file capability. */
|
|
3078
3864
|
removeFile(target) {
|
|
3079
3865
|
const path2 = this.verifyDestructiveTarget(target, ["opaque-file", "mount-manifest"]);
|
|
3080
|
-
if (!
|
|
3866
|
+
if (!existsSync3(path2)) return;
|
|
3081
3867
|
const info = lstatSync(path2);
|
|
3082
3868
|
if (info.isSymbolicLink() || !info.isFile()) {
|
|
3083
3869
|
throw new TypeError("refusing to unlink an unverified image file");
|
|
@@ -3087,7 +3873,7 @@ var DaemonImagePathResolver = class {
|
|
|
3087
3873
|
/** Only empty opaque directories may be removed until the owned-marker layer is composed. */
|
|
3088
3874
|
removeEmptyDirectory(target) {
|
|
3089
3875
|
const path2 = this.verifyDestructiveTarget(target, ["opaque-directory"]);
|
|
3090
|
-
if (!
|
|
3876
|
+
if (!existsSync3(path2)) return;
|
|
3091
3877
|
const info = lstatSync(path2);
|
|
3092
3878
|
if (info.isSymbolicLink() || !info.isDirectory()) {
|
|
3093
3879
|
throw new TypeError("refusing to remove an unverified image directory");
|
|
@@ -3097,7 +3883,7 @@ var DaemonImagePathResolver = class {
|
|
|
3097
3883
|
issue(area, name, kind) {
|
|
3098
3884
|
const handle = Object.freeze({
|
|
3099
3885
|
area,
|
|
3100
|
-
absolutePath:
|
|
3886
|
+
absolutePath: join2(this.#roots[area].path, name),
|
|
3101
3887
|
kind
|
|
3102
3888
|
});
|
|
3103
3889
|
this.#issued.add(handle);
|
|
@@ -3112,11 +3898,11 @@ var DaemonImagePathResolver = class {
|
|
|
3112
3898
|
}
|
|
3113
3899
|
const root = this.#roots[target.area];
|
|
3114
3900
|
assertRootIdentity(root);
|
|
3115
|
-
const candidate =
|
|
3901
|
+
const candidate = resolve2(target.absolutePath);
|
|
3116
3902
|
if (!samePath(dirname2(candidate), root.path) || !isSameOrDescendant(candidate, root.path)) {
|
|
3117
3903
|
throw new TypeError("refusing a destructive operation outside the verified image root");
|
|
3118
3904
|
}
|
|
3119
|
-
const name =
|
|
3905
|
+
const name = basename2(candidate);
|
|
3120
3906
|
const validName = target.kind === "mount-manifest" ? name === MOUNT_MANIFEST_NAME : OPAQUE_NAME.test(name);
|
|
3121
3907
|
if (!validName) throw new TypeError("refusing a caller-derived image filename");
|
|
3122
3908
|
assertNoSymlinkComponents(candidate);
|
|
@@ -3273,13 +4059,13 @@ function decryptValue(envelope, key) {
|
|
|
3273
4059
|
|
|
3274
4060
|
// src/secrets/masterKey.ts
|
|
3275
4061
|
import { randomBytes as randomBytes3 } from "crypto";
|
|
3276
|
-
import { chmodSync as chmodSync2, existsSync as
|
|
3277
|
-
import { homedir as
|
|
3278
|
-
import { dirname as dirname3, join as
|
|
4062
|
+
import { chmodSync as chmodSync2, existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
4063
|
+
import { homedir as homedir3 } from "os";
|
|
4064
|
+
import { dirname as dirname3, join as join3 } from "path";
|
|
3279
4065
|
var MASTER_KEY_ENV = "OMNICROSS_MASTER_KEY";
|
|
3280
4066
|
var KEY_BYTES2 = 32;
|
|
3281
4067
|
function defaultMasterKeyPath() {
|
|
3282
|
-
return
|
|
4068
|
+
return join3(homedir3(), ".omnicross", "master.key");
|
|
3283
4069
|
}
|
|
3284
4070
|
function decodeEnvKey(raw) {
|
|
3285
4071
|
const trimmed = raw.trim();
|
|
@@ -3321,7 +4107,7 @@ function resolveMasterKey(options = {}) {
|
|
|
3321
4107
|
return decodeEnvKey(envRaw);
|
|
3322
4108
|
}
|
|
3323
4109
|
const keyFilePath = options.keyFilePath ?? defaultMasterKeyPath();
|
|
3324
|
-
if (
|
|
4110
|
+
if (existsSync4(keyFilePath)) {
|
|
3325
4111
|
return readKeyFile(keyFilePath);
|
|
3326
4112
|
}
|
|
3327
4113
|
return generateKeyFile(keyFilePath);
|
|
@@ -3939,29 +4725,29 @@ function resolveEnvKey(rawKey) {
|
|
|
3939
4725
|
|
|
3940
4726
|
// src/integrations/IntegrationManager.ts
|
|
3941
4727
|
import { createHash } from "crypto";
|
|
3942
|
-
import { existsSync as
|
|
3943
|
-
import { homedir as
|
|
3944
|
-
import { join as
|
|
4728
|
+
import { existsSync as existsSync6, readFileSync as readFileSync5, unlinkSync as unlinkSync3 } from "fs";
|
|
4729
|
+
import { homedir as homedir4 } from "os";
|
|
4730
|
+
import { join as join4, resolve as resolve4 } from "path";
|
|
3945
4731
|
import {
|
|
3946
4732
|
createIntegrationKey,
|
|
3947
4733
|
effectiveOutboundPermissions
|
|
3948
4734
|
} from "@omnicross/core";
|
|
3949
4735
|
|
|
3950
4736
|
// src/integrations/codexAuthHelper.ts
|
|
3951
|
-
import { resolve as
|
|
4737
|
+
import { resolve as resolve3 } from "path";
|
|
3952
4738
|
function currentProcessCodexAuthHelper(configPath, masterKeyFilePath) {
|
|
3953
4739
|
const entrypoint = process.argv[1];
|
|
3954
4740
|
if (!entrypoint) throw new Error("cannot configure Codex auth helper without a daemon CLI entrypoint");
|
|
3955
4741
|
return {
|
|
3956
4742
|
command: process.execPath,
|
|
3957
4743
|
args: [
|
|
3958
|
-
|
|
4744
|
+
resolve3(entrypoint),
|
|
3959
4745
|
"integrations",
|
|
3960
4746
|
"token",
|
|
3961
4747
|
"codex",
|
|
3962
4748
|
"--config",
|
|
3963
|
-
|
|
3964
|
-
...masterKeyFilePath ? ["--master-key-file",
|
|
4749
|
+
resolve3(configPath),
|
|
4750
|
+
...masterKeyFilePath ? ["--master-key-file", resolve3(masterKeyFilePath)] : []
|
|
3965
4751
|
]
|
|
3966
4752
|
};
|
|
3967
4753
|
}
|
|
@@ -3969,7 +4755,7 @@ function currentProcessCodexAuthHelper(configPath, masterKeyFilePath) {
|
|
|
3969
4755
|
// src/integrations/IntegrationStateStore.ts
|
|
3970
4756
|
import {
|
|
3971
4757
|
chmodSync as chmodSync3,
|
|
3972
|
-
existsSync as
|
|
4758
|
+
existsSync as existsSync5,
|
|
3973
4759
|
mkdirSync as mkdirSync4,
|
|
3974
4760
|
readFileSync as readFileSync4,
|
|
3975
4761
|
renameSync as renameSync2,
|
|
@@ -3986,7 +4772,7 @@ var IntegrationStateStore = class {
|
|
|
3986
4772
|
path;
|
|
3987
4773
|
box;
|
|
3988
4774
|
load() {
|
|
3989
|
-
if (!
|
|
4775
|
+
if (!existsSync5(this.path)) return { ...EMPTY_STATE, clients: {} };
|
|
3990
4776
|
let raw;
|
|
3991
4777
|
try {
|
|
3992
4778
|
raw = JSON.parse(readFileSync4(this.path, "utf8"));
|
|
@@ -4092,7 +4878,7 @@ function atomicWrite(path2, content) {
|
|
|
4092
4878
|
}
|
|
4093
4879
|
throw error;
|
|
4094
4880
|
} finally {
|
|
4095
|
-
if (
|
|
4881
|
+
if (existsSync5(path2)) {
|
|
4096
4882
|
try {
|
|
4097
4883
|
chmodSync3(path2, 384);
|
|
4098
4884
|
} catch {
|
|
@@ -4277,7 +5063,7 @@ var IntegrationManager = class {
|
|
|
4277
5063
|
constructor(options) {
|
|
4278
5064
|
this.options = options;
|
|
4279
5065
|
assertLoopbackGatewayUrl(options.gatewayBaseUrl);
|
|
4280
|
-
this.homeDir = options.homeDir ??
|
|
5066
|
+
this.homeDir = options.homeDir ?? homedir4();
|
|
4281
5067
|
this.codexAuthHelper = options.codexAuthHelper ?? currentProcessCodexAuthHelper(options.configPath);
|
|
4282
5068
|
}
|
|
4283
5069
|
options;
|
|
@@ -4291,7 +5077,7 @@ var IntegrationManager = class {
|
|
|
4291
5077
|
async plan(client, configPath = this.defaultConfigPath(client)) {
|
|
4292
5078
|
const state = this.options.stateStore.load();
|
|
4293
5079
|
const record = state.clients[client];
|
|
4294
|
-
const target = record?.configPath ??
|
|
5080
|
+
const target = record?.configPath ?? resolve4(configPath);
|
|
4295
5081
|
const status = await this.statusFor(client, state, await this.options.keyDb.outboundApiKeysList());
|
|
4296
5082
|
const changes = client === "codex" ? ["model_provider", "model_providers.omnicross", "model_providers.omnicross.auth"] : ["env.ANTHROPIC_BASE_URL", "env.ANTHROPIC_AUTH_TOKEN", "env.ANTHROPIC_API_KEY"];
|
|
4297
5083
|
if (!record) {
|
|
@@ -4307,7 +5093,7 @@ var IntegrationManager = class {
|
|
|
4307
5093
|
return { client, configPath: target, action: "repair", canApply: true, changes, warnings };
|
|
4308
5094
|
}
|
|
4309
5095
|
async install(client, configPath = this.defaultConfigPath(client)) {
|
|
4310
|
-
const target =
|
|
5096
|
+
const target = resolve4(configPath);
|
|
4311
5097
|
const state = this.options.stateStore.load();
|
|
4312
5098
|
const previousState = cloneState(state);
|
|
4313
5099
|
const existingRecord = state.clients[client];
|
|
@@ -4514,6 +5300,33 @@ var IntegrationManager = class {
|
|
|
4514
5300
|
}
|
|
4515
5301
|
return details.secret;
|
|
4516
5302
|
}
|
|
5303
|
+
/**
|
|
5304
|
+
* Resolve ONE access key's plaintext by id — the `--key-id` variant the
|
|
5305
|
+
* command-auth helper serves for key-scoped Codex launches (each terminal
|
|
5306
|
+
* picks its own gateway key, so concurrent sessions can route to different
|
|
5307
|
+
* upstreams through their keys' bindings). Enforces the SAME usability
|
|
5308
|
+
* contract as the client-bound path: existing, enabled, not revoked,
|
|
5309
|
+
* revealable, and holding the codex-required endpoint permissions.
|
|
5310
|
+
*/
|
|
5311
|
+
async getKeyToken(keyId) {
|
|
5312
|
+
const rows = await this.options.keyDb.outboundApiKeysList();
|
|
5313
|
+
const row = rows.find((candidate) => candidate.id === keyId);
|
|
5314
|
+
if (!row) {
|
|
5315
|
+
throw new IntegrationConflictError(`access key '${keyId}' does not exist`);
|
|
5316
|
+
}
|
|
5317
|
+
const secret = await this.options.keyDb.outboundApiKeysReveal(keyId);
|
|
5318
|
+
if (!row.enabled || row.revokedAt !== null || !secret) {
|
|
5319
|
+
throw new IntegrationConflictError(
|
|
5320
|
+
`access key '${keyId}' is disabled, revoked, or not revealable`
|
|
5321
|
+
);
|
|
5322
|
+
}
|
|
5323
|
+
if (!hasRequiredPermissions(row, "codex")) {
|
|
5324
|
+
throw new IntegrationConflictError(
|
|
5325
|
+
`access key '${keyId}' lacks the responses+images endpoint permissions Codex requires`
|
|
5326
|
+
);
|
|
5327
|
+
}
|
|
5328
|
+
return secret;
|
|
5329
|
+
}
|
|
4517
5330
|
/** Compatibility alias for callers predating per-client bindings. */
|
|
4518
5331
|
async getGatewayToken(client = "codex") {
|
|
4519
5332
|
return this.getIntegrationToken(client);
|
|
@@ -4654,7 +5467,7 @@ var IntegrationManager = class {
|
|
|
4654
5467
|
}
|
|
4655
5468
|
}
|
|
4656
5469
|
defaultConfigPath(client) {
|
|
4657
|
-
return client === "codex" ?
|
|
5470
|
+
return client === "codex" ? join4(this.homeDir, ".codex", "config.toml") : join4(this.homeDir, ".claude", "settings.json");
|
|
4658
5471
|
}
|
|
4659
5472
|
renderInstalled(client, base, secret) {
|
|
4660
5473
|
if (client === "claude") {
|
|
@@ -4697,7 +5510,7 @@ function cloneState(state) {
|
|
|
4697
5510
|
};
|
|
4698
5511
|
}
|
|
4699
5512
|
function readOptional(path2) {
|
|
4700
|
-
return
|
|
5513
|
+
return existsSync6(path2) ? readFileSync5(path2, "utf8") : null;
|
|
4701
5514
|
}
|
|
4702
5515
|
function sha256(value) {
|
|
4703
5516
|
return createHash("sha256").update(value, "utf8").digest("hex");
|
|
@@ -4757,7 +5570,7 @@ function writeOptional(path2, content) {
|
|
|
4757
5570
|
atomicWrite(path2, content);
|
|
4758
5571
|
return;
|
|
4759
5572
|
}
|
|
4760
|
-
if (
|
|
5573
|
+
if (existsSync6(path2)) unlinkSync3(path2);
|
|
4761
5574
|
}
|
|
4762
5575
|
async function bestEffortRevoke(db, keyId) {
|
|
4763
5576
|
try {
|
|
@@ -5074,7 +5887,7 @@ import { SUBSCRIPTION_MODEL_CATALOG } from "@omnicross/contracts/subscription-mo
|
|
|
5074
5887
|
import { ANTIGRAVITY_CODE_ASSIST_ENDPOINT as ANTIGRAVITY_CODE_ASSIST_ENDPOINT2 } from "@omnicross/core/auth/GeminiCodeAssistProjectResolver";
|
|
5075
5888
|
import { fetchUpstream as fetchUpstream9 } from "@omnicross/core/pipeline/upstreamFetch";
|
|
5076
5889
|
import { getAntigravityUserAgent as getAntigravityUserAgent2 } from "@omnicross/core/transformer/transformers/antigravityIdentity";
|
|
5077
|
-
function
|
|
5890
|
+
function isRecord8(value) {
|
|
5078
5891
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
5079
5892
|
}
|
|
5080
5893
|
function optionalString(value) {
|
|
@@ -5087,13 +5900,13 @@ function optionalBoolean(value) {
|
|
|
5087
5900
|
return typeof value === "boolean" ? value : void 0;
|
|
5088
5901
|
}
|
|
5089
5902
|
function parseAntigravityAvailableModels(payload) {
|
|
5090
|
-
if (!
|
|
5903
|
+
if (!isRecord8(payload)) return [];
|
|
5091
5904
|
const models = payload["models"];
|
|
5092
|
-
if (!
|
|
5905
|
+
if (!isRecord8(models)) return [];
|
|
5093
5906
|
const out = [];
|
|
5094
5907
|
for (const [id, raw] of Object.entries(models)) {
|
|
5095
5908
|
if (ANTIGRAVITY_DISCOVERY_DENYLIST.has(id)) continue;
|
|
5096
|
-
if (!
|
|
5909
|
+
if (!isRecord8(raw)) continue;
|
|
5097
5910
|
if (raw["isInternal"] === true) continue;
|
|
5098
5911
|
out.push({
|
|
5099
5912
|
id,
|
|
@@ -5154,7 +5967,7 @@ async function fetchAntigravityAvailableModels(accessToken, fetchImpl = (url, in
|
|
|
5154
5967
|
}
|
|
5155
5968
|
if (!response.ok) return null;
|
|
5156
5969
|
const payload = await response.json().catch(() => null);
|
|
5157
|
-
if (!
|
|
5970
|
+
if (!isRecord8(payload) || !isRecord8(payload["models"])) return null;
|
|
5158
5971
|
return parseAntigravityAvailableModels(payload);
|
|
5159
5972
|
}
|
|
5160
5973
|
async function handleAntigravityModelsRoute(deps) {
|
|
@@ -5510,21 +6323,26 @@ async function exchangeGemini(code, codeVerifier, exchangeFetch) {
|
|
|
5510
6323
|
|
|
5511
6324
|
// src/admin/cliLaunch.ts
|
|
5512
6325
|
import { exec, spawn } from "child_process";
|
|
5513
|
-
import { randomUUID as
|
|
5514
|
-
import { chmodSync as chmodSync4, existsSync as
|
|
6326
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
6327
|
+
import { chmodSync as chmodSync4, existsSync as existsSync7, mkdtempSync, rmSync as rmSync2, writeFileSync as writeFileSync5 } from "fs";
|
|
5515
6328
|
import { createServer } from "net";
|
|
5516
6329
|
import { tmpdir as tmpdir2 } from "os";
|
|
5517
|
-
import { delimiter, join as
|
|
6330
|
+
import { delimiter, join as join5 } from "path";
|
|
5518
6331
|
import {
|
|
5519
6332
|
buildChatCliLaunchConfig,
|
|
5520
6333
|
buildClaudeCliLaunchConfig,
|
|
5521
6334
|
buildCodexLaunchConfig,
|
|
5522
|
-
buildGeminiCliLaunchConfig
|
|
6335
|
+
buildGeminiCliLaunchConfig,
|
|
6336
|
+
CODEX_PROXY_PROVIDER_NAME
|
|
5523
6337
|
} from "@omnicross/cli-launcher";
|
|
5524
6338
|
import {
|
|
5525
6339
|
ROUTE_LEASE_REQUEST_SCHEMA,
|
|
5526
6340
|
RouteLeaseError as RouteLeaseError2
|
|
5527
6341
|
} from "@omnicross/core/provider-proxy";
|
|
6342
|
+
import {
|
|
6343
|
+
candidateGatewayBindings,
|
|
6344
|
+
effectiveOutboundPermissions as effectiveOutboundPermissions2
|
|
6345
|
+
} from "@omnicross/core/outbound-api";
|
|
5528
6346
|
|
|
5529
6347
|
// src/routeLeaseRenewal.ts
|
|
5530
6348
|
var TERMINAL_LEASE_TTL_SECONDS = 600;
|
|
@@ -5571,8 +6389,8 @@ function isLaunchCliId(id) {
|
|
|
5571
6389
|
function probeDefault(candidate) {
|
|
5572
6390
|
const segments = (process.env["PATH"] ?? "").split(delimiter).filter(Boolean);
|
|
5573
6391
|
for (const seg of segments) {
|
|
5574
|
-
const full =
|
|
5575
|
-
if (
|
|
6392
|
+
const full = join5(seg, candidate);
|
|
6393
|
+
if (existsSync7(full)) return full;
|
|
5576
6394
|
}
|
|
5577
6395
|
return null;
|
|
5578
6396
|
}
|
|
@@ -5605,6 +6423,75 @@ function resolveLaunchTarget(providers, requested) {
|
|
|
5605
6423
|
function firstModel(p) {
|
|
5606
6424
|
return p.models?.[0] ?? p.modelConfigs?.[0]?.id;
|
|
5607
6425
|
}
|
|
6426
|
+
var CMD_METACHAR_RE = /[&|<>^%]/;
|
|
6427
|
+
var KEY_SCOPED_CODEX_PERMISSIONS = ["responses", "images"];
|
|
6428
|
+
function buildKeyScopedCodexArgs(input) {
|
|
6429
|
+
let root = input.gatewayBaseUrl;
|
|
6430
|
+
while (root.endsWith("/")) root = root.slice(0, -1);
|
|
6431
|
+
const name = CODEX_PROXY_PROVIDER_NAME;
|
|
6432
|
+
const helperArgs = [...input.authHelper.args, "--key-id", input.keyId];
|
|
6433
|
+
return [
|
|
6434
|
+
"-c",
|
|
6435
|
+
`model_provider="${name}"`,
|
|
6436
|
+
"-c",
|
|
6437
|
+
`model_providers.${name}.name="OmniCross Local Gateway"`,
|
|
6438
|
+
"-c",
|
|
6439
|
+
`model_providers.${name}.base_url="${root}/v1"`,
|
|
6440
|
+
"-c",
|
|
6441
|
+
`model_providers.${name}.wire_api="responses"`,
|
|
6442
|
+
"-c",
|
|
6443
|
+
`model_providers.${name}.supports_websockets=false`,
|
|
6444
|
+
"-c",
|
|
6445
|
+
`model_providers.${name}.http_headers={"X-OpenAI-Actor-Authorization"="omnicross"}`,
|
|
6446
|
+
"-c",
|
|
6447
|
+
`model_providers.${name}.auth.command=${JSON.stringify(input.authHelper.command)}`,
|
|
6448
|
+
"-c",
|
|
6449
|
+
`model_providers.${name}.auth.args=${JSON.stringify(helperArgs)}`,
|
|
6450
|
+
"-c",
|
|
6451
|
+
`model_providers.${name}.auth.refresh_interval_ms=0`,
|
|
6452
|
+
"-c",
|
|
6453
|
+
`model_providers.${name}.auth.timeout_ms=5000`,
|
|
6454
|
+
"-c",
|
|
6455
|
+
"disable_response_storage=true"
|
|
6456
|
+
];
|
|
6457
|
+
}
|
|
6458
|
+
async function preflightKeyScopedLaunch(deps, keyId) {
|
|
6459
|
+
if (!deps.gatewayRunning) {
|
|
6460
|
+
return {
|
|
6461
|
+
ok: false,
|
|
6462
|
+
status: 409,
|
|
6463
|
+
message: "the outbound gateway is not running \u2014 key-scoped launches route through it"
|
|
6464
|
+
};
|
|
6465
|
+
}
|
|
6466
|
+
const rows = await deps.keyDb.outboundApiKeysList();
|
|
6467
|
+
const row = rows.find((candidate) => candidate.id === keyId);
|
|
6468
|
+
if (!row) return { ok: false, status: 404, message: `access key '${keyId}' does not exist` };
|
|
6469
|
+
if (!row.enabled || row.revokedAt !== null) {
|
|
6470
|
+
return { ok: false, status: 400, message: `access key '${row.name}' is disabled or revoked` };
|
|
6471
|
+
}
|
|
6472
|
+
const secret = await deps.keyDb.outboundApiKeysReveal(keyId);
|
|
6473
|
+
if (!secret) {
|
|
6474
|
+
return { ok: false, status: 400, message: `access key '${row.name}' is not revealable` };
|
|
6475
|
+
}
|
|
6476
|
+
const allowed = effectiveOutboundPermissions2(row.allowedEndpoints);
|
|
6477
|
+
for (const permission of KEY_SCOPED_CODEX_PERMISSIONS) {
|
|
6478
|
+
if (!allowed.includes(permission)) {
|
|
6479
|
+
return {
|
|
6480
|
+
ok: false,
|
|
6481
|
+
status: 400,
|
|
6482
|
+
message: `access key '${row.name}' lacks the '${permission}' endpoint permission Codex requires`
|
|
6483
|
+
};
|
|
6484
|
+
}
|
|
6485
|
+
}
|
|
6486
|
+
if (candidateGatewayBindings(deps.bindings, keyId, "responses").length === 0) {
|
|
6487
|
+
return {
|
|
6488
|
+
ok: false,
|
|
6489
|
+
status: 400,
|
|
6490
|
+
message: `access key '${row.name}' has no enabled responses route \u2014 bind it to a downstream route on the API Service page first`
|
|
6491
|
+
};
|
|
6492
|
+
}
|
|
6493
|
+
return { ok: true, keyName: row.name };
|
|
6494
|
+
}
|
|
5608
6495
|
async function buildLaunchEnv(cli, llmConfig, target) {
|
|
5609
6496
|
const common = {
|
|
5610
6497
|
llmConfig,
|
|
@@ -5676,10 +6563,10 @@ function openTerminal({ cli, command, extraArgs, env, cwd, platform, onFailure }
|
|
|
5676
6563
|
const runLine = [command, ...extraArgs].map(shq).join(" ");
|
|
5677
6564
|
const script = `${cwd ? `cd ${shq(cwd)}; ` : ""}${runLine}`;
|
|
5678
6565
|
if (platform === "darwin") {
|
|
5679
|
-
const launchDir = mkdtempSync(
|
|
5680
|
-
const commandFile =
|
|
5681
|
-
const bootstrapFile =
|
|
5682
|
-
const socketPath = macIpc.socketPath ??
|
|
6566
|
+
const launchDir = mkdtempSync(join5(tmpdir2(), "omnicross-terminal-"));
|
|
6567
|
+
const commandFile = join5(launchDir, "launch.command");
|
|
6568
|
+
const bootstrapFile = join5(launchDir, "bootstrap.cjs");
|
|
6569
|
+
const socketPath = macIpc.socketPath ?? join5(launchDir, "descriptor.sock");
|
|
5683
6570
|
const openerEnv = { ...process.env };
|
|
5684
6571
|
for (const key of Object.keys(env)) delete openerEnv[key];
|
|
5685
6572
|
let claimed = false;
|
|
@@ -5808,10 +6695,10 @@ function resetCliSessions() {
|
|
|
5808
6695
|
function errBody(message) {
|
|
5809
6696
|
return { error: { type: "admin_api_error", message } };
|
|
5810
6697
|
}
|
|
5811
|
-
var defaultCommandRunner = (command) => new Promise((
|
|
6698
|
+
var defaultCommandRunner = (command) => new Promise((resolve11) => {
|
|
5812
6699
|
exec(command, { timeout: 18e4 }, (err9, _stdout, stderr) => {
|
|
5813
|
-
if (err9)
|
|
5814
|
-
else
|
|
6700
|
+
if (err9) resolve11({ ok: false, error: stderr.trim() || err9.message });
|
|
6701
|
+
else resolve11({ ok: true });
|
|
5815
6702
|
});
|
|
5816
6703
|
});
|
|
5817
6704
|
async function handleCliInstall(cli, runner = defaultCommandRunner) {
|
|
@@ -5850,44 +6737,84 @@ async function handleCliLaunch(cli, body, ctx) {
|
|
|
5850
6737
|
if (!isCliInstalled(meta.command, platform, probe)) {
|
|
5851
6738
|
return { status: 400, body: errBody(`"${meta.command}" is not installed (not found on PATH)`) };
|
|
5852
6739
|
}
|
|
6740
|
+
const keyId = typeof body["keyId"] === "string" && body["keyId"].trim() ? body["keyId"].trim() : void 0;
|
|
5853
6741
|
let target;
|
|
5854
|
-
|
|
5855
|
-
|
|
5856
|
-
providerId: typeof body["providerId"] === "string" ? body["providerId"] : void 0,
|
|
5857
|
-
model: typeof body["model"] === "string" ? body["model"] : void 0
|
|
5858
|
-
});
|
|
5859
|
-
} catch (err9) {
|
|
5860
|
-
return { status: 400, body: errBody(err9 instanceof Error ? err9.message : "no launch target") };
|
|
5861
|
-
}
|
|
5862
|
-
const id = randomUUID2();
|
|
6742
|
+
let keyLaunch;
|
|
6743
|
+
const id = randomUUID3();
|
|
5863
6744
|
let leaseId2;
|
|
5864
6745
|
let launch;
|
|
5865
|
-
|
|
5866
|
-
if (
|
|
5867
|
-
|
|
5868
|
-
|
|
5869
|
-
|
|
5870
|
-
|
|
5871
|
-
|
|
5872
|
-
|
|
5873
|
-
|
|
5874
|
-
|
|
5875
|
-
|
|
5876
|
-
const
|
|
5877
|
-
|
|
5878
|
-
|
|
5879
|
-
|
|
5880
|
-
|
|
5881
|
-
|
|
5882
|
-
|
|
5883
|
-
}
|
|
5884
|
-
}
|
|
5885
|
-
}
|
|
5886
|
-
|
|
6746
|
+
if (keyId) {
|
|
6747
|
+
if (cli !== "codex") {
|
|
6748
|
+
return { status: 400, body: errBody("key-scoped launch is only supported for codex") };
|
|
6749
|
+
}
|
|
6750
|
+
const deps = ctx.keyScoped;
|
|
6751
|
+
if (!deps) {
|
|
6752
|
+
return { status: 501, body: errBody("key-scoped launch is not available in this build") };
|
|
6753
|
+
}
|
|
6754
|
+
const preflight = await preflightKeyScopedLaunch(deps, keyId);
|
|
6755
|
+
if (!preflight.ok) return { status: preflight.status, body: errBody(preflight.message) };
|
|
6756
|
+
if (platform === "win32") {
|
|
6757
|
+
const unsafe = [deps.codexAuthHelper.command, ...deps.codexAuthHelper.args].filter((value) => CMD_METACHAR_RE.test(value));
|
|
6758
|
+
if (unsafe.length > 0) {
|
|
6759
|
+
return {
|
|
6760
|
+
status: 400,
|
|
6761
|
+
body: errBody(
|
|
6762
|
+
"the Codex auth-helper path contains cmd.exe metacharacters and cannot be passed through a Windows terminal launch"
|
|
6763
|
+
)
|
|
6764
|
+
};
|
|
6765
|
+
}
|
|
6766
|
+
}
|
|
6767
|
+
keyLaunch = { keyId, keyName: preflight.keyName };
|
|
6768
|
+
launch = {
|
|
6769
|
+
env: {},
|
|
6770
|
+
extraArgs: buildKeyScopedCodexArgs({
|
|
6771
|
+
gatewayBaseUrl: deps.gatewayBaseUrl,
|
|
6772
|
+
authHelper: deps.codexAuthHelper,
|
|
6773
|
+
keyId
|
|
6774
|
+
}),
|
|
6775
|
+
// No route or lease exists to release — the gateway key outlives the
|
|
6776
|
+
// terminal and its bindings route every request.
|
|
6777
|
+
onSessionEnd: () => {
|
|
6778
|
+
}
|
|
6779
|
+
};
|
|
6780
|
+
} else {
|
|
6781
|
+
let resolved;
|
|
6782
|
+
try {
|
|
6783
|
+
resolved = resolveLaunchTarget(ctx.providers, {
|
|
6784
|
+
providerId: typeof body["providerId"] === "string" ? body["providerId"] : void 0,
|
|
6785
|
+
model: typeof body["model"] === "string" ? body["model"] : void 0
|
|
6786
|
+
});
|
|
6787
|
+
} catch (err9) {
|
|
6788
|
+
return { status: 400, body: errBody(err9 instanceof Error ? err9.message : "no launch target") };
|
|
6789
|
+
}
|
|
6790
|
+
target = resolved;
|
|
6791
|
+
try {
|
|
6792
|
+
if ((cli === "claude" || cli === "codex") && ctx.routeLeaseManager) {
|
|
6793
|
+
const outcome = await ctx.routeLeaseManager.createFromRequest({
|
|
6794
|
+
schemaVersion: ROUTE_LEASE_REQUEST_SCHEMA,
|
|
6795
|
+
consumer: "omnicross-terminal",
|
|
6796
|
+
runtime: cli,
|
|
6797
|
+
upstream: { kind: "provider", providerId: resolved.providerId },
|
|
6798
|
+
model: resolved.model,
|
|
6799
|
+
execution: { sessionId: id }
|
|
6800
|
+
}, `omnicross-terminal:${id}`);
|
|
6801
|
+
leaseId2 = outcome.result.leaseId;
|
|
6802
|
+
const stopRenewal = startTerminalLeaseRenewal(ctx.routeLeaseManager, leaseId2);
|
|
6803
|
+
launch = {
|
|
6804
|
+
env: outcome.result.launch.env,
|
|
6805
|
+
extraArgs: outcome.result.launch.extraArgs,
|
|
6806
|
+
onSessionEnd: () => {
|
|
6807
|
+
stopRenewal();
|
|
6808
|
+
ctx.routeLeaseManager?.release(outcome.result.leaseId);
|
|
6809
|
+
}
|
|
6810
|
+
};
|
|
6811
|
+
} else {
|
|
6812
|
+
launch = await buildLaunchEnv(cli, ctx.llmConfig, resolved);
|
|
6813
|
+
}
|
|
6814
|
+
} catch (err9) {
|
|
6815
|
+
const status = err9 instanceof RouteLeaseError2 ? err9.status : 400;
|
|
6816
|
+
return { status, body: errBody(err9 instanceof Error ? err9.message : "failed to build launch env") };
|
|
5887
6817
|
}
|
|
5888
|
-
} catch (err9) {
|
|
5889
|
-
const status = err9 instanceof RouteLeaseError2 ? err9.status : 400;
|
|
5890
|
-
return { status, body: errBody(err9 instanceof Error ? err9.message : "failed to build launch env") };
|
|
5891
6818
|
}
|
|
5892
6819
|
const cwd = typeof body["cwd"] === "string" && body["cwd"].trim() ? body["cwd"].trim() : void 0;
|
|
5893
6820
|
const opener = ctx.opener ?? defaultTerminalOpener;
|
|
@@ -5926,15 +6853,19 @@ async function handleCliLaunch(cli, body, ctx) {
|
|
|
5926
6853
|
sessions.set(id, {
|
|
5927
6854
|
id,
|
|
5928
6855
|
cli,
|
|
5929
|
-
providerId: target
|
|
5930
|
-
model: target
|
|
6856
|
+
providerId: target?.providerId ?? "",
|
|
6857
|
+
model: target?.model ?? "",
|
|
6858
|
+
...keyLaunch ? { keyId: keyLaunch.keyId, keyName: keyLaunch.keyName } : {},
|
|
5931
6859
|
...leaseId2 ? { leaseId: leaseId2 } : {},
|
|
5932
6860
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5933
6861
|
onSessionEnd
|
|
5934
6862
|
});
|
|
5935
6863
|
published = true;
|
|
5936
6864
|
if (ended) sessions.delete(id);
|
|
5937
|
-
return {
|
|
6865
|
+
return {
|
|
6866
|
+
status: 200,
|
|
6867
|
+
body: keyLaunch ? { sessionId: id, keyId: keyLaunch.keyId, keyName: keyLaunch.keyName } : { sessionId: id, providerId: target?.providerId, model: target?.model }
|
|
6868
|
+
};
|
|
5938
6869
|
}
|
|
5939
6870
|
|
|
5940
6871
|
// src/admin/auditConfigBody.ts
|
|
@@ -6254,17 +7185,17 @@ function sanitizeResultField(value, cap) {
|
|
|
6254
7185
|
const text = typeof value === "string" ? value : value === null || value === void 0 ? "" : String(value);
|
|
6255
7186
|
return text.replace(/[\u0000-\u001f\u007f]/gu, "").slice(0, cap);
|
|
6256
7187
|
}
|
|
6257
|
-
function
|
|
7188
|
+
function writeJson2(res, status, body) {
|
|
6258
7189
|
res.writeHead(status, { "Content-Type": "application/json" });
|
|
6259
7190
|
res.end(JSON.stringify(body));
|
|
6260
7191
|
}
|
|
6261
7192
|
function writeErr(res, status, message) {
|
|
6262
|
-
|
|
7193
|
+
writeJson2(res, status, { error: { type: "admin_api_error", message } });
|
|
6263
7194
|
}
|
|
6264
7195
|
var SEARCH_MAX_BODY_BYTES = 64 * 1024;
|
|
6265
7196
|
var SearchBodyTooLargeError = class extends Error {
|
|
6266
7197
|
};
|
|
6267
|
-
async function
|
|
7198
|
+
async function readJsonBody3(req) {
|
|
6268
7199
|
const chunks = [];
|
|
6269
7200
|
let bytes = 0;
|
|
6270
7201
|
for await (const chunk of req) {
|
|
@@ -6284,7 +7215,7 @@ async function readJsonBody2(req) {
|
|
|
6284
7215
|
}
|
|
6285
7216
|
async function readBodyOrReject(req, res) {
|
|
6286
7217
|
try {
|
|
6287
|
-
return await
|
|
7218
|
+
return await readJsonBody3(req);
|
|
6288
7219
|
} catch (error) {
|
|
6289
7220
|
if (error instanceof SearchBodyTooLargeError) {
|
|
6290
7221
|
writeErr(res, 400, error.message);
|
|
@@ -6342,7 +7273,7 @@ async function handleSearchDiagnostics(res, deps) {
|
|
|
6342
7273
|
},
|
|
6343
7274
|
applySemantics: { codex: "immediate", rest: "restart" }
|
|
6344
7275
|
};
|
|
6345
|
-
return
|
|
7276
|
+
return writeJson2(res, 200, { diagnostics: snapshot });
|
|
6346
7277
|
}
|
|
6347
7278
|
function persistedSearchContributions(search, fetchImpl) {
|
|
6348
7279
|
if (fetchImpl) {
|
|
@@ -6388,11 +7319,11 @@ async function handleSearchTest(req, res, deps) {
|
|
|
6388
7319
|
checkedAt
|
|
6389
7320
|
);
|
|
6390
7321
|
const response = { diagnostic, resultCount: results.length };
|
|
6391
|
-
return
|
|
7322
|
+
return writeJson2(res, 200, { result: response });
|
|
6392
7323
|
} catch (error) {
|
|
6393
7324
|
const diagnostic = classifyLiveSearchOutcome(contribution.id, { kind: "failure", error }, checkedAt);
|
|
6394
7325
|
const response = { diagnostic };
|
|
6395
|
-
return
|
|
7326
|
+
return writeJson2(res, 200, { result: response });
|
|
6396
7327
|
}
|
|
6397
7328
|
}
|
|
6398
7329
|
async function handleSearchQuery(req, res, deps) {
|
|
@@ -6455,18 +7386,18 @@ async function handleSearchQuery(req, res, deps) {
|
|
|
6455
7386
|
resultCount: sanitized.length,
|
|
6456
7387
|
results: sanitized
|
|
6457
7388
|
};
|
|
6458
|
-
return
|
|
7389
|
+
return writeJson2(res, 200, { result: response });
|
|
6459
7390
|
} catch (error) {
|
|
6460
7391
|
const diagnostic = classifyLiveSearchOutcome(providerId, { kind: "failure", error }, checkedAt);
|
|
6461
7392
|
const response = { diagnostic };
|
|
6462
|
-
return
|
|
7393
|
+
return writeJson2(res, 200, { result: response });
|
|
6463
7394
|
}
|
|
6464
7395
|
}
|
|
6465
7396
|
|
|
6466
7397
|
// src/admin/searchAdminView.ts
|
|
6467
7398
|
var API_KEY_PROVIDERS = /* @__PURE__ */ new Set(["tavily", "jina", "zhipu", "z.ai"]);
|
|
6468
7399
|
var BASIC_AUTH_PROVIDERS = /* @__PURE__ */ new Set(["searxng"]);
|
|
6469
|
-
function
|
|
7400
|
+
function isRecord9(value) {
|
|
6470
7401
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
6471
7402
|
}
|
|
6472
7403
|
function redactSearchServerConfig(search) {
|
|
@@ -6516,13 +7447,13 @@ function resolveSecretField(entry, field, stored) {
|
|
|
6516
7447
|
else delete entry[field];
|
|
6517
7448
|
}
|
|
6518
7449
|
function preserveSearchSecrets(incoming, current) {
|
|
6519
|
-
if (!
|
|
7450
|
+
if (!isRecord9(incoming)) return incoming;
|
|
6520
7451
|
const section = { ...incoming };
|
|
6521
7452
|
const providersValue = section["providers"];
|
|
6522
|
-
if (!
|
|
7453
|
+
if (!isRecord9(providersValue)) return section;
|
|
6523
7454
|
const providers = {};
|
|
6524
7455
|
for (const [id, entryValue] of Object.entries(providersValue)) {
|
|
6525
|
-
if (!
|
|
7456
|
+
if (!isRecord9(entryValue)) {
|
|
6526
7457
|
providers[id] = entryValue;
|
|
6527
7458
|
continue;
|
|
6528
7459
|
}
|
|
@@ -6600,7 +7531,8 @@ function parseKeyPolicyBody(body) {
|
|
|
6600
7531
|
var ENDPOINTS = /* @__PURE__ */ new Set(["chat", "responses", "messages", "gemini"]);
|
|
6601
7532
|
var TARGET_KINDS = /* @__PURE__ */ new Set(["account", "account-group", "account-pool", "provider"]);
|
|
6602
7533
|
var FALLBACKS = /* @__PURE__ */ new Set(["next", "fail", "global"]);
|
|
6603
|
-
|
|
7534
|
+
var EFFORT_MAX_LENGTH = 32;
|
|
7535
|
+
function isRecord10(value) {
|
|
6604
7536
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
6605
7537
|
}
|
|
6606
7538
|
function nonBlank(value) {
|
|
@@ -6620,7 +7552,7 @@ function validateGatewayBindingsSegment(patch) {
|
|
|
6620
7552
|
const ids = /* @__PURE__ */ new Set();
|
|
6621
7553
|
raw.forEach((entry, index) => {
|
|
6622
7554
|
const path2 = `bindings[${index}]`;
|
|
6623
|
-
if (!
|
|
7555
|
+
if (!isRecord10(entry)) {
|
|
6624
7556
|
errors.push(`${path2} must be an object`);
|
|
6625
7557
|
return;
|
|
6626
7558
|
}
|
|
@@ -6649,12 +7581,18 @@ function validateGatewayBindingsSegment(patch) {
|
|
|
6649
7581
|
} else if (entry.modelMappings.length > 100) {
|
|
6650
7582
|
errors.push(`${path2}.modelMappings cannot contain more than 100 entries`);
|
|
6651
7583
|
} else if (entry.modelMappings.some(
|
|
6652
|
-
(mapping) => !
|
|
7584
|
+
(mapping) => !isRecord10(mapping) || !nonBlank(mapping.source) || !nonBlank(mapping.target)
|
|
6653
7585
|
)) {
|
|
6654
7586
|
errors.push(`${path2}.modelMappings must contain non-empty source and target strings`);
|
|
7587
|
+
} else if (entry.modelMappings.some(
|
|
7588
|
+
(mapping) => mapping.effort !== void 0 && (typeof mapping.effort !== "string" || mapping.effort.trim() === "" || mapping.effort.trim().length > EFFORT_MAX_LENGTH)
|
|
7589
|
+
)) {
|
|
7590
|
+
errors.push(
|
|
7591
|
+
`${path2}.modelMappings effort must be a non-empty string of at most ${EFFORT_MAX_LENGTH} characters`
|
|
7592
|
+
);
|
|
6655
7593
|
}
|
|
6656
7594
|
}
|
|
6657
|
-
if (!
|
|
7595
|
+
if (!isRecord10(entry.target) || !TARGET_KINDS.has(String(entry.target.kind))) {
|
|
6658
7596
|
errors.push(`${path2}.target is invalid`);
|
|
6659
7597
|
} else {
|
|
6660
7598
|
if (!nonBlank(entry.target.providerId)) errors.push(`${path2}.target.providerId is required`);
|
|
@@ -6669,7 +7607,7 @@ function validateGatewayBindingsSegment(patch) {
|
|
|
6669
7607
|
}
|
|
6670
7608
|
}
|
|
6671
7609
|
if (entry.modelMap !== void 0) {
|
|
6672
|
-
if (!
|
|
7610
|
+
if (!isRecord10(entry.modelMap) || Object.values(entry.modelMap).some((value) => typeof value !== "string")) {
|
|
6673
7611
|
errors.push(`${path2}.modelMap must contain string values`);
|
|
6674
7612
|
}
|
|
6675
7613
|
}
|
|
@@ -6695,23 +7633,23 @@ import {
|
|
|
6695
7633
|
toVoucherInfo,
|
|
6696
7634
|
voucherCodePrefix
|
|
6697
7635
|
} from "@omnicross/core/outbound-api";
|
|
6698
|
-
function
|
|
7636
|
+
function writeJson3(res, status, body) {
|
|
6699
7637
|
res.writeHead(status, { "Content-Type": "application/json" });
|
|
6700
7638
|
res.end(JSON.stringify(body));
|
|
6701
7639
|
}
|
|
6702
7640
|
function writeErr2(res, status, message) {
|
|
6703
|
-
|
|
7641
|
+
writeJson3(res, status, { error: { type: "voucher_error", message } });
|
|
6704
7642
|
}
|
|
6705
|
-
function
|
|
6706
|
-
return new Promise((
|
|
7643
|
+
function readJsonBody4(req) {
|
|
7644
|
+
return new Promise((resolve11, reject) => {
|
|
6707
7645
|
const chunks = [];
|
|
6708
7646
|
req.on("data", (c) => chunks.push(c));
|
|
6709
7647
|
req.on("end", () => {
|
|
6710
7648
|
const raw = Buffer.concat(chunks).toString("utf8");
|
|
6711
|
-
if (!raw.trim()) return
|
|
7649
|
+
if (!raw.trim()) return resolve11({});
|
|
6712
7650
|
try {
|
|
6713
7651
|
const parsed = JSON.parse(raw);
|
|
6714
|
-
|
|
7652
|
+
resolve11(parsed && typeof parsed === "object" ? parsed : {});
|
|
6715
7653
|
} catch {
|
|
6716
7654
|
reject(new Error("invalid-json"));
|
|
6717
7655
|
}
|
|
@@ -6761,13 +7699,13 @@ async function handleVoucher(req, res, method, rest, deps) {
|
|
|
6761
7699
|
if (!voucherDb) return writeErr2(res, 501, "Voucher feature is not available");
|
|
6762
7700
|
if (method === "GET" && rest.length === 0) {
|
|
6763
7701
|
const rows = await voucherDb.voucherList();
|
|
6764
|
-
return
|
|
7702
|
+
return writeJson3(res, 200, { vouchers: rows.map(toVoucherInfo) });
|
|
6765
7703
|
}
|
|
6766
7704
|
if (method === "POST" && rest.length === 0) {
|
|
6767
7705
|
if (!await voucherEnabled(deps)) return writeErr2(res, 403, "Voucher feature is disabled");
|
|
6768
7706
|
let body;
|
|
6769
7707
|
try {
|
|
6770
|
-
body = await
|
|
7708
|
+
body = await readJsonBody4(req);
|
|
6771
7709
|
} catch {
|
|
6772
7710
|
return writeErr2(res, 400, "Invalid JSON in request body");
|
|
6773
7711
|
}
|
|
@@ -6780,7 +7718,7 @@ async function handleVoucher(req, res, method, rest, deps) {
|
|
|
6780
7718
|
codePrefix: voucherCodePrefix(code),
|
|
6781
7719
|
...parsed.input
|
|
6782
7720
|
});
|
|
6783
|
-
return
|
|
7721
|
+
return writeJson3(res, 201, {
|
|
6784
7722
|
id: created.id,
|
|
6785
7723
|
codePrefix: created.codePrefix,
|
|
6786
7724
|
type: created.type,
|
|
@@ -6793,7 +7731,7 @@ async function handleVoucher(req, res, method, rest, deps) {
|
|
|
6793
7731
|
if (method === "POST" && id && rest[1] === "revoke") {
|
|
6794
7732
|
if (!await voucherEnabled(deps)) return writeErr2(res, 403, "Voucher feature is disabled");
|
|
6795
7733
|
const ok = await voucherDb.voucherRevokeCas(id, Date.now());
|
|
6796
|
-
return
|
|
7734
|
+
return writeJson3(res, ok ? 200 : 409, { ok });
|
|
6797
7735
|
}
|
|
6798
7736
|
return writeErr2(res, 405, `method ${method} not allowed on voucher`);
|
|
6799
7737
|
}
|
|
@@ -6970,7 +7908,7 @@ import {
|
|
|
6970
7908
|
} from "@omnicross/core/outbound-api";
|
|
6971
7909
|
|
|
6972
7910
|
// src/ports/account-multi.ts
|
|
6973
|
-
import { randomUUID as
|
|
7911
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
6974
7912
|
var PROVIDER_KEYS = {
|
|
6975
7913
|
claude: { block: "claude", accounts: "claudeAccounts", active: "activeClaudeAccountId" },
|
|
6976
7914
|
codex: { block: "codex", accounts: "codexAccounts", active: "activeCodexAccountId" },
|
|
@@ -7044,7 +7982,7 @@ function migrateLazily(config) {
|
|
|
7044
7982
|
}
|
|
7045
7983
|
function addAccount(config, p, tokens, label) {
|
|
7046
7984
|
const accounts = [...getAccounts(config, p)];
|
|
7047
|
-
const id =
|
|
7985
|
+
const id = randomUUID4();
|
|
7048
7986
|
accounts.push({
|
|
7049
7987
|
id,
|
|
7050
7988
|
label: label ?? `Account ${accounts.length + 1}`,
|
|
@@ -7733,22 +8671,22 @@ async function handlePricingResolveConflicts(body, deps) {
|
|
|
7733
8671
|
}
|
|
7734
8672
|
|
|
7735
8673
|
// src/admin/accountAllowanceApi.ts
|
|
7736
|
-
function
|
|
8674
|
+
function writeJson4(res, status, body) {
|
|
7737
8675
|
res.writeHead(status, { "Content-Type": "application/json" });
|
|
7738
8676
|
res.end(JSON.stringify(body));
|
|
7739
8677
|
}
|
|
7740
8678
|
function writeError2(res, status, message) {
|
|
7741
|
-
|
|
8679
|
+
writeJson4(res, status, { error: { type: "account_allowance_error", message } });
|
|
7742
8680
|
}
|
|
7743
8681
|
function readJson2(req) {
|
|
7744
|
-
return new Promise((
|
|
8682
|
+
return new Promise((resolve11, reject) => {
|
|
7745
8683
|
const chunks = [];
|
|
7746
8684
|
req.on("data", (chunk) => chunks.push(Buffer.from(chunk)));
|
|
7747
8685
|
req.on("end", () => {
|
|
7748
8686
|
try {
|
|
7749
8687
|
const text = Buffer.concat(chunks).toString("utf8");
|
|
7750
8688
|
const parsed = text ? JSON.parse(text) : {};
|
|
7751
|
-
|
|
8689
|
+
resolve11(parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {});
|
|
7752
8690
|
} catch (error) {
|
|
7753
8691
|
reject(error);
|
|
7754
8692
|
}
|
|
@@ -7771,7 +8709,7 @@ async function handleAccountAllowanceApi(req, res, method, rest, service) {
|
|
|
7771
8709
|
if (!service.getSchedulingStatus) {
|
|
7772
8710
|
return writeError2(res, 501, "allowance scheduling diagnostics are not available");
|
|
7773
8711
|
}
|
|
7774
|
-
return
|
|
8712
|
+
return writeJson4(res, 200, { scheduling: service.getSchedulingStatus() });
|
|
7775
8713
|
}
|
|
7776
8714
|
if (method === "GET") {
|
|
7777
8715
|
const params = query(req);
|
|
@@ -7782,7 +8720,7 @@ async function handleAccountAllowanceApi(req, res, method, rest, service) {
|
|
|
7782
8720
|
}
|
|
7783
8721
|
const accountId = rest.length >= 2 ? rest[1] : params.get("accountId") ?? void 0;
|
|
7784
8722
|
const allowances = await service.list({ providerId, accountId });
|
|
7785
|
-
return
|
|
8723
|
+
return writeJson4(res, 200, { allowances });
|
|
7786
8724
|
}
|
|
7787
8725
|
if (method === "POST" && rest[0] === "refresh") {
|
|
7788
8726
|
const body = await readJson2(req);
|
|
@@ -7798,7 +8736,7 @@ async function handleAccountAllowanceApi(req, res, method, rest, service) {
|
|
|
7798
8736
|
if (accountId && allowances2.length === 0) {
|
|
7799
8737
|
return writeError2(res, 404, `Codex account '${accountId}' not found`);
|
|
7800
8738
|
}
|
|
7801
|
-
return
|
|
8739
|
+
return writeJson4(res, 200, { allowances: allowances2 });
|
|
7802
8740
|
}
|
|
7803
8741
|
if (requestedProvider === "kimi") {
|
|
7804
8742
|
if (!service.refreshKimi) {
|
|
@@ -7808,7 +8746,7 @@ async function handleAccountAllowanceApi(req, res, method, rest, service) {
|
|
|
7808
8746
|
if (accountId && allowances2.length === 0) {
|
|
7809
8747
|
return writeError2(res, 404, `Kimi account '${accountId}' not found`);
|
|
7810
8748
|
}
|
|
7811
|
-
return
|
|
8749
|
+
return writeJson4(res, 200, { allowances: allowances2 });
|
|
7812
8750
|
}
|
|
7813
8751
|
if (requestedProvider === "opencodego") {
|
|
7814
8752
|
if (!service.refreshOpenCodeGo) {
|
|
@@ -7818,7 +8756,7 @@ async function handleAccountAllowanceApi(req, res, method, rest, service) {
|
|
|
7818
8756
|
if (accountId && allowances2.length === 0) {
|
|
7819
8757
|
return writeError2(res, 404, `OpenCodeGo account '${accountId}' not found`);
|
|
7820
8758
|
}
|
|
7821
|
-
return
|
|
8759
|
+
return writeJson4(res, 200, { allowances: allowances2 });
|
|
7822
8760
|
}
|
|
7823
8761
|
if (requestedProvider === "copilot") {
|
|
7824
8762
|
if (!service.refreshCopilot) {
|
|
@@ -7828,7 +8766,7 @@ async function handleAccountAllowanceApi(req, res, method, rest, service) {
|
|
|
7828
8766
|
if (accountId && allowances2.length === 0) {
|
|
7829
8767
|
return writeError2(res, 404, `Copilot account '${accountId}' not found`);
|
|
7830
8768
|
}
|
|
7831
|
-
return
|
|
8769
|
+
return writeJson4(res, 200, { allowances: allowances2 });
|
|
7832
8770
|
}
|
|
7833
8771
|
if (requestedProvider === "grok") {
|
|
7834
8772
|
if (!service.refreshGrok) {
|
|
@@ -7838,7 +8776,7 @@ async function handleAccountAllowanceApi(req, res, method, rest, service) {
|
|
|
7838
8776
|
if (accountId && allowances2.length === 0) {
|
|
7839
8777
|
return writeError2(res, 404, `Grok account '${accountId}' not found`);
|
|
7840
8778
|
}
|
|
7841
|
-
return
|
|
8779
|
+
return writeJson4(res, 200, { allowances: allowances2 });
|
|
7842
8780
|
}
|
|
7843
8781
|
if (requestedProvider === "antigravity") {
|
|
7844
8782
|
if (!service.refreshAntigravity) {
|
|
@@ -7848,7 +8786,7 @@ async function handleAccountAllowanceApi(req, res, method, rest, service) {
|
|
|
7848
8786
|
if (accountId && allowances2.length === 0) {
|
|
7849
8787
|
return writeError2(res, 404, `Antigravity account '${accountId}' not found`);
|
|
7850
8788
|
}
|
|
7851
|
-
return
|
|
8789
|
+
return writeJson4(res, 200, { allowances: allowances2 });
|
|
7852
8790
|
}
|
|
7853
8791
|
if (requestedProvider === "gemini") {
|
|
7854
8792
|
if (!service.refreshGemini) {
|
|
@@ -7858,13 +8796,13 @@ async function handleAccountAllowanceApi(req, res, method, rest, service) {
|
|
|
7858
8796
|
if (accountId && allowances2.length === 0) {
|
|
7859
8797
|
return writeError2(res, 404, `Gemini account '${accountId}' not found`);
|
|
7860
8798
|
}
|
|
7861
|
-
return
|
|
8799
|
+
return writeJson4(res, 200, { allowances: allowances2 });
|
|
7862
8800
|
}
|
|
7863
8801
|
const allowances = await service.refreshClaude(accountId);
|
|
7864
8802
|
if (accountId && allowances.length === 0) {
|
|
7865
8803
|
return writeError2(res, 404, `Claude account '${accountId}' not found`);
|
|
7866
8804
|
}
|
|
7867
|
-
return
|
|
8805
|
+
return writeJson4(res, 200, { allowances });
|
|
7868
8806
|
}
|
|
7869
8807
|
return writeError2(res, 405, `method ${method} not allowed on account allowances`);
|
|
7870
8808
|
}
|
|
@@ -7876,14 +8814,14 @@ import {
|
|
|
7876
8814
|
} from "@omnicross/core/pipeline/AccountRouteActivity";
|
|
7877
8815
|
import { getSharedOverloadCounter } from "@omnicross/core/pipeline/ServerOverloadCounter";
|
|
7878
8816
|
function readBody(req) {
|
|
7879
|
-
return new Promise((
|
|
8817
|
+
return new Promise((resolve11, reject) => {
|
|
7880
8818
|
const chunks = [];
|
|
7881
8819
|
req.on("data", (chunk) => chunks.push(chunk));
|
|
7882
|
-
req.on("end", () =>
|
|
8820
|
+
req.on("end", () => resolve11(Buffer.concat(chunks).toString("utf8")));
|
|
7883
8821
|
req.on("error", reject);
|
|
7884
8822
|
});
|
|
7885
8823
|
}
|
|
7886
|
-
async function
|
|
8824
|
+
async function readJsonBody5(req) {
|
|
7887
8825
|
const raw = await readBody(req);
|
|
7888
8826
|
if (!raw.trim()) return {};
|
|
7889
8827
|
try {
|
|
@@ -7893,12 +8831,12 @@ async function readJsonBody4(req) {
|
|
|
7893
8831
|
return {};
|
|
7894
8832
|
}
|
|
7895
8833
|
}
|
|
7896
|
-
function
|
|
8834
|
+
function writeJson5(res, status, body) {
|
|
7897
8835
|
res.writeHead(status, { "Content-Type": "application/json" });
|
|
7898
8836
|
res.end(JSON.stringify(body));
|
|
7899
8837
|
}
|
|
7900
|
-
function
|
|
7901
|
-
|
|
8838
|
+
function writeJsonError2(res, status, message) {
|
|
8839
|
+
writeJson5(res, status, { error: { type: "admin_api_error", message } });
|
|
7902
8840
|
}
|
|
7903
8841
|
function maskProviderApiKey(apiKey) {
|
|
7904
8842
|
if (!apiKey) return "";
|
|
@@ -7919,7 +8857,7 @@ function toKeyInfo(row) {
|
|
|
7919
8857
|
lastUsedAt: row.lastUsedAt,
|
|
7920
8858
|
revoked: row.revokedAt !== null,
|
|
7921
8859
|
kind: row.kind,
|
|
7922
|
-
allowedEndpoints: [...
|
|
8860
|
+
allowedEndpoints: [...effectiveOutboundPermissions3(row.allowedEndpoints)],
|
|
7923
8861
|
legacyPermissions: row.allowedEndpoints === void 0,
|
|
7924
8862
|
loopbackOnly: row.loopbackOnly,
|
|
7925
8863
|
maxConcurrency: row.maxConcurrency,
|
|
@@ -7937,7 +8875,12 @@ function toKeyInfo(row) {
|
|
|
7937
8875
|
// Per-key model restriction (#6) — secret-free scalars the UI pre-fills.
|
|
7938
8876
|
enableModelRestriction: row.enableModelRestriction,
|
|
7939
8877
|
restrictionMode: row.restrictionMode,
|
|
7940
|
-
restrictedModels: row.restrictedModels
|
|
8878
|
+
restrictedModels: row.restrictedModels,
|
|
8879
|
+
// Direct upstream passthrough target (key→upstream binding) — references
|
|
8880
|
+
// only, never a credential. Absent = the key is served by the downstream
|
|
8881
|
+
// routes. `boundUpstreamProviderId` is the legacy first-cut shape.
|
|
8882
|
+
boundUpstream: row.boundUpstream,
|
|
8883
|
+
boundUpstreamProviderId: row.boundUpstreamProviderId
|
|
7941
8884
|
};
|
|
7942
8885
|
}
|
|
7943
8886
|
function toProviderView(row) {
|
|
@@ -8031,10 +8974,10 @@ async function handleAdminApi(req, res, path2, deps) {
|
|
|
8031
8974
|
case "pricing":
|
|
8032
8975
|
return await handlePricing(req, res, method, rest, deps);
|
|
8033
8976
|
default:
|
|
8034
|
-
return
|
|
8977
|
+
return writeJsonError2(res, 404, `unknown admin resource '${resource}'`);
|
|
8035
8978
|
}
|
|
8036
8979
|
} catch (err9) {
|
|
8037
|
-
|
|
8980
|
+
writeJsonError2(res, 500, err9 instanceof Error ? err9.message : String(err9));
|
|
8038
8981
|
}
|
|
8039
8982
|
}
|
|
8040
8983
|
function requestQuery(req) {
|
|
@@ -8043,35 +8986,35 @@ function requestQuery(req) {
|
|
|
8043
8986
|
return new URLSearchParams(qIdx >= 0 ? raw.slice(qIdx + 1) : "");
|
|
8044
8987
|
}
|
|
8045
8988
|
function writeResult(res, result) {
|
|
8046
|
-
|
|
8989
|
+
writeJson5(res, result.status, result.body);
|
|
8047
8990
|
}
|
|
8048
8991
|
async function handleUsage(req, res, method, rest, deps) {
|
|
8049
|
-
if (method !== "GET") return
|
|
8992
|
+
if (method !== "GET") return writeJsonError2(res, 405, `method ${method} not allowed on usage`);
|
|
8050
8993
|
return writeResult(res, await handleUsageGet(rest[0], requestQuery(req), deps));
|
|
8051
8994
|
}
|
|
8052
8995
|
async function handleDashboardRoute(res, method, deps) {
|
|
8053
|
-
if (method !== "GET") return
|
|
8996
|
+
if (method !== "GET") return writeJsonError2(res, 405, `method ${method} not allowed on dashboard`);
|
|
8054
8997
|
const result = await handleDashboard(deps);
|
|
8055
|
-
return
|
|
8998
|
+
return writeJson5(res, result.status, result.body);
|
|
8056
8999
|
}
|
|
8057
9000
|
async function handlePricing(req, res, method, rest, deps) {
|
|
8058
9001
|
if (rest.length === 0) {
|
|
8059
9002
|
if (method === "GET") return writeResult(res, await handlePricingList(deps));
|
|
8060
9003
|
if (method === "PUT") {
|
|
8061
|
-
return writeResult(res, await handlePricingUpsert(await
|
|
9004
|
+
return writeResult(res, await handlePricingUpsert(await readJsonBody5(req), deps));
|
|
8062
9005
|
}
|
|
8063
9006
|
if (method === "DELETE") {
|
|
8064
9007
|
return writeResult(res, await handlePricingDelete(requestQuery(req), deps));
|
|
8065
9008
|
}
|
|
8066
|
-
return
|
|
9009
|
+
return writeJsonError2(res, 405, `method ${method} not allowed on pricing`);
|
|
8067
9010
|
}
|
|
8068
9011
|
if (method === "POST" && rest.length === 1 && rest[0] === "fetch-latest") {
|
|
8069
9012
|
return writeResult(res, await handlePricingFetchLatest(deps));
|
|
8070
9013
|
}
|
|
8071
9014
|
if (method === "POST" && rest.length === 1 && rest[0] === "resolve-conflicts") {
|
|
8072
|
-
return writeResult(res, await handlePricingResolveConflicts(await
|
|
9015
|
+
return writeResult(res, await handlePricingResolveConflicts(await readJsonBody5(req), deps));
|
|
8073
9016
|
}
|
|
8074
|
-
return
|
|
9017
|
+
return writeJsonError2(res, 404, `unknown pricing route '${rest.join("/")}'`);
|
|
8075
9018
|
}
|
|
8076
9019
|
function migrationDeps(deps) {
|
|
8077
9020
|
return {
|
|
@@ -8082,16 +9025,16 @@ function migrationDeps(deps) {
|
|
|
8082
9025
|
};
|
|
8083
9026
|
}
|
|
8084
9027
|
async function handleMigrationExport(req, res, method, deps) {
|
|
8085
|
-
if (method !== "POST") return
|
|
8086
|
-
const body = await
|
|
9028
|
+
if (method !== "POST") return writeJsonError2(res, 405, `method ${method} not allowed on export`);
|
|
9029
|
+
const body = await readJsonBody5(req);
|
|
8087
9030
|
const result = await handleExport(body, migrationDeps(deps));
|
|
8088
|
-
return
|
|
9031
|
+
return writeJson5(res, result.status, result.body);
|
|
8089
9032
|
}
|
|
8090
9033
|
async function handleMigrationImport(req, res, method, deps) {
|
|
8091
|
-
if (method !== "POST") return
|
|
8092
|
-
const body = await
|
|
9034
|
+
if (method !== "POST") return writeJsonError2(res, 405, `method ${method} not allowed on import`);
|
|
9035
|
+
const body = await readJsonBody5(req);
|
|
8093
9036
|
const result = await handleImport(body, migrationDeps(deps));
|
|
8094
|
-
return
|
|
9037
|
+
return writeJson5(res, result.status, result.body);
|
|
8095
9038
|
}
|
|
8096
9039
|
async function handleProviders(req, res, method, rest, deps) {
|
|
8097
9040
|
const cfg = loadConfig(deps.configPath);
|
|
@@ -8124,52 +9067,52 @@ async function handleProviders(req, res, method, rest, deps) {
|
|
|
8124
9067
|
}
|
|
8125
9068
|
if (method === "GET" && rest.length === 2 && rest[1] === "reveal-key") {
|
|
8126
9069
|
const row = cfg.providers.find((p) => p.id === rest[0]);
|
|
8127
|
-
if (!row) return
|
|
8128
|
-
return
|
|
9070
|
+
if (!row) return writeJsonError2(res, 404, `provider '${rest[0]}' not found`);
|
|
9071
|
+
return writeJson5(res, 200, { apiKey: row.apiKey ?? "" });
|
|
8129
9072
|
}
|
|
8130
9073
|
if (method === "GET") {
|
|
8131
|
-
return
|
|
9074
|
+
return writeJson5(res, 200, { providers: cfg.providers.map(toProviderView) });
|
|
8132
9075
|
}
|
|
8133
9076
|
if (method === "POST") {
|
|
8134
|
-
const body = await
|
|
9077
|
+
const body = await readJsonBody5(req);
|
|
8135
9078
|
const provider = parseProviderInput(body, void 0);
|
|
8136
|
-
if (!provider) return
|
|
9079
|
+
if (!provider) return writeJsonError2(res, 400, "invalid provider (id, apiFormat, baseUrl required)");
|
|
8137
9080
|
if (cfg.providers.some((p) => p.id === provider.id)) {
|
|
8138
|
-
return
|
|
9081
|
+
return writeJsonError2(res, 409, `provider '${provider.id}' already exists`);
|
|
8139
9082
|
}
|
|
8140
9083
|
cfg.providers.push(provider);
|
|
8141
9084
|
persistProviders(cfg, deps);
|
|
8142
|
-
return
|
|
9085
|
+
return writeJson5(res, 201, { provider: toProviderView(provider) });
|
|
8143
9086
|
}
|
|
8144
9087
|
const id = rest[0];
|
|
8145
|
-
if (!id) return
|
|
9088
|
+
if (!id) return writeJsonError2(res, 400, "provider id required in path");
|
|
8146
9089
|
const idx = cfg.providers.findIndex((p) => p.id === id);
|
|
8147
|
-
if (idx < 0) return
|
|
9090
|
+
if (idx < 0) return writeJsonError2(res, 404, `provider '${id}' not found`);
|
|
8148
9091
|
if (method === "PUT") {
|
|
8149
|
-
const body = await
|
|
9092
|
+
const body = await readJsonBody5(req);
|
|
8150
9093
|
const existing = cfg.providers[idx];
|
|
8151
9094
|
const updated = parseProviderInput(body, existing);
|
|
8152
|
-
if (!updated) return
|
|
9095
|
+
if (!updated) return writeJsonError2(res, 400, "invalid provider (apiFormat, baseUrl required)");
|
|
8153
9096
|
cfg.providers[idx] = updated;
|
|
8154
9097
|
persistProviders(cfg, deps);
|
|
8155
|
-
return
|
|
9098
|
+
return writeJson5(res, 200, { provider: toProviderView(updated) });
|
|
8156
9099
|
}
|
|
8157
9100
|
if (method === "DELETE") {
|
|
8158
9101
|
cfg.providers.splice(idx, 1);
|
|
8159
9102
|
persistProviders(cfg, deps);
|
|
8160
|
-
return
|
|
9103
|
+
return writeJson5(res, 200, { ok: true });
|
|
8161
9104
|
}
|
|
8162
|
-
return
|
|
9105
|
+
return writeJsonError2(res, 405, `method ${method} not allowed on providers`);
|
|
8163
9106
|
}
|
|
8164
9107
|
function persistProviders(cfg, deps) {
|
|
8165
9108
|
saveConfig(deps.configPath, cfg);
|
|
8166
9109
|
deps.llmConfig.reload(cfg);
|
|
8167
9110
|
}
|
|
8168
9111
|
async function handleProviderReorder(req, res, cfg, deps) {
|
|
8169
|
-
const body = await
|
|
9112
|
+
const body = await readJsonBody5(req);
|
|
8170
9113
|
const rawOrder = body["order"];
|
|
8171
9114
|
if (!Array.isArray(rawOrder)) {
|
|
8172
|
-
return
|
|
9115
|
+
return writeJsonError2(res, 400, "reorder requires { order: string[] }");
|
|
8173
9116
|
}
|
|
8174
9117
|
const order = rawOrder.filter((x) => typeof x === "string");
|
|
8175
9118
|
const byId = new Map(cfg.providers.map((p) => [p.id, p]));
|
|
@@ -8190,17 +9133,17 @@ async function handleProviderReorder(req, res, cfg, deps) {
|
|
|
8190
9133
|
}
|
|
8191
9134
|
cfg.providers = reordered;
|
|
8192
9135
|
persistProviders(cfg, deps);
|
|
8193
|
-
return
|
|
9136
|
+
return writeJson5(res, 200, { ok: true, providers: cfg.providers.map(toProviderView) });
|
|
8194
9137
|
}
|
|
8195
9138
|
function expandRowExtraHeaders(row) {
|
|
8196
9139
|
return mergeExtraHeaders({}, row.extraHeaders);
|
|
8197
9140
|
}
|
|
8198
9141
|
async function handleDiscoverModels(res, id, cfg) {
|
|
8199
|
-
if (!id) return
|
|
9142
|
+
if (!id) return writeJsonError2(res, 400, "provider id required in path");
|
|
8200
9143
|
const row = cfg.providers.find((p) => p.id === id);
|
|
8201
|
-
if (!row) return
|
|
9144
|
+
if (!row) return writeJsonError2(res, 404, `provider '${id}' not found`);
|
|
8202
9145
|
if (row.apiFormat !== "openai" && row.apiFormat !== "openai-response") {
|
|
8203
|
-
return
|
|
9146
|
+
return writeJson5(res, 200, { models: [], unsupportedFormat: true });
|
|
8204
9147
|
}
|
|
8205
9148
|
const resolvedKey = resolveEnvKey(row.apiKey);
|
|
8206
9149
|
const base = row.baseUrl.replace(/\/+$/, "");
|
|
@@ -8209,6 +9152,7 @@ async function handleDiscoverModels(res, id, cfg) {
|
|
|
8209
9152
|
const headers = { Accept: "application/json" };
|
|
8210
9153
|
if (resolvedKey) headers["Authorization"] = `Bearer ${resolvedKey}`;
|
|
8211
9154
|
Object.assign(headers, expandRowExtraHeaders(row));
|
|
9155
|
+
applyAdminProbeIdentity(headers, row);
|
|
8212
9156
|
const response = await fetchUpstream10(url, { method: "GET", headers }, { providerId: "byo" });
|
|
8213
9157
|
if (!response.ok) {
|
|
8214
9158
|
const text = await response.text().catch(() => "");
|
|
@@ -8218,32 +9162,32 @@ async function handleDiscoverModels(res, id, cfg) {
|
|
|
8218
9162
|
message = parsed?.error?.message || parsed?.message || message;
|
|
8219
9163
|
} catch {
|
|
8220
9164
|
}
|
|
8221
|
-
return
|
|
9165
|
+
return writeJson5(res, 200, {
|
|
8222
9166
|
models: [],
|
|
8223
9167
|
error: `discovery failed (${response.status})${message ? `: ${message}` : ""}`
|
|
8224
9168
|
});
|
|
8225
9169
|
}
|
|
8226
9170
|
const data = await response.json();
|
|
8227
9171
|
const models = Array.isArray(data?.data) ? data.data.map((m) => typeof m?.id === "string" ? m.id : "").filter((m) => m.length > 0) : [];
|
|
8228
|
-
return
|
|
9172
|
+
return writeJson5(res, 200, { models });
|
|
8229
9173
|
} catch (err9) {
|
|
8230
9174
|
const message = err9 instanceof Error ? err9.message : String(err9);
|
|
8231
|
-
return
|
|
9175
|
+
return writeJson5(res, 200, { models: [], error: `discovery failed: ${message}` });
|
|
8232
9176
|
}
|
|
8233
9177
|
}
|
|
8234
9178
|
async function handleTestModel(req, res, id, cfg) {
|
|
8235
|
-
if (!id) return
|
|
9179
|
+
if (!id) return writeJsonError2(res, 400, "provider id required in path");
|
|
8236
9180
|
const row = cfg.providers.find((p) => p.id === id);
|
|
8237
|
-
if (!row) return
|
|
8238
|
-
const body = await
|
|
9181
|
+
if (!row) return writeJsonError2(res, 404, `provider '${id}' not found`);
|
|
9182
|
+
const body = await readJsonBody5(req);
|
|
8239
9183
|
const model = typeof body["model"] === "string" ? body["model"].trim() : "";
|
|
8240
|
-
if (!model) return
|
|
9184
|
+
if (!model) return writeJsonError2(res, 400, "test requires a { model } string");
|
|
8241
9185
|
if (row.apiFormat === "gemini") {
|
|
8242
|
-
return
|
|
9186
|
+
return writeJson5(res, 200, { ok: false, unsupportedFormat: true });
|
|
8243
9187
|
}
|
|
8244
9188
|
const resolvedKey = resolveEnvKey(row.apiKey);
|
|
8245
9189
|
if (!resolvedKey) {
|
|
8246
|
-
return
|
|
9190
|
+
return writeJson5(res, 200, { ok: false, message: "no API key configured for this provider" });
|
|
8247
9191
|
}
|
|
8248
9192
|
let url = row.baseUrl.replace(/\/+$/, "");
|
|
8249
9193
|
const prompt = "Reply with the single word: OK.";
|
|
@@ -8267,6 +9211,7 @@ async function handleTestModel(req, res, id, cfg) {
|
|
|
8267
9211
|
};
|
|
8268
9212
|
}
|
|
8269
9213
|
Object.assign(headers, expandRowExtraHeaders(row));
|
|
9214
|
+
applyAdminProbeIdentity(headers, row);
|
|
8270
9215
|
const startedAt = Date.now();
|
|
8271
9216
|
try {
|
|
8272
9217
|
const response = await fetchUpstream10(
|
|
@@ -8283,9 +9228,9 @@ async function handleTestModel(req, res, id, cfg) {
|
|
|
8283
9228
|
message = parsed?.error?.message || parsed?.message || message;
|
|
8284
9229
|
} catch {
|
|
8285
9230
|
}
|
|
8286
|
-
return
|
|
9231
|
+
return writeJson5(res, 200, { ok: false, status: response.status, latencyMs, message });
|
|
8287
9232
|
}
|
|
8288
|
-
return
|
|
9233
|
+
return writeJson5(res, 200, {
|
|
8289
9234
|
ok: true,
|
|
8290
9235
|
status: response.status,
|
|
8291
9236
|
latencyMs,
|
|
@@ -8293,7 +9238,7 @@ async function handleTestModel(req, res, id, cfg) {
|
|
|
8293
9238
|
});
|
|
8294
9239
|
} catch (err9) {
|
|
8295
9240
|
const message = err9 instanceof Error ? err9.message : String(err9);
|
|
8296
|
-
return
|
|
9241
|
+
return writeJson5(res, 200, { ok: false, latencyMs: Date.now() - startedAt, message });
|
|
8297
9242
|
}
|
|
8298
9243
|
}
|
|
8299
9244
|
function extractSampleText(text, apiFormat) {
|
|
@@ -8330,9 +9275,9 @@ function toPoolKeyView(row, cooldown, deps) {
|
|
|
8330
9275
|
});
|
|
8331
9276
|
}
|
|
8332
9277
|
async function handleProviderKeys(res, id, cfg, deps) {
|
|
8333
|
-
if (!id) return
|
|
9278
|
+
if (!id) return writeJsonError2(res, 400, "provider id required in path");
|
|
8334
9279
|
const row = cfg.providers.find((p) => p.id === id);
|
|
8335
|
-
if (!row) return
|
|
9280
|
+
if (!row) return writeJsonError2(res, 404, `provider '${id}' not found`);
|
|
8336
9281
|
const cooldown = await deps.apiKeyPool.getKeyHealth(id);
|
|
8337
9282
|
const views = toPoolKeyView(row, cooldown, deps);
|
|
8338
9283
|
if (deps.providerKeyQuota) {
|
|
@@ -8344,19 +9289,19 @@ async function handleProviderKeys(res, id, cfg, deps) {
|
|
|
8344
9289
|
if (settled.status === "fulfilled" && settled.value) view.quota = settled.value;
|
|
8345
9290
|
});
|
|
8346
9291
|
}
|
|
8347
|
-
return
|
|
9292
|
+
return writeJson5(res, 200, { keys: views });
|
|
8348
9293
|
}
|
|
8349
9294
|
async function handleProviderKeyQuotaRefresh(res, id, keyId, cfg, deps) {
|
|
8350
|
-
if (!deps.providerKeyQuota) return
|
|
8351
|
-
if (!id || !keyId) return
|
|
9295
|
+
if (!deps.providerKeyQuota) return writeJsonError2(res, 501, "provider key quota is not available");
|
|
9296
|
+
if (!id || !keyId) return writeJsonError2(res, 400, "provider id and key id required in path");
|
|
8352
9297
|
const row = cfg.providers.find((p) => p.id === id);
|
|
8353
|
-
if (!row) return
|
|
9298
|
+
if (!row) return writeJsonError2(res, 404, `provider '${id}' not found`);
|
|
8354
9299
|
try {
|
|
8355
9300
|
const quota = await deps.providerKeyQuota.quotaFor(row, keyId, { force: true });
|
|
8356
|
-
if (!quota) return
|
|
8357
|
-
return
|
|
9301
|
+
if (!quota) return writeJsonError2(res, 404, `no quota endpoint for key '${keyId}'`);
|
|
9302
|
+
return writeJson5(res, 200, { quota });
|
|
8358
9303
|
} catch {
|
|
8359
|
-
return
|
|
9304
|
+
return writeJsonError2(res, 502, "quota refresh failed");
|
|
8360
9305
|
}
|
|
8361
9306
|
}
|
|
8362
9307
|
function parsePoolKeyInput(body, existing) {
|
|
@@ -8373,12 +9318,12 @@ function parsePoolKeyInput(body, existing) {
|
|
|
8373
9318
|
return out;
|
|
8374
9319
|
}
|
|
8375
9320
|
async function handleAddProviderKey(req, res, id, cfg, deps) {
|
|
8376
|
-
if (!id) return
|
|
9321
|
+
if (!id) return writeJsonError2(res, 400, "provider id required in path");
|
|
8377
9322
|
const idx = cfg.providers.findIndex((p) => p.id === id);
|
|
8378
|
-
if (idx < 0) return
|
|
8379
|
-
const body = await
|
|
9323
|
+
if (idx < 0) return writeJsonError2(res, 404, `provider '${id}' not found`);
|
|
9324
|
+
const body = await readJsonBody5(req);
|
|
8380
9325
|
const parsed = parsePoolKeyInput(body);
|
|
8381
|
-
if (!parsed.apiKey) return
|
|
9326
|
+
if (!parsed.apiKey) return writeJsonError2(res, 400, "add requires a non-empty apiKey");
|
|
8382
9327
|
const keyId = `key-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
8383
9328
|
const entry = { id: keyId, apiKey: parsed.apiKey };
|
|
8384
9329
|
if (parsed.label !== void 0) entry.label = parsed.label;
|
|
@@ -8388,17 +9333,17 @@ async function handleAddProviderKey(req, res, id, cfg, deps) {
|
|
|
8388
9333
|
row.apiKeys = [...row.apiKeys ?? [], entry];
|
|
8389
9334
|
persistProviders(cfg, deps);
|
|
8390
9335
|
const cooldown = await deps.apiKeyPool.getKeyHealth(id);
|
|
8391
|
-
return
|
|
9336
|
+
return writeJson5(res, 201, { keys: toPoolKeyView(row, cooldown, deps) });
|
|
8392
9337
|
}
|
|
8393
9338
|
async function handleUpdateProviderKey(req, res, id, keyId, cfg, deps) {
|
|
8394
|
-
if (!id) return
|
|
8395
|
-
if (!keyId) return
|
|
9339
|
+
if (!id) return writeJsonError2(res, 400, "provider id required in path");
|
|
9340
|
+
if (!keyId) return writeJsonError2(res, 400, "key id required in path");
|
|
8396
9341
|
const idx = cfg.providers.findIndex((p) => p.id === id);
|
|
8397
|
-
if (idx < 0) return
|
|
9342
|
+
if (idx < 0) return writeJsonError2(res, 404, `provider '${id}' not found`);
|
|
8398
9343
|
const row = cfg.providers[idx];
|
|
8399
9344
|
const keyIdx = (row.apiKeys ?? []).findIndex((k) => k.id === keyId);
|
|
8400
|
-
if (keyIdx < 0) return
|
|
8401
|
-
const body = await
|
|
9345
|
+
if (keyIdx < 0) return writeJsonError2(res, 404, `pool key '${keyId}' not found`);
|
|
9346
|
+
const body = await readJsonBody5(req);
|
|
8402
9347
|
const existing = row.apiKeys[keyIdx];
|
|
8403
9348
|
const parsed = parsePoolKeyInput(body, existing);
|
|
8404
9349
|
const entry = { id: keyId, apiKey: parsed.apiKey || existing.apiKey };
|
|
@@ -8408,35 +9353,35 @@ async function handleUpdateProviderKey(req, res, id, keyId, cfg, deps) {
|
|
|
8408
9353
|
row.apiKeys[keyIdx] = entry;
|
|
8409
9354
|
persistProviders(cfg, deps);
|
|
8410
9355
|
const cooldown = await deps.apiKeyPool.getKeyHealth(id);
|
|
8411
|
-
return
|
|
9356
|
+
return writeJson5(res, 200, { keys: toPoolKeyView(row, cooldown, deps) });
|
|
8412
9357
|
}
|
|
8413
9358
|
async function handleDeleteProviderKey(res, id, keyId, cfg, deps) {
|
|
8414
|
-
if (!id) return
|
|
8415
|
-
if (!keyId) return
|
|
9359
|
+
if (!id) return writeJsonError2(res, 400, "provider id required in path");
|
|
9360
|
+
if (!keyId) return writeJsonError2(res, 400, "key id required in path");
|
|
8416
9361
|
const idx = cfg.providers.findIndex((p) => p.id === id);
|
|
8417
|
-
if (idx < 0) return
|
|
9362
|
+
if (idx < 0) return writeJsonError2(res, 404, `provider '${id}' not found`);
|
|
8418
9363
|
const row = cfg.providers[idx];
|
|
8419
9364
|
const keyIdx = (row.apiKeys ?? []).findIndex((k) => k.id === keyId);
|
|
8420
|
-
if (keyIdx < 0) return
|
|
9365
|
+
if (keyIdx < 0) return writeJsonError2(res, 404, `pool key '${keyId}' not found`);
|
|
8421
9366
|
row.apiKeys.splice(keyIdx, 1);
|
|
8422
9367
|
if (row.apiKeys.length === 0) row.apiKeys = void 0;
|
|
8423
9368
|
persistProviders(cfg, deps);
|
|
8424
9369
|
const cooldown = await deps.apiKeyPool.getKeyHealth(id);
|
|
8425
|
-
return
|
|
9370
|
+
return writeJson5(res, 200, { keys: toPoolKeyView(row, cooldown, deps) });
|
|
8426
9371
|
}
|
|
8427
9372
|
async function handleToggleProviderKey(req, res, id, keyId, cfg, deps) {
|
|
8428
|
-
if (!id) return
|
|
8429
|
-
if (!keyId) return
|
|
9373
|
+
if (!id) return writeJsonError2(res, 400, "provider id required in path");
|
|
9374
|
+
if (!keyId) return writeJsonError2(res, 400, "key id required in path");
|
|
8430
9375
|
const idx = cfg.providers.findIndex((p) => p.id === id);
|
|
8431
|
-
if (idx < 0) return
|
|
9376
|
+
if (idx < 0) return writeJsonError2(res, 404, `provider '${id}' not found`);
|
|
8432
9377
|
const row = cfg.providers[idx];
|
|
8433
9378
|
const keyIdx = (row.apiKeys ?? []).findIndex((k) => k.id === keyId);
|
|
8434
|
-
if (keyIdx < 0) return
|
|
8435
|
-
const body = await
|
|
9379
|
+
if (keyIdx < 0) return writeJsonError2(res, 404, `pool key '${keyId}' not found`);
|
|
9380
|
+
const body = await readJsonBody5(req);
|
|
8436
9381
|
row.apiKeys[keyIdx].enabled = Boolean(body["enabled"]);
|
|
8437
9382
|
persistProviders(cfg, deps);
|
|
8438
9383
|
const cooldown = await deps.apiKeyPool.getKeyHealth(id);
|
|
8439
|
-
return
|
|
9384
|
+
return writeJson5(res, 200, { keys: toPoolKeyView(row, cooldown, deps) });
|
|
8440
9385
|
}
|
|
8441
9386
|
function parseApiKeysInput(raw, existing) {
|
|
8442
9387
|
if (!Array.isArray(raw)) return existing;
|
|
@@ -8608,7 +9553,7 @@ function parseProviderInput(body, existing) {
|
|
|
8608
9553
|
};
|
|
8609
9554
|
}
|
|
8610
9555
|
function handlePresets(res, method) {
|
|
8611
|
-
if (method !== "GET") return
|
|
9556
|
+
if (method !== "GET") return writeJsonError2(res, 405, `method ${method} not allowed on presets`);
|
|
8612
9557
|
const { mappable, excluded } = listMappablePresets();
|
|
8613
9558
|
const presets = mappable.map((p) => ({
|
|
8614
9559
|
id: p.id,
|
|
@@ -8627,13 +9572,13 @@ function handlePresets(res, method) {
|
|
|
8627
9572
|
// row (the write gateway re-validates via the shared allowlist).
|
|
8628
9573
|
extraHeaders: p.extraHeaders
|
|
8629
9574
|
}));
|
|
8630
|
-
return
|
|
9575
|
+
return writeJson5(res, 200, { presets, excluded });
|
|
8631
9576
|
}
|
|
8632
9577
|
async function handleKeys(req, res, method, rest, deps) {
|
|
8633
9578
|
if (method === "GET" && rest.length === 0) {
|
|
8634
9579
|
const rows = await deps.keyDb.outboundApiKeysList();
|
|
8635
9580
|
const reader = deps.keySpendReader;
|
|
8636
|
-
if (!reader) return
|
|
9581
|
+
if (!reader) return writeJson5(res, 200, { keys: rows.map(toKeyInfo) });
|
|
8637
9582
|
const now = Date.now();
|
|
8638
9583
|
const keys = await Promise.all(
|
|
8639
9584
|
rows.map(async (row) => {
|
|
@@ -8645,13 +9590,13 @@ async function handleKeys(req, res, method, rest, deps) {
|
|
|
8645
9590
|
return info;
|
|
8646
9591
|
})
|
|
8647
9592
|
);
|
|
8648
|
-
return
|
|
9593
|
+
return writeJson5(res, 200, { keys });
|
|
8649
9594
|
}
|
|
8650
9595
|
if (method === "POST" && rest.length === 0) {
|
|
8651
|
-
const body = await
|
|
9596
|
+
const body = await readJsonBody5(req);
|
|
8652
9597
|
const name = typeof body["name"] === "string" && body["name"].trim() ? body["name"].trim() : "key";
|
|
8653
9598
|
const created = await createNamedKey(deps.keyDb, name);
|
|
8654
|
-
return
|
|
9599
|
+
return writeJson5(res, 201, {
|
|
8655
9600
|
id: created.id,
|
|
8656
9601
|
name: created.name,
|
|
8657
9602
|
keyPrefix: created.keyPrefix,
|
|
@@ -8662,10 +9607,10 @@ async function handleKeys(req, res, method, rest, deps) {
|
|
|
8662
9607
|
}
|
|
8663
9608
|
if (method === "GET" && rest.length === 2 && rest[1] === "reveal") {
|
|
8664
9609
|
const revealed = await deps.keyDb.outboundApiKeysReveal(rest[0]);
|
|
8665
|
-
if (revealed !== null) return
|
|
9610
|
+
if (revealed !== null) return writeJson5(res, 200, { key: revealed });
|
|
8666
9611
|
const exists = (await deps.keyDb.outboundApiKeysList()).some((r) => r.id === rest[0]);
|
|
8667
|
-
if (!exists) return
|
|
8668
|
-
return
|
|
9612
|
+
if (!exists) return writeJsonError2(res, 404, `key '${rest[0]}' not found`);
|
|
9613
|
+
return writeJsonError2(
|
|
8669
9614
|
res,
|
|
8670
9615
|
409,
|
|
8671
9616
|
`key '${rest[0]}' is not revealable (created before revealable key storage)`
|
|
@@ -8675,55 +9620,55 @@ async function handleKeys(req, res, method, rest, deps) {
|
|
|
8675
9620
|
const action = rest[1];
|
|
8676
9621
|
if (method === "POST" && id && action === "revoke") {
|
|
8677
9622
|
const bound = await integrationKeyRequirement(deps, id);
|
|
8678
|
-
if (bound) return
|
|
9623
|
+
if (bound) return writeJsonError2(res, 409, bound);
|
|
8679
9624
|
const ok = await deps.keyDb.outboundApiKeysRevoke(id);
|
|
8680
|
-
return
|
|
9625
|
+
return writeJson5(res, ok ? 200 : 404, { ok });
|
|
8681
9626
|
}
|
|
8682
9627
|
if (method === "DELETE" && id && !action) {
|
|
8683
9628
|
const bound = await integrationKeyRequirement(deps, id);
|
|
8684
|
-
if (bound) return
|
|
9629
|
+
if (bound) return writeJsonError2(res, 409, bound);
|
|
8685
9630
|
const ok = await deps.keyDb.outboundApiKeysDelete(id);
|
|
8686
|
-
return
|
|
9631
|
+
return writeJson5(res, ok ? 200 : 404, { ok });
|
|
8687
9632
|
}
|
|
8688
9633
|
if (method === "POST" && id && action === "enabled") {
|
|
8689
|
-
const body = await
|
|
9634
|
+
const body = await readJsonBody5(req);
|
|
8690
9635
|
const enabled = body["enabled"] === true;
|
|
8691
9636
|
if (!enabled) {
|
|
8692
9637
|
const bound = await integrationKeyRequirement(deps, id);
|
|
8693
|
-
if (bound) return
|
|
9638
|
+
if (bound) return writeJsonError2(res, 409, bound);
|
|
8694
9639
|
}
|
|
8695
9640
|
const ok = await deps.keyDb.outboundApiKeysSetEnabled(id, enabled);
|
|
8696
|
-
return
|
|
9641
|
+
return writeJson5(res, ok ? 200 : 404, { ok, enabled });
|
|
8697
9642
|
}
|
|
8698
9643
|
if (method === "POST" && id && action === "permissions") {
|
|
8699
|
-
const body = await
|
|
9644
|
+
const body = await readJsonBody5(req);
|
|
8700
9645
|
if (Object.keys(body).length !== 1 || !Object.prototype.hasOwnProperty.call(body, "permissions")) {
|
|
8701
|
-
return
|
|
9646
|
+
return writeJsonError2(res, 400, "body must contain only permissions");
|
|
8702
9647
|
}
|
|
8703
9648
|
let permissions;
|
|
8704
9649
|
try {
|
|
8705
9650
|
permissions = validateOutboundPermissions(body["permissions"]);
|
|
8706
9651
|
} catch {
|
|
8707
|
-
return
|
|
9652
|
+
return writeJsonError2(
|
|
8708
9653
|
res,
|
|
8709
9654
|
400,
|
|
8710
9655
|
"permissions must be an array of unique chat, responses, messages, gemini, or images values"
|
|
8711
9656
|
);
|
|
8712
9657
|
}
|
|
8713
9658
|
const before = (await deps.keyDb.outboundApiKeysList()).find((row) => row.id === id);
|
|
8714
|
-
if (!before) return
|
|
8715
|
-
if (before.revokedAt !== null) return
|
|
9659
|
+
if (!before) return writeJson5(res, 404, { ok: false });
|
|
9660
|
+
if (before.revokedAt !== null) return writeJson5(res, 409, { ok: false });
|
|
8716
9661
|
const required = await integrationKeyRequirement(deps, id, permissions);
|
|
8717
|
-
if (required) return
|
|
9662
|
+
if (required) return writeJsonError2(res, 409, required);
|
|
8718
9663
|
const ok = await deps.keyDb.outboundApiKeysSetPermissions(id, permissions);
|
|
8719
9664
|
if (!ok) {
|
|
8720
9665
|
const current = (await deps.keyDb.outboundApiKeysList()).find((row) => row.id === id);
|
|
8721
|
-
return
|
|
9666
|
+
return writeJson5(res, current?.revokedAt !== null ? 409 : 404, { ok: false });
|
|
8722
9667
|
}
|
|
8723
|
-
return
|
|
9668
|
+
return writeJson5(res, 200, { ok: true, allowedEndpoints: permissions });
|
|
8724
9669
|
}
|
|
8725
9670
|
if (method === "POST" && id && action === "max-concurrency") {
|
|
8726
|
-
const body = await
|
|
9671
|
+
const body = await readJsonBody5(req);
|
|
8727
9672
|
const raw = body["maxConcurrency"];
|
|
8728
9673
|
let value;
|
|
8729
9674
|
if (raw === null) {
|
|
@@ -8731,23 +9676,73 @@ async function handleKeys(req, res, method, rest, deps) {
|
|
|
8731
9676
|
} else if (typeof raw === "number" && Number.isInteger(raw) && raw >= 1 && raw <= 1e3) {
|
|
8732
9677
|
value = raw;
|
|
8733
9678
|
} else {
|
|
8734
|
-
return
|
|
9679
|
+
return writeJsonError2(
|
|
8735
9680
|
res,
|
|
8736
9681
|
400,
|
|
8737
9682
|
"maxConcurrency must be an integer 1..1000 or null"
|
|
8738
9683
|
);
|
|
8739
9684
|
}
|
|
8740
9685
|
const ok = await deps.keyDb.outboundApiKeysSetMaxConcurrency(id, value);
|
|
8741
|
-
return
|
|
9686
|
+
return writeJson5(res, ok ? 200 : 404, { ok, maxConcurrency: value });
|
|
9687
|
+
}
|
|
9688
|
+
if (method === "POST" && id && action === "upstream") {
|
|
9689
|
+
const body = await readJsonBody5(req);
|
|
9690
|
+
let raw;
|
|
9691
|
+
if (Object.prototype.hasOwnProperty.call(body, "target")) {
|
|
9692
|
+
raw = body["target"];
|
|
9693
|
+
} else if (Object.prototype.hasOwnProperty.call(body, "providerId")) {
|
|
9694
|
+
const legacy = body["providerId"];
|
|
9695
|
+
raw = legacy === null || legacy === void 0 ? null : { kind: "provider", providerId: legacy };
|
|
9696
|
+
} else {
|
|
9697
|
+
return writeJsonError2(res, 400, "body must contain target (or the legacy providerId)");
|
|
9698
|
+
}
|
|
9699
|
+
if (raw === null || raw === void 0) {
|
|
9700
|
+
const ok2 = await deps.keyDb.outboundApiKeysSetUpstream(id, null);
|
|
9701
|
+
return writeJson5(res, ok2 ? 200 : 404, { ok: ok2, target: null });
|
|
9702
|
+
}
|
|
9703
|
+
if (typeof raw !== "object" || Array.isArray(raw)) {
|
|
9704
|
+
return writeJsonError2(res, 400, "target must be an object or null");
|
|
9705
|
+
}
|
|
9706
|
+
const t = raw;
|
|
9707
|
+
const kind = t["kind"];
|
|
9708
|
+
const providerId = typeof t["providerId"] === "string" ? t["providerId"].trim() : "";
|
|
9709
|
+
if (!providerId) {
|
|
9710
|
+
return writeJsonError2(res, 400, "target.providerId is required");
|
|
9711
|
+
}
|
|
9712
|
+
if (kind === "provider") {
|
|
9713
|
+
const cfg = loadConfig(deps.configPath);
|
|
9714
|
+
if (!cfg.providers.some((p) => p.id === providerId)) {
|
|
9715
|
+
return writeJsonError2(res, 404, `provider '${providerId}' not found`);
|
|
9716
|
+
}
|
|
9717
|
+
} else if (kind === "account" || kind === "account-group" || kind === "account-pool") {
|
|
9718
|
+
if (providerId !== "claude" && providerId !== "kimi") {
|
|
9719
|
+
return writeJsonError2(
|
|
9720
|
+
res,
|
|
9721
|
+
400,
|
|
9722
|
+
`subscription provider '${providerId}' cannot be direct-bound: its upstream wire differs from the client wire (translation required \u2014 use a downstream route); only claude and kimi subscriptions speak the same Anthropic Messages wire as the client`
|
|
9723
|
+
);
|
|
9724
|
+
}
|
|
9725
|
+
if (kind === "account" && typeof t["accountId"] !== "string") {
|
|
9726
|
+
return writeJsonError2(res, 400, "target.accountId is required for kind account");
|
|
9727
|
+
}
|
|
9728
|
+
if (kind === "account-group" && typeof t["group"] !== "string") {
|
|
9729
|
+
return writeJsonError2(res, 400, "target.group is required for kind account-group");
|
|
9730
|
+
}
|
|
9731
|
+
} else {
|
|
9732
|
+
return writeJsonError2(res, 400, "target.kind must be provider, account, account-group, or account-pool");
|
|
9733
|
+
}
|
|
9734
|
+
const target = raw;
|
|
9735
|
+
const ok = await deps.keyDb.outboundApiKeysSetUpstream(id, target);
|
|
9736
|
+
return writeJson5(res, ok ? 200 : 404, { ok, target });
|
|
8742
9737
|
}
|
|
8743
9738
|
if (method === "POST" && id && action === "policy") {
|
|
8744
|
-
const body = await
|
|
9739
|
+
const body = await readJsonBody5(req);
|
|
8745
9740
|
const parsed = parseKeyPolicyBody(body);
|
|
8746
|
-
if (!parsed.ok) return
|
|
9741
|
+
if (!parsed.ok) return writeJsonError2(res, 400, parsed.message);
|
|
8747
9742
|
const ok = await deps.keyDb.outboundApiKeysSetPolicy(id, parsed.policy);
|
|
8748
|
-
return
|
|
9743
|
+
return writeJson5(res, ok ? 200 : 404, { ok });
|
|
8749
9744
|
}
|
|
8750
|
-
return
|
|
9745
|
+
return writeJsonError2(res, 405, `method ${method} not allowed on keys`);
|
|
8751
9746
|
}
|
|
8752
9747
|
function validateQueueSegments(patch) {
|
|
8753
9748
|
const errors = [];
|
|
@@ -8911,17 +9906,17 @@ async function handleServer(req, res, method, deps) {
|
|
|
8911
9906
|
search: redactSearchServerConfig(config.search)
|
|
8912
9907
|
};
|
|
8913
9908
|
}
|
|
8914
|
-
return
|
|
9909
|
+
return writeJson5(res, 200, { server: projectImagesConfigForAdmin(server) });
|
|
8915
9910
|
}
|
|
8916
9911
|
if (method === "PUT") {
|
|
8917
|
-
const patch = await
|
|
9912
|
+
const patch = await readJsonBody5(req);
|
|
8918
9913
|
const queueErrors = validateQueueSegments(patch);
|
|
8919
9914
|
if (queueErrors.length > 0) {
|
|
8920
|
-
return
|
|
9915
|
+
return writeJsonError2(res, 400, `invalid queue config: ${queueErrors.join("; ")}`);
|
|
8921
9916
|
}
|
|
8922
9917
|
const allowanceErrors = validateAllowanceSchedulingSegment(patch);
|
|
8923
9918
|
if (allowanceErrors.length > 0) {
|
|
8924
|
-
return
|
|
9919
|
+
return writeJsonError2(
|
|
8925
9920
|
res,
|
|
8926
9921
|
400,
|
|
8927
9922
|
`invalid allowance scheduling config: ${allowanceErrors.join("; ")}`
|
|
@@ -8929,19 +9924,19 @@ async function handleServer(req, res, method, deps) {
|
|
|
8929
9924
|
}
|
|
8930
9925
|
const bindingErrors = validateGatewayBindingsSegment(patch);
|
|
8931
9926
|
if (bindingErrors.length > 0) {
|
|
8932
|
-
return
|
|
9927
|
+
return writeJsonError2(res, 400, `invalid gateway bindings: ${bindingErrors.join("; ")}`);
|
|
8933
9928
|
}
|
|
8934
9929
|
const webhookErrors = validateWebhookSegment(patch);
|
|
8935
9930
|
if (webhookErrors.length > 0) {
|
|
8936
|
-
return
|
|
9931
|
+
return writeJsonError2(res, 400, `invalid webhook config: ${webhookErrors.join("; ")}`);
|
|
8937
9932
|
}
|
|
8938
9933
|
const auditErrors = validateAuditSegment(patch);
|
|
8939
9934
|
if (auditErrors.length > 0) {
|
|
8940
|
-
return
|
|
9935
|
+
return writeJsonError2(res, 400, `invalid audit config: ${auditErrors.join("; ")}`);
|
|
8941
9936
|
}
|
|
8942
9937
|
const billingErrors = validateBillingSegment(patch);
|
|
8943
9938
|
if (billingErrors.length > 0) {
|
|
8944
|
-
return
|
|
9939
|
+
return writeJsonError2(res, 400, `invalid billing config: ${billingErrors.join("; ")}`);
|
|
8945
9940
|
}
|
|
8946
9941
|
const current = await loadServerConfig3(deps.settingsStore);
|
|
8947
9942
|
let effectivePatch = patch;
|
|
@@ -8960,7 +9955,7 @@ async function handleServer(req, res, method, deps) {
|
|
|
8960
9955
|
remoteResolverAvailable: deps.imageRemoteResolverAvailable
|
|
8961
9956
|
});
|
|
8962
9957
|
if (imageErrors.length > 0) {
|
|
8963
|
-
return
|
|
9958
|
+
return writeJsonError2(res, 400, `invalid Images config: ${imageErrors.join("; ")}`);
|
|
8964
9959
|
}
|
|
8965
9960
|
effectivePatch = { ...effectivePatch, images };
|
|
8966
9961
|
}
|
|
@@ -8971,7 +9966,7 @@ async function handleServer(req, res, method, deps) {
|
|
|
8971
9966
|
);
|
|
8972
9967
|
const searchErrors = validateSearchServerConfig(searchPatch);
|
|
8973
9968
|
if (searchErrors.length > 0) {
|
|
8974
|
-
return
|
|
9969
|
+
return writeJsonError2(res, 400, `invalid search config: ${searchErrors.join("; ")}`);
|
|
8975
9970
|
}
|
|
8976
9971
|
effectivePatch = {
|
|
8977
9972
|
...effectivePatch,
|
|
@@ -9013,7 +10008,7 @@ async function handleServer(req, res, method, deps) {
|
|
|
9013
10008
|
});
|
|
9014
10009
|
} catch (error) {
|
|
9015
10010
|
if (error instanceof ServerConfigTransactionError) {
|
|
9016
|
-
return
|
|
10011
|
+
return writeJsonError2(res, 500, error.message);
|
|
9017
10012
|
}
|
|
9018
10013
|
throw error;
|
|
9019
10014
|
}
|
|
@@ -9034,24 +10029,27 @@ async function handleServer(req, res, method, deps) {
|
|
|
9034
10029
|
...merged,
|
|
9035
10030
|
search: redactSearchServerConfig(merged.search)
|
|
9036
10031
|
} : merged;
|
|
9037
|
-
return
|
|
10032
|
+
return writeJson5(res, 200, { server: projectImagesConfigForAdmin(mergedForAdmin) });
|
|
9038
10033
|
}
|
|
9039
|
-
return
|
|
10034
|
+
return writeJsonError2(res, 405, `method ${method} not allowed on server`);
|
|
9040
10035
|
}
|
|
9041
10036
|
async function handleAccounts(req, res, method, rest, deps) {
|
|
9042
10037
|
if (rest[0] === "route-activity" && rest.length === 1) {
|
|
9043
10038
|
if (method !== "GET") {
|
|
9044
|
-
return
|
|
10039
|
+
return writeJsonError2(res, 405, `method ${method} not allowed on account route activity`);
|
|
9045
10040
|
}
|
|
9046
10041
|
const query2 = requestQuery(req);
|
|
9047
10042
|
const parsedLimit = Number(query2.get("limit") ?? "100");
|
|
10043
|
+
const kindParam = query2.get("credentialKind");
|
|
10044
|
+
const credentialKind = kindParam === "subscription-account" || kindParam === "provider-key" ? kindParam : void 0;
|
|
9048
10045
|
const records = getSharedAccountRouteActivity().list({
|
|
9049
10046
|
providerId: query2.get("providerId") ?? void 0,
|
|
9050
10047
|
accountId: query2.get("accountId") ?? void 0,
|
|
9051
10048
|
sessionKey: query2.get("sessionKey") ?? void 0,
|
|
10049
|
+
credentialKind,
|
|
9052
10050
|
limit: Number.isFinite(parsedLimit) ? parsedLimit : 100
|
|
9053
10051
|
});
|
|
9054
|
-
return
|
|
10052
|
+
return writeJson5(res, 200, {
|
|
9055
10053
|
available: true,
|
|
9056
10054
|
records,
|
|
9057
10055
|
capacity: ACCOUNT_ROUTE_ACTIVITY_LIMIT,
|
|
@@ -9060,14 +10058,14 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
9060
10058
|
}
|
|
9061
10059
|
if (rest[0] === "overload-counters" && rest.length === 1) {
|
|
9062
10060
|
if (method !== "GET") {
|
|
9063
|
-
return
|
|
10061
|
+
return writeJsonError2(res, 405, `method ${method} not allowed on overload counters`);
|
|
9064
10062
|
}
|
|
9065
10063
|
const query2 = requestQuery(req);
|
|
9066
10064
|
const entries = getSharedOverloadCounter().list({
|
|
9067
10065
|
providerId: query2.get("providerId") ?? void 0,
|
|
9068
10066
|
accountId: query2.get("accountId") ?? void 0
|
|
9069
10067
|
});
|
|
9070
|
-
return
|
|
10068
|
+
return writeJson5(res, 200, {
|
|
9071
10069
|
available: true,
|
|
9072
10070
|
entries,
|
|
9073
10071
|
collectedAt: Date.now()
|
|
@@ -9086,21 +10084,21 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
9086
10084
|
const result = await handleAntigravityModelsRoute({
|
|
9087
10085
|
resolveAntigravityAccessToken: deps.resolveAntigravityAccessToken ?? (async () => null)
|
|
9088
10086
|
});
|
|
9089
|
-
return
|
|
10087
|
+
return writeJson5(res, result.status, result.body);
|
|
9090
10088
|
}
|
|
9091
10089
|
if (method === "GET" && rest.length === 0) {
|
|
9092
10090
|
const accounts = await deps.subscriptionAccounts.listAll();
|
|
9093
10091
|
const providerAccounts = await deps.subscriptionTokenWriter.listSanitizedAccounts();
|
|
9094
10092
|
const externalCli = await deps.subscriptionTokenWriter.listExternalCliAvailability();
|
|
9095
|
-
return
|
|
10093
|
+
return writeJson5(res, 200, { accounts, providerAccounts, externalCli });
|
|
9096
10094
|
}
|
|
9097
10095
|
if (method === "POST" && rest[0] === "batch" && rest.length === 1) {
|
|
9098
|
-
const body = await
|
|
10096
|
+
const body = await readJsonBody5(req);
|
|
9099
10097
|
const parsed = validateAccountBatchBody(body);
|
|
9100
|
-
if (!parsed) return
|
|
10098
|
+
if (!parsed) return writeJsonError2(res, 400, "invalid account batch request");
|
|
9101
10099
|
const result = await deps.subscriptionTokenWriter.batchManageAccounts(parsed.refs, parsed.mutation);
|
|
9102
10100
|
if (!result.ok) {
|
|
9103
|
-
return
|
|
10101
|
+
return writeJsonError2(
|
|
9104
10102
|
res,
|
|
9105
10103
|
404,
|
|
9106
10104
|
`account '${result.missing.accountId}' not found for provider '${result.missing.providerId}'`
|
|
@@ -9111,23 +10109,23 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
9111
10109
|
deps.accountAllowanceService?.removeAccountSnapshot?.(ref.providerId, ref.accountId);
|
|
9112
10110
|
}
|
|
9113
10111
|
}
|
|
9114
|
-
return
|
|
10112
|
+
return writeJson5(res, 200, { ok: true, affected: result.affected });
|
|
9115
10113
|
}
|
|
9116
10114
|
if (method === "GET" && (rest[0] === "codex" || rest[0] === "kimi" || rest[0] === "grok" || rest[0] === "copilot" || rest[0] === "antigravity") && rest[1] === "oauth" && rest[3] === "status") {
|
|
9117
10115
|
const result = rest[0] === "codex" ? handleCodexOAuthStatus(rest[2], deps) : rest[0] === "kimi" ? handleKimiOAuthStatus(rest[2], deps) : rest[0] === "grok" ? handleGrokOAuthStatus(rest[2], deps) : rest[0] === "copilot" ? handleCopilotOAuthStatus(rest[2], deps) : handleAntigravityOAuthStatus(rest[2], deps);
|
|
9118
|
-
return
|
|
10116
|
+
return writeJson5(res, result.status, result.body);
|
|
9119
10117
|
}
|
|
9120
10118
|
if (method === "DELETE" && (rest[0] === "codex" || rest[0] === "kimi" || rest[0] === "grok" || rest[0] === "copilot" || rest[0] === "antigravity") && rest[1] === "oauth" && rest[2]) {
|
|
9121
10119
|
const result = rest[0] === "codex" ? handleCodexOAuthCancel(rest[2], deps) : rest[0] === "kimi" ? handleKimiOAuthCancel(rest[2], deps) : rest[0] === "grok" ? handleGrokOAuthCancel(rest[2], deps) : rest[0] === "copilot" ? handleCopilotOAuthCancel(rest[2], deps) : handleAntigravityOAuthCancel(rest[2], deps);
|
|
9122
|
-
return
|
|
10120
|
+
return writeJson5(res, result.status, result.body);
|
|
9123
10121
|
}
|
|
9124
10122
|
if (method === "GET" && rest.length === 3 && rest[2] === "diagnostics") {
|
|
9125
10123
|
const providerId = asSubscriptionProviderId(rest[0]);
|
|
9126
|
-
if (!providerId) return
|
|
10124
|
+
if (!providerId) return writeJsonError2(res, 400, `unknown subscription provider '${rest[0] ?? ""}'`);
|
|
9127
10125
|
const accountId = rest[1];
|
|
9128
10126
|
const listed = await deps.subscriptionTokenWriter.listSanitizedAccounts();
|
|
9129
10127
|
if (!(listed[providerId] ?? []).some((account) => account.id === accountId)) {
|
|
9130
|
-
return
|
|
10128
|
+
return writeJsonError2(res, 404, `account '${accountId}' not found`);
|
|
9131
10129
|
}
|
|
9132
10130
|
const health2 = getSharedAccountHealth().getDiagnostics({ providerId, accountId });
|
|
9133
10131
|
const allowance = deps.accountAllowanceService?.getSchedulingStatus?.()?.history.filter((entry) => entry.providerId === providerId && entry.accountId === accountId).map((entry) => ({
|
|
@@ -9141,88 +10139,88 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
9141
10139
|
resumeAt: entry.resumeAt
|
|
9142
10140
|
})) ?? [];
|
|
9143
10141
|
const diagnostics = [...health2, ...allowance].sort((left, right) => right.at - left.at).slice(0, 200);
|
|
9144
|
-
return
|
|
10142
|
+
return writeJson5(res, 200, { diagnostics });
|
|
9145
10143
|
}
|
|
9146
10144
|
if (method === "GET" && rest.length === 3 && rest[2] === "events") {
|
|
9147
10145
|
const providerId = asSubscriptionProviderId(rest[0]);
|
|
9148
|
-
if (!providerId) return
|
|
10146
|
+
if (!providerId) return writeJsonError2(res, 400, `unknown subscription provider '${rest[0] ?? ""}'`);
|
|
9149
10147
|
const accountId = rest[1];
|
|
9150
10148
|
const listed = await deps.subscriptionTokenWriter.listSanitizedAccounts();
|
|
9151
10149
|
if (!(listed[providerId] ?? []).some((account) => account.id === accountId)) {
|
|
9152
|
-
return
|
|
10150
|
+
return writeJsonError2(res, 404, `account '${accountId}' not found`);
|
|
9153
10151
|
}
|
|
9154
10152
|
const snapshot = deps.accountProbeService?.getAllHistory().find((entry) => entry.providerId === providerId && entry.accountId === accountId);
|
|
9155
10153
|
const diagnostics = getSharedAccountHealth().getDiagnostics({ providerId, accountId });
|
|
9156
|
-
return
|
|
10154
|
+
return writeJson5(res, 200, { events: snapshot?.records ?? [], diagnostics });
|
|
9157
10155
|
}
|
|
9158
10156
|
if (method === "PATCH" && rest.length === 2) {
|
|
9159
10157
|
const providerId = asSubscriptionProviderId(rest[0]);
|
|
9160
|
-
if (!providerId) return
|
|
9161
|
-
const body = await
|
|
10158
|
+
if (!providerId) return writeJsonError2(res, 400, `unknown subscription provider '${rest[0] ?? ""}'`);
|
|
10159
|
+
const body = await readJsonBody5(req);
|
|
9162
10160
|
const patch = validateAccountMetadataPatch(body);
|
|
9163
|
-
if (!patch) return
|
|
10161
|
+
if (!patch) return writeJsonError2(res, 400, "invalid account metadata patch");
|
|
9164
10162
|
const result = await deps.subscriptionTokenWriter.patchAccountMetadata(providerId, rest[1], patch);
|
|
9165
|
-
if (!result.ok) return
|
|
9166
|
-
return
|
|
10163
|
+
if (!result.ok) return writeJsonError2(res, 404, `account '${rest[1]}' not found`);
|
|
10164
|
+
return writeJson5(res, 200, { ok: true });
|
|
9167
10165
|
}
|
|
9168
10166
|
if (method === "PUT" || method === "POST" || method === "DELETE") {
|
|
9169
10167
|
const providerId = asSubscriptionProviderId(rest[0]);
|
|
9170
10168
|
if (!providerId) {
|
|
9171
|
-
return
|
|
10169
|
+
return writeJsonError2(res, 400, `unknown subscription provider '${rest[0] ?? ""}'`);
|
|
9172
10170
|
}
|
|
9173
10171
|
if (method === "POST" && rest[1] === "oauth" && rest[2] === "start") {
|
|
9174
10172
|
if (providerId === "codex") {
|
|
9175
10173
|
const result2 = handleCodexOAuthStart(deps);
|
|
9176
|
-
return
|
|
10174
|
+
return writeJson5(res, result2.status, result2.body);
|
|
9177
10175
|
}
|
|
9178
10176
|
if (providerId === "kimi") {
|
|
9179
10177
|
const result2 = await handleKimiOAuthStart(deps);
|
|
9180
|
-
return
|
|
10178
|
+
return writeJson5(res, result2.status, result2.body);
|
|
9181
10179
|
}
|
|
9182
10180
|
if (providerId === "grok") {
|
|
9183
10181
|
const result2 = await handleGrokOAuthStart(deps);
|
|
9184
|
-
return
|
|
10182
|
+
return writeJson5(res, result2.status, result2.body);
|
|
9185
10183
|
}
|
|
9186
10184
|
if (providerId === "copilot") {
|
|
9187
|
-
const body2 = await
|
|
10185
|
+
const body2 = await readJsonBody5(req);
|
|
9188
10186
|
const result2 = await handleCopilotOAuthStart(deps, body2["enterpriseUrl"]);
|
|
9189
|
-
return
|
|
10187
|
+
return writeJson5(res, result2.status, result2.body);
|
|
9190
10188
|
}
|
|
9191
10189
|
if (providerId === "antigravity") {
|
|
9192
10190
|
const result2 = handleAntigravityOAuthStart(deps);
|
|
9193
|
-
return
|
|
10191
|
+
return writeJson5(res, result2.status, result2.body);
|
|
9194
10192
|
}
|
|
9195
10193
|
const result = handleOAuthStart(providerId, deps);
|
|
9196
|
-
return
|
|
10194
|
+
return writeJson5(res, result.status, result.body);
|
|
9197
10195
|
}
|
|
9198
10196
|
if (method === "POST" && rest[1] === "oauth" && rest[2] === "complete") {
|
|
9199
|
-
const body2 = await
|
|
10197
|
+
const body2 = await readJsonBody5(req);
|
|
9200
10198
|
const result = await handleOAuthComplete(providerId, body2, deps);
|
|
9201
|
-
return
|
|
10199
|
+
return writeJson5(res, result.status, result.body);
|
|
9202
10200
|
}
|
|
9203
10201
|
if (method === "POST" && rest[1] === "accounts") {
|
|
9204
|
-
const body2 = await
|
|
10202
|
+
const body2 = await readJsonBody5(req);
|
|
9205
10203
|
const block = validateTokenBody(providerId, body2);
|
|
9206
10204
|
if (!block) {
|
|
9207
|
-
return
|
|
10205
|
+
return writeJsonError2(res, 400, `malformed token body for provider '${providerId}'`);
|
|
9208
10206
|
}
|
|
9209
10207
|
const label = typeof body2["label"] === "string" && body2["label"].trim() ? body2["label"].trim() : void 0;
|
|
9210
10208
|
await deps.subscriptionAccountAppender.appendProviderAccount(providerId, block, label);
|
|
9211
10209
|
const status2 = await statusEntryFor(deps.subscriptionAccounts, providerId);
|
|
9212
|
-
return
|
|
10210
|
+
return writeJson5(res, 200, status2 ? { account: status2 } : { ok: true });
|
|
9213
10211
|
}
|
|
9214
10212
|
if (method === "POST" && rest[1] === "import-external") {
|
|
9215
10213
|
if (providerId !== "claude" && providerId !== "codex") {
|
|
9216
|
-
return
|
|
10214
|
+
return writeJsonError2(res, 400, `provider '${providerId}' has no external CLI store`);
|
|
9217
10215
|
}
|
|
9218
|
-
const body2 = await
|
|
10216
|
+
const body2 = await readJsonBody5(req);
|
|
9219
10217
|
const label = typeof body2["label"] === "string" && body2["label"].trim() ? body2["label"].trim() : void 0;
|
|
9220
10218
|
const result = await deps.subscriptionTokenWriter.importExternalCliAccount(providerId, label);
|
|
9221
10219
|
if (!result.ok) {
|
|
9222
|
-
return
|
|
10220
|
+
return writeJsonError2(res, 409, `no usable external ${providerId} CLI credential found`);
|
|
9223
10221
|
}
|
|
9224
10222
|
const status2 = await statusEntryFor(deps.subscriptionAccounts, providerId);
|
|
9225
|
-
return
|
|
10223
|
+
return writeJson5(res, 200, {
|
|
9226
10224
|
ok: true,
|
|
9227
10225
|
account: status2 ?? void 0,
|
|
9228
10226
|
nativeCredentialMode: result.nativeCredentialMode,
|
|
@@ -9232,22 +10230,22 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
9232
10230
|
}
|
|
9233
10231
|
if (method === "POST" && rest[1] === "refresh") {
|
|
9234
10232
|
if (providerId === "opencodego") {
|
|
9235
|
-
return
|
|
10233
|
+
return writeJsonError2(res, 400, "opencodego credentials are not refreshable");
|
|
9236
10234
|
}
|
|
9237
10235
|
const writer2 = deps.subscriptionTokenWriter;
|
|
9238
10236
|
const ok = providerId === "claude" ? await writer2.refreshClaudeToken() : providerId === "codex" ? await writer2.refreshCodexToken() : await writer2.refreshGeminiToken();
|
|
9239
10237
|
const status2 = await statusEntryFor(deps.subscriptionAccounts, providerId);
|
|
9240
|
-
return
|
|
10238
|
+
return writeJson5(res, 200, { ok, account: status2 ?? void 0 });
|
|
9241
10239
|
}
|
|
9242
10240
|
if (method === "POST" && rest.length === 3 && rest[2] === "test") {
|
|
9243
10241
|
const accountId = rest[1];
|
|
9244
|
-
if (!deps.accountProbeService) return
|
|
10242
|
+
if (!deps.accountProbeService) return writeJsonError2(res, 501, "account probe service unavailable");
|
|
9245
10243
|
const listed = await deps.subscriptionTokenWriter.listSanitizedAccounts();
|
|
9246
10244
|
if (!(listed[providerId] ?? []).some((account) => account.id === accountId)) {
|
|
9247
|
-
return
|
|
10245
|
+
return writeJsonError2(res, 404, `account '${accountId}' not found`);
|
|
9248
10246
|
}
|
|
9249
10247
|
const result = await deps.accountProbeService.testAccountConnection(providerId, accountId);
|
|
9250
|
-
return
|
|
10248
|
+
return writeJson5(res, 200, {
|
|
9251
10249
|
ok: result.ok,
|
|
9252
10250
|
marked: result.marked,
|
|
9253
10251
|
tier: result.tier,
|
|
@@ -9256,176 +10254,189 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
9256
10254
|
}
|
|
9257
10255
|
if (method === "POST" && rest[2] === "label") {
|
|
9258
10256
|
const accountId = rest[1];
|
|
9259
|
-
const body2 = await
|
|
10257
|
+
const body2 = await readJsonBody5(req);
|
|
9260
10258
|
const label = typeof body2["label"] === "string" ? body2["label"].trim() : "";
|
|
9261
10259
|
const result = await deps.subscriptionTokenWriter.renameAccount(providerId, accountId, label);
|
|
9262
|
-
if (!result.ok) return
|
|
9263
|
-
return
|
|
10260
|
+
if (!result.ok) return writeJsonError2(res, 404, `account '${accountId}' not found`);
|
|
10261
|
+
return writeJson5(res, 200, { ok: true });
|
|
9264
10262
|
}
|
|
9265
10263
|
if (method === "POST" && rest[2] === "priority") {
|
|
9266
10264
|
const accountId = rest[1];
|
|
9267
|
-
const body2 = await
|
|
10265
|
+
const body2 = await readJsonBody5(req);
|
|
9268
10266
|
const raw = body2["priority"];
|
|
9269
10267
|
const priority = typeof raw === "number" ? raw : Number(raw);
|
|
9270
10268
|
if (!Number.isFinite(priority)) {
|
|
9271
|
-
return
|
|
10269
|
+
return writeJsonError2(res, 400, "priority must be a finite number");
|
|
9272
10270
|
}
|
|
9273
10271
|
const result = await deps.subscriptionTokenWriter.setAccountPriority(providerId, accountId, priority);
|
|
9274
|
-
if (!result.ok) return
|
|
9275
|
-
return
|
|
10272
|
+
if (!result.ok) return writeJsonError2(res, 404, `account '${accountId}' not found`);
|
|
10273
|
+
return writeJson5(res, 200, { ok: true });
|
|
9276
10274
|
}
|
|
9277
10275
|
if (method === "POST" && rest[2] === "proxy") {
|
|
9278
10276
|
const accountId = rest[1];
|
|
9279
|
-
const body2 = await
|
|
10277
|
+
const body2 = await readJsonBody5(req);
|
|
9280
10278
|
const rawProxy = body2["proxy"];
|
|
9281
10279
|
let proxy;
|
|
9282
10280
|
if (rawProxy !== null && rawProxy !== void 0) {
|
|
9283
10281
|
proxy = normalizeProxyConfig(rawProxy);
|
|
9284
|
-
if (!proxy) return
|
|
10282
|
+
if (!proxy) return writeJsonError2(res, 400, "invalid proxy config");
|
|
9285
10283
|
}
|
|
9286
10284
|
const result = await deps.subscriptionTokenWriter.setAccountProxy(providerId, accountId, proxy);
|
|
9287
|
-
if (!result.ok) return
|
|
9288
|
-
return
|
|
10285
|
+
if (!result.ok) return writeJsonError2(res, 404, `account '${accountId}' not found`);
|
|
10286
|
+
return writeJson5(res, 200, { ok: true });
|
|
9289
10287
|
}
|
|
9290
10288
|
if (method === "POST" && rest[2] === "supported-models") {
|
|
9291
10289
|
const accountId = rest[1];
|
|
9292
|
-
const body2 = await
|
|
10290
|
+
const body2 = await readJsonBody5(req);
|
|
9293
10291
|
const parsed = validateSupportedModelsBody(body2["supportedModels"]);
|
|
9294
|
-
if (!parsed.ok) return
|
|
10292
|
+
if (!parsed.ok) return writeJsonError2(res, 400, "invalid supportedModels");
|
|
9295
10293
|
const result = await deps.subscriptionTokenWriter.setAccountSupportedModels(providerId, accountId, parsed.value);
|
|
9296
|
-
if (!result.ok) return
|
|
9297
|
-
return
|
|
10294
|
+
if (!result.ok) return writeJsonError2(res, 404, `account '${accountId}' not found`);
|
|
10295
|
+
return writeJson5(res, 200, { ok: true });
|
|
9298
10296
|
}
|
|
9299
10297
|
if (method === "PUT" && rest[1] === "active") {
|
|
9300
|
-
const body2 = await
|
|
10298
|
+
const body2 = await readJsonBody5(req);
|
|
9301
10299
|
const id = typeof body2["id"] === "string" ? body2["id"] : "";
|
|
9302
|
-
if (!id) return
|
|
10300
|
+
if (!id) return writeJsonError2(res, 400, "active switch requires { id }");
|
|
9303
10301
|
const result = await deps.subscriptionTokenWriter.setActiveAccount(providerId, id);
|
|
9304
|
-
if (!result.ok) return
|
|
9305
|
-
return
|
|
10302
|
+
if (!result.ok) return writeJsonError2(res, 404, `account '${id}' not found`);
|
|
10303
|
+
return writeJson5(res, 200, { ok: true });
|
|
9306
10304
|
}
|
|
9307
10305
|
if (method === "DELETE" && rest.length === 2) {
|
|
9308
10306
|
const accountId = rest[1];
|
|
9309
10307
|
const result = await deps.subscriptionTokenWriter.removeAccount(providerId, accountId);
|
|
9310
|
-
if (!result.removed) return
|
|
10308
|
+
if (!result.removed) return writeJsonError2(res, 404, `account '${accountId}' not found`);
|
|
9311
10309
|
deps.accountAllowanceService?.removeAccountSnapshot?.(providerId, accountId);
|
|
9312
|
-
return
|
|
10310
|
+
return writeJson5(res, 200, { ok: true });
|
|
9313
10311
|
}
|
|
9314
10312
|
if (method === "DELETE" && rest.length === 1) {
|
|
9315
10313
|
await deps.subscriptionTokenWriter.clearProvider(providerId);
|
|
9316
10314
|
deps.accountAllowanceService?.removeProviderSnapshots?.(providerId);
|
|
9317
|
-
return
|
|
10315
|
+
return writeJson5(res, 200, { ok: true });
|
|
9318
10316
|
}
|
|
9319
10317
|
if (method === "DELETE") {
|
|
9320
|
-
return
|
|
10318
|
+
return writeJsonError2(res, 405, "method DELETE not allowed on this accounts path");
|
|
9321
10319
|
}
|
|
9322
|
-
const body = await
|
|
10320
|
+
const body = await readJsonBody5(req);
|
|
9323
10321
|
const config = validateTokenBody(providerId, body);
|
|
9324
10322
|
if (!config) {
|
|
9325
|
-
return
|
|
10323
|
+
return writeJsonError2(res, 400, `malformed token body for provider '${providerId}'`);
|
|
9326
10324
|
}
|
|
9327
10325
|
await deps.subscriptionTokenWriter.writeProviderTokens(providerId, config);
|
|
9328
10326
|
const status = await statusEntryFor(deps.subscriptionAccounts, providerId);
|
|
9329
|
-
return
|
|
10327
|
+
return writeJson5(res, 200, status ? { account: status } : { ok: true });
|
|
9330
10328
|
}
|
|
9331
|
-
return
|
|
10329
|
+
return writeJsonError2(res, 405, `method ${method} not allowed on accounts`);
|
|
9332
10330
|
}
|
|
9333
10331
|
async function handleCli(req, res, method, rest, deps) {
|
|
9334
10332
|
if (method === "GET" && rest.length === 0) {
|
|
9335
10333
|
const result = handleCliList(process.platform, deps.cliPathProbe);
|
|
9336
|
-
return
|
|
10334
|
+
return writeJson5(res, result.status, result.body);
|
|
9337
10335
|
}
|
|
9338
10336
|
if (method === "GET" && rest[0] === "sessions") {
|
|
9339
10337
|
const result = handleCliSessions();
|
|
9340
|
-
return
|
|
10338
|
+
return writeJson5(res, result.status, result.body);
|
|
9341
10339
|
}
|
|
9342
10340
|
if (method === "DELETE" && rest[0] === "sessions" && rest[1]) {
|
|
9343
10341
|
const result = handleCliStop(rest[1]);
|
|
9344
|
-
return
|
|
10342
|
+
return writeJson5(res, result.status, result.body);
|
|
9345
10343
|
}
|
|
9346
10344
|
if (method === "POST" && rest[1] === "install") {
|
|
9347
10345
|
const cli = rest[0];
|
|
9348
10346
|
if (!isLaunchCliId(cli)) {
|
|
9349
|
-
return
|
|
10347
|
+
return writeJsonError2(res, 400, `unknown cli '${cli ?? ""}'`);
|
|
9350
10348
|
}
|
|
9351
10349
|
const result = await handleCliInstall(cli, deps.cliCommandRunner);
|
|
9352
|
-
return
|
|
10350
|
+
return writeJson5(res, result.status, result.body);
|
|
9353
10351
|
}
|
|
9354
10352
|
if (method === "POST" && rest[1] === "launch") {
|
|
9355
10353
|
const cli = rest[0];
|
|
9356
10354
|
if (!isLaunchCliId(cli)) {
|
|
9357
|
-
return
|
|
10355
|
+
return writeJsonError2(res, 400, `unknown cli '${cli ?? ""}'`);
|
|
9358
10356
|
}
|
|
9359
|
-
const body = await
|
|
10357
|
+
const body = await readJsonBody5(req);
|
|
9360
10358
|
const providers = loadConfig(deps.configPath).providers ?? [];
|
|
10359
|
+
const codexAuthHelper = deps.codexAuthHelper;
|
|
10360
|
+
const keyScoped = codexAuthHelper ? await (async () => {
|
|
10361
|
+
const gateway = deps.outboundApiServer.getStatus();
|
|
10362
|
+
const serverConfig = await loadServerConfig3(deps.settingsStore);
|
|
10363
|
+
return {
|
|
10364
|
+
keyDb: deps.keyDb,
|
|
10365
|
+
bindings: serverConfig.bindings ?? [],
|
|
10366
|
+
gatewayRunning: gateway.running,
|
|
10367
|
+
gatewayBaseUrl: gateway.loopbackUrl ?? `http://127.0.0.1:${gateway.port}`,
|
|
10368
|
+
codexAuthHelper
|
|
10369
|
+
};
|
|
10370
|
+
})() : void 0;
|
|
9361
10371
|
const result = await handleCliLaunch(cli, body, {
|
|
9362
10372
|
llmConfig: deps.llmConfig,
|
|
9363
10373
|
providers,
|
|
9364
10374
|
routeLeaseManager: deps.routeLeaseManager,
|
|
10375
|
+
keyScoped,
|
|
9365
10376
|
opener: deps.cliTerminalOpener,
|
|
9366
10377
|
probe: deps.cliPathProbe
|
|
9367
10378
|
});
|
|
9368
|
-
return
|
|
10379
|
+
return writeJson5(res, result.status, result.body);
|
|
9369
10380
|
}
|
|
9370
|
-
return
|
|
10381
|
+
return writeJsonError2(res, 405, `method ${method} not allowed on cli`);
|
|
9371
10382
|
}
|
|
9372
10383
|
async function handleIntegrations(req, res, method, rest, deps) {
|
|
9373
10384
|
const factory = deps.integrationManagerFactory;
|
|
9374
|
-
if (!factory) return
|
|
10385
|
+
if (!factory) return writeJsonError2(res, 501, "native CLI integration is not available");
|
|
9375
10386
|
const manager = factory();
|
|
9376
10387
|
try {
|
|
9377
10388
|
if (method === "GET" && rest.length === 0) {
|
|
9378
|
-
return
|
|
10389
|
+
return writeJson5(res, 200, {
|
|
9379
10390
|
integrations: await manager.listStatus(),
|
|
9380
10391
|
gateway: deps.outboundApiServer.getStatus()
|
|
9381
10392
|
});
|
|
9382
10393
|
}
|
|
9383
10394
|
if (method === "POST" && rest.length === 1 && rest[0] === "rotate") {
|
|
9384
10395
|
await manager.rotateGatewayKey();
|
|
9385
|
-
return
|
|
10396
|
+
return writeJson5(res, 200, { ok: true, integrations: await manager.listStatus() });
|
|
9386
10397
|
}
|
|
9387
10398
|
const client = rest[0];
|
|
9388
10399
|
if (!isIntegrationClient(client)) {
|
|
9389
|
-
return
|
|
10400
|
+
return writeJsonError2(res, 400, `unknown integration client '${client ?? ""}'`);
|
|
9390
10401
|
}
|
|
9391
10402
|
if (method === "POST" && rest[1] === "key") {
|
|
9392
|
-
const body = await
|
|
10403
|
+
const body = await readJsonBody5(req);
|
|
9393
10404
|
if (Object.keys(body).length !== 1 || typeof body.keyId !== "string" || !body.keyId.trim()) {
|
|
9394
|
-
return
|
|
10405
|
+
return writeJsonError2(res, 400, "body must contain a non-empty keyId string");
|
|
9395
10406
|
}
|
|
9396
10407
|
const status = await manager.bindIntegrationKey(client, body.keyId.trim());
|
|
9397
|
-
return
|
|
10408
|
+
return writeJson5(res, 200, { integration: status });
|
|
9398
10409
|
}
|
|
9399
10410
|
if (method === "POST" && rest[1] === "plan") {
|
|
9400
|
-
const body = await
|
|
10411
|
+
const body = await readJsonBody5(req);
|
|
9401
10412
|
const configPath = body.configPath;
|
|
9402
10413
|
if (configPath !== void 0 && typeof configPath !== "string") {
|
|
9403
|
-
return
|
|
10414
|
+
return writeJsonError2(res, 400, "configPath must be a string");
|
|
9404
10415
|
}
|
|
9405
10416
|
const plan = await manager.plan(client, configPath);
|
|
9406
|
-
return
|
|
10417
|
+
return writeJson5(res, 200, { plan });
|
|
9407
10418
|
}
|
|
9408
10419
|
if (method === "POST" && (rest[1] === "install" || rest[1] === "apply")) {
|
|
9409
|
-
const body = await
|
|
10420
|
+
const body = await readJsonBody5(req);
|
|
9410
10421
|
const configPath = body.configPath;
|
|
9411
10422
|
if (configPath !== void 0 && typeof configPath !== "string") {
|
|
9412
|
-
return
|
|
10423
|
+
return writeJsonError2(res, 400, "configPath must be a string");
|
|
9413
10424
|
}
|
|
9414
10425
|
const status = await manager.install(client, configPath);
|
|
9415
|
-
return
|
|
10426
|
+
return writeJson5(res, 200, { integration: status });
|
|
9416
10427
|
}
|
|
9417
10428
|
if (method === "POST" && rest[1] === "repair") {
|
|
9418
10429
|
const status = await manager.repair(client);
|
|
9419
|
-
return
|
|
10430
|
+
return writeJson5(res, 200, { integration: status });
|
|
9420
10431
|
}
|
|
9421
10432
|
if (method === "DELETE" && rest.length === 1 || method === "POST" && rest[1] === "remove") {
|
|
9422
10433
|
const status = await manager.remove(client);
|
|
9423
|
-
return
|
|
10434
|
+
return writeJson5(res, 200, { integration: status });
|
|
9424
10435
|
}
|
|
9425
|
-
return
|
|
10436
|
+
return writeJsonError2(res, 405, `method ${method} not allowed on integrations`);
|
|
9426
10437
|
} catch (error) {
|
|
9427
10438
|
if (error instanceof IntegrationConflictError) {
|
|
9428
|
-
return
|
|
10439
|
+
return writeJsonError2(res, 409, error.message);
|
|
9429
10440
|
}
|
|
9430
10441
|
throw error;
|
|
9431
10442
|
}
|
|
@@ -9568,7 +10579,7 @@ function imageProviderEvidence(images, capabilities) {
|
|
|
9568
10579
|
async function handleImagesVerifyLive(req, res, deps) {
|
|
9569
10580
|
const verifier = deps.imageLiveVerifier;
|
|
9570
10581
|
if (!verifier) {
|
|
9571
|
-
return
|
|
10582
|
+
return writeJsonError2(res, 501, "Images live verification is not available");
|
|
9572
10583
|
}
|
|
9573
10584
|
const serverConfig = await loadServerConfig3(deps.settingsStore);
|
|
9574
10585
|
const images = serverConfig.images ?? DEFAULT_IMAGES_SERVER_CONFIG;
|
|
@@ -9577,7 +10588,7 @@ async function handleImagesVerifyLive(req, res, deps) {
|
|
|
9577
10588
|
req.on("close", () => controller.abort());
|
|
9578
10589
|
const result = await verifier.verifyLive(images, controller.signal);
|
|
9579
10590
|
const antigravityRouted = Object.values(images.models).includes("antigravity-subscription");
|
|
9580
|
-
return
|
|
10591
|
+
return writeJson5(res, 200, {
|
|
9581
10592
|
...result,
|
|
9582
10593
|
...antigravityRouted ? { antigravityDeferred: true } : {}
|
|
9583
10594
|
});
|
|
@@ -9585,18 +10596,18 @@ async function handleImagesVerifyLive(req, res, deps) {
|
|
|
9585
10596
|
async function handleImages(req, res, method, rest, deps) {
|
|
9586
10597
|
if (rest.length === 1 && rest[0] === "verify-live") {
|
|
9587
10598
|
if (method !== "POST") {
|
|
9588
|
-
return
|
|
10599
|
+
return writeJsonError2(res, 405, `method ${method} not allowed on Images verify-live`);
|
|
9589
10600
|
}
|
|
9590
10601
|
return handleImagesVerifyLive(req, res, deps);
|
|
9591
10602
|
}
|
|
9592
10603
|
if (rest.length !== 1 || rest[0] !== "capabilities") {
|
|
9593
|
-
return
|
|
10604
|
+
return writeJsonError2(res, 404, "unknown Images admin resource");
|
|
9594
10605
|
}
|
|
9595
10606
|
if (method !== "GET") {
|
|
9596
|
-
return
|
|
10607
|
+
return writeJsonError2(res, 405, `method ${method} not allowed on Images capabilities`);
|
|
9597
10608
|
}
|
|
9598
10609
|
const reader = deps.imageRuntimeStatus;
|
|
9599
|
-
if (!reader) return
|
|
10610
|
+
if (!reader) return writeJsonError2(res, 501, "Images runtime status is not available");
|
|
9600
10611
|
const serverConfig = await loadServerConfig3(deps.settingsStore);
|
|
9601
10612
|
const images = serverConfig.images ?? DEFAULT_IMAGES_SERVER_CONFIG;
|
|
9602
10613
|
const lifecycle = reader.status();
|
|
@@ -9617,7 +10628,7 @@ async function handleImages(req, res, method, rest, deps) {
|
|
|
9617
10628
|
httpLeases: safeStatusCount(generation.httpLeases),
|
|
9618
10629
|
hostedLeases: safeStatusCount(generation.hostedLeases)
|
|
9619
10630
|
}));
|
|
9620
|
-
return
|
|
10631
|
+
return writeJson5(res, 200, {
|
|
9621
10632
|
configured: {
|
|
9622
10633
|
enabled: images.enabled,
|
|
9623
10634
|
provider: images.models[images.defaultModel] ?? "codex-subscription",
|
|
@@ -9644,7 +10655,7 @@ async function handleImages(req, res, method, rest, deps) {
|
|
|
9644
10655
|
});
|
|
9645
10656
|
}
|
|
9646
10657
|
async function handleStatus(res, method, deps) {
|
|
9647
|
-
if (method !== "GET") return
|
|
10658
|
+
if (method !== "GET") return writeJsonError2(res, 405, `method ${method} not allowed on status`);
|
|
9648
10659
|
const status = deps.outboundApiServer.getStatus();
|
|
9649
10660
|
const serverConfig = await loadServerConfig3(deps.settingsStore);
|
|
9650
10661
|
const endpoints = ["chat", "responses", "messages", "gemini"].map((endpoint) => {
|
|
@@ -9684,14 +10695,14 @@ async function handleStatus(res, method, deps) {
|
|
|
9684
10695
|
})() : void 0;
|
|
9685
10696
|
if (status.running) {
|
|
9686
10697
|
const queueStatus = deps.outboundApiServer.getQueueStatus();
|
|
9687
|
-
return
|
|
10698
|
+
return writeJson5(res, 200, {
|
|
9688
10699
|
...status,
|
|
9689
10700
|
endpoints,
|
|
9690
10701
|
queueStatus,
|
|
9691
10702
|
...imageRuntime ? { imageRuntime } : {}
|
|
9692
10703
|
});
|
|
9693
10704
|
}
|
|
9694
|
-
return
|
|
10705
|
+
return writeJson5(res, 200, {
|
|
9695
10706
|
...status,
|
|
9696
10707
|
endpoints,
|
|
9697
10708
|
...imageRuntime ? { imageRuntime } : {}
|
|
@@ -9714,23 +10725,23 @@ function resolvePlaygroundPath(endpoint, body) {
|
|
|
9714
10725
|
}
|
|
9715
10726
|
}
|
|
9716
10727
|
async function handlePlayground(req, res, method, deps) {
|
|
9717
|
-
if (method !== "POST") return
|
|
9718
|
-
const body = await
|
|
10728
|
+
if (method !== "POST") return writeJsonError2(res, 405, `method ${method} not allowed on playground`);
|
|
10729
|
+
const body = await readJsonBody5(req);
|
|
9719
10730
|
const endpoint = typeof body["endpoint"] === "string" ? body["endpoint"] : "";
|
|
9720
10731
|
const key = typeof body["key"] === "string" ? body["key"] : "";
|
|
9721
10732
|
const payload = body["body"];
|
|
9722
10733
|
const status = deps.outboundApiServer.getStatus();
|
|
9723
|
-
if (!status.running || !status.port) return
|
|
9724
|
-
const path2 = resolvePlaygroundPath(endpoint,
|
|
9725
|
-
if (!path2) return
|
|
10734
|
+
if (!status.running || !status.port) return writeJsonError2(res, 503, "outbound server not running");
|
|
10735
|
+
const path2 = resolvePlaygroundPath(endpoint, isRecord11(payload) ? payload : {});
|
|
10736
|
+
if (!path2) return writeJsonError2(res, 400, `unknown endpoint '${endpoint}'`);
|
|
9726
10737
|
const upstreamBody = typeof payload === "string" ? payload : JSON.stringify(payload ?? {});
|
|
9727
10738
|
await proxyToOutbound(res, status.port, path2, key, upstreamBody);
|
|
9728
10739
|
}
|
|
9729
|
-
function
|
|
10740
|
+
function isRecord11(v) {
|
|
9730
10741
|
return !!v && typeof v === "object" && !Array.isArray(v);
|
|
9731
10742
|
}
|
|
9732
10743
|
function proxyToOutbound(res, outboundPort, path2, key, body) {
|
|
9733
|
-
return new Promise((
|
|
10744
|
+
return new Promise((resolve11) => {
|
|
9734
10745
|
const upstream = http.request(
|
|
9735
10746
|
{
|
|
9736
10747
|
host: "127.0.0.1",
|
|
@@ -9751,14 +10762,14 @@ function proxyToOutbound(res, outboundPort, path2, key, body) {
|
|
|
9751
10762
|
proxRes.on("data", (chunk) => res.write(chunk));
|
|
9752
10763
|
proxRes.on("end", () => {
|
|
9753
10764
|
res.end();
|
|
9754
|
-
|
|
10765
|
+
resolve11();
|
|
9755
10766
|
});
|
|
9756
10767
|
}
|
|
9757
10768
|
);
|
|
9758
10769
|
upstream.on("error", (err9) => {
|
|
9759
|
-
if (!res.headersSent)
|
|
10770
|
+
if (!res.headersSent) writeJsonError2(res, 502, `playground proxy failed: ${err9.message}`);
|
|
9760
10771
|
else res.end();
|
|
9761
|
-
|
|
10772
|
+
resolve11();
|
|
9762
10773
|
});
|
|
9763
10774
|
upstream.write(body);
|
|
9764
10775
|
upstream.end();
|
|
@@ -9766,8 +10777,8 @@ function proxyToOutbound(res, outboundPort, path2, key, body) {
|
|
|
9766
10777
|
}
|
|
9767
10778
|
|
|
9768
10779
|
// src/admin/uiStatic.ts
|
|
9769
|
-
import { existsSync as
|
|
9770
|
-
import { readFile } from "fs/promises";
|
|
10780
|
+
import { existsSync as existsSync8, statSync as statSync3 } from "fs";
|
|
10781
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
9771
10782
|
import { createRequire } from "module";
|
|
9772
10783
|
import path from "path";
|
|
9773
10784
|
var CONTENT_TYPES = {
|
|
@@ -9789,13 +10800,13 @@ var CONTENT_TYPES = {
|
|
|
9789
10800
|
function resolveUiDist() {
|
|
9790
10801
|
const fromEnv = process.env["OMNICROSS_UI_DIST"];
|
|
9791
10802
|
if (fromEnv) {
|
|
9792
|
-
return
|
|
10803
|
+
return existsSync8(path.join(fromEnv, "index.html")) ? path.resolve(fromEnv) : null;
|
|
9793
10804
|
}
|
|
9794
10805
|
try {
|
|
9795
10806
|
const req = createRequire(typeof __filename !== "undefined" ? __filename : import.meta.url);
|
|
9796
10807
|
const pkgJson = req.resolve("@omnicross/ui/package.json");
|
|
9797
10808
|
const dist = path.join(path.dirname(pkgJson), "dist");
|
|
9798
|
-
return
|
|
10809
|
+
return existsSync8(path.join(dist, "index.html")) ? dist : null;
|
|
9799
10810
|
} catch {
|
|
9800
10811
|
return null;
|
|
9801
10812
|
}
|
|
@@ -9844,7 +10855,7 @@ async function handleUiStatic(req, res, urlPath, uiDist) {
|
|
|
9844
10855
|
return true;
|
|
9845
10856
|
}
|
|
9846
10857
|
let target = filePath;
|
|
9847
|
-
if (!
|
|
10858
|
+
if (!existsSync8(target) || statSync3(target).isDirectory()) {
|
|
9848
10859
|
if (path.extname(rel) === "") {
|
|
9849
10860
|
target = path.join(uiDist, "index.html");
|
|
9850
10861
|
} else {
|
|
@@ -9853,7 +10864,7 @@ async function handleUiStatic(req, res, urlPath, uiDist) {
|
|
|
9853
10864
|
return true;
|
|
9854
10865
|
}
|
|
9855
10866
|
}
|
|
9856
|
-
const body = await
|
|
10867
|
+
const body = await readFile2(target);
|
|
9857
10868
|
const type = CONTENT_TYPES[path.extname(target).toLowerCase()] ?? "application/octet-stream";
|
|
9858
10869
|
res.writeHead(200, { "Content-Type": type, "Content-Length": body.length });
|
|
9859
10870
|
res.end(req.method === "HEAD" ? void 0 : body);
|
|
@@ -9861,7 +10872,7 @@ async function handleUiStatic(req, res, urlPath, uiDist) {
|
|
|
9861
10872
|
}
|
|
9862
10873
|
|
|
9863
10874
|
// src/admin/version.ts
|
|
9864
|
-
var DAEMON_VERSION = true ? "0.4.
|
|
10875
|
+
var DAEMON_VERSION = true ? "0.4.5" : "0.0.0-dev";
|
|
9865
10876
|
|
|
9866
10877
|
// src/admin/AdminServer.ts
|
|
9867
10878
|
var LOOPBACK_ADDR = "127.0.0.1";
|
|
@@ -9900,14 +10911,14 @@ var AdminServer = class {
|
|
|
9900
10911
|
}
|
|
9901
10912
|
/** Bind once; on EADDRINUSE retry with an ephemeral port (port 0). */
|
|
9902
10913
|
listen(bindAddr, port) {
|
|
9903
|
-
return new Promise((
|
|
10914
|
+
return new Promise((resolve11, reject) => {
|
|
9904
10915
|
const server = http2.createServer((req, res) => {
|
|
9905
10916
|
this.onRequest(req, res);
|
|
9906
10917
|
});
|
|
9907
10918
|
const onError = (err9) => {
|
|
9908
10919
|
if (err9.code === "EADDRINUSE" && port !== 0) {
|
|
9909
10920
|
server.removeListener("error", onError);
|
|
9910
|
-
this.listen(bindAddr, 0).then(
|
|
10921
|
+
this.listen(bindAddr, 0).then(resolve11, reject);
|
|
9911
10922
|
return;
|
|
9912
10923
|
}
|
|
9913
10924
|
reject(err9);
|
|
@@ -9919,7 +10930,7 @@ var AdminServer = class {
|
|
|
9919
10930
|
server.removeListener("error", onError);
|
|
9920
10931
|
server.on("error", (e) => this.deps.logger.error("[AdminServer] server error", e));
|
|
9921
10932
|
this.server = server;
|
|
9922
|
-
|
|
10933
|
+
resolve11(addr.port);
|
|
9923
10934
|
} else {
|
|
9924
10935
|
reject(new Error("Failed to get admin server address"));
|
|
9925
10936
|
}
|
|
@@ -9993,6 +11004,10 @@ var AdminServer = class {
|
|
|
9993
11004
|
await handleRouteLeaseApi(req, res, path2, this.deps);
|
|
9994
11005
|
return;
|
|
9995
11006
|
}
|
|
11007
|
+
if (path2 === "/admin/api/codex-sessions" || path2 === "/admin/api/codex-sessions/preview" || path2 === "/admin/api/codex-sessions/apply") {
|
|
11008
|
+
await handleCodexSessionApi(req, res, path2, this.deps.codexSessionManager);
|
|
11009
|
+
return;
|
|
11010
|
+
}
|
|
9996
11011
|
if (path2.startsWith("/admin/api/")) {
|
|
9997
11012
|
await handleAdminApi(req, res, path2, this.deps);
|
|
9998
11013
|
return;
|
|
@@ -10016,8 +11031,8 @@ var AdminServer = class {
|
|
|
10016
11031
|
if (!server) return;
|
|
10017
11032
|
this.server = null;
|
|
10018
11033
|
this.boundPort = 0;
|
|
10019
|
-
return new Promise((
|
|
10020
|
-
server.close(() =>
|
|
11034
|
+
return new Promise((resolve11) => {
|
|
11035
|
+
server.close(() => resolve11());
|
|
10021
11036
|
});
|
|
10022
11037
|
}
|
|
10023
11038
|
/** A live status snapshot. */
|
|
@@ -10156,7 +11171,7 @@ function awaitLoopbackCode(expectedState, timeoutMs = DEFAULT_TIMEOUT_MS, signal
|
|
|
10156
11171
|
const port = binding.port ?? LOOPBACK_PORT;
|
|
10157
11172
|
const callbackPath = binding.path ?? CALLBACK_PATH;
|
|
10158
11173
|
const label = binding.label ?? "codex";
|
|
10159
|
-
return new Promise((
|
|
11174
|
+
return new Promise((resolve11, reject) => {
|
|
10160
11175
|
let settled = false;
|
|
10161
11176
|
const finish = (server2, fn) => {
|
|
10162
11177
|
if (settled) return;
|
|
@@ -10188,7 +11203,7 @@ function awaitLoopbackCode(expectedState, timeoutMs = DEFAULT_TIMEOUT_MS, signal
|
|
|
10188
11203
|
}
|
|
10189
11204
|
res.writeHead(200, HTML_HEADERS);
|
|
10190
11205
|
res.end(pageHtml("Login complete."));
|
|
10191
|
-
finish(server, () =>
|
|
11206
|
+
finish(server, () => resolve11(code));
|
|
10192
11207
|
});
|
|
10193
11208
|
const abort = () => finish(server, () => reject(new Error("login: cancelled")));
|
|
10194
11209
|
if (signal?.aborted) {
|
|
@@ -10317,7 +11332,7 @@ function secondsUntil9(instant, now) {
|
|
|
10317
11332
|
if (!instant) return void 0;
|
|
10318
11333
|
return Math.max(0, Math.floor((Date.parse(instant) - now) / 1e3));
|
|
10319
11334
|
}
|
|
10320
|
-
function
|
|
11335
|
+
function isRecord12(value) {
|
|
10321
11336
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
10322
11337
|
}
|
|
10323
11338
|
function detectProviderKeyQuotaAdapter(baseUrl) {
|
|
@@ -10384,17 +11399,17 @@ function zaiWindowIdLabel(durationMs) {
|
|
|
10384
11399
|
return { id: "quota", label: "Quota" };
|
|
10385
11400
|
}
|
|
10386
11401
|
function parseZaiQuotaPayload(payload, now) {
|
|
10387
|
-
if (!
|
|
10388
|
-
const data =
|
|
11402
|
+
if (!isRecord12(payload)) return null;
|
|
11403
|
+
const data = isRecord12(payload["data"]) ? payload["data"] : payload;
|
|
10389
11404
|
if (payload["success"] === false) return null;
|
|
10390
11405
|
const limits = Array.isArray(data["limits"]) ? data["limits"] : [];
|
|
10391
11406
|
const byWindow = /* @__PURE__ */ new Map();
|
|
10392
11407
|
for (const raw of limits) {
|
|
10393
|
-
if (!
|
|
11408
|
+
if (!isRecord12(raw)) continue;
|
|
10394
11409
|
const item = raw;
|
|
10395
11410
|
if (item.type === void 0) continue;
|
|
10396
11411
|
const details = raw["usageDetails"];
|
|
10397
|
-
if (Array.isArray(details) && details.some((d) =>
|
|
11412
|
+
if (Array.isArray(details) && details.some((d) => isRecord12(d) && d["modelCode"] === "zread")) {
|
|
10398
11413
|
continue;
|
|
10399
11414
|
}
|
|
10400
11415
|
const durationMs = zaiWindowDurationMs(item);
|
|
@@ -10427,7 +11442,7 @@ function parseZaiQuotaPayload(payload, now) {
|
|
|
10427
11442
|
var MINIMAX_STATUS_EXHAUSTED = 2;
|
|
10428
11443
|
var MINIMAX_SHARED_BUCKET = "general";
|
|
10429
11444
|
function parseMiniMaxBucket(value) {
|
|
10430
|
-
if (!
|
|
11445
|
+
if (!isRecord12(value)) return null;
|
|
10431
11446
|
const modelName = typeof value["model_name"] === "string" ? value["model_name"].trim() : "";
|
|
10432
11447
|
if (!modelName) return null;
|
|
10433
11448
|
const instant = (v) => {
|
|
@@ -10459,9 +11474,9 @@ function minimaxWindow(id, label, windowMinutes, resetsAtMs, remainingPercent, s
|
|
|
10459
11474
|
};
|
|
10460
11475
|
}
|
|
10461
11476
|
function parseMiniMaxTokenPlanPayload(payload, now) {
|
|
10462
|
-
if (!
|
|
11477
|
+
if (!isRecord12(payload)) return null;
|
|
10463
11478
|
const baseResp = payload["base_resp"];
|
|
10464
|
-
if (!
|
|
11479
|
+
if (!isRecord12(baseResp) || baseResp["status_code"] !== 0) return null;
|
|
10465
11480
|
const buckets = Array.isArray(payload["model_remains"]) ? payload["model_remains"] : [];
|
|
10466
11481
|
let general = null;
|
|
10467
11482
|
for (const raw of buckets) {
|
|
@@ -10494,11 +11509,11 @@ function parseMiniMaxTokenPlanPayload(payload, now) {
|
|
|
10494
11509
|
];
|
|
10495
11510
|
}
|
|
10496
11511
|
function parseUmansUsagePayload(payload, now) {
|
|
10497
|
-
if (!
|
|
10498
|
-
const limits =
|
|
10499
|
-
const requests = limits &&
|
|
10500
|
-
const usage =
|
|
10501
|
-
const window =
|
|
11512
|
+
if (!isRecord12(payload)) return null;
|
|
11513
|
+
const limits = isRecord12(payload["limits"]) ? payload["limits"] : void 0;
|
|
11514
|
+
const requests = limits && isRecord12(limits["requests"]) ? limits["requests"] : void 0;
|
|
11515
|
+
const usage = isRecord12(payload["usage"]) ? payload["usage"] : void 0;
|
|
11516
|
+
const window = isRecord12(payload["window"]) ? payload["window"] : void 0;
|
|
10502
11517
|
const hardCap = finiteNumber5(requests?.["hard_cap"]);
|
|
10503
11518
|
const softLimit = finiteNumber5(requests?.["limit"]);
|
|
10504
11519
|
const requestsInWindow = finiteNumber5(usage?.["requests_in_window"]);
|
|
@@ -10525,9 +11540,9 @@ function parseUmansUsagePayload(payload, now) {
|
|
|
10525
11540
|
];
|
|
10526
11541
|
}
|
|
10527
11542
|
function parseSyntheticQuotasPayload(payload, now) {
|
|
10528
|
-
if (!
|
|
10529
|
-
const fiveHour =
|
|
10530
|
-
const weekly =
|
|
11543
|
+
if (!isRecord12(payload)) return null;
|
|
11544
|
+
const fiveHour = isRecord12(payload["rollingFiveHourLimit"]) ? payload["rollingFiveHourLimit"] : void 0;
|
|
11545
|
+
const weekly = isRecord12(payload["weeklyTokenLimit"]) ? payload["weeklyTokenLimit"] : void 0;
|
|
10531
11546
|
const windows = [];
|
|
10532
11547
|
if (fiveHour) {
|
|
10533
11548
|
const max = finiteNumber5(fiveHour["max"]);
|
|
@@ -10568,12 +11583,12 @@ var CLINE_WINDOW_CONFIG = {
|
|
|
10568
11583
|
monthly: { id: "thirty-day", label: "30 days", minutes: 30 * 24 * 60 }
|
|
10569
11584
|
};
|
|
10570
11585
|
function parseClinePassUsageLimitsPayload(payload, now) {
|
|
10571
|
-
if (!
|
|
10572
|
-
const data =
|
|
11586
|
+
if (!isRecord12(payload)) return null;
|
|
11587
|
+
const data = isRecord12(payload["data"]) ? payload["data"] : payload;
|
|
10573
11588
|
const limits = Array.isArray(data["limits"]) ? data["limits"] : [];
|
|
10574
11589
|
const windows = [];
|
|
10575
11590
|
for (const raw of limits) {
|
|
10576
|
-
if (!
|
|
11591
|
+
if (!isRecord12(raw)) continue;
|
|
10577
11592
|
const config = CLINE_WINDOW_CONFIG[typeof raw["type"] === "string" ? raw["type"] : ""];
|
|
10578
11593
|
if (!config) continue;
|
|
10579
11594
|
const usedPercent = finitePercent4(raw["percentUsed"]);
|
|
@@ -10726,36 +11741,36 @@ var ProviderKeyQuotaService = class {
|
|
|
10726
11741
|
};
|
|
10727
11742
|
|
|
10728
11743
|
// src/commands/paths.ts
|
|
10729
|
-
import { dirname as dirname5, join as
|
|
11744
|
+
import { dirname as dirname5, join as join6 } from "path";
|
|
10730
11745
|
function defaultVouchersPath(configPath) {
|
|
10731
|
-
return
|
|
11746
|
+
return join6(dirname5(configPath), "vouchers.json");
|
|
10732
11747
|
}
|
|
10733
11748
|
function defaultIntegrationsPath(configPath) {
|
|
10734
|
-
return
|
|
11749
|
+
return join6(dirname5(configPath), "integrations.json");
|
|
10735
11750
|
}
|
|
10736
11751
|
function defaultPricingPath(configPath) {
|
|
10737
|
-
return
|
|
11752
|
+
return join6(dirname5(configPath), "pricing.json");
|
|
10738
11753
|
}
|
|
10739
11754
|
function defaultPricingRefreshStatePath(configPath) {
|
|
10740
|
-
return
|
|
11755
|
+
return join6(dirname5(configPath), "pricing-refresh.json");
|
|
10741
11756
|
}
|
|
10742
11757
|
function defaultAccountAllowancePath(configPath) {
|
|
10743
|
-
return
|
|
11758
|
+
return join6(dirname5(configPath), "allowance-cache.json");
|
|
10744
11759
|
}
|
|
10745
11760
|
function defaultUsageEventsPath(configPath) {
|
|
10746
|
-
return
|
|
11761
|
+
return join6(dirname5(configPath), "usage-events.jsonl");
|
|
10747
11762
|
}
|
|
10748
11763
|
function defaultLogDir(configPath) {
|
|
10749
|
-
return
|
|
11764
|
+
return join6(dirname5(configPath), "logs");
|
|
10750
11765
|
}
|
|
10751
11766
|
function defaultDaemonLogPath(configPath) {
|
|
10752
|
-
return
|
|
11767
|
+
return join6(defaultLogDir(configPath), "daemon.log");
|
|
10753
11768
|
}
|
|
10754
11769
|
function defaultAuditDir(configPath) {
|
|
10755
|
-
return
|
|
11770
|
+
return join6(dirname5(configPath), "audit");
|
|
10756
11771
|
}
|
|
10757
11772
|
function defaultBillingDir(configPath) {
|
|
10758
|
-
return
|
|
11773
|
+
return join6(dirname5(configPath), "billing");
|
|
10759
11774
|
}
|
|
10760
11775
|
|
|
10761
11776
|
// src/image-generation/ImageDoctorService.ts
|
|
@@ -10775,7 +11790,7 @@ import { createHmac as createHmac2, randomBytes as randomBytes5 } from "crypto";
|
|
|
10775
11790
|
import {
|
|
10776
11791
|
chmodSync as chmodSync6,
|
|
10777
11792
|
closeSync as closeSync2,
|
|
10778
|
-
existsSync as
|
|
11793
|
+
existsSync as existsSync10,
|
|
10779
11794
|
fsyncSync as fsyncSync2,
|
|
10780
11795
|
lstatSync as lstatSync3,
|
|
10781
11796
|
openSync as openSync2,
|
|
@@ -10784,7 +11799,7 @@ import {
|
|
|
10784
11799
|
unlinkSync as unlinkSync4,
|
|
10785
11800
|
writeFileSync as writeFileSync7
|
|
10786
11801
|
} from "fs";
|
|
10787
|
-
import { basename as
|
|
11802
|
+
import { basename as basename3, dirname as dirname6, join as join8, resolve as resolve5 } from "path";
|
|
10788
11803
|
import { IMAGE_SERVER_HARD_CEILINGS } from "@omnicross/core/outbound-api";
|
|
10789
11804
|
|
|
10790
11805
|
// src/image-generation/imageTenantHmac.ts
|
|
@@ -10793,7 +11808,7 @@ import {
|
|
|
10793
11808
|
chmodSync as chmodSync5,
|
|
10794
11809
|
closeSync,
|
|
10795
11810
|
constants,
|
|
10796
|
-
existsSync as
|
|
11811
|
+
existsSync as existsSync9,
|
|
10797
11812
|
fstatSync,
|
|
10798
11813
|
fsyncSync,
|
|
10799
11814
|
lstatSync as lstatSync2,
|
|
@@ -10801,7 +11816,7 @@ import {
|
|
|
10801
11816
|
readFileSync as readFileSync6,
|
|
10802
11817
|
writeFileSync as writeFileSync6
|
|
10803
11818
|
} from "fs";
|
|
10804
|
-
import { join as
|
|
11819
|
+
import { join as join7 } from "path";
|
|
10805
11820
|
var TENANT_SALT_NAME = "tenant-hmac-salt.v1.bin";
|
|
10806
11821
|
var TENANT_KEY_PATTERN = /^[a-f0-9]{64}$/u;
|
|
10807
11822
|
var REFERENCE_DOMAIN = Buffer.from("omnicross:image-reference:tenant:v1\0", "utf8");
|
|
@@ -10819,8 +11834,8 @@ function deriveImageTenantHmac(salt, purpose, tenantId) {
|
|
|
10819
11834
|
}
|
|
10820
11835
|
function loadOrCreateImageTenantHmacSalt(paths, random) {
|
|
10821
11836
|
const root = paths.verifiedRoot("mountManifest");
|
|
10822
|
-
const path2 =
|
|
10823
|
-
if (!
|
|
11837
|
+
const path2 = join7(root, TENANT_SALT_NAME);
|
|
11838
|
+
if (!existsSync9(path2)) {
|
|
10824
11839
|
const salt = random(32);
|
|
10825
11840
|
if (salt.byteLength !== 32) throw new TypeError("image tenant HMAC salt generator returned invalid bytes");
|
|
10826
11841
|
let fd2;
|
|
@@ -10925,8 +11940,8 @@ function validateObservation(value) {
|
|
|
10925
11940
|
}
|
|
10926
11941
|
}
|
|
10927
11942
|
function samePath2(left, right) {
|
|
10928
|
-
const a =
|
|
10929
|
-
const b =
|
|
11943
|
+
const a = resolve5(left);
|
|
11944
|
+
const b = resolve5(right);
|
|
10930
11945
|
return process.platform === "win32" ? a.toLowerCase() === b.toLowerCase() : a === b;
|
|
10931
11946
|
}
|
|
10932
11947
|
function capabilityValues(entry) {
|
|
@@ -10988,7 +12003,7 @@ var FileCodexImageCapabilityEvidenceManifestOwner = class {
|
|
|
10988
12003
|
this.#replaceManifest = options.replaceManifest ?? ((target, contents) => {
|
|
10989
12004
|
this.#atomicReplace(target, contents);
|
|
10990
12005
|
});
|
|
10991
|
-
if (
|
|
12006
|
+
if (existsSync10(this.#manifestPath())) this.#load();
|
|
10992
12007
|
}
|
|
10993
12008
|
createSource(ttlMs) {
|
|
10994
12009
|
return new FileCodexImageCapabilityEvidenceSource({ owner: this, ttlMs });
|
|
@@ -11100,7 +12115,7 @@ var FileCodexImageCapabilityEvidenceManifestOwner = class {
|
|
|
11100
12115
|
return Math.min(entry.expiresAt, entry.verifiedAt + ttlMs);
|
|
11101
12116
|
}
|
|
11102
12117
|
#manifestPath() {
|
|
11103
|
-
return
|
|
12118
|
+
return join8(this.#paths.verifiedRoot("evidence"), MANIFEST_NAME);
|
|
11104
12119
|
}
|
|
11105
12120
|
#serialized(entries, revision) {
|
|
11106
12121
|
const manifest = {
|
|
@@ -11145,15 +12160,15 @@ var FileCodexImageCapabilityEvidenceManifestOwner = class {
|
|
|
11145
12160
|
this.#entries = entries;
|
|
11146
12161
|
}
|
|
11147
12162
|
#refresh() {
|
|
11148
|
-
if (!
|
|
12163
|
+
if (!existsSync10(this.#manifestPath())) return;
|
|
11149
12164
|
this.#load();
|
|
11150
12165
|
}
|
|
11151
12166
|
#atomicReplace(targetPath, contents) {
|
|
11152
12167
|
const root = this.#paths.verifiedRoot("evidence");
|
|
11153
|
-
if (!samePath2(dirname6(
|
|
12168
|
+
if (!samePath2(dirname6(resolve5(targetPath)), root) || basename3(targetPath) !== MANIFEST_NAME) {
|
|
11154
12169
|
throw new TypeError("Codex image evidence manifest target is invalid");
|
|
11155
12170
|
}
|
|
11156
|
-
const temporaryPath =
|
|
12171
|
+
const temporaryPath = join8(
|
|
11157
12172
|
root,
|
|
11158
12173
|
`.codex-image-evidence.${process.pid}.${this.#random(8).toString("hex")}.tmp`
|
|
11159
12174
|
);
|
|
@@ -11173,7 +12188,7 @@ var FileCodexImageCapabilityEvidenceManifestOwner = class {
|
|
|
11173
12188
|
} catch {
|
|
11174
12189
|
}
|
|
11175
12190
|
}
|
|
11176
|
-
if (
|
|
12191
|
+
if (existsSync10(temporaryPath)) {
|
|
11177
12192
|
try {
|
|
11178
12193
|
unlinkSync4(temporaryPath);
|
|
11179
12194
|
} catch {
|
|
@@ -11765,11 +12780,11 @@ var DaemonImageExecutionScheduler = class {
|
|
|
11765
12780
|
retrySafety: "before_acceptance"
|
|
11766
12781
|
});
|
|
11767
12782
|
}
|
|
11768
|
-
return new Promise((
|
|
12783
|
+
return new Promise((resolve11, reject) => {
|
|
11769
12784
|
const waiter = {
|
|
11770
12785
|
tenantKey,
|
|
11771
12786
|
signal: request.signal,
|
|
11772
|
-
resolve:
|
|
12787
|
+
resolve: resolve11,
|
|
11773
12788
|
reject,
|
|
11774
12789
|
onAbort: () => void 0,
|
|
11775
12790
|
settled: false
|
|
@@ -11932,7 +12947,7 @@ import {
|
|
|
11932
12947
|
ImageGenerationError as ImageGenerationError3,
|
|
11933
12948
|
ImageRequestResourceScope
|
|
11934
12949
|
} from "@omnicross/core/image-generation";
|
|
11935
|
-
import { dirname as dirname7, resolve as
|
|
12950
|
+
import { dirname as dirname7, resolve as resolve6 } from "path";
|
|
11936
12951
|
function capacityExceeded() {
|
|
11937
12952
|
throw new ImageGenerationError3("image_too_large");
|
|
11938
12953
|
}
|
|
@@ -12017,10 +13032,10 @@ var DaemonImageActiveScopeRegistry = class {
|
|
|
12017
13032
|
#temporaryRoot;
|
|
12018
13033
|
#active = /* @__PURE__ */ new Set();
|
|
12019
13034
|
constructor(paths) {
|
|
12020
|
-
this.#temporaryRoot =
|
|
13035
|
+
this.#temporaryRoot = resolve6(paths.paths.temporaryRoot);
|
|
12021
13036
|
}
|
|
12022
13037
|
register(privateDirectory) {
|
|
12023
|
-
const normalized2 =
|
|
13038
|
+
const normalized2 = resolve6(privateDirectory);
|
|
12024
13039
|
if (dirname7(normalized2) !== this.#temporaryRoot || this.#active.has(normalized2)) {
|
|
12025
13040
|
throw new TypeError("image temporary scope directory is invalid or already active");
|
|
12026
13041
|
}
|
|
@@ -12033,7 +13048,7 @@ var DaemonImageActiveScopeRegistry = class {
|
|
|
12033
13048
|
};
|
|
12034
13049
|
}
|
|
12035
13050
|
isActive(privateDirectory) {
|
|
12036
|
-
return this.#active.has(
|
|
13051
|
+
return this.#active.has(resolve6(privateDirectory));
|
|
12037
13052
|
}
|
|
12038
13053
|
status() {
|
|
12039
13054
|
return Object.freeze({ activeDirectories: this.#active.size });
|
|
@@ -12327,14 +13342,14 @@ function createImageRuntimeGeneration(options) {
|
|
|
12327
13342
|
|
|
12328
13343
|
// src/image-generation/ImageStartupReconciler.ts
|
|
12329
13344
|
import {
|
|
12330
|
-
existsSync as
|
|
13345
|
+
existsSync as existsSync14,
|
|
12331
13346
|
lstatSync as lstatSync7,
|
|
12332
13347
|
readFileSync as readFileSync11,
|
|
12333
13348
|
readdirSync as readdirSync3,
|
|
12334
13349
|
rmdirSync as rmdirSync2,
|
|
12335
13350
|
unlinkSync as unlinkSync8
|
|
12336
13351
|
} from "fs";
|
|
12337
|
-
import { basename as
|
|
13352
|
+
import { basename as basename7, dirname as dirname11, isAbsolute as isAbsolute3, relative as relative2, resolve as resolve10 } from "path";
|
|
12338
13353
|
import {
|
|
12339
13354
|
IMAGE_REQUEST_DIRECTORY_MARKER_CONTENT,
|
|
12340
13355
|
IMAGE_REQUEST_DIRECTORY_MARKER_NAME
|
|
@@ -12344,7 +13359,7 @@ import {
|
|
|
12344
13359
|
import { randomBytes as randomBytes9 } from "crypto";
|
|
12345
13360
|
import {
|
|
12346
13361
|
closeSync as closeSync5,
|
|
12347
|
-
existsSync as
|
|
13362
|
+
existsSync as existsSync13,
|
|
12348
13363
|
fsyncSync as fsyncSync5,
|
|
12349
13364
|
lstatSync as lstatSync6,
|
|
12350
13365
|
openSync as openSync5,
|
|
@@ -12354,7 +13369,7 @@ import {
|
|
|
12354
13369
|
unlinkSync as unlinkSync7,
|
|
12355
13370
|
writeFileSync as writeFileSync10
|
|
12356
13371
|
} from "fs";
|
|
12357
|
-
import { basename as
|
|
13372
|
+
import { basename as basename6, dirname as dirname10, isAbsolute as isAbsolute2, join as join11, resolve as resolve9 } from "path";
|
|
12358
13373
|
import { ImageGenerationError as ImageGenerationError7 } from "@omnicross/core/image-generation";
|
|
12359
13374
|
|
|
12360
13375
|
// src/image-generation/FileImageReferenceStore.ts
|
|
@@ -12363,7 +13378,7 @@ import {
|
|
|
12363
13378
|
closeSync as closeSync3,
|
|
12364
13379
|
constants as constants2,
|
|
12365
13380
|
createReadStream,
|
|
12366
|
-
existsSync as
|
|
13381
|
+
existsSync as existsSync11,
|
|
12367
13382
|
fstatSync as fstatSync2,
|
|
12368
13383
|
fsyncSync as fsyncSync3,
|
|
12369
13384
|
lstatSync as lstatSync4,
|
|
@@ -12375,8 +13390,8 @@ import {
|
|
|
12375
13390
|
unlinkSync as unlinkSync5,
|
|
12376
13391
|
writeFileSync as writeFileSync8
|
|
12377
13392
|
} from "fs";
|
|
12378
|
-
import { open } from "fs/promises";
|
|
12379
|
-
import { basename as
|
|
13393
|
+
import { open as open2 } from "fs/promises";
|
|
13394
|
+
import { basename as basename4, dirname as dirname8, join as join9, resolve as resolve7 } from "path";
|
|
12380
13395
|
import { Readable } from "stream";
|
|
12381
13396
|
import {
|
|
12382
13397
|
ImageGenerationError as ImageGenerationError5
|
|
@@ -12402,8 +13417,8 @@ function safeInteger(value) {
|
|
|
12402
13417
|
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
12403
13418
|
}
|
|
12404
13419
|
function samePath3(left, right) {
|
|
12405
|
-
const normalizedLeft =
|
|
12406
|
-
const normalizedRight =
|
|
13420
|
+
const normalizedLeft = resolve7(left);
|
|
13421
|
+
const normalizedRight = resolve7(right);
|
|
12407
13422
|
return process.platform === "win32" ? normalizedLeft.toLowerCase() === normalizedRight.toLowerCase() : normalizedLeft === normalizedRight;
|
|
12408
13423
|
}
|
|
12409
13424
|
function validEntry2(value) {
|
|
@@ -12705,7 +13720,7 @@ var FileImageReferenceStore = class {
|
|
|
12705
13720
|
const ownedArtifact = ARTIFACT_FILE.test(name);
|
|
12706
13721
|
const incomplete = /^artifact-[a-f0-9]{32}\.tmp$/u.test(name);
|
|
12707
13722
|
if ((!ownedArtifact || referenced.has(name)) && !incomplete) continue;
|
|
12708
|
-
const path2 =
|
|
13723
|
+
const path2 = resolve7(root, name);
|
|
12709
13724
|
let info;
|
|
12710
13725
|
try {
|
|
12711
13726
|
info = lstatSync4(path2);
|
|
@@ -12856,10 +13871,10 @@ var FileImageReferenceStore = class {
|
|
|
12856
13871
|
async writeArtifact(asset, limits) {
|
|
12857
13872
|
const root = this.#paths.verifiedRoot("artifacts");
|
|
12858
13873
|
const suffix = this.#random(16).toString("hex");
|
|
12859
|
-
const tempPath =
|
|
13874
|
+
const tempPath = join9(root, `artifact-${suffix}.tmp`);
|
|
12860
13875
|
const fileName = `artifact-${suffix}.bin`;
|
|
12861
|
-
const finalPath =
|
|
12862
|
-
const handle = await
|
|
13876
|
+
const finalPath = join9(root, fileName);
|
|
13877
|
+
const handle = await open2(tempPath, "wx", 384);
|
|
12863
13878
|
let reader;
|
|
12864
13879
|
let observed = 0;
|
|
12865
13880
|
let writeFailure;
|
|
@@ -12908,8 +13923,8 @@ var FileImageReferenceStore = class {
|
|
|
12908
13923
|
artifactPath(fileName) {
|
|
12909
13924
|
if (!ARTIFACT_FILE.test(fileName)) throw new TypeError("invalid image artifact filename");
|
|
12910
13925
|
const root = this.#paths.verifiedRoot("artifacts");
|
|
12911
|
-
const path2 =
|
|
12912
|
-
if (!samePath3(dirname8(path2), root) ||
|
|
13926
|
+
const path2 = resolve7(root, fileName);
|
|
13927
|
+
if (!samePath3(dirname8(path2), root) || basename4(path2) !== fileName) {
|
|
12913
13928
|
throw new TypeError("image artifact escaped its root");
|
|
12914
13929
|
}
|
|
12915
13930
|
return path2;
|
|
@@ -12932,12 +13947,12 @@ var FileImageReferenceStore = class {
|
|
|
12932
13947
|
}
|
|
12933
13948
|
safeUnlinkArtifactPath(path2) {
|
|
12934
13949
|
const root = this.#paths.verifiedRoot("artifacts");
|
|
12935
|
-
const target =
|
|
12936
|
-
const name =
|
|
13950
|
+
const target = resolve7(path2);
|
|
13951
|
+
const name = basename4(target);
|
|
12937
13952
|
if (!samePath3(dirname8(target), root) || !/^artifact-[a-f0-9]{32}\.(?:bin|tmp)$/u.test(name)) {
|
|
12938
13953
|
throw new TypeError("refusing to unlink an unverified image artifact");
|
|
12939
13954
|
}
|
|
12940
|
-
if (!
|
|
13955
|
+
if (!existsSync11(target)) return;
|
|
12941
13956
|
const info = lstatSync4(target);
|
|
12942
13957
|
if (info.isSymbolicLink() || !info.isFile()) throw new TypeError("refusing to unlink an unverified image artifact");
|
|
12943
13958
|
unlinkSync5(target);
|
|
@@ -12959,7 +13974,7 @@ var FileImageReferenceStore = class {
|
|
|
12959
13974
|
});
|
|
12960
13975
|
}
|
|
12961
13976
|
manifestPath() {
|
|
12962
|
-
return
|
|
13977
|
+
return join9(this.#paths.verifiedRoot("state"), MANIFEST_NAME2);
|
|
12963
13978
|
}
|
|
12964
13979
|
persist(entries, tombstones) {
|
|
12965
13980
|
const manifest = {
|
|
@@ -12975,10 +13990,10 @@ var FileImageReferenceStore = class {
|
|
|
12975
13990
|
}
|
|
12976
13991
|
atomicReplace(targetPath, contents) {
|
|
12977
13992
|
const root = this.#paths.verifiedRoot("state");
|
|
12978
|
-
if (!samePath3(dirname8(
|
|
13993
|
+
if (!samePath3(dirname8(resolve7(targetPath)), root) || basename4(targetPath) !== MANIFEST_NAME2) {
|
|
12979
13994
|
throw new TypeError("invalid image reference manifest target");
|
|
12980
13995
|
}
|
|
12981
|
-
const temporaryPath =
|
|
13996
|
+
const temporaryPath = join9(root, `.references.${process.pid}.${this.#random(8).toString("hex")}.tmp`);
|
|
12982
13997
|
let fd;
|
|
12983
13998
|
try {
|
|
12984
13999
|
fd = openSync3(temporaryPath, "wx", 384);
|
|
@@ -12994,7 +14009,7 @@ var FileImageReferenceStore = class {
|
|
|
12994
14009
|
} catch {
|
|
12995
14010
|
}
|
|
12996
14011
|
}
|
|
12997
|
-
if (
|
|
14012
|
+
if (existsSync11(temporaryPath)) {
|
|
12998
14013
|
try {
|
|
12999
14014
|
unlinkSync5(temporaryPath);
|
|
13000
14015
|
} catch {
|
|
@@ -13004,7 +14019,7 @@ var FileImageReferenceStore = class {
|
|
|
13004
14019
|
}
|
|
13005
14020
|
loadManifest() {
|
|
13006
14021
|
const path2 = this.manifestPath();
|
|
13007
|
-
if (!
|
|
14022
|
+
if (!existsSync11(path2)) return;
|
|
13008
14023
|
const info = lstatSync4(path2);
|
|
13009
14024
|
if (info.isSymbolicLink() || !info.isFile() || info.size > MAX_MANIFEST_BYTES2) {
|
|
13010
14025
|
throw new TypeError("image reference manifest is invalid");
|
|
@@ -13053,7 +14068,7 @@ var FileImageReferenceStore = class {
|
|
|
13053
14068
|
import { randomBytes as randomBytes8 } from "crypto";
|
|
13054
14069
|
import {
|
|
13055
14070
|
closeSync as closeSync4,
|
|
13056
|
-
existsSync as
|
|
14071
|
+
existsSync as existsSync12,
|
|
13057
14072
|
fsyncSync as fsyncSync4,
|
|
13058
14073
|
lstatSync as lstatSync5,
|
|
13059
14074
|
openSync as openSync4,
|
|
@@ -13062,7 +14077,7 @@ import {
|
|
|
13062
14077
|
unlinkSync as unlinkSync6,
|
|
13063
14078
|
writeFileSync as writeFileSync9
|
|
13064
14079
|
} from "fs";
|
|
13065
|
-
import { basename as
|
|
14080
|
+
import { basename as basename5, dirname as dirname9, join as join10, resolve as resolve8 } from "path";
|
|
13066
14081
|
import { ImageGenerationError as ImageGenerationError6 } from "@omnicross/core/image-generation";
|
|
13067
14082
|
var MANIFEST_VERSION3 = 1;
|
|
13068
14083
|
var MANIFEST_NAME3 = "responses-image-state.v1.json";
|
|
@@ -13115,8 +14130,8 @@ function validPendingReferenceDelete(value) {
|
|
|
13115
14130
|
return exactKeys2(row, ["callId", "referenceTenantKey", "referenceId", "expiresAt"]) && typeof row.callId === "string" && CALL_ID_PATTERN.test(row.callId) && isImageTenantHmac(row.referenceTenantKey) && typeof row.referenceId === "string" && REFERENCE_ID_PATTERN.test(row.referenceId) && safeTimestamp2(row.expiresAt);
|
|
13116
14131
|
}
|
|
13117
14132
|
function samePath4(left, right) {
|
|
13118
|
-
const normalizedLeft =
|
|
13119
|
-
const normalizedRight =
|
|
14133
|
+
const normalizedLeft = resolve8(left);
|
|
14134
|
+
const normalizedRight = resolve8(right);
|
|
13120
14135
|
return process.platform === "win32" ? normalizedLeft.toLowerCase() === normalizedRight.toLowerCase() : normalizedLeft === normalizedRight;
|
|
13121
14136
|
}
|
|
13122
14137
|
function sameBinding(left, right) {
|
|
@@ -13599,7 +14614,7 @@ var FileResponsesImageStateStore = class {
|
|
|
13599
14614
|
});
|
|
13600
14615
|
}
|
|
13601
14616
|
manifestPath() {
|
|
13602
|
-
return
|
|
14617
|
+
return join10(this.#paths.verifiedRoot("state"), MANIFEST_NAME3);
|
|
13603
14618
|
}
|
|
13604
14619
|
persist(calls, responses, tombstones, pendingReferenceDeletes) {
|
|
13605
14620
|
const manifest = {
|
|
@@ -13619,10 +14634,10 @@ var FileResponsesImageStateStore = class {
|
|
|
13619
14634
|
}
|
|
13620
14635
|
atomicReplace(targetPath, contents) {
|
|
13621
14636
|
const root = this.#paths.verifiedRoot("state");
|
|
13622
|
-
if (!samePath4(dirname9(
|
|
14637
|
+
if (!samePath4(dirname9(resolve8(targetPath)), root) || basename5(targetPath) !== MANIFEST_NAME3) {
|
|
13623
14638
|
throw new TypeError("invalid responses image state manifest target");
|
|
13624
14639
|
}
|
|
13625
|
-
const temporaryPath =
|
|
14640
|
+
const temporaryPath = join10(
|
|
13626
14641
|
root,
|
|
13627
14642
|
`.responses-image-state.${process.pid}.${this.#random(8).toString("hex")}.tmp`
|
|
13628
14643
|
);
|
|
@@ -13641,7 +14656,7 @@ var FileResponsesImageStateStore = class {
|
|
|
13641
14656
|
} catch {
|
|
13642
14657
|
}
|
|
13643
14658
|
}
|
|
13644
|
-
if (
|
|
14659
|
+
if (existsSync12(temporaryPath)) {
|
|
13645
14660
|
try {
|
|
13646
14661
|
unlinkSync6(temporaryPath);
|
|
13647
14662
|
} catch {
|
|
@@ -13651,7 +14666,7 @@ var FileResponsesImageStateStore = class {
|
|
|
13651
14666
|
}
|
|
13652
14667
|
loadManifest() {
|
|
13653
14668
|
const path2 = this.manifestPath();
|
|
13654
|
-
if (!
|
|
14669
|
+
if (!existsSync12(path2)) return;
|
|
13655
14670
|
const info = lstatSync5(path2);
|
|
13656
14671
|
if (info.isSymbolicLink() || !info.isFile() || info.size > MAX_MANIFEST_BYTES3) {
|
|
13657
14672
|
throw new TypeError("responses image state manifest is invalid");
|
|
@@ -13745,8 +14760,8 @@ function validMount(value) {
|
|
|
13745
14760
|
return exactKeys3(row, ["id", "durableRoot", "createdAt"]) && typeof row.id === "string" && MOUNT_ID_PATTERN.test(row.id) && typeof row.durableRoot === "string" && isAbsolute2(row.durableRoot) && safeInteger2(row.createdAt);
|
|
13746
14761
|
}
|
|
13747
14762
|
function samePath5(left, right) {
|
|
13748
|
-
const normalizedLeft =
|
|
13749
|
-
const normalizedRight =
|
|
14763
|
+
const normalizedLeft = resolve9(left);
|
|
14764
|
+
const normalizedRight = resolve9(right);
|
|
13750
14765
|
return process.platform === "win32" ? normalizedLeft.toLowerCase() === normalizedRight.toLowerCase() : normalizedLeft === normalizedRight;
|
|
13751
14766
|
}
|
|
13752
14767
|
var ImageStorageMountCatalog = class {
|
|
@@ -13774,7 +14789,7 @@ var ImageStorageMountCatalog = class {
|
|
|
13774
14789
|
this.#catalogResolver = this.createResolver(options.activeStorageRoot);
|
|
13775
14790
|
this.#reconcileCorruptManifests = options.reconcileCorruptManifests ?? false;
|
|
13776
14791
|
this.#replaceCatalog = options.replaceCatalog ?? ((target, contents) => this.atomicReplace(target, contents));
|
|
13777
|
-
if (
|
|
14792
|
+
if (existsSync13(this.catalogPath())) {
|
|
13778
14793
|
try {
|
|
13779
14794
|
this.loadCatalog();
|
|
13780
14795
|
} catch (error) {
|
|
@@ -14026,15 +15041,15 @@ var ImageStorageMountCatalog = class {
|
|
|
14026
15041
|
}
|
|
14027
15042
|
quarantineManifest(resolver, area, name, label) {
|
|
14028
15043
|
const root = resolver.verifiedRoot(area);
|
|
14029
|
-
const source =
|
|
14030
|
-
if (!
|
|
15044
|
+
const source = join11(root, name);
|
|
15045
|
+
if (!existsSync13(source)) throw new TypeError("corrupt image manifest is missing");
|
|
14031
15046
|
const info = lstatSync6(source);
|
|
14032
15047
|
if (info.isSymbolicLink() || !info.isFile()) {
|
|
14033
15048
|
throw new TypeError("refusing to quarantine an unverified image manifest");
|
|
14034
15049
|
}
|
|
14035
15050
|
for (let attempt = 0; attempt < 8; attempt += 1) {
|
|
14036
|
-
const target =
|
|
14037
|
-
if (
|
|
15051
|
+
const target = join11(root, `.corrupt-${label}-${this.#random(8).toString("hex")}.json`);
|
|
15052
|
+
if (existsSync13(target)) continue;
|
|
14038
15053
|
resolver.verifiedRoot(area);
|
|
14039
15054
|
renameSync6(source, target);
|
|
14040
15055
|
this.#corruptManifestsQuarantined += 1;
|
|
@@ -14079,10 +15094,10 @@ var ImageStorageMountCatalog = class {
|
|
|
14079
15094
|
}
|
|
14080
15095
|
atomicReplace(targetPath, contents) {
|
|
14081
15096
|
const root = this.#catalogResolver.verifiedRoot("mountManifest");
|
|
14082
|
-
if (!samePath5(dirname10(
|
|
15097
|
+
if (!samePath5(dirname10(resolve9(targetPath)), root) || basename6(targetPath) !== CATALOG_NAME) {
|
|
14083
15098
|
throw new TypeError("invalid image storage mount catalog target");
|
|
14084
15099
|
}
|
|
14085
|
-
const temporaryPath =
|
|
15100
|
+
const temporaryPath = join11(
|
|
14086
15101
|
root,
|
|
14087
15102
|
`.catalog.${process.pid}.${this.#random(8).toString("hex")}.tmp`
|
|
14088
15103
|
);
|
|
@@ -14101,7 +15116,7 @@ var ImageStorageMountCatalog = class {
|
|
|
14101
15116
|
} catch {
|
|
14102
15117
|
}
|
|
14103
15118
|
}
|
|
14104
|
-
if (
|
|
15119
|
+
if (existsSync13(temporaryPath)) {
|
|
14105
15120
|
try {
|
|
14106
15121
|
unlinkSync7(temporaryPath);
|
|
14107
15122
|
} catch {
|
|
@@ -14323,12 +15338,12 @@ function positiveInteger4(value, name) {
|
|
|
14323
15338
|
return value;
|
|
14324
15339
|
}
|
|
14325
15340
|
function samePath6(left, right) {
|
|
14326
|
-
const normalizedLeft =
|
|
14327
|
-
const normalizedRight =
|
|
15341
|
+
const normalizedLeft = resolve10(left);
|
|
15342
|
+
const normalizedRight = resolve10(right);
|
|
14328
15343
|
return process.platform === "win32" ? normalizedLeft.toLowerCase() === normalizedRight.toLowerCase() : normalizedLeft === normalizedRight;
|
|
14329
15344
|
}
|
|
14330
15345
|
function isDirectChild(path2, root) {
|
|
14331
|
-
const target =
|
|
15346
|
+
const target = resolve10(path2);
|
|
14332
15347
|
const rel = relative2(root, target);
|
|
14333
15348
|
return !samePath6(target, root) && !isAbsolute3(rel) && !rel.startsWith("..") && samePath6(dirname11(target), root);
|
|
14334
15349
|
}
|
|
@@ -14450,8 +15465,8 @@ var ImageStartupReconciler = class {
|
|
|
14450
15465
|
let removed = 0;
|
|
14451
15466
|
let invalid = 0;
|
|
14452
15467
|
for (const name of readdirSync3(root).filter((value) => pattern.test(value)).slice(0, limit)) {
|
|
14453
|
-
const path2 =
|
|
14454
|
-
if (!isDirectChild(path2, root) ||
|
|
15468
|
+
const path2 = resolve10(root, name);
|
|
15469
|
+
if (!isDirectChild(path2, root) || basename7(path2) !== name) {
|
|
14455
15470
|
invalid += 1;
|
|
14456
15471
|
continue;
|
|
14457
15472
|
}
|
|
@@ -14473,7 +15488,7 @@ var ImageStartupReconciler = class {
|
|
|
14473
15488
|
let invalid = 0;
|
|
14474
15489
|
let active = 0;
|
|
14475
15490
|
for (const name of readdirSync3(root).slice(0, this.#maxTemporaryDirectoriesPerPass)) {
|
|
14476
|
-
const path2 =
|
|
15491
|
+
const path2 = resolve10(root, name);
|
|
14477
15492
|
if (!OWNED_TEMPORARY_DIRECTORY.test(name) || !isDirectChild(path2, root)) {
|
|
14478
15493
|
foreign += 1;
|
|
14479
15494
|
continue;
|
|
@@ -14488,8 +15503,8 @@ var ImageStartupReconciler = class {
|
|
|
14488
15503
|
active += 1;
|
|
14489
15504
|
continue;
|
|
14490
15505
|
}
|
|
14491
|
-
const markerPath =
|
|
14492
|
-
if (!isDirectChild(markerPath, path2) || !
|
|
15506
|
+
const markerPath = resolve10(path2, IMAGE_REQUEST_DIRECTORY_MARKER_NAME);
|
|
15507
|
+
if (!isDirectChild(markerPath, path2) || !existsSync14(markerPath)) {
|
|
14493
15508
|
foreign += 1;
|
|
14494
15509
|
continue;
|
|
14495
15510
|
}
|
|
@@ -14521,7 +15536,7 @@ var ImageStartupReconciler = class {
|
|
|
14521
15536
|
throw new TypeError("refusing to remove an unsupported temporary descendant");
|
|
14522
15537
|
}
|
|
14523
15538
|
for (const name of readdirSync3(path2)) {
|
|
14524
|
-
this.removeTreeWithoutFollowingSymlinks(
|
|
15539
|
+
this.removeTreeWithoutFollowingSymlinks(resolve10(path2, name), path2);
|
|
14525
15540
|
}
|
|
14526
15541
|
rmdirSync2(path2);
|
|
14527
15542
|
}
|
|
@@ -14990,8 +16005,8 @@ function createHostedImageContributionFactory(manager) {
|
|
|
14990
16005
|
function deferredRecord(generation, phase) {
|
|
14991
16006
|
let resolveDisposed;
|
|
14992
16007
|
let rejectDisposed;
|
|
14993
|
-
const disposed = new Promise((
|
|
14994
|
-
resolveDisposed =
|
|
16008
|
+
const disposed = new Promise((resolve11, reject) => {
|
|
16009
|
+
resolveDisposed = resolve11;
|
|
14995
16010
|
rejectDisposed = reject;
|
|
14996
16011
|
});
|
|
14997
16012
|
void disposed.catch(() => void 0);
|
|
@@ -15570,7 +16585,7 @@ function toLLMProvider(row) {
|
|
|
15570
16585
|
// src/ports/ConfigurableLogger.ts
|
|
15571
16586
|
import {
|
|
15572
16587
|
createWriteStream,
|
|
15573
|
-
existsSync as
|
|
16588
|
+
existsSync as existsSync15,
|
|
15574
16589
|
renameSync as renameSync7,
|
|
15575
16590
|
statSync as statSync4,
|
|
15576
16591
|
unlinkSync as unlinkSync9
|
|
@@ -15622,7 +16637,7 @@ var ConfigurableLogger = class {
|
|
|
15622
16637
|
this.fileStream = null;
|
|
15623
16638
|
this.rotateQueue = [];
|
|
15624
16639
|
if (!stream) return Promise.resolve();
|
|
15625
|
-
return new Promise((
|
|
16640
|
+
return new Promise((resolve11) => stream.end(() => resolve11()));
|
|
15626
16641
|
}
|
|
15627
16642
|
emit(level, message, error, meta) {
|
|
15628
16643
|
if (LEVEL_ORDER[level] > this.threshold) return;
|
|
@@ -15704,12 +16719,12 @@ var ConfigurableLogger = class {
|
|
|
15704
16719
|
/** `<file>.N` unlinked, `<file>.k` → `<file>.k+1`, `<file>` → `<file>.1`. */
|
|
15705
16720
|
shiftGenerations(path2) {
|
|
15706
16721
|
const oldest = `${path2}.${this.maxFiles}`;
|
|
15707
|
-
if (
|
|
16722
|
+
if (existsSync15(oldest)) unlinkSync9(oldest);
|
|
15708
16723
|
for (let i = this.maxFiles - 1; i >= 1; i--) {
|
|
15709
16724
|
const from = `${path2}.${i}`;
|
|
15710
|
-
if (
|
|
16725
|
+
if (existsSync15(from)) renameSync7(from, `${path2}.${i + 1}`);
|
|
15711
16726
|
}
|
|
15712
|
-
if (
|
|
16727
|
+
if (existsSync15(path2)) renameSync7(path2, `${path2}.1`);
|
|
15713
16728
|
}
|
|
15714
16729
|
/**
|
|
15715
16730
|
* Lazily open the append-only file stream; disable the sink on any error. The
|
|
@@ -15720,7 +16735,7 @@ var ConfigurableLogger = class {
|
|
|
15720
16735
|
if (this.fileDisabled || !this.filePath) return null;
|
|
15721
16736
|
if (this.fileStream) return this.fileStream;
|
|
15722
16737
|
try {
|
|
15723
|
-
this.fileBytes =
|
|
16738
|
+
this.fileBytes = existsSync15(this.filePath) ? statSync4(this.filePath).size : 0;
|
|
15724
16739
|
const stream = createWriteStream(this.filePath, { flags: "a" });
|
|
15725
16740
|
stream.on("error", () => {
|
|
15726
16741
|
this.fileDisabled = true;
|
|
@@ -15793,7 +16808,7 @@ function safeStringify(value) {
|
|
|
15793
16808
|
import { randomBytes as randomBytes10 } from "crypto";
|
|
15794
16809
|
import {
|
|
15795
16810
|
closeSync as closeSync6,
|
|
15796
|
-
existsSync as
|
|
16811
|
+
existsSync as existsSync16,
|
|
15797
16812
|
fsyncSync as fsyncSync6,
|
|
15798
16813
|
openSync as openSync6,
|
|
15799
16814
|
readFileSync as readFileSync12,
|
|
@@ -15801,12 +16816,12 @@ import {
|
|
|
15801
16816
|
unlinkSync as unlinkSync10,
|
|
15802
16817
|
writeFileSync as writeFileSync11
|
|
15803
16818
|
} from "fs";
|
|
15804
|
-
import { basename as
|
|
16819
|
+
import { basename as basename8, dirname as dirname12, join as join12 } from "path";
|
|
15805
16820
|
import { OUTBOUND_API_SERVER_CONFIG_KEY } from "@omnicross/core/outbound-api";
|
|
15806
16821
|
function atomicReplaceDocument(targetPath, contents) {
|
|
15807
|
-
const tempPath =
|
|
16822
|
+
const tempPath = join12(
|
|
15808
16823
|
dirname12(targetPath),
|
|
15809
|
-
`.${
|
|
16824
|
+
`.${basename8(targetPath)}.${process.pid}.${randomBytes10(8).toString("hex")}.tmp`
|
|
15810
16825
|
);
|
|
15811
16826
|
let fd;
|
|
15812
16827
|
try {
|
|
@@ -15823,7 +16838,7 @@ function atomicReplaceDocument(targetPath, contents) {
|
|
|
15823
16838
|
} catch {
|
|
15824
16839
|
}
|
|
15825
16840
|
}
|
|
15826
|
-
if (
|
|
16841
|
+
if (existsSync16(tempPath)) {
|
|
15827
16842
|
try {
|
|
15828
16843
|
unlinkSync10(tempPath);
|
|
15829
16844
|
} catch {
|
|
@@ -15866,13 +16881,13 @@ var JsonApiServerSettingsStore = class {
|
|
|
15866
16881
|
}
|
|
15867
16882
|
/** Capture the exact prior document for an admin transaction rollback. */
|
|
15868
16883
|
captureDocumentSnapshot() {
|
|
15869
|
-
if (!
|
|
16884
|
+
if (!existsSync16(this.configPath)) return { existed: false };
|
|
15870
16885
|
return { existed: true, bytes: readFileSync12(this.configPath) };
|
|
15871
16886
|
}
|
|
15872
16887
|
/** Restore exact prior bytes (including unrelated fields and encrypted secrets). */
|
|
15873
16888
|
restoreDocumentSnapshot(snapshot) {
|
|
15874
16889
|
if (!snapshot.existed) {
|
|
15875
|
-
if (
|
|
16890
|
+
if (existsSync16(this.configPath)) unlinkSync10(this.configPath);
|
|
15876
16891
|
return;
|
|
15877
16892
|
}
|
|
15878
16893
|
if (!snapshot.bytes) throw new TypeError("settings snapshot is missing prior bytes");
|
|
@@ -15909,13 +16924,13 @@ var JsonApiServerSettingsStore = class {
|
|
|
15909
16924
|
};
|
|
15910
16925
|
|
|
15911
16926
|
// src/ports/JsonlUsageEventStore.ts
|
|
15912
|
-
import { randomUUID as
|
|
16927
|
+
import { randomUUID as randomUUID5 } from "crypto";
|
|
15913
16928
|
import { appendFileSync, mkdirSync as mkdirSync5 } from "fs";
|
|
15914
|
-
import { join as
|
|
16929
|
+
import { join as join16 } from "path";
|
|
15915
16930
|
|
|
15916
16931
|
// src/usage/usageFiles.ts
|
|
15917
|
-
import { readdir } from "fs/promises";
|
|
15918
|
-
import { dirname as dirname13, join as
|
|
16932
|
+
import { readdir as readdir2 } from "fs/promises";
|
|
16933
|
+
import { dirname as dirname13, join as join13 } from "path";
|
|
15919
16934
|
var USAGE_DIR_NAME = "usage";
|
|
15920
16935
|
var USAGE_SHARD_RE = /^usage-(\d{4})-(\d{2})-(\d{2})\.jsonl$/;
|
|
15921
16936
|
var USAGE_ROLLUP_RE = /^usage-(\d{4})-(\d{2})-(\d{2})\.rollup\.json$/;
|
|
@@ -15932,7 +16947,7 @@ function usageRollupName(dayKey) {
|
|
|
15932
16947
|
return `usage-${dayKey}.rollup.json`;
|
|
15933
16948
|
}
|
|
15934
16949
|
function usageDirFor(eventsPath) {
|
|
15935
|
-
return
|
|
16950
|
+
return join13(dirname13(eventsPath), USAGE_DIR_NAME);
|
|
15936
16951
|
}
|
|
15937
16952
|
function dayKeyStartTs(dayKey) {
|
|
15938
16953
|
const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(dayKey);
|
|
@@ -15953,7 +16968,7 @@ function dayKeyEndTs(dayKey) {
|
|
|
15953
16968
|
async function listUsageDays(usageDir) {
|
|
15954
16969
|
let names;
|
|
15955
16970
|
try {
|
|
15956
|
-
names = await
|
|
16971
|
+
names = await readdir2(usageDir);
|
|
15957
16972
|
} catch {
|
|
15958
16973
|
return [];
|
|
15959
16974
|
}
|
|
@@ -16193,12 +17208,12 @@ function isUsageDayRollup(parsed) {
|
|
|
16193
17208
|
}
|
|
16194
17209
|
|
|
16195
17210
|
// src/usage/usageRollupStore.ts
|
|
16196
|
-
import { mkdir, readFile as
|
|
16197
|
-
import { join as
|
|
17211
|
+
import { mkdir, readFile as readFile3, rename as rename2, unlink as unlink2, writeFile as writeFile2 } from "fs/promises";
|
|
17212
|
+
import { join as join15 } from "path";
|
|
16198
17213
|
|
|
16199
17214
|
// src/usage/usageShardCache.ts
|
|
16200
|
-
import { open as
|
|
16201
|
-
import { join as
|
|
17215
|
+
import { open as open3 } from "fs/promises";
|
|
17216
|
+
import { join as join14 } from "path";
|
|
16202
17217
|
|
|
16203
17218
|
// src/usage/usageRow.ts
|
|
16204
17219
|
var NUMERIC_FIELDS = [
|
|
@@ -16258,10 +17273,10 @@ var NEWLINE = 10;
|
|
|
16258
17273
|
var READ_CHUNK_BYTES = 8 * 1024 * 1024;
|
|
16259
17274
|
var DEFAULT_MAX_RESIDENT_DAYS = 3;
|
|
16260
17275
|
async function streamShardRows(usageDir, dayKey, onRow) {
|
|
16261
|
-
const path2 =
|
|
17276
|
+
const path2 = join14(usageDir, usageShardName(dayKey));
|
|
16262
17277
|
let handle;
|
|
16263
17278
|
try {
|
|
16264
|
-
handle = await
|
|
17279
|
+
handle = await open3(path2, "r");
|
|
16265
17280
|
} catch {
|
|
16266
17281
|
return;
|
|
16267
17282
|
}
|
|
@@ -16291,7 +17306,7 @@ async function streamShardRows(usageDir, dayKey, onRow) {
|
|
|
16291
17306
|
async function shardSize(usageDir, dayKey) {
|
|
16292
17307
|
let handle;
|
|
16293
17308
|
try {
|
|
16294
|
-
handle = await
|
|
17309
|
+
handle = await open3(join14(usageDir, usageShardName(dayKey)), "r");
|
|
16295
17310
|
} catch {
|
|
16296
17311
|
return null;
|
|
16297
17312
|
}
|
|
@@ -16347,10 +17362,10 @@ var UsageShardCache = class {
|
|
|
16347
17362
|
} else {
|
|
16348
17363
|
this.touch(dayKey);
|
|
16349
17364
|
}
|
|
16350
|
-
const path2 =
|
|
17365
|
+
const path2 = join14(this.usageDir, usageShardName(dayKey));
|
|
16351
17366
|
let handle;
|
|
16352
17367
|
try {
|
|
16353
|
-
handle = await
|
|
17368
|
+
handle = await open3(path2, "r");
|
|
16354
17369
|
} catch {
|
|
16355
17370
|
return entry;
|
|
16356
17371
|
}
|
|
@@ -16469,7 +17484,7 @@ var UsageRollupStore = class {
|
|
|
16469
17484
|
}
|
|
16470
17485
|
async readSidecar(dayKey) {
|
|
16471
17486
|
try {
|
|
16472
|
-
const raw = await
|
|
17487
|
+
const raw = await readFile3(join15(this.usageDir, usageRollupName(dayKey)), "utf8");
|
|
16473
17488
|
const parsed = JSON.parse(raw);
|
|
16474
17489
|
return isUsageDayRollup(parsed) && parsed.date === dayKey ? parsed : null;
|
|
16475
17490
|
} catch {
|
|
@@ -16477,14 +17492,14 @@ var UsageRollupStore = class {
|
|
|
16477
17492
|
}
|
|
16478
17493
|
}
|
|
16479
17494
|
async writeSidecar(dayKey, rollup) {
|
|
16480
|
-
const target =
|
|
17495
|
+
const target = join15(this.usageDir, usageRollupName(dayKey));
|
|
16481
17496
|
const temp = `${target}.tmp`;
|
|
16482
17497
|
try {
|
|
16483
17498
|
await mkdir(this.usageDir, { recursive: true });
|
|
16484
|
-
await
|
|
16485
|
-
await
|
|
17499
|
+
await writeFile2(temp, JSON.stringify(rollup), "utf8");
|
|
17500
|
+
await rename2(temp, target);
|
|
16486
17501
|
} catch {
|
|
16487
|
-
await
|
|
17502
|
+
await unlink2(temp).catch(() => {
|
|
16488
17503
|
});
|
|
16489
17504
|
}
|
|
16490
17505
|
}
|
|
@@ -16559,13 +17574,13 @@ var JsonlUsageEventStore = class {
|
|
|
16559
17574
|
async insert(input) {
|
|
16560
17575
|
const row = {
|
|
16561
17576
|
...input,
|
|
16562
|
-
id:
|
|
17577
|
+
id: randomUUID5(),
|
|
16563
17578
|
ts: input.ts ?? Date.now()
|
|
16564
17579
|
};
|
|
16565
17580
|
const dayKey = usageDayKey(row.ts);
|
|
16566
17581
|
this.ensureDir();
|
|
16567
17582
|
const line = JSON.stringify(row) + "\n";
|
|
16568
|
-
appendFileSync(
|
|
17583
|
+
appendFileSync(join16(this.usageDir, usageShardName(dayKey)), line, "utf8");
|
|
16569
17584
|
if (dayKey !== this.lastAppendDay) {
|
|
16570
17585
|
this.rollups.invalidate(dayKey);
|
|
16571
17586
|
this.lastAppendDay = dayKey;
|
|
@@ -16940,7 +17955,7 @@ function bucketLabel(bucketStartTs, bucket) {
|
|
|
16940
17955
|
}
|
|
16941
17956
|
|
|
16942
17957
|
// src/ports/JsonOutboundKeyDb.ts
|
|
16943
|
-
import { existsSync as
|
|
17958
|
+
import { existsSync as existsSync18, readFileSync as readFileSync13 } from "fs";
|
|
16944
17959
|
import {
|
|
16945
17960
|
validateOutboundPermissions as validateOutboundPermissions3
|
|
16946
17961
|
} from "@omnicross/core";
|
|
@@ -16949,18 +17964,18 @@ import {
|
|
|
16949
17964
|
import { randomBytes as randomBytes11 } from "crypto";
|
|
16950
17965
|
import {
|
|
16951
17966
|
closeSync as closeSync7,
|
|
16952
|
-
existsSync as
|
|
17967
|
+
existsSync as existsSync17,
|
|
16953
17968
|
fsyncSync as fsyncSync7,
|
|
16954
17969
|
openSync as openSync7,
|
|
16955
17970
|
renameSync as renameSync9,
|
|
16956
17971
|
unlinkSync as unlinkSync11,
|
|
16957
17972
|
writeFileSync as writeFileSync12
|
|
16958
17973
|
} from "fs";
|
|
16959
|
-
import { basename as
|
|
17974
|
+
import { basename as basename9, dirname as dirname14, join as join17 } from "path";
|
|
16960
17975
|
function atomicReplaceUtf8(targetPath, contents) {
|
|
16961
|
-
const tempPath =
|
|
17976
|
+
const tempPath = join17(
|
|
16962
17977
|
dirname14(targetPath),
|
|
16963
|
-
`.${
|
|
17978
|
+
`.${basename9(targetPath)}.${process.pid}.${randomBytes11(8).toString("hex")}.tmp`
|
|
16964
17979
|
);
|
|
16965
17980
|
let fd;
|
|
16966
17981
|
try {
|
|
@@ -16977,7 +17992,7 @@ function atomicReplaceUtf8(targetPath, contents) {
|
|
|
16977
17992
|
} catch {
|
|
16978
17993
|
}
|
|
16979
17994
|
}
|
|
16980
|
-
if (
|
|
17995
|
+
if (existsSync17(tempPath)) {
|
|
16981
17996
|
try {
|
|
16982
17997
|
unlinkSync11(tempPath);
|
|
16983
17998
|
} catch {
|
|
@@ -17088,6 +18103,19 @@ var JsonOutboundKeyDb = class {
|
|
|
17088
18103
|
return true;
|
|
17089
18104
|
});
|
|
17090
18105
|
}
|
|
18106
|
+
async outboundApiKeysSetUpstream(id, target) {
|
|
18107
|
+
return this.mutateRow(id, (row) => {
|
|
18108
|
+
if (row.revokedAt !== null) return false;
|
|
18109
|
+
if (target === null) {
|
|
18110
|
+
delete row.boundUpstream;
|
|
18111
|
+
delete row.boundUpstreamProviderId;
|
|
18112
|
+
} else {
|
|
18113
|
+
row.boundUpstream = target;
|
|
18114
|
+
delete row.boundUpstreamProviderId;
|
|
18115
|
+
}
|
|
18116
|
+
return true;
|
|
18117
|
+
});
|
|
18118
|
+
}
|
|
17091
18119
|
async outboundApiKeysSetPolicy(id, policy) {
|
|
17092
18120
|
return this.mutateRow(id, (row) => {
|
|
17093
18121
|
if (row.revokedAt !== null) return false;
|
|
@@ -17130,7 +18158,7 @@ var JsonOutboundKeyDb = class {
|
|
|
17130
18158
|
}
|
|
17131
18159
|
/** Read the key rows, tolerating a missing/corrupt file (→ empty list). */
|
|
17132
18160
|
readRows() {
|
|
17133
|
-
if (!
|
|
18161
|
+
if (!existsSync18(this.keysPath)) return [];
|
|
17134
18162
|
try {
|
|
17135
18163
|
const parsed = JSON.parse(readFileSync13(this.keysPath, "utf8"));
|
|
17136
18164
|
return Array.isArray(parsed) ? parsed : [];
|
|
@@ -17149,8 +18177,8 @@ function applyPolicyField(row, field, value) {
|
|
|
17149
18177
|
}
|
|
17150
18178
|
|
|
17151
18179
|
// src/ports/JsonPricingStore.ts
|
|
17152
|
-
import { existsSync as
|
|
17153
|
-
import { randomUUID as
|
|
18180
|
+
import { existsSync as existsSync19, readFileSync as readFileSync14, renameSync as renameSync10, rmSync as rmSync3, writeFileSync as writeFileSync13 } from "fs";
|
|
18181
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
17154
18182
|
var JsonPricingStore = class {
|
|
17155
18183
|
constructor(pricingPath) {
|
|
17156
18184
|
this.pricingPath = pricingPath;
|
|
@@ -17164,7 +18192,7 @@ var JsonPricingStore = class {
|
|
|
17164
18192
|
* otherwise unusable pricing table after a crash or manual file edit.
|
|
17165
18193
|
*/
|
|
17166
18194
|
hasUsableSnapshot() {
|
|
17167
|
-
if (!
|
|
18195
|
+
if (!existsSync19(this.pricingPath)) return false;
|
|
17168
18196
|
try {
|
|
17169
18197
|
const parsed = JSON.parse(readFileSync14(this.pricingPath, "utf8"));
|
|
17170
18198
|
return Array.isArray(parsed) && parsed.some(isUsablePricingRow);
|
|
@@ -17279,7 +18307,7 @@ var JsonPricingStore = class {
|
|
|
17279
18307
|
}
|
|
17280
18308
|
/** Read the pricing rows, tolerating a missing/corrupt file (→ empty list). */
|
|
17281
18309
|
readRows() {
|
|
17282
|
-
if (!
|
|
18310
|
+
if (!existsSync19(this.pricingPath)) return [];
|
|
17283
18311
|
try {
|
|
17284
18312
|
const parsed = JSON.parse(readFileSync14(this.pricingPath, "utf8"));
|
|
17285
18313
|
return Array.isArray(parsed) ? parsed : [];
|
|
@@ -17288,7 +18316,7 @@ var JsonPricingStore = class {
|
|
|
17288
18316
|
}
|
|
17289
18317
|
}
|
|
17290
18318
|
writeRows(rows) {
|
|
17291
|
-
const temporaryPath = `${this.pricingPath}.${process.pid}.${
|
|
18319
|
+
const temporaryPath = `${this.pricingPath}.${process.pid}.${randomUUID6()}.tmp`;
|
|
17292
18320
|
try {
|
|
17293
18321
|
writeFileSync13(temporaryPath, JSON.stringify(rows, null, 2) + "\n", {
|
|
17294
18322
|
encoding: "utf8",
|
|
@@ -17311,7 +18339,7 @@ function isUsablePricingRow(value) {
|
|
|
17311
18339
|
}
|
|
17312
18340
|
|
|
17313
18341
|
// src/pricing/PricingRefreshScheduler.ts
|
|
17314
|
-
import { existsSync as
|
|
18342
|
+
import { existsSync as existsSync20, readFileSync as readFileSync15, renameSync as renameSync11, writeFileSync as writeFileSync14 } from "fs";
|
|
17315
18343
|
var EMPTY_STATE2 = {
|
|
17316
18344
|
lastAttemptAt: null,
|
|
17317
18345
|
lastSuccessAt: null,
|
|
@@ -17349,7 +18377,7 @@ var PricingRefreshScheduler = class {
|
|
|
17349
18377
|
this.timer = null;
|
|
17350
18378
|
}
|
|
17351
18379
|
getState() {
|
|
17352
|
-
if (!
|
|
18380
|
+
if (!existsSync20(this.statePath)) return { ...EMPTY_STATE2, sources: [] };
|
|
17353
18381
|
try {
|
|
17354
18382
|
const value = JSON.parse(readFileSync15(this.statePath, "utf8"));
|
|
17355
18383
|
return {
|
|
@@ -17414,7 +18442,7 @@ function finiteOrNull(value) {
|
|
|
17414
18442
|
}
|
|
17415
18443
|
|
|
17416
18444
|
// src/ports/JsonVoucherDb.ts
|
|
17417
|
-
import { existsSync as
|
|
18445
|
+
import { existsSync as existsSync21, readFileSync as readFileSync16, writeFileSync as writeFileSync15 } from "fs";
|
|
17418
18446
|
var JsonVoucherDb = class {
|
|
17419
18447
|
constructor(vouchersPath) {
|
|
17420
18448
|
this.vouchersPath = vouchersPath;
|
|
@@ -17492,7 +18520,7 @@ var JsonVoucherDb = class {
|
|
|
17492
18520
|
}
|
|
17493
18521
|
/** Read the voucher rows, tolerating a missing/corrupt file (→ empty list). */
|
|
17494
18522
|
readRows() {
|
|
17495
|
-
if (!
|
|
18523
|
+
if (!existsSync21(this.vouchersPath)) return [];
|
|
17496
18524
|
try {
|
|
17497
18525
|
const parsed = JSON.parse(readFileSync16(this.vouchersPath, "utf8"));
|
|
17498
18526
|
return Array.isArray(parsed) ? parsed : [];
|
|
@@ -17506,7 +18534,7 @@ var JsonVoucherDb = class {
|
|
|
17506
18534
|
};
|
|
17507
18535
|
|
|
17508
18536
|
// src/ports/JsonSubscriptionCredentialStore.ts
|
|
17509
|
-
import { existsSync as
|
|
18537
|
+
import { existsSync as existsSync23, mkdirSync as mkdirSync6, readFileSync as readFileSync18, renameSync as renameSync12 } from "fs";
|
|
17510
18538
|
import { dirname as dirname15 } from "path";
|
|
17511
18539
|
import { getAntigravityProjectResolver as getAntigravityProjectResolver2 } from "@omnicross/core/auth/GeminiCodeAssistProjectResolver";
|
|
17512
18540
|
import { getSharedAccountHealth as getSharedAccountHealth2 } from "@omnicross/core/pipeline/SubscriptionAccountHealth";
|
|
@@ -17564,11 +18592,11 @@ function findDuplicateCredentialIds(accounts) {
|
|
|
17564
18592
|
}
|
|
17565
18593
|
|
|
17566
18594
|
// src/ports/external-cli-credentials.ts
|
|
17567
|
-
import { existsSync as
|
|
17568
|
-
import { homedir as
|
|
17569
|
-
import { join as
|
|
17570
|
-
function externalStorePath(provider, home =
|
|
17571
|
-
return provider === "claude" ?
|
|
18595
|
+
import { existsSync as existsSync22, readFileSync as readFileSync17 } from "fs";
|
|
18596
|
+
import { homedir as homedir5 } from "os";
|
|
18597
|
+
import { join as join18 } from "path";
|
|
18598
|
+
function externalStorePath(provider, home = homedir5()) {
|
|
18599
|
+
return provider === "claude" ? join18(home, ".claude", ".credentials.json") : join18(home, ".codex", "auth.json");
|
|
17572
18600
|
}
|
|
17573
18601
|
function decodeJwtExpiryMs(token) {
|
|
17574
18602
|
try {
|
|
@@ -17615,9 +18643,9 @@ function parseCodexTokensEnvelope(raw) {
|
|
|
17615
18643
|
}
|
|
17616
18644
|
return parsed;
|
|
17617
18645
|
}
|
|
17618
|
-
function readExternalCliCredentials(provider, home =
|
|
18646
|
+
function readExternalCliCredentials(provider, home = homedir5()) {
|
|
17619
18647
|
const path2 = externalStorePath(provider, home);
|
|
17620
|
-
if (!
|
|
18648
|
+
if (!existsSync22(path2)) return null;
|
|
17621
18649
|
let raw;
|
|
17622
18650
|
try {
|
|
17623
18651
|
const parsed = JSON.parse(readFileSync17(path2, "utf8"));
|
|
@@ -18424,7 +19452,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
18424
19452
|
* its parse try.
|
|
18425
19453
|
*/
|
|
18426
19454
|
readConfig() {
|
|
18427
|
-
if (!
|
|
19455
|
+
if (!existsSync23(this.tokensPath)) return { updatedAt: "" };
|
|
18428
19456
|
let parsed;
|
|
18429
19457
|
try {
|
|
18430
19458
|
const raw = JSON.parse(readFileSync18(this.tokensPath, "utf8"));
|
|
@@ -19001,14 +20029,14 @@ var AccountHealthSweeper = class {
|
|
|
19001
20029
|
};
|
|
19002
20030
|
|
|
19003
20031
|
// src/audit/AuditPruneSweeper.ts
|
|
19004
|
-
import { createReadStream as createReadStream3, createWriteStream as createWriteStream2, existsSync as
|
|
19005
|
-
import { join as
|
|
20032
|
+
import { createReadStream as createReadStream3, createWriteStream as createWriteStream2, existsSync as existsSync26, readdirSync as readdirSync6, rmSync as rmSync4, unlinkSync as unlinkSync13 } from "fs";
|
|
20033
|
+
import { join as join21 } from "path";
|
|
19006
20034
|
import { pipeline } from "stream/promises";
|
|
19007
20035
|
import { createGzip } from "zlib";
|
|
19008
20036
|
|
|
19009
20037
|
// src/audit/auditDictionary.ts
|
|
19010
|
-
import { existsSync as
|
|
19011
|
-
import { join as
|
|
20038
|
+
import { existsSync as existsSync24, readdirSync as readdirSync4, readFileSync as readFileSync19, renameSync as renameSync13, unlinkSync as unlinkSync12, writeFileSync as writeFileSync16 } from "fs";
|
|
20039
|
+
import { join as join19 } from "path";
|
|
19012
20040
|
|
|
19013
20041
|
// src/audit/auditBodyStore.ts
|
|
19014
20042
|
var ANCHOR_EVERY = 64;
|
|
@@ -19264,10 +20292,10 @@ function chooseDictionary(anchors) {
|
|
|
19264
20292
|
}
|
|
19265
20293
|
var EMPTY = { shards: 0, anchors: 0, savedBytes: 0 };
|
|
19266
20294
|
function compactAuditDay(dayPath) {
|
|
19267
|
-
const bodiesPath =
|
|
19268
|
-
if (!
|
|
19269
|
-
const dictPath =
|
|
19270
|
-
if (
|
|
20295
|
+
const bodiesPath = join19(dayPath, AUDIT_BODIES_DIR);
|
|
20296
|
+
if (!existsSync24(bodiesPath)) return EMPTY;
|
|
20297
|
+
const dictPath = join19(bodiesPath, AUDIT_DICT_FILE);
|
|
20298
|
+
if (existsSync24(dictPath) || existsSync24(`${dictPath}.gz`)) return EMPTY;
|
|
19271
20299
|
const shardFiles = plainShards(bodiesPath);
|
|
19272
20300
|
if (shardFiles.length < 2) return EMPTY;
|
|
19273
20301
|
const loaded = /* @__PURE__ */ new Map();
|
|
@@ -19275,7 +20303,7 @@ function compactAuditDay(dayPath) {
|
|
|
19275
20303
|
for (const file of shardFiles) {
|
|
19276
20304
|
let entries;
|
|
19277
20305
|
try {
|
|
19278
|
-
entries = parseEntries(readFileSync19(
|
|
20306
|
+
entries = parseEntries(readFileSync19(join19(bodiesPath, file), "utf8"));
|
|
19279
20307
|
} catch {
|
|
19280
20308
|
continue;
|
|
19281
20309
|
}
|
|
@@ -19309,14 +20337,14 @@ function compactAuditDay(dayPath) {
|
|
|
19309
20337
|
};
|
|
19310
20338
|
});
|
|
19311
20339
|
if (!changed) continue;
|
|
19312
|
-
const target =
|
|
20340
|
+
const target = join19(bodiesPath, file);
|
|
19313
20341
|
const temp = `${target}.compacting`;
|
|
19314
20342
|
try {
|
|
19315
20343
|
writeFileSync16(temp, rewritten.map((e) => JSON.stringify(e)).join("\n") + "\n", "utf8");
|
|
19316
20344
|
renameSync13(temp, target);
|
|
19317
20345
|
} catch {
|
|
19318
20346
|
try {
|
|
19319
|
-
if (
|
|
20347
|
+
if (existsSync24(temp)) unlinkSync12(temp);
|
|
19320
20348
|
} catch {
|
|
19321
20349
|
}
|
|
19322
20350
|
continue;
|
|
@@ -19335,7 +20363,7 @@ function compactAuditDay(dayPath) {
|
|
|
19335
20363
|
}
|
|
19336
20364
|
function compactAllClosedAuditDays(auditDir, now = Date.now) {
|
|
19337
20365
|
const run = { days: 0, shards: 0, savedBytes: 0 };
|
|
19338
|
-
if (!
|
|
20366
|
+
if (!existsSync24(auditDir)) return run;
|
|
19339
20367
|
const today = auditDayDirName(now());
|
|
19340
20368
|
let names;
|
|
19341
20369
|
try {
|
|
@@ -19346,7 +20374,7 @@ function compactAllClosedAuditDays(auditDir, now = Date.now) {
|
|
|
19346
20374
|
for (const name of names) {
|
|
19347
20375
|
if (name === today) continue;
|
|
19348
20376
|
try {
|
|
19349
|
-
const result = compactAuditDay(
|
|
20377
|
+
const result = compactAuditDay(join19(auditDir, name));
|
|
19350
20378
|
if (result.shards === 0) continue;
|
|
19351
20379
|
run.days += 1;
|
|
19352
20380
|
run.shards += result.shards;
|
|
@@ -19360,13 +20388,13 @@ function compactAllClosedAuditDays(auditDir, now = Date.now) {
|
|
|
19360
20388
|
// src/audit/auditStats.ts
|
|
19361
20389
|
import {
|
|
19362
20390
|
createReadStream as createReadStream2,
|
|
19363
|
-
existsSync as
|
|
20391
|
+
existsSync as existsSync25,
|
|
19364
20392
|
readFileSync as readFileSync20,
|
|
19365
20393
|
readdirSync as readdirSync5,
|
|
19366
20394
|
statSync as statSync5,
|
|
19367
20395
|
writeFileSync as writeFileSync17
|
|
19368
20396
|
} from "fs";
|
|
19369
|
-
import { basename as
|
|
20397
|
+
import { basename as basename10, dirname as dirname16, join as join20 } from "path";
|
|
19370
20398
|
var SIDECAR_VERSION = 1;
|
|
19371
20399
|
var META_PREFIX_BYTES = 64 * 1024;
|
|
19372
20400
|
var READ_CHUNK_BYTES2 = 4 * 1024 * 1024;
|
|
@@ -19374,7 +20402,7 @@ function auditStatsFileName(auditFile) {
|
|
|
19374
20402
|
return auditFile.replace(/\.jsonl$/, ".stats.json");
|
|
19375
20403
|
}
|
|
19376
20404
|
function readPersisted(path2) {
|
|
19377
|
-
if (!
|
|
20405
|
+
if (!existsSync25(path2)) return null;
|
|
19378
20406
|
try {
|
|
19379
20407
|
const value = JSON.parse(readFileSync20(path2, "utf8"));
|
|
19380
20408
|
if (value.version !== SIDECAR_VERSION || !Number.isSafeInteger(value.auditBytes) || (value.auditBytes ?? -1) < 0 || !Number.isSafeInteger(value.requestCount) || (value.requestCount ?? -1) < 0 || !Number.isSafeInteger(value.errorCount) || (value.errorCount ?? -1) < 0 || (value.errorCount ?? 0) > (value.requestCount ?? -1) || typeof value.complete !== "boolean" || value.minTs !== null && !Number.isFinite(value.minTs) || value.maxTs !== null && !Number.isFinite(value.maxTs)) {
|
|
@@ -19386,7 +20414,7 @@ function readPersisted(path2) {
|
|
|
19386
20414
|
}
|
|
19387
20415
|
}
|
|
19388
20416
|
function updateAuditStatsAfterAppend(auditPath, auditBytesBefore, auditBytesAfter, record) {
|
|
19389
|
-
const statsPath =
|
|
20417
|
+
const statsPath = join20(dirname16(auditPath), auditStatsFileName(basename10(auditPath)));
|
|
19390
20418
|
const previous = auditBytesBefore === 0 ? {
|
|
19391
20419
|
version: SIDECAR_VERSION,
|
|
19392
20420
|
auditBytes: 0,
|
|
@@ -19517,20 +20545,20 @@ function mergePersistedStats(previous, appended) {
|
|
|
19517
20545
|
};
|
|
19518
20546
|
}
|
|
19519
20547
|
async function readAuditStats(auditDir, query2 = {}) {
|
|
19520
|
-
if (!
|
|
20548
|
+
if (!existsSync25(auditDir)) return { requestCount: 0, errorCount: 0, complete: true };
|
|
19521
20549
|
const from = typeof query2.from === "number" ? query2.from : -Infinity;
|
|
19522
20550
|
const to = typeof query2.to === "number" ? query2.to : Infinity;
|
|
19523
20551
|
let sources;
|
|
19524
20552
|
try {
|
|
19525
20553
|
sources = readdirSync5(auditDir).filter((name) => fileOverlaps(name, from, to)).sort().map(
|
|
19526
20554
|
(name) => AUDIT_DAY_DIR_RE.test(name) ? {
|
|
19527
|
-
auditPath:
|
|
19528
|
-
statsPath:
|
|
20555
|
+
auditPath: join20(auditDir, name, AUDIT_META_FILE),
|
|
20556
|
+
statsPath: join20(auditDir, name, auditStatsFileName(AUDIT_META_FILE))
|
|
19529
20557
|
} : {
|
|
19530
|
-
auditPath:
|
|
19531
|
-
statsPath:
|
|
20558
|
+
auditPath: join20(auditDir, name),
|
|
20559
|
+
statsPath: join20(auditDir, auditStatsFileName(name))
|
|
19532
20560
|
}
|
|
19533
|
-
).filter((source) =>
|
|
20561
|
+
).filter((source) => existsSync25(source.auditPath));
|
|
19534
20562
|
} catch {
|
|
19535
20563
|
return { requestCount: 0, errorCount: 0, complete: false };
|
|
19536
20564
|
}
|
|
@@ -19628,7 +20656,7 @@ var AuditPruneSweeper = class {
|
|
|
19628
20656
|
if (!this.config.enabled || this.sweeping) return 0;
|
|
19629
20657
|
this.sweeping = true;
|
|
19630
20658
|
try {
|
|
19631
|
-
if (!
|
|
20659
|
+
if (!existsSync26(this.auditDir)) return 0;
|
|
19632
20660
|
const cutoff = this.todayMidnight() - (this.config.retentionDays - 1) * DAY_MS4;
|
|
19633
20661
|
let removed = 0;
|
|
19634
20662
|
for (const name of readdirSync6(this.auditDir)) {
|
|
@@ -19636,11 +20664,11 @@ var AuditPruneSweeper = class {
|
|
|
19636
20664
|
if (dateMs === null || dateMs >= cutoff) continue;
|
|
19637
20665
|
try {
|
|
19638
20666
|
if (isAuditDayDir(name)) {
|
|
19639
|
-
rmSync4(
|
|
20667
|
+
rmSync4(join21(this.auditDir, name), { recursive: true, force: true });
|
|
19640
20668
|
} else {
|
|
19641
|
-
unlinkSync13(
|
|
19642
|
-
const statsPath =
|
|
19643
|
-
if (
|
|
20669
|
+
unlinkSync13(join21(this.auditDir, name));
|
|
20670
|
+
const statsPath = join21(this.auditDir, auditStatsFileName(name));
|
|
20671
|
+
if (existsSync26(statsPath)) unlinkSync13(statsPath);
|
|
19644
20672
|
}
|
|
19645
20673
|
removed += 1;
|
|
19646
20674
|
} catch (error) {
|
|
@@ -19670,14 +20698,14 @@ var AuditPruneSweeper = class {
|
|
|
19670
20698
|
if (!this.config.enabled || this.archiving) return 0;
|
|
19671
20699
|
this.archiving = true;
|
|
19672
20700
|
try {
|
|
19673
|
-
if (!
|
|
20701
|
+
if (!existsSync26(this.auditDir)) return 0;
|
|
19674
20702
|
const today = this.todayMidnight();
|
|
19675
20703
|
let compressed = 0;
|
|
19676
20704
|
for (const name of readdirSync6(this.auditDir)) {
|
|
19677
20705
|
if (compressed >= ARCHIVE_BATCH) break;
|
|
19678
20706
|
const dateMs = auditFileDateMs(name);
|
|
19679
20707
|
if (dateMs === null || dateMs >= today || !isAuditDayDir(name)) continue;
|
|
19680
|
-
const dayPath =
|
|
20708
|
+
const dayPath = join21(this.auditDir, name);
|
|
19681
20709
|
try {
|
|
19682
20710
|
const compaction = compactAuditDay(dayPath);
|
|
19683
20711
|
if (compaction.shards > 0) {
|
|
@@ -19695,7 +20723,7 @@ var AuditPruneSweeper = class {
|
|
|
19695
20723
|
});
|
|
19696
20724
|
}
|
|
19697
20725
|
compressed += await this.archiveDay(
|
|
19698
|
-
|
|
20726
|
+
join21(dayPath, AUDIT_BODIES_DIR),
|
|
19699
20727
|
ARCHIVE_BATCH - compressed
|
|
19700
20728
|
);
|
|
19701
20729
|
}
|
|
@@ -19721,10 +20749,10 @@ var AuditPruneSweeper = class {
|
|
|
19721
20749
|
let compressed = 0;
|
|
19722
20750
|
for (const shard of shards) {
|
|
19723
20751
|
if (compressed >= budget) break;
|
|
19724
|
-
const source =
|
|
20752
|
+
const source = join21(bodiesPath, shard);
|
|
19725
20753
|
const target = `${source}.gz`;
|
|
19726
20754
|
try {
|
|
19727
|
-
if (
|
|
20755
|
+
if (existsSync26(target)) {
|
|
19728
20756
|
unlinkSync13(source);
|
|
19729
20757
|
continue;
|
|
19730
20758
|
}
|
|
@@ -19733,7 +20761,7 @@ var AuditPruneSweeper = class {
|
|
|
19733
20761
|
compressed += 1;
|
|
19734
20762
|
} catch (error) {
|
|
19735
20763
|
try {
|
|
19736
|
-
if (
|
|
20764
|
+
if (existsSync26(target)) unlinkSync13(target);
|
|
19737
20765
|
} catch {
|
|
19738
20766
|
}
|
|
19739
20767
|
this.logger.warn("[AuditPruneSweeper] failed to archive audit body shard", {
|
|
@@ -19748,8 +20776,8 @@ var AuditPruneSweeper = class {
|
|
|
19748
20776
|
|
|
19749
20777
|
// src/usage/usageMigrate.ts
|
|
19750
20778
|
import { createReadStream as createReadStream4 } from "fs";
|
|
19751
|
-
import { mkdir as mkdir2, open as
|
|
19752
|
-
import { join as
|
|
20779
|
+
import { mkdir as mkdir2, open as open4, readdir as readdir3, rename as rename3, rm, stat as stat2, unlink as unlink3, writeFile as writeFile3 } from "fs/promises";
|
|
20780
|
+
import { join as join22 } from "path";
|
|
19753
20781
|
import { createInterface } from "readline";
|
|
19754
20782
|
var FLUSH_BYTES = 4 * 1024 * 1024;
|
|
19755
20783
|
async function writeLine(writer2, line) {
|
|
@@ -19773,14 +20801,14 @@ var IDLE = {
|
|
|
19773
20801
|
};
|
|
19774
20802
|
async function migrateLegacyUsageEvents(opts) {
|
|
19775
20803
|
const { eventsPath, usageDir, logger } = opts;
|
|
19776
|
-
const legacy = await
|
|
20804
|
+
const legacy = await stat2(eventsPath).catch(() => null);
|
|
19777
20805
|
if (!legacy || !legacy.isFile()) return IDLE;
|
|
19778
20806
|
if (legacy.size === 0) {
|
|
19779
|
-
await
|
|
20807
|
+
await unlink3(eventsPath).catch(() => {
|
|
19780
20808
|
});
|
|
19781
20809
|
return { ...IDLE, migrated: true };
|
|
19782
20810
|
}
|
|
19783
|
-
const scratch =
|
|
20811
|
+
const scratch = join22(usageDir, USAGE_MIGRATING_DIR);
|
|
19784
20812
|
logger?.info("[usage] migrating legacy usage-events.jsonl into day shards", {
|
|
19785
20813
|
bytes: legacy.size
|
|
19786
20814
|
});
|
|
@@ -19809,7 +20837,7 @@ async function migrateLegacyUsageEvents(opts) {
|
|
|
19809
20837
|
let writer2 = writers.get(dayKey);
|
|
19810
20838
|
if (!writer2) {
|
|
19811
20839
|
writer2 = {
|
|
19812
|
-
handle: await
|
|
20840
|
+
handle: await open4(join22(scratch, usageShardName(dayKey)), "a"),
|
|
19813
20841
|
buffer: [],
|
|
19814
20842
|
bytes: 0
|
|
19815
20843
|
};
|
|
@@ -19839,17 +20867,17 @@ async function migrateLegacyUsageEvents(opts) {
|
|
|
19839
20867
|
const today = usageDayKey(opts.now ?? Date.now());
|
|
19840
20868
|
for (const [dayKey, acc] of rollups) {
|
|
19841
20869
|
if (dayKey === today) continue;
|
|
19842
|
-
const size = await
|
|
20870
|
+
const size = await stat2(join22(scratch, usageShardName(dayKey))).then((s) => s.size).catch(() => null);
|
|
19843
20871
|
if (size === null) continue;
|
|
19844
|
-
await
|
|
19845
|
-
|
|
20872
|
+
await writeFile3(
|
|
20873
|
+
join22(scratch, usageRollupName(dayKey)),
|
|
19846
20874
|
JSON.stringify(acc.finish(dayKey, size)),
|
|
19847
20875
|
"utf8"
|
|
19848
20876
|
);
|
|
19849
20877
|
}
|
|
19850
20878
|
await mkdir2(usageDir, { recursive: true });
|
|
19851
|
-
const staged = await
|
|
19852
|
-
const existing = new Set(await
|
|
20879
|
+
const staged = await readdir3(scratch);
|
|
20880
|
+
const existing = new Set(await readdir3(usageDir).catch(() => []));
|
|
19853
20881
|
const collisions = staged.filter((name) => existing.has(name));
|
|
19854
20882
|
if (collisions.length > 0) {
|
|
19855
20883
|
const reason = `refusing to overwrite existing shards in ${usageDir} (${collisions.slice(0, 3).join(", ")}${collisions.length > 3 ? ", \u2026" : ""}); a previous migration may have partially committed \u2014 move or remove them and restart`;
|
|
@@ -19859,11 +20887,11 @@ async function migrateLegacyUsageEvents(opts) {
|
|
|
19859
20887
|
return { ...IDLE, linesRead, rowsWritten, skipped, reason };
|
|
19860
20888
|
}
|
|
19861
20889
|
for (const name of staged) {
|
|
19862
|
-
await
|
|
20890
|
+
await rename3(join22(scratch, name), join22(usageDir, name));
|
|
19863
20891
|
}
|
|
19864
20892
|
await rm(scratch, { recursive: true, force: true }).catch(() => {
|
|
19865
20893
|
});
|
|
19866
|
-
await
|
|
20894
|
+
await unlink3(eventsPath);
|
|
19867
20895
|
const days = rollups.size;
|
|
19868
20896
|
logger?.info("[usage] migration complete; legacy usage-events.jsonl removed", {
|
|
19869
20897
|
linesRead,
|
|
@@ -19884,8 +20912,8 @@ async function closeAll(writers) {
|
|
|
19884
20912
|
}
|
|
19885
20913
|
|
|
19886
20914
|
// src/usage/UsagePruneSweeper.ts
|
|
19887
|
-
import { unlink as
|
|
19888
|
-
import { join as
|
|
20915
|
+
import { unlink as unlink4 } from "fs/promises";
|
|
20916
|
+
import { join as join23 } from "path";
|
|
19889
20917
|
var DAY_MS5 = 24 * 60 * 6e4;
|
|
19890
20918
|
var SWEEP_INTERVAL_MS3 = 60 * 6e4;
|
|
19891
20919
|
var DEFAULT_USAGE_RETENTION_DAYS = 90;
|
|
@@ -19966,7 +20994,7 @@ var UsagePruneSweeper = class {
|
|
|
19966
20994
|
continue;
|
|
19967
20995
|
}
|
|
19968
20996
|
try {
|
|
19969
|
-
await
|
|
20997
|
+
await unlink4(join23(this.usageDir, usageShardName(entry.dayKey)));
|
|
19970
20998
|
removed += 1;
|
|
19971
20999
|
} catch (error) {
|
|
19972
21000
|
this.logger.warn("[usage] retention: could not remove expired shard", {
|
|
@@ -20001,8 +21029,8 @@ var UsagePruneSweeper = class {
|
|
|
20001
21029
|
};
|
|
20002
21030
|
|
|
20003
21031
|
// src/audit/auditBodyReader.ts
|
|
20004
|
-
import { existsSync as
|
|
20005
|
-
import { join as
|
|
21032
|
+
import { existsSync as existsSync27, readdirSync as readdirSync7, readFileSync as readFileSync21, statSync as statSync7 } from "fs";
|
|
21033
|
+
import { join as join24 } from "path";
|
|
20006
21034
|
import { gunzipSync } from "zlib";
|
|
20007
21035
|
|
|
20008
21036
|
// src/audit/auditJsonl.ts
|
|
@@ -20065,7 +21093,7 @@ function forEachLineFromTail(path2, onLine) {
|
|
|
20065
21093
|
function candidateDays(auditDir, ts) {
|
|
20066
21094
|
if (typeof ts === "number" && Number.isFinite(ts)) {
|
|
20067
21095
|
const named = auditDayDirName(ts);
|
|
20068
|
-
if (
|
|
21096
|
+
if (existsSync27(join24(auditDir, named))) return [named];
|
|
20069
21097
|
}
|
|
20070
21098
|
try {
|
|
20071
21099
|
return readdirSync7(auditDir).filter(isAuditDayDir).sort().reverse();
|
|
@@ -20074,11 +21102,11 @@ function candidateDays(auditDir, ts) {
|
|
|
20074
21102
|
}
|
|
20075
21103
|
}
|
|
20076
21104
|
function readShard(auditDir, day, sessionKey) {
|
|
20077
|
-
const base =
|
|
21105
|
+
const base = join24(auditDir, day, AUDIT_BODIES_DIR, auditBodyFileName(sessionKey));
|
|
20078
21106
|
try {
|
|
20079
|
-
if (
|
|
21107
|
+
if (existsSync27(base)) return readFileSync21(base, "utf8");
|
|
20080
21108
|
const gz = `${base}.gz`;
|
|
20081
|
-
if (
|
|
21109
|
+
if (existsSync27(gz)) return gunzipSync(readFileSync21(gz)).toString("utf8");
|
|
20082
21110
|
} catch {
|
|
20083
21111
|
return null;
|
|
20084
21112
|
}
|
|
@@ -20108,11 +21136,11 @@ function withDictionary(auditDir, day, entries) {
|
|
|
20108
21136
|
}
|
|
20109
21137
|
}
|
|
20110
21138
|
if (!needed) return entries;
|
|
20111
|
-
const base =
|
|
21139
|
+
const base = join24(auditDir, day, AUDIT_BODIES_DIR, AUDIT_DICT_FILE);
|
|
20112
21140
|
let raw = null;
|
|
20113
21141
|
try {
|
|
20114
|
-
if (
|
|
20115
|
-
else if (
|
|
21142
|
+
if (existsSync27(base)) raw = readFileSync21(base, "utf8");
|
|
21143
|
+
else if (existsSync27(`${base}.gz`)) raw = gunzipSync(readFileSync21(`${base}.gz`)).toString("utf8");
|
|
20116
21144
|
} catch {
|
|
20117
21145
|
return entries;
|
|
20118
21146
|
}
|
|
@@ -20145,7 +21173,7 @@ function reconstructRequest(entries, entry) {
|
|
|
20145
21173
|
}
|
|
20146
21174
|
function readAuditBody(auditDir, query2) {
|
|
20147
21175
|
if (!isSafeSessionKey(query2.sessionKey) || !query2.id) return {};
|
|
20148
|
-
if (!
|
|
21176
|
+
if (!existsSync27(auditDir)) return {};
|
|
20149
21177
|
for (const day of candidateDays(auditDir, query2.ts)) {
|
|
20150
21178
|
const raw = readShard(auditDir, day, query2.sessionKey);
|
|
20151
21179
|
if (raw === null) continue;
|
|
@@ -20170,7 +21198,7 @@ function readLegacyInlineBody(auditDir, id) {
|
|
|
20170
21198
|
const needle = JSON.stringify(id);
|
|
20171
21199
|
let found = {};
|
|
20172
21200
|
for (const name of names) {
|
|
20173
|
-
forEachLineFromTail(
|
|
21201
|
+
forEachLineFromTail(join24(auditDir, name), (line) => {
|
|
20174
21202
|
if (!line.includes(needle)) return false;
|
|
20175
21203
|
let parsed;
|
|
20176
21204
|
try {
|
|
@@ -20192,8 +21220,8 @@ function readLegacyInlineBody(auditDir, id) {
|
|
|
20192
21220
|
}
|
|
20193
21221
|
|
|
20194
21222
|
// src/audit/auditReader.ts
|
|
20195
|
-
import { existsSync as
|
|
20196
|
-
import { join as
|
|
21223
|
+
import { existsSync as existsSync28, readdirSync as readdirSync8 } from "fs";
|
|
21224
|
+
import { join as join25 } from "path";
|
|
20197
21225
|
var DEFAULT_LIMIT = 200;
|
|
20198
21226
|
var MAX_LIMIT = 2e3;
|
|
20199
21227
|
var OVERSCAN = 256;
|
|
@@ -20209,10 +21237,10 @@ function daySources(auditDir) {
|
|
|
20209
21237
|
const dateMs = auditFileDateMs(name);
|
|
20210
21238
|
if (dateMs === null) continue;
|
|
20211
21239
|
if (AUDIT_DAY_DIR_RE.test(name)) {
|
|
20212
|
-
const path2 =
|
|
20213
|
-
if (
|
|
21240
|
+
const path2 = join25(auditDir, name, AUDIT_META_FILE);
|
|
21241
|
+
if (existsSync28(path2)) sources.push({ path: path2, dateMs });
|
|
20214
21242
|
} else if (AUDIT_FILE_RE.test(name)) {
|
|
20215
|
-
sources.push({ path:
|
|
21243
|
+
sources.push({ path: join25(auditDir, name), dateMs });
|
|
20216
21244
|
}
|
|
20217
21245
|
}
|
|
20218
21246
|
return sources.sort((a, b) => b.dateMs - a.dateMs);
|
|
@@ -20228,7 +21256,7 @@ function toMetaRecord(record) {
|
|
|
20228
21256
|
return { ...meta, hasBody: true };
|
|
20229
21257
|
}
|
|
20230
21258
|
function readAuditRecords(auditDir, query2 = {}) {
|
|
20231
|
-
if (!
|
|
21259
|
+
if (!existsSync28(auditDir)) return [];
|
|
20232
21260
|
const from = typeof query2.from === "number" ? query2.from : -Infinity;
|
|
20233
21261
|
const to = typeof query2.to === "number" ? query2.to : Infinity;
|
|
20234
21262
|
const limit = Math.min(MAX_LIMIT, Math.max(1, Math.trunc(query2.limit ?? DEFAULT_LIMIT)));
|
|
@@ -20256,8 +21284,8 @@ function readAuditRecords(auditDir, query2 = {}) {
|
|
|
20256
21284
|
}
|
|
20257
21285
|
|
|
20258
21286
|
// src/audit/AuditWriter.ts
|
|
20259
|
-
import { appendFileSync as appendFileSync2, existsSync as
|
|
20260
|
-
import { join as
|
|
21287
|
+
import { appendFileSync as appendFileSync2, existsSync as existsSync29, mkdirSync as mkdirSync7, statSync as statSync8 } from "fs";
|
|
21288
|
+
import { join as join26 } from "path";
|
|
20261
21289
|
var AuditWriter = class {
|
|
20262
21290
|
constructor(auditDir, logger, defer = (fn) => setTimeout(fn, 0)) {
|
|
20263
21291
|
this.auditDir = auditDir;
|
|
@@ -20297,7 +21325,7 @@ var AuditWriter = class {
|
|
|
20297
21325
|
*/
|
|
20298
21326
|
appendNow(record) {
|
|
20299
21327
|
const dayDir = auditDayDirName(record.ts);
|
|
20300
|
-
const dayPath = this.ensureDir(
|
|
21328
|
+
const dayPath = this.ensureDir(join26(this.auditDir, dayDir));
|
|
20301
21329
|
this.appendMeta(dayPath, record);
|
|
20302
21330
|
this.appendBody(dayPath, dayDir, record);
|
|
20303
21331
|
}
|
|
@@ -20312,9 +21340,9 @@ var AuditWriter = class {
|
|
|
20312
21340
|
/** Write the body-free metadata line + refresh the exact-count sidecar. */
|
|
20313
21341
|
appendMeta(dayPath, record) {
|
|
20314
21342
|
const { requestBody: _req, responseBody: _res, ...meta } = record;
|
|
20315
|
-
const file =
|
|
21343
|
+
const file = join26(dayPath, AUDIT_META_FILE);
|
|
20316
21344
|
const line = JSON.stringify(meta) + "\n";
|
|
20317
|
-
const bytesBefore =
|
|
21345
|
+
const bytesBefore = existsSync29(file) ? statSync8(file).size : 0;
|
|
20318
21346
|
appendFileSync2(file, line, "utf8");
|
|
20319
21347
|
try {
|
|
20320
21348
|
updateAuditStatsAfterAppend(
|
|
@@ -20346,8 +21374,8 @@ var AuditWriter = class {
|
|
|
20346
21374
|
try {
|
|
20347
21375
|
const line = encodeBodyEntry(record, sessionKey, dayDir, this.bases);
|
|
20348
21376
|
if (line === null) return;
|
|
20349
|
-
const bodiesPath = this.ensureDir(
|
|
20350
|
-
appendFileSync2(
|
|
21377
|
+
const bodiesPath = this.ensureDir(join26(dayPath, AUDIT_BODIES_DIR));
|
|
21378
|
+
appendFileSync2(join26(bodiesPath, auditBodyFileName(sessionKey)), line + "\n", "utf8");
|
|
20351
21379
|
} catch (error) {
|
|
20352
21380
|
this.bases.forget(sessionKey);
|
|
20353
21381
|
this.logger.warn("[AuditWriter] failed to append audit body shard", {
|
|
@@ -20361,7 +21389,7 @@ var AuditWriter = class {
|
|
|
20361
21389
|
// src/billing/BillingPublisher.ts
|
|
20362
21390
|
import { appendFileSync as appendFileSync3, mkdirSync as mkdirSync8 } from "fs";
|
|
20363
21391
|
import { createHmac as createHmac5 } from "crypto";
|
|
20364
|
-
import { join as
|
|
21392
|
+
import { join as join27 } from "path";
|
|
20365
21393
|
import { fetchUpstream as fetchUpstream14 } from "@omnicross/core/pipeline/upstreamFetch";
|
|
20366
21394
|
|
|
20367
21395
|
// src/billing/billingFiles.ts
|
|
@@ -20432,7 +21460,7 @@ var BillingPublisher = class {
|
|
|
20432
21460
|
*/
|
|
20433
21461
|
appendNow(event) {
|
|
20434
21462
|
this.ensureDir();
|
|
20435
|
-
const file =
|
|
21463
|
+
const file = join27(this.billingDir, billingFileName(event.ts));
|
|
20436
21464
|
appendFileSync3(file, JSON.stringify(event) + "\n", "utf8");
|
|
20437
21465
|
}
|
|
20438
21466
|
/**
|
|
@@ -20482,7 +21510,7 @@ var BillingPublisher = class {
|
|
|
20482
21510
|
markDelivered(event) {
|
|
20483
21511
|
try {
|
|
20484
21512
|
this.ensureDir();
|
|
20485
|
-
const file =
|
|
21513
|
+
const file = join27(this.billingDir, deliveredFileName(event.ts));
|
|
20486
21514
|
appendFileSync3(file, JSON.stringify({ id: event.id, deliveredAt: this.now() }) + "\n", "utf8");
|
|
20487
21515
|
} catch (error) {
|
|
20488
21516
|
this.logger.warn("[BillingPublisher] failed to append delivery marker", {
|
|
@@ -20498,11 +21526,11 @@ var BillingPublisher = class {
|
|
|
20498
21526
|
};
|
|
20499
21527
|
|
|
20500
21528
|
// src/billing/billingReader.ts
|
|
20501
|
-
import { existsSync as
|
|
20502
|
-
import { join as
|
|
21529
|
+
import { existsSync as existsSync30, readdirSync as readdirSync9, readFileSync as readFileSync22 } from "fs";
|
|
21530
|
+
import { join as join28 } from "path";
|
|
20503
21531
|
function readBillingLedger(billingDir) {
|
|
20504
21532
|
const view = { events: [], deliveredIds: /* @__PURE__ */ new Set() };
|
|
20505
|
-
if (!
|
|
21533
|
+
if (!existsSync30(billingDir)) return view;
|
|
20506
21534
|
let files;
|
|
20507
21535
|
try {
|
|
20508
21536
|
files = readdirSync9(billingDir);
|
|
@@ -20536,7 +21564,7 @@ function readBillingStatus(billingDir) {
|
|
|
20536
21564
|
function parseLines(dir, file) {
|
|
20537
21565
|
let raw;
|
|
20538
21566
|
try {
|
|
20539
|
-
raw = readFileSync22(
|
|
21567
|
+
raw = readFileSync22(join28(dir, file), "utf8");
|
|
20540
21568
|
} catch {
|
|
20541
21569
|
return [];
|
|
20542
21570
|
}
|
|
@@ -21348,6 +22376,7 @@ function buildDaemon(config, paths) {
|
|
|
21348
22376
|
});
|
|
21349
22377
|
const auditDir = defaultAuditDir(paths.configPath);
|
|
21350
22378
|
const billingDir = defaultBillingDir(paths.configPath);
|
|
22379
|
+
const codexSessionManager = new CodexSessionManager();
|
|
21351
22380
|
const adminServer = new AdminServer({
|
|
21352
22381
|
configPath: paths.configPath,
|
|
21353
22382
|
llmConfig,
|
|
@@ -21445,6 +22474,10 @@ function buildDaemon(config, paths) {
|
|
|
21445
22474
|
cliTerminalOpener: paths.cliTerminalOpener,
|
|
21446
22475
|
cliPathProbe: paths.cliPathProbe,
|
|
21447
22476
|
cliCommandRunner: paths.cliCommandRunner,
|
|
22477
|
+
// One helper invocation shared by the integration install and KEY-SCOPED
|
|
22478
|
+
// launches (the latter append `--key-id` per spawn) — same entrypoint, same
|
|
22479
|
+
// config/master-key resolution, so the two paths can never drift.
|
|
22480
|
+
codexAuthHelper: currentProcessCodexAuthHelper(paths.configPath, paths.masterKeyFilePath),
|
|
21448
22481
|
integrationManagerFactory: () => {
|
|
21449
22482
|
const live = outboundApiServer.getStatus();
|
|
21450
22483
|
const port = live.port || decryptedConfig.server?.port || DEFAULT_OUTBOUND_PORT;
|
|
@@ -21489,7 +22522,8 @@ function buildDaemon(config, paths) {
|
|
|
21489
22522
|
auditCompactor: () => compactAllClosedAuditDays(auditDir),
|
|
21490
22523
|
// billing-event-stream: the AUTHED `GET /admin/api/billing-status` returns the
|
|
21491
22524
|
// secret-free total/delivered/pending counts of the durable ledger.
|
|
21492
|
-
billingStatusReader: () => readBillingStatus(billingDir)
|
|
22525
|
+
billingStatusReader: () => readBillingStatus(billingDir),
|
|
22526
|
+
codexSessionManager
|
|
21493
22527
|
});
|
|
21494
22528
|
const webhookDispatcher = new WebhookDispatcher({
|
|
21495
22529
|
logger,
|
|
@@ -21555,6 +22589,7 @@ function buildDaemon(config, paths) {
|
|
|
21555
22589
|
pricingEngine,
|
|
21556
22590
|
pricingRefreshScheduler,
|
|
21557
22591
|
usageRecorder,
|
|
22592
|
+
codexSessionManager,
|
|
21558
22593
|
adminServer,
|
|
21559
22594
|
tokenRefreshScheduler,
|
|
21560
22595
|
accountHealthSweeper,
|
|
@@ -21590,7 +22625,7 @@ function resetDaemonSingletonsForTests() {
|
|
|
21590
22625
|
}
|
|
21591
22626
|
function isTokensStoreReadable(tokensPath) {
|
|
21592
22627
|
try {
|
|
21593
|
-
if (!
|
|
22628
|
+
if (!existsSync31(tokensPath)) return true;
|
|
21594
22629
|
accessSync(tokensPath, fsConstants.R_OK);
|
|
21595
22630
|
return true;
|
|
21596
22631
|
} catch {
|
|
@@ -21695,6 +22730,8 @@ function mapCcrToOmnicross(ccr) {
|
|
|
21695
22730
|
}
|
|
21696
22731
|
export {
|
|
21697
22732
|
AdminServer,
|
|
22733
|
+
CodexSessionManager,
|
|
22734
|
+
CodexSessionManagerError,
|
|
21698
22735
|
ConfigFileProviderConfigSource,
|
|
21699
22736
|
ConfigurableLogger,
|
|
21700
22737
|
ConsoleLogger,
|
|
@@ -21714,6 +22751,7 @@ export {
|
|
|
21714
22751
|
loadConfig,
|
|
21715
22752
|
mapCcrToOmnicross,
|
|
21716
22753
|
parseCcrConfig,
|
|
22754
|
+
replaceStructuredProviderFields,
|
|
21717
22755
|
resetDaemonSingletonsForTests,
|
|
21718
22756
|
resolveAdminConfig,
|
|
21719
22757
|
saveConfig,
|