@omnicross/daemon 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +892 -219
- package/dist/cli.js +849 -160
- package/dist/index.cjs +733 -206
- package/dist/index.d.cts +69 -0
- package/dist/index.d.ts +69 -0
- package/dist/index.js +691 -153
- package/package.json +6 -6
package/dist/index.cjs
CHANGED
|
@@ -65,17 +65,17 @@ var import_billing_types = require("@omnicross/contracts/billing-types");
|
|
|
65
65
|
var import_core4 = require("@omnicross/core");
|
|
66
66
|
var import_GeminiCodeAssistProjectResolver = require("@omnicross/core/auth/GeminiCodeAssistProjectResolver");
|
|
67
67
|
var import_ApiKeyPoolService = require("@omnicross/core/completion/ApiKeyPoolService");
|
|
68
|
-
var
|
|
68
|
+
var import_outbound_api10 = require("@omnicross/core/outbound-api");
|
|
69
69
|
var import_subscriptionRegistryPort = require("@omnicross/core/outbound-api/subscriptionRegistryPort");
|
|
70
70
|
var import_SubscriptionAccountHealth4 = require("@omnicross/core/pipeline/SubscriptionAccountHealth");
|
|
71
71
|
var import_AccountAllowanceStore4 = require("@omnicross/core/pipeline/AccountAllowanceStore");
|
|
72
72
|
var import_AccountAllowanceScheduling5 = require("@omnicross/core/pipeline/AccountAllowanceScheduling");
|
|
73
|
-
var
|
|
73
|
+
var import_upstreamFetch9 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
74
74
|
var import_SubscriptionIdentityStore3 = require("@omnicross/core/provider-proxy/identity/SubscriptionIdentityStore");
|
|
75
75
|
var import_gemini_code_assist_resolver = require("@omnicross/core/ports/gemini-code-assist-resolver");
|
|
76
76
|
var import_provider_proxy4 = require("@omnicross/core/provider-proxy");
|
|
77
77
|
var import_cli_launcher2 = require("@omnicross/cli-launcher");
|
|
78
|
-
var
|
|
78
|
+
var import_outbound_api11 = require("@omnicross/core/outbound-api");
|
|
79
79
|
var import_usage2 = require("@omnicross/core/usage");
|
|
80
80
|
var import_subscriptions6 = require("@omnicross/subscriptions");
|
|
81
81
|
|
|
@@ -879,11 +879,11 @@ async function handleRouteLeaseApi(req, res, path2, deps) {
|
|
|
879
879
|
|
|
880
880
|
// src/admin/adminApi.ts
|
|
881
881
|
var import_node_http = __toESM(require("http"), 1);
|
|
882
|
-
var
|
|
882
|
+
var import_outbound_api5 = require("@omnicross/core/outbound-api");
|
|
883
883
|
var import_image_generation_types = require("@omnicross/contracts/image-generation-types");
|
|
884
884
|
var import_AccountAllowanceScheduling2 = require("@omnicross/core/pipeline/AccountAllowanceScheduling");
|
|
885
885
|
var import_SubscriptionAccountHealth = require("@omnicross/core/pipeline/SubscriptionAccountHealth");
|
|
886
|
-
var
|
|
886
|
+
var import_upstreamFetch4 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
887
887
|
|
|
888
888
|
// src/image-generation/imagesConfigValidation.ts
|
|
889
889
|
var import_outbound_api = require("@omnicross/core/outbound-api");
|
|
@@ -3739,6 +3739,488 @@ async function handleDashboard(deps) {
|
|
|
3739
3739
|
return { status: 200, body: summary };
|
|
3740
3740
|
}
|
|
3741
3741
|
|
|
3742
|
+
// src/admin/searchAdminApi.ts
|
|
3743
|
+
var import_outbound_api2 = require("@omnicross/core/outbound-api");
|
|
3744
|
+
var import_api3 = require("@omnicross/core/search/api");
|
|
3745
|
+
var import_http3 = require("@omnicross/core/search/http");
|
|
3746
|
+
var import_search2 = require("@omnicross/core/search");
|
|
3747
|
+
|
|
3748
|
+
// src/search/searchDoctorProjection.ts
|
|
3749
|
+
var import_search_types = require("@omnicross/contracts/search-types");
|
|
3750
|
+
var import_api = require("@omnicross/core/search/api");
|
|
3751
|
+
var import_http = require("@omnicross/core/search/http");
|
|
3752
|
+
var API_DOCTOR_PROVIDERS = [
|
|
3753
|
+
{
|
|
3754
|
+
id: "tavily",
|
|
3755
|
+
capabilities: import_api.TAVILY_CAPABILITIES,
|
|
3756
|
+
configured: (configs) => configs.tavily !== void 0,
|
|
3757
|
+
missingReason: "no API key configured"
|
|
3758
|
+
},
|
|
3759
|
+
{
|
|
3760
|
+
id: "jina",
|
|
3761
|
+
capabilities: import_api.JINA_CAPABILITIES,
|
|
3762
|
+
configured: (configs) => configs.jina !== void 0,
|
|
3763
|
+
// Honest about the asymmetry: Jina CAN run keyless, but a provider nobody
|
|
3764
|
+
// asked for is still not enabled.
|
|
3765
|
+
missingReason: "not configured (Jina can run without a key, but must be enabled explicitly)"
|
|
3766
|
+
},
|
|
3767
|
+
{
|
|
3768
|
+
id: "searxng",
|
|
3769
|
+
capabilities: import_api.SEARXNG_CAPABILITIES,
|
|
3770
|
+
configured: (configs) => configs.searxng !== void 0,
|
|
3771
|
+
missingReason: "no API host configured"
|
|
3772
|
+
},
|
|
3773
|
+
{
|
|
3774
|
+
id: "zhipu",
|
|
3775
|
+
capabilities: import_api.ZHIPU_CAPABILITIES,
|
|
3776
|
+
configured: (configs) => configs.zhipu !== void 0,
|
|
3777
|
+
missingReason: "no API key configured"
|
|
3778
|
+
},
|
|
3779
|
+
{
|
|
3780
|
+
id: "z.ai",
|
|
3781
|
+
capabilities: import_api.ZHIPU_CAPABILITIES,
|
|
3782
|
+
configured: (configs) => configs["z.ai"] !== void 0,
|
|
3783
|
+
missingReason: "no API key configured"
|
|
3784
|
+
}
|
|
3785
|
+
];
|
|
3786
|
+
function buildSearchDoctorSnapshot(contributions = (0, import_http.builtinHttpSearchContributions)(), apiConfigs) {
|
|
3787
|
+
const rows = contributions.map((contribution) => ({
|
|
3788
|
+
providerId: contribution.id,
|
|
3789
|
+
source: contribution.source,
|
|
3790
|
+
kind: contribution.kind,
|
|
3791
|
+
capabilities: contribution.capabilities
|
|
3792
|
+
}));
|
|
3793
|
+
if (apiConfigs === void 0) return rows;
|
|
3794
|
+
for (const provider of API_DOCTOR_PROVIDERS) {
|
|
3795
|
+
if (provider.configured(apiConfigs)) continue;
|
|
3796
|
+
rows.push({
|
|
3797
|
+
providerId: provider.id,
|
|
3798
|
+
source: "builtin",
|
|
3799
|
+
kind: "api",
|
|
3800
|
+
capabilities: provider.capabilities,
|
|
3801
|
+
status: "unconfigured",
|
|
3802
|
+
reason: provider.missingReason
|
|
3803
|
+
});
|
|
3804
|
+
}
|
|
3805
|
+
return rows;
|
|
3806
|
+
}
|
|
3807
|
+
var SEARCH_DOCTOR_QUERY = "MDN HTTP headers documentation";
|
|
3808
|
+
function classifyLiveSearchOutcome(providerId, outcome, checkedAt) {
|
|
3809
|
+
if (outcome.kind === "results") {
|
|
3810
|
+
if (outcome.count > 0) return { providerId, status: "healthy", checkedAt };
|
|
3811
|
+
return {
|
|
3812
|
+
providerId,
|
|
3813
|
+
status: "degraded",
|
|
3814
|
+
checkedAt,
|
|
3815
|
+
reason: "reachable, but the engine returned no usable results (possible partial drift)"
|
|
3816
|
+
};
|
|
3817
|
+
}
|
|
3818
|
+
const error = (0, import_search_types.toSearchErrorShape)(outcome.error);
|
|
3819
|
+
const stage = error.details?.stage;
|
|
3820
|
+
const { status, reason } = classifySearchFailure(stage, error.code);
|
|
3821
|
+
return { providerId, status, checkedAt, reason, error };
|
|
3822
|
+
}
|
|
3823
|
+
function classifySearchFailure(stage, code) {
|
|
3824
|
+
if (stage === "challenge") {
|
|
3825
|
+
return { status: "blocked", reason: "the engine served a bot challenge instead of results" };
|
|
3826
|
+
}
|
|
3827
|
+
if (stage === "trust") {
|
|
3828
|
+
return {
|
|
3829
|
+
status: "blocked",
|
|
3830
|
+
reason: "the engine served a page that failed the anti-decoy trust check"
|
|
3831
|
+
};
|
|
3832
|
+
}
|
|
3833
|
+
if (code === "policy_denied") {
|
|
3834
|
+
return {
|
|
3835
|
+
status: "blocked",
|
|
3836
|
+
reason: "the egress policy refused the request target"
|
|
3837
|
+
};
|
|
3838
|
+
}
|
|
3839
|
+
if (code === "parse_failed") {
|
|
3840
|
+
return {
|
|
3841
|
+
status: "failed",
|
|
3842
|
+
reason: "the response was not recognizable as a search result page (parser drift suspected)"
|
|
3843
|
+
};
|
|
3844
|
+
}
|
|
3845
|
+
if (code === "timeout") {
|
|
3846
|
+
return { status: "failed", reason: "the request exceeded its time budget" };
|
|
3847
|
+
}
|
|
3848
|
+
return { status: "failed", reason: `the request failed (${code})` };
|
|
3849
|
+
}
|
|
3850
|
+
|
|
3851
|
+
// src/search/SearchAssembly.ts
|
|
3852
|
+
var import_upstreamFetch3 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
3853
|
+
var import_search = require("@omnicross/core/search");
|
|
3854
|
+
var import_api2 = require("@omnicross/core/search/api");
|
|
3855
|
+
var import_http2 = require("@omnicross/core/search/http");
|
|
3856
|
+
function searchEgressPolicyFrom(config) {
|
|
3857
|
+
const hosts = config.egress.allowedPrivateHosts;
|
|
3858
|
+
return hosts.length > 0 ? { allowedPrivateHosts: [...hosts] } : {};
|
|
3859
|
+
}
|
|
3860
|
+
function searchPolicyFrom(config) {
|
|
3861
|
+
const { preferred, allowed, fallbackEnabled, maxAttempts } = config.policy;
|
|
3862
|
+
return {
|
|
3863
|
+
...preferred !== void 0 ? { preferred } : {},
|
|
3864
|
+
...allowed !== void 0 ? { allowed: [...allowed] } : {},
|
|
3865
|
+
fallbackEnabled,
|
|
3866
|
+
...maxAttempts !== void 0 ? { maxAttempts } : {}
|
|
3867
|
+
};
|
|
3868
|
+
}
|
|
3869
|
+
function resolveSearchUpstreamDispatcher(url) {
|
|
3870
|
+
return (0, import_upstreamFetch3.resolveUpstreamDispatcher)({ url });
|
|
3871
|
+
}
|
|
3872
|
+
var searchUpstreamProxyConfig = createUpstreamProxyResolver();
|
|
3873
|
+
function resolveSearchUpstreamProxyConfig(url) {
|
|
3874
|
+
return searchUpstreamProxyConfig({ url });
|
|
3875
|
+
}
|
|
3876
|
+
function searchContributionsFrom(config) {
|
|
3877
|
+
return [
|
|
3878
|
+
...(0, import_http2.builtinHttpSearchContributions)(
|
|
3879
|
+
(0, import_http2.createSearchHttpTransport)({
|
|
3880
|
+
resolveProxyDispatcher: resolveSearchUpstreamDispatcher,
|
|
3881
|
+
resolveProxyConfig: resolveSearchUpstreamProxyConfig
|
|
3882
|
+
})
|
|
3883
|
+
),
|
|
3884
|
+
...(0, import_api2.apiSearchContributions)(config.providers, {
|
|
3885
|
+
egressPolicy: searchEgressPolicyFrom(config),
|
|
3886
|
+
resolveProxyDispatcher: resolveSearchUpstreamDispatcher
|
|
3887
|
+
})
|
|
3888
|
+
];
|
|
3889
|
+
}
|
|
3890
|
+
function buildSearchRuntime(config, options = {}) {
|
|
3891
|
+
const logger = options.logger ?? null;
|
|
3892
|
+
return (0, import_search.createSearchRuntime)({
|
|
3893
|
+
contributions: options.contributions ?? searchContributionsFrom(config),
|
|
3894
|
+
policy: searchPolicyFrom(config),
|
|
3895
|
+
...logger ? {
|
|
3896
|
+
onEvent: (event) => {
|
|
3897
|
+
logger.debug(`[search] ${formatSearchEvent(event)}`);
|
|
3898
|
+
}
|
|
3899
|
+
} : {}
|
|
3900
|
+
});
|
|
3901
|
+
}
|
|
3902
|
+
function formatSearchEvent(event) {
|
|
3903
|
+
const parts = [
|
|
3904
|
+
`type=${event.type}`,
|
|
3905
|
+
`request=${event.requestId}`,
|
|
3906
|
+
`queryHash=${event.queryHash}`,
|
|
3907
|
+
`durationMs=${event.durationMs}`
|
|
3908
|
+
];
|
|
3909
|
+
if ("providerId" in event && event.providerId !== void 0) {
|
|
3910
|
+
parts.push(`provider=${event.providerId}`);
|
|
3911
|
+
}
|
|
3912
|
+
if ("outcome" in event && event.outcome !== void 0) parts.push(`outcome=${event.outcome}`);
|
|
3913
|
+
if ("errorCode" in event && event.errorCode !== void 0) parts.push(`error=${event.errorCode}`);
|
|
3914
|
+
if ("resultCount" in event && event.resultCount !== void 0) {
|
|
3915
|
+
parts.push(`results=${event.resultCount}`);
|
|
3916
|
+
}
|
|
3917
|
+
if ("fallbackCount" in event && event.fallbackCount !== void 0) {
|
|
3918
|
+
parts.push(`fallbacks=${event.fallbackCount}`);
|
|
3919
|
+
}
|
|
3920
|
+
return parts.join(" ");
|
|
3921
|
+
}
|
|
3922
|
+
|
|
3923
|
+
// src/admin/searchAdminApi.ts
|
|
3924
|
+
var KEYLESS_HTTP_PROVIDER_IDS = /* @__PURE__ */ new Set(["http-bing", "http-duckduckgo"]);
|
|
3925
|
+
var API_PROVIDER_IDS = /* @__PURE__ */ new Set([
|
|
3926
|
+
"tavily",
|
|
3927
|
+
"jina",
|
|
3928
|
+
"searxng",
|
|
3929
|
+
"zhipu",
|
|
3930
|
+
"z.ai"
|
|
3931
|
+
]);
|
|
3932
|
+
var SEARCH_QUERY_MAX_CODE_UNITS = 256;
|
|
3933
|
+
var QUERY_CONTROL_CHARS = /[\u0000-\u001f\u007f]/u;
|
|
3934
|
+
var SEARCH_RESULT_FIELD_CAPS = { title: 512, url: 2048, content: 1024 };
|
|
3935
|
+
var SEARCH_QUERY_MAX_RESULTS = 5;
|
|
3936
|
+
function sanitizeResultField(value, cap) {
|
|
3937
|
+
const text = typeof value === "string" ? value : value === null || value === void 0 ? "" : String(value);
|
|
3938
|
+
return text.replace(/[\u0000-\u001f\u007f]/gu, "").slice(0, cap);
|
|
3939
|
+
}
|
|
3940
|
+
function writeJson(res, status, body) {
|
|
3941
|
+
res.writeHead(status, { "Content-Type": "application/json" });
|
|
3942
|
+
res.end(JSON.stringify(body));
|
|
3943
|
+
}
|
|
3944
|
+
function writeErr(res, status, message) {
|
|
3945
|
+
writeJson(res, status, { error: { type: "admin_api_error", message } });
|
|
3946
|
+
}
|
|
3947
|
+
var SEARCH_MAX_BODY_BYTES = 64 * 1024;
|
|
3948
|
+
var SearchBodyTooLargeError = class extends Error {
|
|
3949
|
+
};
|
|
3950
|
+
async function readJsonBody2(req) {
|
|
3951
|
+
const chunks = [];
|
|
3952
|
+
let bytes = 0;
|
|
3953
|
+
for await (const chunk of req) {
|
|
3954
|
+
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
3955
|
+
bytes += buffer.length;
|
|
3956
|
+
if (bytes > SEARCH_MAX_BODY_BYTES) throw new SearchBodyTooLargeError("request body is too large");
|
|
3957
|
+
chunks.push(buffer);
|
|
3958
|
+
}
|
|
3959
|
+
const raw = Buffer.concat(chunks).toString("utf8");
|
|
3960
|
+
if (!raw.trim()) return {};
|
|
3961
|
+
try {
|
|
3962
|
+
const parsed = JSON.parse(raw);
|
|
3963
|
+
return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
3964
|
+
} catch {
|
|
3965
|
+
return {};
|
|
3966
|
+
}
|
|
3967
|
+
}
|
|
3968
|
+
async function readBodyOrReject(req, res) {
|
|
3969
|
+
try {
|
|
3970
|
+
return await readJsonBody2(req);
|
|
3971
|
+
} catch (error) {
|
|
3972
|
+
if (error instanceof SearchBodyTooLargeError) {
|
|
3973
|
+
writeErr(res, 400, error.message);
|
|
3974
|
+
return void 0;
|
|
3975
|
+
}
|
|
3976
|
+
throw error;
|
|
3977
|
+
}
|
|
3978
|
+
}
|
|
3979
|
+
async function handleSearchAdmin(req, res, method, rest, deps) {
|
|
3980
|
+
if (rest.length === 1 && rest[0] === "diagnostics") {
|
|
3981
|
+
if (!deps.searchStatus) {
|
|
3982
|
+
return writeErr(res, 501, "Search status is not available in this build");
|
|
3983
|
+
}
|
|
3984
|
+
if (method !== "GET") {
|
|
3985
|
+
return writeErr(res, 405, `method ${method} not allowed on search diagnostics`);
|
|
3986
|
+
}
|
|
3987
|
+
return handleSearchDiagnostics(res, deps);
|
|
3988
|
+
}
|
|
3989
|
+
if (rest.length === 1 && rest[0] === "test") {
|
|
3990
|
+
if (!deps.searchStatus) {
|
|
3991
|
+
return writeErr(res, 501, "Search status is not available in this build");
|
|
3992
|
+
}
|
|
3993
|
+
if (method !== "POST") {
|
|
3994
|
+
return writeErr(res, 405, `method ${method} not allowed on search test`);
|
|
3995
|
+
}
|
|
3996
|
+
return handleSearchTest(req, res, deps);
|
|
3997
|
+
}
|
|
3998
|
+
if (rest.length === 1 && rest[0] === "query") {
|
|
3999
|
+
if (!deps.searchStatus) {
|
|
4000
|
+
return writeErr(res, 501, "Search status is not available in this build");
|
|
4001
|
+
}
|
|
4002
|
+
if (method !== "POST") {
|
|
4003
|
+
return writeErr(res, 405, `method ${method} not allowed on search query`);
|
|
4004
|
+
}
|
|
4005
|
+
return handleSearchQuery(req, res, deps);
|
|
4006
|
+
}
|
|
4007
|
+
return writeErr(res, 404, `unknown search route '/${rest.join("/")}'`);
|
|
4008
|
+
}
|
|
4009
|
+
async function handleSearchDiagnostics(res, deps) {
|
|
4010
|
+
const status = deps.searchStatus;
|
|
4011
|
+
const persisted = await (0, import_outbound_api2.loadServerConfig)(deps.settingsStore);
|
|
4012
|
+
const search = persisted.search ?? import_outbound_api2.DEFAULT_SEARCH_SERVER_CONFIG;
|
|
4013
|
+
const rows = buildSearchDoctorSnapshot(
|
|
4014
|
+
status.runtime.listProviders(),
|
|
4015
|
+
search.providers
|
|
4016
|
+
);
|
|
4017
|
+
const snapshot = {
|
|
4018
|
+
rows,
|
|
4019
|
+
modes: {
|
|
4020
|
+
// codex is read from the LIVE config per request — an admin PUT has
|
|
4021
|
+
// already applied. responses/anthropic were captured at bootstrap.
|
|
4022
|
+
codex: search.modes.codex,
|
|
4023
|
+
responses: status.modes.responses,
|
|
4024
|
+
anthropic: status.modes.anthropic
|
|
4025
|
+
},
|
|
4026
|
+
applySemantics: { codex: "immediate", rest: "restart" }
|
|
4027
|
+
};
|
|
4028
|
+
return writeJson(res, 200, { diagnostics: snapshot });
|
|
4029
|
+
}
|
|
4030
|
+
function persistedSearchContributions(search, fetchImpl) {
|
|
4031
|
+
if (fetchImpl) {
|
|
4032
|
+
const egressPolicy = searchEgressPolicyFrom(search);
|
|
4033
|
+
return [
|
|
4034
|
+
...(0, import_http3.builtinHttpSearchContributions)(
|
|
4035
|
+
(0, import_http3.createSearchHttpTransport)({ fetch: fetchImpl, egressPolicy })
|
|
4036
|
+
),
|
|
4037
|
+
...(0, import_api3.apiSearchContributions)(search.providers, { egressPolicy, fetchImpl })
|
|
4038
|
+
];
|
|
4039
|
+
}
|
|
4040
|
+
return searchContributionsFrom(search);
|
|
4041
|
+
}
|
|
4042
|
+
async function handleSearchTest(req, res, deps) {
|
|
4043
|
+
const status = deps.searchStatus;
|
|
4044
|
+
const body = await readBodyOrReject(req, res);
|
|
4045
|
+
if (body === void 0) return;
|
|
4046
|
+
const providerId = body["providerId"];
|
|
4047
|
+
if (typeof providerId !== "string" || providerId.length === 0) {
|
|
4048
|
+
return writeErr(res, 400, "providerId must be a non-empty string");
|
|
4049
|
+
}
|
|
4050
|
+
if (!KEYLESS_HTTP_PROVIDER_IDS.has(providerId) && !API_PROVIDER_IDS.has(providerId)) {
|
|
4051
|
+
return writeErr(res, 404, `unknown search provider '${providerId}'`);
|
|
4052
|
+
}
|
|
4053
|
+
const persisted = await (0, import_outbound_api2.loadServerConfig)(deps.settingsStore);
|
|
4054
|
+
const search = persisted.search ?? import_outbound_api2.DEFAULT_SEARCH_SERVER_CONFIG;
|
|
4055
|
+
const providers = search.providers;
|
|
4056
|
+
if (!KEYLESS_HTTP_PROVIDER_IDS.has(providerId) && providers[providerId] === void 0) {
|
|
4057
|
+
return writeErr(res, 400, `search provider '${providerId}' is not configured`);
|
|
4058
|
+
}
|
|
4059
|
+
const fetchImpl = status.testFetch;
|
|
4060
|
+
const contributions = persistedSearchContributions(search, fetchImpl);
|
|
4061
|
+
const contribution = contributions.find((c) => c.id === providerId);
|
|
4062
|
+
if (!contribution) {
|
|
4063
|
+
return writeErr(res, 400, `search provider '${providerId}' is not configured`);
|
|
4064
|
+
}
|
|
4065
|
+
const checkedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4066
|
+
try {
|
|
4067
|
+
const results = await contribution.provider.search(SEARCH_DOCTOR_QUERY, { maxResults: 5 });
|
|
4068
|
+
const diagnostic = classifyLiveSearchOutcome(
|
|
4069
|
+
contribution.id,
|
|
4070
|
+
{ kind: "results", count: results.length },
|
|
4071
|
+
checkedAt
|
|
4072
|
+
);
|
|
4073
|
+
const response = { diagnostic, resultCount: results.length };
|
|
4074
|
+
return writeJson(res, 200, { result: response });
|
|
4075
|
+
} catch (error) {
|
|
4076
|
+
const diagnostic = classifyLiveSearchOutcome(contribution.id, { kind: "failure", error }, checkedAt);
|
|
4077
|
+
const response = { diagnostic };
|
|
4078
|
+
return writeJson(res, 200, { result: response });
|
|
4079
|
+
}
|
|
4080
|
+
}
|
|
4081
|
+
async function handleSearchQuery(req, res, deps) {
|
|
4082
|
+
const status = deps.searchStatus;
|
|
4083
|
+
const body = await readBodyOrReject(req, res);
|
|
4084
|
+
if (body === void 0) return;
|
|
4085
|
+
const providerId = body["providerId"];
|
|
4086
|
+
if (typeof providerId !== "string" || providerId.length === 0) {
|
|
4087
|
+
return writeErr(res, 400, "providerId must be a non-empty string");
|
|
4088
|
+
}
|
|
4089
|
+
if (!KEYLESS_HTTP_PROVIDER_IDS.has(providerId) && !API_PROVIDER_IDS.has(providerId)) {
|
|
4090
|
+
return writeErr(res, 404, `unknown search provider '${providerId}'`);
|
|
4091
|
+
}
|
|
4092
|
+
const query2 = body["query"];
|
|
4093
|
+
if (typeof query2 !== "string" || query2.trim().length === 0) {
|
|
4094
|
+
return writeErr(res, 400, "query must be a non-empty string");
|
|
4095
|
+
}
|
|
4096
|
+
if (query2.length > SEARCH_QUERY_MAX_CODE_UNITS) {
|
|
4097
|
+
return writeErr(res, 400, `query must be at most ${SEARCH_QUERY_MAX_CODE_UNITS} characters`);
|
|
4098
|
+
}
|
|
4099
|
+
if (QUERY_CONTROL_CHARS.test(query2)) {
|
|
4100
|
+
return writeErr(res, 400, "query must not contain control characters");
|
|
4101
|
+
}
|
|
4102
|
+
const persisted = await (0, import_outbound_api2.loadServerConfig)(deps.settingsStore);
|
|
4103
|
+
const search = persisted.search ?? import_outbound_api2.DEFAULT_SEARCH_SERVER_CONFIG;
|
|
4104
|
+
const providers = search.providers;
|
|
4105
|
+
if (!KEYLESS_HTTP_PROVIDER_IDS.has(providerId) && providers[providerId] === void 0) {
|
|
4106
|
+
return writeErr(res, 400, `search provider '${providerId}' is not configured`);
|
|
4107
|
+
}
|
|
4108
|
+
const fetchImpl = status.testFetch;
|
|
4109
|
+
const runtime = (0, import_search2.createSearchRuntime)({
|
|
4110
|
+
contributions: persistedSearchContributions(search, fetchImpl),
|
|
4111
|
+
policy: {
|
|
4112
|
+
...searchPolicyFrom(search),
|
|
4113
|
+
// The panel always walks: it answers "does a search WORK for this
|
|
4114
|
+
// operator", not "does this one provider behave" — that is `/test`'s
|
|
4115
|
+
// job. The persisted policy's allowlist still bounds the walk.
|
|
4116
|
+
fallbackEnabled: true,
|
|
4117
|
+
preferred: providerId
|
|
4118
|
+
}
|
|
4119
|
+
});
|
|
4120
|
+
const checkedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4121
|
+
try {
|
|
4122
|
+
const orchestrated = await runtime.search({ query: query2, options: { maxResults: 5 } });
|
|
4123
|
+
const results = orchestrated.results;
|
|
4124
|
+
const sanitized = results.slice(0, SEARCH_QUERY_MAX_RESULTS).map((result) => ({
|
|
4125
|
+
title: sanitizeResultField(result.title, SEARCH_RESULT_FIELD_CAPS.title),
|
|
4126
|
+
url: sanitizeResultField(result.url, SEARCH_RESULT_FIELD_CAPS.url),
|
|
4127
|
+
content: sanitizeResultField(result.content, SEARCH_RESULT_FIELD_CAPS.content)
|
|
4128
|
+
}));
|
|
4129
|
+
const diagnostic = sanitized.length === 0 ? { providerId: orchestrated.providerId, status: "healthy", checkedAt } : classifyLiveSearchOutcome(
|
|
4130
|
+
orchestrated.providerId,
|
|
4131
|
+
{ kind: "results", count: sanitized.length },
|
|
4132
|
+
checkedAt
|
|
4133
|
+
);
|
|
4134
|
+
const response = {
|
|
4135
|
+
diagnostic,
|
|
4136
|
+
providerUsed: orchestrated.providerId,
|
|
4137
|
+
fallbackCount: orchestrated.fallbackCount,
|
|
4138
|
+
resultCount: sanitized.length,
|
|
4139
|
+
results: sanitized
|
|
4140
|
+
};
|
|
4141
|
+
return writeJson(res, 200, { result: response });
|
|
4142
|
+
} catch (error) {
|
|
4143
|
+
const diagnostic = classifyLiveSearchOutcome(providerId, { kind: "failure", error }, checkedAt);
|
|
4144
|
+
const response = { diagnostic };
|
|
4145
|
+
return writeJson(res, 200, { result: response });
|
|
4146
|
+
}
|
|
4147
|
+
}
|
|
4148
|
+
|
|
4149
|
+
// src/admin/searchAdminView.ts
|
|
4150
|
+
var API_KEY_PROVIDERS = /* @__PURE__ */ new Set(["tavily", "jina", "zhipu", "z.ai"]);
|
|
4151
|
+
var BASIC_AUTH_PROVIDERS = /* @__PURE__ */ new Set(["searxng"]);
|
|
4152
|
+
function isRecord(value) {
|
|
4153
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
4154
|
+
}
|
|
4155
|
+
function redactSearchServerConfig(search) {
|
|
4156
|
+
const providers = {};
|
|
4157
|
+
for (const [id, raw] of Object.entries(search.providers)) {
|
|
4158
|
+
const entry = raw;
|
|
4159
|
+
const view = {};
|
|
4160
|
+
if (entry.apiHost !== void 0) view.apiHost = entry.apiHost;
|
|
4161
|
+
if (entry.basicAuthUsername !== void 0) view.basicAuthUsername = entry.basicAuthUsername;
|
|
4162
|
+
if (API_KEY_PROVIDERS.has(id)) {
|
|
4163
|
+
view.apiKeyConfigured = typeof entry.apiKey === "string" && entry.apiKey.length > 0;
|
|
4164
|
+
}
|
|
4165
|
+
if (BASIC_AUTH_PROVIDERS.has(id)) {
|
|
4166
|
+
view.basicAuthPasswordConfigured = typeof entry.basicAuthPassword === "string" && entry.basicAuthPassword.length > 0;
|
|
4167
|
+
}
|
|
4168
|
+
providers[id] = view;
|
|
4169
|
+
}
|
|
4170
|
+
return {
|
|
4171
|
+
modes: search.modes,
|
|
4172
|
+
providers,
|
|
4173
|
+
egress: { allowedPrivateHosts: [...search.egress.allowedPrivateHosts] },
|
|
4174
|
+
policy: { ...search.policy, ...search.policy.allowed ? { allowed: [...search.policy.allowed] } : {} }
|
|
4175
|
+
};
|
|
4176
|
+
}
|
|
4177
|
+
function storedSecrets(current, id) {
|
|
4178
|
+
const entry = current.providers[id];
|
|
4179
|
+
if (!entry) return {};
|
|
4180
|
+
const out = {};
|
|
4181
|
+
if (typeof entry.apiKey === "string" && entry.apiKey.length > 0) out.apiKey = entry.apiKey;
|
|
4182
|
+
if (typeof entry.basicAuthPassword === "string" && entry.basicAuthPassword.length > 0) {
|
|
4183
|
+
out.basicAuthPassword = entry.basicAuthPassword;
|
|
4184
|
+
}
|
|
4185
|
+
return out;
|
|
4186
|
+
}
|
|
4187
|
+
function resolveSecretField(entry, field, stored) {
|
|
4188
|
+
if (!(field in entry)) {
|
|
4189
|
+
if (stored !== void 0) entry[field] = stored;
|
|
4190
|
+
return;
|
|
4191
|
+
}
|
|
4192
|
+
const value = entry[field];
|
|
4193
|
+
if (value === null) {
|
|
4194
|
+
delete entry[field];
|
|
4195
|
+
return;
|
|
4196
|
+
}
|
|
4197
|
+
if (typeof value === "string" && value.trim().length > 0) return;
|
|
4198
|
+
if (stored !== void 0) entry[field] = stored;
|
|
4199
|
+
else delete entry[field];
|
|
4200
|
+
}
|
|
4201
|
+
function preserveSearchSecrets(incoming, current) {
|
|
4202
|
+
if (!isRecord(incoming)) return incoming;
|
|
4203
|
+
const section = { ...incoming };
|
|
4204
|
+
const providersValue = section["providers"];
|
|
4205
|
+
if (!isRecord(providersValue)) return section;
|
|
4206
|
+
const providers = {};
|
|
4207
|
+
for (const [id, entryValue] of Object.entries(providersValue)) {
|
|
4208
|
+
if (!isRecord(entryValue)) {
|
|
4209
|
+
providers[id] = entryValue;
|
|
4210
|
+
continue;
|
|
4211
|
+
}
|
|
4212
|
+
const entry = { ...entryValue };
|
|
4213
|
+
delete entry["apiKeyConfigured"];
|
|
4214
|
+
delete entry["basicAuthPasswordConfigured"];
|
|
4215
|
+
const stored = storedSecrets(current, id);
|
|
4216
|
+
resolveSecretField(entry, "apiKey", stored.apiKey);
|
|
4217
|
+
resolveSecretField(entry, "basicAuthPassword", stored.basicAuthPassword);
|
|
4218
|
+
providers[id] = entry;
|
|
4219
|
+
}
|
|
4220
|
+
section["providers"] = providers;
|
|
4221
|
+
return section;
|
|
4222
|
+
}
|
|
4223
|
+
|
|
3742
4224
|
// src/admin/keyPolicyBody.ts
|
|
3743
4225
|
function parseKeyPolicyBody(body) {
|
|
3744
4226
|
const policy = {};
|
|
@@ -3801,7 +4283,7 @@ function parseKeyPolicyBody(body) {
|
|
|
3801
4283
|
var ENDPOINTS = /* @__PURE__ */ new Set(["chat", "responses", "messages", "gemini"]);
|
|
3802
4284
|
var TARGET_KINDS = /* @__PURE__ */ new Set(["account", "account-group", "account-pool", "provider"]);
|
|
3803
4285
|
var FALLBACKS = /* @__PURE__ */ new Set(["next", "fail", "global"]);
|
|
3804
|
-
function
|
|
4286
|
+
function isRecord2(value) {
|
|
3805
4287
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
3806
4288
|
}
|
|
3807
4289
|
function nonBlank(value) {
|
|
@@ -3821,7 +4303,7 @@ function validateGatewayBindingsSegment(patch) {
|
|
|
3821
4303
|
const ids = /* @__PURE__ */ new Set();
|
|
3822
4304
|
raw.forEach((entry, index) => {
|
|
3823
4305
|
const path2 = `bindings[${index}]`;
|
|
3824
|
-
if (!
|
|
4306
|
+
if (!isRecord2(entry)) {
|
|
3825
4307
|
errors.push(`${path2} must be an object`);
|
|
3826
4308
|
return;
|
|
3827
4309
|
}
|
|
@@ -3850,12 +4332,12 @@ function validateGatewayBindingsSegment(patch) {
|
|
|
3850
4332
|
} else if (entry.modelMappings.length > 100) {
|
|
3851
4333
|
errors.push(`${path2}.modelMappings cannot contain more than 100 entries`);
|
|
3852
4334
|
} else if (entry.modelMappings.some(
|
|
3853
|
-
(mapping) => !
|
|
4335
|
+
(mapping) => !isRecord2(mapping) || !nonBlank(mapping.source) || !nonBlank(mapping.target)
|
|
3854
4336
|
)) {
|
|
3855
4337
|
errors.push(`${path2}.modelMappings must contain non-empty source and target strings`);
|
|
3856
4338
|
}
|
|
3857
4339
|
}
|
|
3858
|
-
if (!
|
|
4340
|
+
if (!isRecord2(entry.target) || !TARGET_KINDS.has(String(entry.target.kind))) {
|
|
3859
4341
|
errors.push(`${path2}.target is invalid`);
|
|
3860
4342
|
} else {
|
|
3861
4343
|
if (!nonBlank(entry.target.providerId)) errors.push(`${path2}.target.providerId is required`);
|
|
@@ -3870,7 +4352,7 @@ function validateGatewayBindingsSegment(patch) {
|
|
|
3870
4352
|
}
|
|
3871
4353
|
}
|
|
3872
4354
|
if (entry.modelMap !== void 0) {
|
|
3873
|
-
if (!
|
|
4355
|
+
if (!isRecord2(entry.modelMap) || Object.values(entry.modelMap).some((value) => typeof value !== "string")) {
|
|
3874
4356
|
errors.push(`${path2}.modelMap must contain string values`);
|
|
3875
4357
|
}
|
|
3876
4358
|
}
|
|
@@ -3888,15 +4370,15 @@ function validateGatewayBindingsSegment(patch) {
|
|
|
3888
4370
|
}
|
|
3889
4371
|
|
|
3890
4372
|
// src/admin/voucherAdmin.ts
|
|
3891
|
-
var
|
|
3892
|
-
function
|
|
4373
|
+
var import_outbound_api3 = require("@omnicross/core/outbound-api");
|
|
4374
|
+
function writeJson2(res, status, body) {
|
|
3893
4375
|
res.writeHead(status, { "Content-Type": "application/json" });
|
|
3894
4376
|
res.end(JSON.stringify(body));
|
|
3895
4377
|
}
|
|
3896
|
-
function
|
|
3897
|
-
|
|
4378
|
+
function writeErr2(res, status, message) {
|
|
4379
|
+
writeJson2(res, status, { error: { type: "voucher_error", message } });
|
|
3898
4380
|
}
|
|
3899
|
-
function
|
|
4381
|
+
function readJsonBody3(req) {
|
|
3900
4382
|
return new Promise((resolve10, reject) => {
|
|
3901
4383
|
const chunks = [];
|
|
3902
4384
|
req.on("data", (c) => chunks.push(c));
|
|
@@ -3947,34 +4429,34 @@ function parseVoucherCreateBody(body) {
|
|
|
3947
4429
|
return { ok: true, input };
|
|
3948
4430
|
}
|
|
3949
4431
|
async function voucherEnabled(deps) {
|
|
3950
|
-
const config = await (0,
|
|
4432
|
+
const config = await (0, import_outbound_api3.loadServerConfig)(deps.settingsStore);
|
|
3951
4433
|
return config.voucher?.enabled === true;
|
|
3952
4434
|
}
|
|
3953
4435
|
async function handleVoucher(req, res, method, rest, deps) {
|
|
3954
4436
|
const voucherDb = deps.voucherDb;
|
|
3955
|
-
if (!voucherDb) return
|
|
4437
|
+
if (!voucherDb) return writeErr2(res, 501, "Voucher feature is not available");
|
|
3956
4438
|
if (method === "GET" && rest.length === 0) {
|
|
3957
4439
|
const rows = await voucherDb.voucherList();
|
|
3958
|
-
return
|
|
4440
|
+
return writeJson2(res, 200, { vouchers: rows.map(import_outbound_api3.toVoucherInfo) });
|
|
3959
4441
|
}
|
|
3960
4442
|
if (method === "POST" && rest.length === 0) {
|
|
3961
|
-
if (!await voucherEnabled(deps)) return
|
|
4443
|
+
if (!await voucherEnabled(deps)) return writeErr2(res, 403, "Voucher feature is disabled");
|
|
3962
4444
|
let body;
|
|
3963
4445
|
try {
|
|
3964
|
-
body = await
|
|
4446
|
+
body = await readJsonBody3(req);
|
|
3965
4447
|
} catch {
|
|
3966
|
-
return
|
|
4448
|
+
return writeErr2(res, 400, "Invalid JSON in request body");
|
|
3967
4449
|
}
|
|
3968
4450
|
const parsed = parseVoucherCreateBody(body);
|
|
3969
|
-
if (!parsed.ok) return
|
|
3970
|
-
const code = (0,
|
|
4451
|
+
if (!parsed.ok) return writeErr2(res, 400, parsed.message);
|
|
4452
|
+
const code = (0, import_outbound_api3.generateVoucherCode)();
|
|
3971
4453
|
const created = await voucherDb.voucherCreate({
|
|
3972
|
-
id: (0,
|
|
3973
|
-
codeHash: (0,
|
|
3974
|
-
codePrefix: (0,
|
|
4454
|
+
id: (0, import_outbound_api3.newVoucherId)(),
|
|
4455
|
+
codeHash: (0, import_outbound_api3.hashVoucherCode)(code),
|
|
4456
|
+
codePrefix: (0, import_outbound_api3.voucherCodePrefix)(code),
|
|
3975
4457
|
...parsed.input
|
|
3976
4458
|
});
|
|
3977
|
-
return
|
|
4459
|
+
return writeJson2(res, 201, {
|
|
3978
4460
|
id: created.id,
|
|
3979
4461
|
codePrefix: created.codePrefix,
|
|
3980
4462
|
type: created.type,
|
|
@@ -3985,11 +4467,11 @@ async function handleVoucher(req, res, method, rest, deps) {
|
|
|
3985
4467
|
}
|
|
3986
4468
|
const id = rest[0];
|
|
3987
4469
|
if (method === "POST" && id && rest[1] === "revoke") {
|
|
3988
|
-
if (!await voucherEnabled(deps)) return
|
|
4470
|
+
if (!await voucherEnabled(deps)) return writeErr2(res, 403, "Voucher feature is disabled");
|
|
3989
4471
|
const ok = await voucherDb.voucherRevokeCas(id, Date.now());
|
|
3990
|
-
return
|
|
4472
|
+
return writeJson2(res, ok ? 200 : 409, { ok });
|
|
3991
4473
|
}
|
|
3992
|
-
return
|
|
4474
|
+
return writeErr2(res, 405, `method ${method} not allowed on voucher`);
|
|
3993
4475
|
}
|
|
3994
4476
|
|
|
3995
4477
|
// src/admin/webhookConfigBody.ts
|
|
@@ -4156,7 +4638,7 @@ function resetBillingRuntimeForTests() {
|
|
|
4156
4638
|
}
|
|
4157
4639
|
|
|
4158
4640
|
// src/migration/migration.ts
|
|
4159
|
-
var
|
|
4641
|
+
var import_outbound_api4 = require("@omnicross/core/outbound-api");
|
|
4160
4642
|
|
|
4161
4643
|
// src/ports/account-multi.ts
|
|
4162
4644
|
var import_node_crypto8 = require("crypto");
|
|
@@ -4584,7 +5066,7 @@ async function gatherExport(deps, passphrase) {
|
|
|
4584
5066
|
v: BUNDLE_VERSION,
|
|
4585
5067
|
providers: cfg.providers,
|
|
4586
5068
|
tokens,
|
|
4587
|
-
server: (0,
|
|
5069
|
+
server: (0, import_outbound_api4.normalizeServerConfig)(cfg.server)
|
|
4588
5070
|
};
|
|
4589
5071
|
return sealPack(JSON.stringify(bundle), passphrase);
|
|
4590
5072
|
}
|
|
@@ -4633,7 +5115,7 @@ async function applyImport(packString, passphrase, mode, deps, parseProviderInpu
|
|
|
4633
5115
|
const imageErrors = validateImagesAdminConfig(rawServer["images"]);
|
|
4634
5116
|
if (imageErrors.length > 0) throw new Error("migration pack has an invalid Images config");
|
|
4635
5117
|
}
|
|
4636
|
-
validatedServer = (0,
|
|
5118
|
+
validatedServer = (0, import_outbound_api4.normalizeServerConfig)(bundle.server);
|
|
4637
5119
|
}
|
|
4638
5120
|
const validatedProviders = [];
|
|
4639
5121
|
for (const raw of rawProviders) {
|
|
@@ -4914,12 +5396,12 @@ async function handlePricingResolveConflicts(body, deps) {
|
|
|
4914
5396
|
}
|
|
4915
5397
|
|
|
4916
5398
|
// src/admin/accountAllowanceApi.ts
|
|
4917
|
-
function
|
|
5399
|
+
function writeJson3(res, status, body) {
|
|
4918
5400
|
res.writeHead(status, { "Content-Type": "application/json" });
|
|
4919
5401
|
res.end(JSON.stringify(body));
|
|
4920
5402
|
}
|
|
4921
5403
|
function writeError2(res, status, message) {
|
|
4922
|
-
|
|
5404
|
+
writeJson3(res, status, { error: { type: "account_allowance_error", message } });
|
|
4923
5405
|
}
|
|
4924
5406
|
function readJson2(req) {
|
|
4925
5407
|
return new Promise((resolve10, reject) => {
|
|
@@ -4952,7 +5434,7 @@ async function handleAccountAllowanceApi(req, res, method, rest, service) {
|
|
|
4952
5434
|
if (!service.getSchedulingStatus) {
|
|
4953
5435
|
return writeError2(res, 501, "allowance scheduling diagnostics are not available");
|
|
4954
5436
|
}
|
|
4955
|
-
return
|
|
5437
|
+
return writeJson3(res, 200, { scheduling: service.getSchedulingStatus() });
|
|
4956
5438
|
}
|
|
4957
5439
|
if (method === "GET") {
|
|
4958
5440
|
const params = query(req);
|
|
@@ -4961,7 +5443,7 @@ async function handleAccountAllowanceApi(req, res, method, rest, service) {
|
|
|
4961
5443
|
if (providerId === null) return writeError2(res, 400, "providerId must be claude or codex");
|
|
4962
5444
|
const accountId = rest.length >= 2 ? rest[1] : params.get("accountId") ?? void 0;
|
|
4963
5445
|
const allowances = await service.list({ providerId, accountId });
|
|
4964
|
-
return
|
|
5446
|
+
return writeJson3(res, 200, { allowances });
|
|
4965
5447
|
}
|
|
4966
5448
|
if (method === "POST" && rest[0] === "refresh") {
|
|
4967
5449
|
const body = await readJson2(req);
|
|
@@ -4976,7 +5458,7 @@ async function handleAccountAllowanceApi(req, res, method, rest, service) {
|
|
|
4976
5458
|
if (accountId && allowances.length === 0) {
|
|
4977
5459
|
return writeError2(res, 404, `Claude account '${accountId}' not found`);
|
|
4978
5460
|
}
|
|
4979
|
-
return
|
|
5461
|
+
return writeJson3(res, 200, { allowances });
|
|
4980
5462
|
}
|
|
4981
5463
|
return writeError2(res, 405, `method ${method} not allowed on account allowances`);
|
|
4982
5464
|
}
|
|
@@ -4992,7 +5474,7 @@ function readBody(req) {
|
|
|
4992
5474
|
req.on("error", reject);
|
|
4993
5475
|
});
|
|
4994
5476
|
}
|
|
4995
|
-
async function
|
|
5477
|
+
async function readJsonBody4(req) {
|
|
4996
5478
|
const raw = await readBody(req);
|
|
4997
5479
|
if (!raw.trim()) return {};
|
|
4998
5480
|
try {
|
|
@@ -5002,12 +5484,12 @@ async function readJsonBody3(req) {
|
|
|
5002
5484
|
return {};
|
|
5003
5485
|
}
|
|
5004
5486
|
}
|
|
5005
|
-
function
|
|
5487
|
+
function writeJson4(res, status, body) {
|
|
5006
5488
|
res.writeHead(status, { "Content-Type": "application/json" });
|
|
5007
5489
|
res.end(JSON.stringify(body));
|
|
5008
5490
|
}
|
|
5009
5491
|
function writeJsonError(res, status, message) {
|
|
5010
|
-
|
|
5492
|
+
writeJson4(res, status, { error: { type: "admin_api_error", message } });
|
|
5011
5493
|
}
|
|
5012
5494
|
function maskProviderApiKey(apiKey) {
|
|
5013
5495
|
if (!apiKey) return "";
|
|
@@ -5028,7 +5510,7 @@ function toKeyInfo(row) {
|
|
|
5028
5510
|
lastUsedAt: row.lastUsedAt,
|
|
5029
5511
|
revoked: row.revokedAt !== null,
|
|
5030
5512
|
kind: row.kind,
|
|
5031
|
-
allowedEndpoints: [...(0,
|
|
5513
|
+
allowedEndpoints: [...(0, import_outbound_api5.effectiveOutboundPermissions)(row.allowedEndpoints)],
|
|
5032
5514
|
legacyPermissions: row.allowedEndpoints === void 0,
|
|
5033
5515
|
loopbackOnly: row.loopbackOnly,
|
|
5034
5516
|
maxConcurrency: row.maxConcurrency,
|
|
@@ -5114,6 +5596,8 @@ async function handleAdminApi(req, res, path2, deps) {
|
|
|
5114
5596
|
return await handleServer(req, res, method, deps);
|
|
5115
5597
|
case "images":
|
|
5116
5598
|
return await handleImages(res, method, rest, deps);
|
|
5599
|
+
case "search":
|
|
5600
|
+
return await handleSearchAdmin(req, res, method, rest, deps);
|
|
5117
5601
|
case "accounts":
|
|
5118
5602
|
return await handleAccounts(req, res, method, rest, deps);
|
|
5119
5603
|
case "cli":
|
|
@@ -5147,7 +5631,7 @@ function requestQuery(req) {
|
|
|
5147
5631
|
return new URLSearchParams(qIdx >= 0 ? raw.slice(qIdx + 1) : "");
|
|
5148
5632
|
}
|
|
5149
5633
|
function writeResult(res, result) {
|
|
5150
|
-
|
|
5634
|
+
writeJson4(res, result.status, result.body);
|
|
5151
5635
|
}
|
|
5152
5636
|
async function handleUsage(req, res, method, rest, deps) {
|
|
5153
5637
|
if (method !== "GET") return writeJsonError(res, 405, `method ${method} not allowed on usage`);
|
|
@@ -5156,13 +5640,13 @@ async function handleUsage(req, res, method, rest, deps) {
|
|
|
5156
5640
|
async function handleDashboardRoute(res, method, deps) {
|
|
5157
5641
|
if (method !== "GET") return writeJsonError(res, 405, `method ${method} not allowed on dashboard`);
|
|
5158
5642
|
const result = await handleDashboard(deps);
|
|
5159
|
-
return
|
|
5643
|
+
return writeJson4(res, result.status, result.body);
|
|
5160
5644
|
}
|
|
5161
5645
|
async function handlePricing(req, res, method, rest, deps) {
|
|
5162
5646
|
if (rest.length === 0) {
|
|
5163
5647
|
if (method === "GET") return writeResult(res, await handlePricingList(deps));
|
|
5164
5648
|
if (method === "PUT") {
|
|
5165
|
-
return writeResult(res, await handlePricingUpsert(await
|
|
5649
|
+
return writeResult(res, await handlePricingUpsert(await readJsonBody4(req), deps));
|
|
5166
5650
|
}
|
|
5167
5651
|
if (method === "DELETE") {
|
|
5168
5652
|
return writeResult(res, await handlePricingDelete(requestQuery(req), deps));
|
|
@@ -5173,7 +5657,7 @@ async function handlePricing(req, res, method, rest, deps) {
|
|
|
5173
5657
|
return writeResult(res, await handlePricingFetchLatest(deps));
|
|
5174
5658
|
}
|
|
5175
5659
|
if (method === "POST" && rest.length === 1 && rest[0] === "resolve-conflicts") {
|
|
5176
|
-
return writeResult(res, await handlePricingResolveConflicts(await
|
|
5660
|
+
return writeResult(res, await handlePricingResolveConflicts(await readJsonBody4(req), deps));
|
|
5177
5661
|
}
|
|
5178
5662
|
return writeJsonError(res, 404, `unknown pricing route '${rest.join("/")}'`);
|
|
5179
5663
|
}
|
|
@@ -5187,15 +5671,15 @@ function migrationDeps(deps) {
|
|
|
5187
5671
|
}
|
|
5188
5672
|
async function handleMigrationExport(req, res, method, deps) {
|
|
5189
5673
|
if (method !== "POST") return writeJsonError(res, 405, `method ${method} not allowed on export`);
|
|
5190
|
-
const body = await
|
|
5674
|
+
const body = await readJsonBody4(req);
|
|
5191
5675
|
const result = await handleExport(body, migrationDeps(deps));
|
|
5192
|
-
return
|
|
5676
|
+
return writeJson4(res, result.status, result.body);
|
|
5193
5677
|
}
|
|
5194
5678
|
async function handleMigrationImport(req, res, method, deps) {
|
|
5195
5679
|
if (method !== "POST") return writeJsonError(res, 405, `method ${method} not allowed on import`);
|
|
5196
|
-
const body = await
|
|
5680
|
+
const body = await readJsonBody4(req);
|
|
5197
5681
|
const result = await handleImport(body, migrationDeps(deps));
|
|
5198
|
-
return
|
|
5682
|
+
return writeJson4(res, result.status, result.body);
|
|
5199
5683
|
}
|
|
5200
5684
|
async function handleProviders(req, res, method, rest, deps) {
|
|
5201
5685
|
const cfg = loadConfig(deps.configPath);
|
|
@@ -5226,13 +5710,13 @@ async function handleProviders(req, res, method, rest, deps) {
|
|
|
5226
5710
|
if (method === "GET" && rest.length === 2 && rest[1] === "reveal-key") {
|
|
5227
5711
|
const row = cfg.providers.find((p) => p.id === rest[0]);
|
|
5228
5712
|
if (!row) return writeJsonError(res, 404, `provider '${rest[0]}' not found`);
|
|
5229
|
-
return
|
|
5713
|
+
return writeJson4(res, 200, { apiKey: row.apiKey ?? "" });
|
|
5230
5714
|
}
|
|
5231
5715
|
if (method === "GET") {
|
|
5232
|
-
return
|
|
5716
|
+
return writeJson4(res, 200, { providers: cfg.providers.map(toProviderView) });
|
|
5233
5717
|
}
|
|
5234
5718
|
if (method === "POST") {
|
|
5235
|
-
const body = await
|
|
5719
|
+
const body = await readJsonBody4(req);
|
|
5236
5720
|
const provider = parseProviderInput(body, void 0);
|
|
5237
5721
|
if (!provider) return writeJsonError(res, 400, "invalid provider (id, apiFormat, baseUrl required)");
|
|
5238
5722
|
if (cfg.providers.some((p) => p.id === provider.id)) {
|
|
@@ -5240,25 +5724,25 @@ async function handleProviders(req, res, method, rest, deps) {
|
|
|
5240
5724
|
}
|
|
5241
5725
|
cfg.providers.push(provider);
|
|
5242
5726
|
persistProviders(cfg, deps);
|
|
5243
|
-
return
|
|
5727
|
+
return writeJson4(res, 201, { provider: toProviderView(provider) });
|
|
5244
5728
|
}
|
|
5245
5729
|
const id = rest[0];
|
|
5246
5730
|
if (!id) return writeJsonError(res, 400, "provider id required in path");
|
|
5247
5731
|
const idx = cfg.providers.findIndex((p) => p.id === id);
|
|
5248
5732
|
if (idx < 0) return writeJsonError(res, 404, `provider '${id}' not found`);
|
|
5249
5733
|
if (method === "PUT") {
|
|
5250
|
-
const body = await
|
|
5734
|
+
const body = await readJsonBody4(req);
|
|
5251
5735
|
const existing = cfg.providers[idx];
|
|
5252
5736
|
const updated = parseProviderInput(body, existing);
|
|
5253
5737
|
if (!updated) return writeJsonError(res, 400, "invalid provider (apiFormat, baseUrl required)");
|
|
5254
5738
|
cfg.providers[idx] = updated;
|
|
5255
5739
|
persistProviders(cfg, deps);
|
|
5256
|
-
return
|
|
5740
|
+
return writeJson4(res, 200, { provider: toProviderView(updated) });
|
|
5257
5741
|
}
|
|
5258
5742
|
if (method === "DELETE") {
|
|
5259
5743
|
cfg.providers.splice(idx, 1);
|
|
5260
5744
|
persistProviders(cfg, deps);
|
|
5261
|
-
return
|
|
5745
|
+
return writeJson4(res, 200, { ok: true });
|
|
5262
5746
|
}
|
|
5263
5747
|
return writeJsonError(res, 405, `method ${method} not allowed on providers`);
|
|
5264
5748
|
}
|
|
@@ -5267,7 +5751,7 @@ function persistProviders(cfg, deps) {
|
|
|
5267
5751
|
deps.llmConfig.reload(cfg);
|
|
5268
5752
|
}
|
|
5269
5753
|
async function handleProviderReorder(req, res, cfg, deps) {
|
|
5270
|
-
const body = await
|
|
5754
|
+
const body = await readJsonBody4(req);
|
|
5271
5755
|
const rawOrder = body["order"];
|
|
5272
5756
|
if (!Array.isArray(rawOrder)) {
|
|
5273
5757
|
return writeJsonError(res, 400, "reorder requires { order: string[] }");
|
|
@@ -5291,14 +5775,14 @@ async function handleProviderReorder(req, res, cfg, deps) {
|
|
|
5291
5775
|
}
|
|
5292
5776
|
cfg.providers = reordered;
|
|
5293
5777
|
persistProviders(cfg, deps);
|
|
5294
|
-
return
|
|
5778
|
+
return writeJson4(res, 200, { ok: true, providers: cfg.providers.map(toProviderView) });
|
|
5295
5779
|
}
|
|
5296
5780
|
async function handleDiscoverModels(res, id, cfg) {
|
|
5297
5781
|
if (!id) return writeJsonError(res, 400, "provider id required in path");
|
|
5298
5782
|
const row = cfg.providers.find((p) => p.id === id);
|
|
5299
5783
|
if (!row) return writeJsonError(res, 404, `provider '${id}' not found`);
|
|
5300
5784
|
if (row.apiFormat !== "openai" && row.apiFormat !== "openai-response") {
|
|
5301
|
-
return
|
|
5785
|
+
return writeJson4(res, 200, { models: [], unsupportedFormat: true });
|
|
5302
5786
|
}
|
|
5303
5787
|
const resolvedKey = resolveEnvKey(row.apiKey);
|
|
5304
5788
|
const base = row.baseUrl.replace(/\/+$/, "");
|
|
@@ -5306,7 +5790,7 @@ async function handleDiscoverModels(res, id, cfg) {
|
|
|
5306
5790
|
try {
|
|
5307
5791
|
const headers = { Accept: "application/json" };
|
|
5308
5792
|
if (resolvedKey) headers["Authorization"] = `Bearer ${resolvedKey}`;
|
|
5309
|
-
const response = await (0,
|
|
5793
|
+
const response = await (0, import_upstreamFetch4.fetchUpstream)(url, { method: "GET", headers }, { providerId: "byo" });
|
|
5310
5794
|
if (!response.ok) {
|
|
5311
5795
|
const text = await response.text().catch(() => "");
|
|
5312
5796
|
let message = text.slice(0, 300);
|
|
@@ -5315,32 +5799,32 @@ async function handleDiscoverModels(res, id, cfg) {
|
|
|
5315
5799
|
message = parsed?.error?.message || parsed?.message || message;
|
|
5316
5800
|
} catch {
|
|
5317
5801
|
}
|
|
5318
|
-
return
|
|
5802
|
+
return writeJson4(res, 200, {
|
|
5319
5803
|
models: [],
|
|
5320
5804
|
error: `discovery failed (${response.status})${message ? `: ${message}` : ""}`
|
|
5321
5805
|
});
|
|
5322
5806
|
}
|
|
5323
5807
|
const data = await response.json();
|
|
5324
5808
|
const models = Array.isArray(data?.data) ? data.data.map((m) => typeof m?.id === "string" ? m.id : "").filter((m) => m.length > 0) : [];
|
|
5325
|
-
return
|
|
5809
|
+
return writeJson4(res, 200, { models });
|
|
5326
5810
|
} catch (err5) {
|
|
5327
5811
|
const message = err5 instanceof Error ? err5.message : String(err5);
|
|
5328
|
-
return
|
|
5812
|
+
return writeJson4(res, 200, { models: [], error: `discovery failed: ${message}` });
|
|
5329
5813
|
}
|
|
5330
5814
|
}
|
|
5331
5815
|
async function handleTestModel(req, res, id, cfg) {
|
|
5332
5816
|
if (!id) return writeJsonError(res, 400, "provider id required in path");
|
|
5333
5817
|
const row = cfg.providers.find((p) => p.id === id);
|
|
5334
5818
|
if (!row) return writeJsonError(res, 404, `provider '${id}' not found`);
|
|
5335
|
-
const body = await
|
|
5819
|
+
const body = await readJsonBody4(req);
|
|
5336
5820
|
const model = typeof body["model"] === "string" ? body["model"].trim() : "";
|
|
5337
5821
|
if (!model) return writeJsonError(res, 400, "test requires a { model } string");
|
|
5338
5822
|
if (row.apiFormat === "gemini") {
|
|
5339
|
-
return
|
|
5823
|
+
return writeJson4(res, 200, { ok: false, unsupportedFormat: true });
|
|
5340
5824
|
}
|
|
5341
5825
|
const resolvedKey = resolveEnvKey(row.apiKey);
|
|
5342
5826
|
if (!resolvedKey) {
|
|
5343
|
-
return
|
|
5827
|
+
return writeJson4(res, 200, { ok: false, message: "no API key configured for this provider" });
|
|
5344
5828
|
}
|
|
5345
5829
|
let url = row.baseUrl.replace(/\/+$/, "");
|
|
5346
5830
|
const prompt = "Reply with the single word: OK.";
|
|
@@ -5365,7 +5849,7 @@ async function handleTestModel(req, res, id, cfg) {
|
|
|
5365
5849
|
}
|
|
5366
5850
|
const startedAt = Date.now();
|
|
5367
5851
|
try {
|
|
5368
|
-
const response = await (0,
|
|
5852
|
+
const response = await (0, import_upstreamFetch4.fetchUpstream)(
|
|
5369
5853
|
url,
|
|
5370
5854
|
{ method: "POST", headers, body: JSON.stringify(payload) },
|
|
5371
5855
|
{ providerId: "byo" }
|
|
@@ -5379,9 +5863,9 @@ async function handleTestModel(req, res, id, cfg) {
|
|
|
5379
5863
|
message = parsed?.error?.message || parsed?.message || message;
|
|
5380
5864
|
} catch {
|
|
5381
5865
|
}
|
|
5382
|
-
return
|
|
5866
|
+
return writeJson4(res, 200, { ok: false, status: response.status, latencyMs, message });
|
|
5383
5867
|
}
|
|
5384
|
-
return
|
|
5868
|
+
return writeJson4(res, 200, {
|
|
5385
5869
|
ok: true,
|
|
5386
5870
|
status: response.status,
|
|
5387
5871
|
latencyMs,
|
|
@@ -5389,7 +5873,7 @@ async function handleTestModel(req, res, id, cfg) {
|
|
|
5389
5873
|
});
|
|
5390
5874
|
} catch (err5) {
|
|
5391
5875
|
const message = err5 instanceof Error ? err5.message : String(err5);
|
|
5392
|
-
return
|
|
5876
|
+
return writeJson4(res, 200, { ok: false, latencyMs: Date.now() - startedAt, message });
|
|
5393
5877
|
}
|
|
5394
5878
|
}
|
|
5395
5879
|
function extractSampleText(text, apiFormat) {
|
|
@@ -5430,7 +5914,7 @@ async function handleProviderKeys(res, id, cfg, deps) {
|
|
|
5430
5914
|
const row = cfg.providers.find((p) => p.id === id);
|
|
5431
5915
|
if (!row) return writeJsonError(res, 404, `provider '${id}' not found`);
|
|
5432
5916
|
const cooldown = await deps.apiKeyPool.getKeyHealth(id);
|
|
5433
|
-
return
|
|
5917
|
+
return writeJson4(res, 200, { keys: toPoolKeyView(row, cooldown, deps) });
|
|
5434
5918
|
}
|
|
5435
5919
|
function parsePoolKeyInput(body, existing) {
|
|
5436
5920
|
const out = {};
|
|
@@ -5449,7 +5933,7 @@ async function handleAddProviderKey(req, res, id, cfg, deps) {
|
|
|
5449
5933
|
if (!id) return writeJsonError(res, 400, "provider id required in path");
|
|
5450
5934
|
const idx = cfg.providers.findIndex((p) => p.id === id);
|
|
5451
5935
|
if (idx < 0) return writeJsonError(res, 404, `provider '${id}' not found`);
|
|
5452
|
-
const body = await
|
|
5936
|
+
const body = await readJsonBody4(req);
|
|
5453
5937
|
const parsed = parsePoolKeyInput(body);
|
|
5454
5938
|
if (!parsed.apiKey) return writeJsonError(res, 400, "add requires a non-empty apiKey");
|
|
5455
5939
|
const keyId = `key-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
@@ -5461,7 +5945,7 @@ async function handleAddProviderKey(req, res, id, cfg, deps) {
|
|
|
5461
5945
|
row.apiKeys = [...row.apiKeys ?? [], entry];
|
|
5462
5946
|
persistProviders(cfg, deps);
|
|
5463
5947
|
const cooldown = await deps.apiKeyPool.getKeyHealth(id);
|
|
5464
|
-
return
|
|
5948
|
+
return writeJson4(res, 201, { keys: toPoolKeyView(row, cooldown, deps) });
|
|
5465
5949
|
}
|
|
5466
5950
|
async function handleUpdateProviderKey(req, res, id, keyId, cfg, deps) {
|
|
5467
5951
|
if (!id) return writeJsonError(res, 400, "provider id required in path");
|
|
@@ -5471,7 +5955,7 @@ async function handleUpdateProviderKey(req, res, id, keyId, cfg, deps) {
|
|
|
5471
5955
|
const row = cfg.providers[idx];
|
|
5472
5956
|
const keyIdx = (row.apiKeys ?? []).findIndex((k) => k.id === keyId);
|
|
5473
5957
|
if (keyIdx < 0) return writeJsonError(res, 404, `pool key '${keyId}' not found`);
|
|
5474
|
-
const body = await
|
|
5958
|
+
const body = await readJsonBody4(req);
|
|
5475
5959
|
const existing = row.apiKeys[keyIdx];
|
|
5476
5960
|
const parsed = parsePoolKeyInput(body, existing);
|
|
5477
5961
|
const entry = { id: keyId, apiKey: parsed.apiKey || existing.apiKey };
|
|
@@ -5481,7 +5965,7 @@ async function handleUpdateProviderKey(req, res, id, keyId, cfg, deps) {
|
|
|
5481
5965
|
row.apiKeys[keyIdx] = entry;
|
|
5482
5966
|
persistProviders(cfg, deps);
|
|
5483
5967
|
const cooldown = await deps.apiKeyPool.getKeyHealth(id);
|
|
5484
|
-
return
|
|
5968
|
+
return writeJson4(res, 200, { keys: toPoolKeyView(row, cooldown, deps) });
|
|
5485
5969
|
}
|
|
5486
5970
|
async function handleDeleteProviderKey(res, id, keyId, cfg, deps) {
|
|
5487
5971
|
if (!id) return writeJsonError(res, 400, "provider id required in path");
|
|
@@ -5495,7 +5979,7 @@ async function handleDeleteProviderKey(res, id, keyId, cfg, deps) {
|
|
|
5495
5979
|
if (row.apiKeys.length === 0) row.apiKeys = void 0;
|
|
5496
5980
|
persistProviders(cfg, deps);
|
|
5497
5981
|
const cooldown = await deps.apiKeyPool.getKeyHealth(id);
|
|
5498
|
-
return
|
|
5982
|
+
return writeJson4(res, 200, { keys: toPoolKeyView(row, cooldown, deps) });
|
|
5499
5983
|
}
|
|
5500
5984
|
async function handleToggleProviderKey(req, res, id, keyId, cfg, deps) {
|
|
5501
5985
|
if (!id) return writeJsonError(res, 400, "provider id required in path");
|
|
@@ -5505,11 +5989,11 @@ async function handleToggleProviderKey(req, res, id, keyId, cfg, deps) {
|
|
|
5505
5989
|
const row = cfg.providers[idx];
|
|
5506
5990
|
const keyIdx = (row.apiKeys ?? []).findIndex((k) => k.id === keyId);
|
|
5507
5991
|
if (keyIdx < 0) return writeJsonError(res, 404, `pool key '${keyId}' not found`);
|
|
5508
|
-
const body = await
|
|
5992
|
+
const body = await readJsonBody4(req);
|
|
5509
5993
|
row.apiKeys[keyIdx].enabled = Boolean(body["enabled"]);
|
|
5510
5994
|
persistProviders(cfg, deps);
|
|
5511
5995
|
const cooldown = await deps.apiKeyPool.getKeyHealth(id);
|
|
5512
|
-
return
|
|
5996
|
+
return writeJson4(res, 200, { keys: toPoolKeyView(row, cooldown, deps) });
|
|
5513
5997
|
}
|
|
5514
5998
|
function parseApiKeysInput(raw, existing) {
|
|
5515
5999
|
if (!Array.isArray(raw)) return existing;
|
|
@@ -5695,13 +6179,13 @@ function handlePresets(res, method) {
|
|
|
5695
6179
|
website: p.website,
|
|
5696
6180
|
modelsEndpoint: p.modelsEndpoint
|
|
5697
6181
|
}));
|
|
5698
|
-
return
|
|
6182
|
+
return writeJson4(res, 200, { presets, excluded });
|
|
5699
6183
|
}
|
|
5700
6184
|
async function handleKeys(req, res, method, rest, deps) {
|
|
5701
6185
|
if (method === "GET" && rest.length === 0) {
|
|
5702
6186
|
const rows = await deps.keyDb.outboundApiKeysList();
|
|
5703
6187
|
const reader = deps.keySpendReader;
|
|
5704
|
-
if (!reader) return
|
|
6188
|
+
if (!reader) return writeJson4(res, 200, { keys: rows.map(toKeyInfo) });
|
|
5705
6189
|
const now = Date.now();
|
|
5706
6190
|
const keys = await Promise.all(
|
|
5707
6191
|
rows.map(async (row) => {
|
|
@@ -5713,13 +6197,13 @@ async function handleKeys(req, res, method, rest, deps) {
|
|
|
5713
6197
|
return info;
|
|
5714
6198
|
})
|
|
5715
6199
|
);
|
|
5716
|
-
return
|
|
6200
|
+
return writeJson4(res, 200, { keys });
|
|
5717
6201
|
}
|
|
5718
6202
|
if (method === "POST" && rest.length === 0) {
|
|
5719
|
-
const body = await
|
|
6203
|
+
const body = await readJsonBody4(req);
|
|
5720
6204
|
const name = typeof body["name"] === "string" && body["name"].trim() ? body["name"].trim() : "key";
|
|
5721
|
-
const created = await (0,
|
|
5722
|
-
return
|
|
6205
|
+
const created = await (0, import_outbound_api5.createNamedKey)(deps.keyDb, name);
|
|
6206
|
+
return writeJson4(res, 201, {
|
|
5723
6207
|
id: created.id,
|
|
5724
6208
|
name: created.name,
|
|
5725
6209
|
keyPrefix: created.keyPrefix,
|
|
@@ -5730,7 +6214,7 @@ async function handleKeys(req, res, method, rest, deps) {
|
|
|
5730
6214
|
}
|
|
5731
6215
|
if (method === "GET" && rest.length === 2 && rest[1] === "reveal") {
|
|
5732
6216
|
const revealed = await deps.keyDb.outboundApiKeysReveal(rest[0]);
|
|
5733
|
-
if (revealed !== null) return
|
|
6217
|
+
if (revealed !== null) return writeJson4(res, 200, { key: revealed });
|
|
5734
6218
|
const exists = (await deps.keyDb.outboundApiKeysList()).some((r) => r.id === rest[0]);
|
|
5735
6219
|
if (!exists) return writeJsonError(res, 404, `key '${rest[0]}' not found`);
|
|
5736
6220
|
return writeJsonError(
|
|
@@ -5745,32 +6229,32 @@ async function handleKeys(req, res, method, rest, deps) {
|
|
|
5745
6229
|
const bound = await integrationKeyRequirement(deps, id);
|
|
5746
6230
|
if (bound) return writeJsonError(res, 409, bound);
|
|
5747
6231
|
const ok = await deps.keyDb.outboundApiKeysRevoke(id);
|
|
5748
|
-
return
|
|
6232
|
+
return writeJson4(res, ok ? 200 : 404, { ok });
|
|
5749
6233
|
}
|
|
5750
6234
|
if (method === "DELETE" && id && !action) {
|
|
5751
6235
|
const bound = await integrationKeyRequirement(deps, id);
|
|
5752
6236
|
if (bound) return writeJsonError(res, 409, bound);
|
|
5753
6237
|
const ok = await deps.keyDb.outboundApiKeysDelete(id);
|
|
5754
|
-
return
|
|
6238
|
+
return writeJson4(res, ok ? 200 : 404, { ok });
|
|
5755
6239
|
}
|
|
5756
6240
|
if (method === "POST" && id && action === "enabled") {
|
|
5757
|
-
const body = await
|
|
6241
|
+
const body = await readJsonBody4(req);
|
|
5758
6242
|
const enabled = body["enabled"] === true;
|
|
5759
6243
|
if (!enabled) {
|
|
5760
6244
|
const bound = await integrationKeyRequirement(deps, id);
|
|
5761
6245
|
if (bound) return writeJsonError(res, 409, bound);
|
|
5762
6246
|
}
|
|
5763
6247
|
const ok = await deps.keyDb.outboundApiKeysSetEnabled(id, enabled);
|
|
5764
|
-
return
|
|
6248
|
+
return writeJson4(res, ok ? 200 : 404, { ok, enabled });
|
|
5765
6249
|
}
|
|
5766
6250
|
if (method === "POST" && id && action === "permissions") {
|
|
5767
|
-
const body = await
|
|
6251
|
+
const body = await readJsonBody4(req);
|
|
5768
6252
|
if (Object.keys(body).length !== 1 || !Object.prototype.hasOwnProperty.call(body, "permissions")) {
|
|
5769
6253
|
return writeJsonError(res, 400, "body must contain only permissions");
|
|
5770
6254
|
}
|
|
5771
6255
|
let permissions;
|
|
5772
6256
|
try {
|
|
5773
|
-
permissions = (0,
|
|
6257
|
+
permissions = (0, import_outbound_api5.validateOutboundPermissions)(body["permissions"]);
|
|
5774
6258
|
} catch {
|
|
5775
6259
|
return writeJsonError(
|
|
5776
6260
|
res,
|
|
@@ -5779,19 +6263,19 @@ async function handleKeys(req, res, method, rest, deps) {
|
|
|
5779
6263
|
);
|
|
5780
6264
|
}
|
|
5781
6265
|
const before = (await deps.keyDb.outboundApiKeysList()).find((row) => row.id === id);
|
|
5782
|
-
if (!before) return
|
|
5783
|
-
if (before.revokedAt !== null) return
|
|
6266
|
+
if (!before) return writeJson4(res, 404, { ok: false });
|
|
6267
|
+
if (before.revokedAt !== null) return writeJson4(res, 409, { ok: false });
|
|
5784
6268
|
const required = await integrationKeyRequirement(deps, id, permissions);
|
|
5785
6269
|
if (required) return writeJsonError(res, 409, required);
|
|
5786
6270
|
const ok = await deps.keyDb.outboundApiKeysSetPermissions(id, permissions);
|
|
5787
6271
|
if (!ok) {
|
|
5788
6272
|
const current = (await deps.keyDb.outboundApiKeysList()).find((row) => row.id === id);
|
|
5789
|
-
return
|
|
6273
|
+
return writeJson4(res, current?.revokedAt !== null ? 409 : 404, { ok: false });
|
|
5790
6274
|
}
|
|
5791
|
-
return
|
|
6275
|
+
return writeJson4(res, 200, { ok: true, allowedEndpoints: permissions });
|
|
5792
6276
|
}
|
|
5793
6277
|
if (method === "POST" && id && action === "max-concurrency") {
|
|
5794
|
-
const body = await
|
|
6278
|
+
const body = await readJsonBody4(req);
|
|
5795
6279
|
const raw = body["maxConcurrency"];
|
|
5796
6280
|
let value;
|
|
5797
6281
|
if (raw === null) {
|
|
@@ -5806,14 +6290,14 @@ async function handleKeys(req, res, method, rest, deps) {
|
|
|
5806
6290
|
);
|
|
5807
6291
|
}
|
|
5808
6292
|
const ok = await deps.keyDb.outboundApiKeysSetMaxConcurrency(id, value);
|
|
5809
|
-
return
|
|
6293
|
+
return writeJson4(res, ok ? 200 : 404, { ok, maxConcurrency: value });
|
|
5810
6294
|
}
|
|
5811
6295
|
if (method === "POST" && id && action === "policy") {
|
|
5812
|
-
const body = await
|
|
6296
|
+
const body = await readJsonBody4(req);
|
|
5813
6297
|
const parsed = parseKeyPolicyBody(body);
|
|
5814
6298
|
if (!parsed.ok) return writeJsonError(res, 400, parsed.message);
|
|
5815
6299
|
const ok = await deps.keyDb.outboundApiKeysSetPolicy(id, parsed.policy);
|
|
5816
|
-
return
|
|
6300
|
+
return writeJson4(res, ok ? 200 : 404, { ok });
|
|
5817
6301
|
}
|
|
5818
6302
|
return writeJsonError(res, 405, `method ${method} not allowed on keys`);
|
|
5819
6303
|
}
|
|
@@ -5904,7 +6388,8 @@ function outboundServerConfigInput(config) {
|
|
|
5904
6388
|
userMessageQueue: config.userMessageQueue,
|
|
5905
6389
|
concurrencyQueue: config.concurrencyQueue,
|
|
5906
6390
|
voucher: config.voucher,
|
|
5907
|
-
anthropic: config.anthropic
|
|
6391
|
+
anthropic: config.anthropic,
|
|
6392
|
+
search: config.search
|
|
5908
6393
|
};
|
|
5909
6394
|
}
|
|
5910
6395
|
function projectImagesConfigForAdmin(config) {
|
|
@@ -5940,8 +6425,8 @@ function sameConfigValue(left, right) {
|
|
|
5940
6425
|
return JSON.stringify(left) === JSON.stringify(right);
|
|
5941
6426
|
}
|
|
5942
6427
|
function imageConfigurationAuditFields(previous, next) {
|
|
5943
|
-
const before = previous ??
|
|
5944
|
-
const after = next ??
|
|
6428
|
+
const before = previous ?? import_outbound_api5.DEFAULT_IMAGES_SERVER_CONFIG;
|
|
6429
|
+
const after = next ?? import_outbound_api5.DEFAULT_IMAGES_SERVER_CONFIG;
|
|
5945
6430
|
const fields = [];
|
|
5946
6431
|
if (before.enabled !== after.enabled) fields.push("enablement");
|
|
5947
6432
|
if (before.provider !== after.provider) fields.push("provider");
|
|
@@ -5967,15 +6452,21 @@ function currentImageGenerationId(deps) {
|
|
|
5967
6452
|
}
|
|
5968
6453
|
async function handleServer(req, res, method, deps) {
|
|
5969
6454
|
if (method === "GET") {
|
|
5970
|
-
const config = await (0,
|
|
6455
|
+
const config = await (0, import_outbound_api5.loadServerConfig)(deps.settingsStore);
|
|
5971
6456
|
let server = config;
|
|
5972
6457
|
if (config.proxy) server = { ...server, proxy: redactOutboundProxy(config.proxy) };
|
|
5973
6458
|
if (config.webhook) server = { ...server, webhook: redactWebhookConfig(config.webhook) };
|
|
5974
6459
|
if (config.billing) server = { ...server, billing: redactBillingConfig(config.billing) };
|
|
5975
|
-
|
|
6460
|
+
if (config.search) {
|
|
6461
|
+
server = {
|
|
6462
|
+
...server,
|
|
6463
|
+
search: redactSearchServerConfig(config.search)
|
|
6464
|
+
};
|
|
6465
|
+
}
|
|
6466
|
+
return writeJson4(res, 200, { server: projectImagesConfigForAdmin(server) });
|
|
5976
6467
|
}
|
|
5977
6468
|
if (method === "PUT") {
|
|
5978
|
-
const patch = await
|
|
6469
|
+
const patch = await readJsonBody4(req);
|
|
5979
6470
|
const queueErrors = validateQueueSegments(patch);
|
|
5980
6471
|
if (queueErrors.length > 0) {
|
|
5981
6472
|
return writeJsonError(res, 400, `invalid queue config: ${queueErrors.join("; ")}`);
|
|
@@ -6004,7 +6495,7 @@ async function handleServer(req, res, method, deps) {
|
|
|
6004
6495
|
if (billingErrors.length > 0) {
|
|
6005
6496
|
return writeJsonError(res, 400, `invalid billing config: ${billingErrors.join("; ")}`);
|
|
6006
6497
|
}
|
|
6007
|
-
const current = await (0,
|
|
6498
|
+
const current = await (0, import_outbound_api5.loadServerConfig)(deps.settingsStore);
|
|
6008
6499
|
let effectivePatch = patch;
|
|
6009
6500
|
if (patch.proxy) {
|
|
6010
6501
|
effectivePatch = { ...effectivePatch, proxy: preserveOutboundProxySecrets(patch.proxy, current.proxy) };
|
|
@@ -6025,7 +6516,21 @@ async function handleServer(req, res, method, deps) {
|
|
|
6025
6516
|
}
|
|
6026
6517
|
effectivePatch = { ...effectivePatch, images };
|
|
6027
6518
|
}
|
|
6028
|
-
|
|
6519
|
+
if (patch.search !== void 0) {
|
|
6520
|
+
const searchPatch = preserveSearchSecrets(
|
|
6521
|
+
patch.search,
|
|
6522
|
+
current.search ?? import_outbound_api5.DEFAULT_SEARCH_SERVER_CONFIG
|
|
6523
|
+
);
|
|
6524
|
+
const searchErrors = (0, import_outbound_api5.validateSearchServerConfig)(searchPatch);
|
|
6525
|
+
if (searchErrors.length > 0) {
|
|
6526
|
+
return writeJsonError(res, 400, `invalid search config: ${searchErrors.join("; ")}`);
|
|
6527
|
+
}
|
|
6528
|
+
effectivePatch = {
|
|
6529
|
+
...effectivePatch,
|
|
6530
|
+
search: searchPatch
|
|
6531
|
+
};
|
|
6532
|
+
}
|
|
6533
|
+
const merged = (0, import_outbound_api5.mergeServerConfig)(current, effectivePatch);
|
|
6029
6534
|
const priorImageGenerationId = currentImageGenerationId(deps);
|
|
6030
6535
|
try {
|
|
6031
6536
|
await applyServerConfigTransaction(current, merged, {
|
|
@@ -6055,7 +6560,7 @@ async function handleServer(req, res, method, deps) {
|
|
|
6055
6560
|
throw error;
|
|
6056
6561
|
}
|
|
6057
6562
|
},
|
|
6058
|
-
persist: (next) => (0,
|
|
6563
|
+
persist: (next) => (0, import_outbound_api5.saveServerConfig)(deps.settingsStore, next),
|
|
6059
6564
|
restorePersisted: (snapshot) => deps.settingsStore.restoreDocumentSnapshot(snapshot)
|
|
6060
6565
|
});
|
|
6061
6566
|
} catch (error) {
|
|
@@ -6077,7 +6582,11 @@ async function handleServer(req, res, method, deps) {
|
|
|
6077
6582
|
} catch {
|
|
6078
6583
|
}
|
|
6079
6584
|
}
|
|
6080
|
-
|
|
6585
|
+
const mergedForAdmin = merged.search ? {
|
|
6586
|
+
...merged,
|
|
6587
|
+
search: redactSearchServerConfig(merged.search)
|
|
6588
|
+
} : merged;
|
|
6589
|
+
return writeJson4(res, 200, { server: projectImagesConfigForAdmin(mergedForAdmin) });
|
|
6081
6590
|
}
|
|
6082
6591
|
return writeJsonError(res, 405, `method ${method} not allowed on server`);
|
|
6083
6592
|
}
|
|
@@ -6094,7 +6603,7 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
6094
6603
|
sessionKey: query2.get("sessionKey") ?? void 0,
|
|
6095
6604
|
limit: Number.isFinite(parsedLimit) ? parsedLimit : 100
|
|
6096
6605
|
});
|
|
6097
|
-
return
|
|
6606
|
+
return writeJson4(res, 200, {
|
|
6098
6607
|
available: true,
|
|
6099
6608
|
records,
|
|
6100
6609
|
capacity: import_AccountRouteActivity.ACCOUNT_ROUTE_ACTIVITY_LIMIT,
|
|
@@ -6110,7 +6619,7 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
6110
6619
|
providerId: query2.get("providerId") ?? void 0,
|
|
6111
6620
|
accountId: query2.get("accountId") ?? void 0
|
|
6112
6621
|
});
|
|
6113
|
-
return
|
|
6622
|
+
return writeJson4(res, 200, {
|
|
6114
6623
|
available: true,
|
|
6115
6624
|
entries,
|
|
6116
6625
|
collectedAt: Date.now()
|
|
@@ -6129,10 +6638,10 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
6129
6638
|
const accounts = await deps.subscriptionAccounts.listAll();
|
|
6130
6639
|
const providerAccounts = await deps.subscriptionTokenWriter.listSanitizedAccounts();
|
|
6131
6640
|
const externalCli = await deps.subscriptionTokenWriter.listExternalCliAvailability();
|
|
6132
|
-
return
|
|
6641
|
+
return writeJson4(res, 200, { accounts, providerAccounts, externalCli });
|
|
6133
6642
|
}
|
|
6134
6643
|
if (method === "POST" && rest[0] === "batch" && rest.length === 1) {
|
|
6135
|
-
const body = await
|
|
6644
|
+
const body = await readJsonBody4(req);
|
|
6136
6645
|
const parsed = validateAccountBatchBody(body);
|
|
6137
6646
|
if (!parsed) return writeJsonError(res, 400, "invalid account batch request");
|
|
6138
6647
|
const result = await deps.subscriptionTokenWriter.batchManageAccounts(parsed.refs, parsed.mutation);
|
|
@@ -6148,15 +6657,15 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
6148
6657
|
deps.accountAllowanceService?.removeAccountSnapshot?.(ref.providerId, ref.accountId);
|
|
6149
6658
|
}
|
|
6150
6659
|
}
|
|
6151
|
-
return
|
|
6660
|
+
return writeJson4(res, 200, { ok: true, affected: result.affected });
|
|
6152
6661
|
}
|
|
6153
6662
|
if (method === "GET" && rest[0] === "codex" && rest[1] === "oauth" && rest[3] === "status") {
|
|
6154
6663
|
const result = handleCodexOAuthStatus(rest[2], deps);
|
|
6155
|
-
return
|
|
6664
|
+
return writeJson4(res, result.status, result.body);
|
|
6156
6665
|
}
|
|
6157
6666
|
if (method === "DELETE" && rest[0] === "codex" && rest[1] === "oauth" && rest[2]) {
|
|
6158
6667
|
const result = handleCodexOAuthCancel(rest[2], deps);
|
|
6159
|
-
return
|
|
6668
|
+
return writeJson4(res, result.status, result.body);
|
|
6160
6669
|
}
|
|
6161
6670
|
if (method === "GET" && rest.length === 3 && rest[2] === "diagnostics") {
|
|
6162
6671
|
const providerId = asSubscriptionProviderId(rest[0]);
|
|
@@ -6178,7 +6687,7 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
6178
6687
|
resumeAt: entry.resumeAt
|
|
6179
6688
|
})) ?? [];
|
|
6180
6689
|
const diagnostics = [...health2, ...allowance].sort((left, right) => right.at - left.at).slice(0, 200);
|
|
6181
|
-
return
|
|
6690
|
+
return writeJson4(res, 200, { diagnostics });
|
|
6182
6691
|
}
|
|
6183
6692
|
if (method === "GET" && rest.length === 3 && rest[2] === "events") {
|
|
6184
6693
|
const providerId = asSubscriptionProviderId(rest[0]);
|
|
@@ -6190,17 +6699,17 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
6190
6699
|
}
|
|
6191
6700
|
const snapshot = deps.accountProbeService?.getAllHistory().find((entry) => entry.providerId === providerId && entry.accountId === accountId);
|
|
6192
6701
|
const diagnostics = (0, import_SubscriptionAccountHealth.getSharedAccountHealth)().getDiagnostics({ providerId, accountId });
|
|
6193
|
-
return
|
|
6702
|
+
return writeJson4(res, 200, { events: snapshot?.records ?? [], diagnostics });
|
|
6194
6703
|
}
|
|
6195
6704
|
if (method === "PATCH" && rest.length === 2) {
|
|
6196
6705
|
const providerId = asSubscriptionProviderId(rest[0]);
|
|
6197
6706
|
if (!providerId) return writeJsonError(res, 400, `unknown subscription provider '${rest[0] ?? ""}'`);
|
|
6198
|
-
const body = await
|
|
6707
|
+
const body = await readJsonBody4(req);
|
|
6199
6708
|
const patch = validateAccountMetadataPatch(body);
|
|
6200
6709
|
if (!patch) return writeJsonError(res, 400, "invalid account metadata patch");
|
|
6201
6710
|
const result = await deps.subscriptionTokenWriter.patchAccountMetadata(providerId, rest[1], patch);
|
|
6202
6711
|
if (!result.ok) return writeJsonError(res, 404, `account '${rest[1]}' not found`);
|
|
6203
|
-
return
|
|
6712
|
+
return writeJson4(res, 200, { ok: true });
|
|
6204
6713
|
}
|
|
6205
6714
|
if (method === "PUT" || method === "POST" || method === "DELETE") {
|
|
6206
6715
|
const providerId = asSubscriptionProviderId(rest[0]);
|
|
@@ -6209,15 +6718,15 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
6209
6718
|
}
|
|
6210
6719
|
if (method === "POST" && rest[1] === "oauth" && rest[2] === "start") {
|
|
6211
6720
|
const result = providerId === "codex" ? handleCodexOAuthStart(deps) : handleOAuthStart(providerId, deps);
|
|
6212
|
-
return
|
|
6721
|
+
return writeJson4(res, result.status, result.body);
|
|
6213
6722
|
}
|
|
6214
6723
|
if (method === "POST" && rest[1] === "oauth" && rest[2] === "complete") {
|
|
6215
|
-
const body2 = await
|
|
6724
|
+
const body2 = await readJsonBody4(req);
|
|
6216
6725
|
const result = await handleOAuthComplete(providerId, body2, deps);
|
|
6217
|
-
return
|
|
6726
|
+
return writeJson4(res, result.status, result.body);
|
|
6218
6727
|
}
|
|
6219
6728
|
if (method === "POST" && rest[1] === "accounts") {
|
|
6220
|
-
const body2 = await
|
|
6729
|
+
const body2 = await readJsonBody4(req);
|
|
6221
6730
|
const block = validateTokenBody(providerId, body2);
|
|
6222
6731
|
if (!block) {
|
|
6223
6732
|
return writeJsonError(res, 400, `malformed token body for provider '${providerId}'`);
|
|
@@ -6225,20 +6734,20 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
6225
6734
|
const label = typeof body2["label"] === "string" && body2["label"].trim() ? body2["label"].trim() : void 0;
|
|
6226
6735
|
await deps.subscriptionAccountAppender.appendProviderAccount(providerId, block, label);
|
|
6227
6736
|
const status2 = await statusEntryFor(deps.subscriptionAccounts, providerId);
|
|
6228
|
-
return
|
|
6737
|
+
return writeJson4(res, 200, status2 ? { account: status2 } : { ok: true });
|
|
6229
6738
|
}
|
|
6230
6739
|
if (method === "POST" && rest[1] === "import-external") {
|
|
6231
6740
|
if (providerId !== "claude" && providerId !== "codex") {
|
|
6232
6741
|
return writeJsonError(res, 400, `provider '${providerId}' has no external CLI store`);
|
|
6233
6742
|
}
|
|
6234
|
-
const body2 = await
|
|
6743
|
+
const body2 = await readJsonBody4(req);
|
|
6235
6744
|
const label = typeof body2["label"] === "string" && body2["label"].trim() ? body2["label"].trim() : void 0;
|
|
6236
6745
|
const result = await deps.subscriptionTokenWriter.importExternalCliAccount(providerId, label);
|
|
6237
6746
|
if (!result.ok) {
|
|
6238
6747
|
return writeJsonError(res, 409, `no usable external ${providerId} CLI credential found`);
|
|
6239
6748
|
}
|
|
6240
6749
|
const status2 = await statusEntryFor(deps.subscriptionAccounts, providerId);
|
|
6241
|
-
return
|
|
6750
|
+
return writeJson4(res, 200, {
|
|
6242
6751
|
ok: true,
|
|
6243
6752
|
account: status2 ?? void 0,
|
|
6244
6753
|
nativeCredentialMode: result.nativeCredentialMode,
|
|
@@ -6253,7 +6762,7 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
6253
6762
|
const writer2 = deps.subscriptionTokenWriter;
|
|
6254
6763
|
const ok = providerId === "claude" ? await writer2.refreshClaudeToken() : providerId === "codex" ? await writer2.refreshCodexToken() : await writer2.refreshGeminiToken();
|
|
6255
6764
|
const status2 = await statusEntryFor(deps.subscriptionAccounts, providerId);
|
|
6256
|
-
return
|
|
6765
|
+
return writeJson4(res, 200, { ok, account: status2 ?? void 0 });
|
|
6257
6766
|
}
|
|
6258
6767
|
if (method === "POST" && rest.length === 3 && rest[2] === "test") {
|
|
6259
6768
|
const accountId = rest[1];
|
|
@@ -6263,7 +6772,7 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
6263
6772
|
return writeJsonError(res, 404, `account '${accountId}' not found`);
|
|
6264
6773
|
}
|
|
6265
6774
|
const result = await deps.accountProbeService.testAccountConnection(providerId, accountId);
|
|
6266
|
-
return
|
|
6775
|
+
return writeJson4(res, 200, {
|
|
6267
6776
|
ok: result.ok,
|
|
6268
6777
|
marked: result.marked,
|
|
6269
6778
|
tier: result.tier,
|
|
@@ -6272,15 +6781,15 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
6272
6781
|
}
|
|
6273
6782
|
if (method === "POST" && rest[2] === "label") {
|
|
6274
6783
|
const accountId = rest[1];
|
|
6275
|
-
const body2 = await
|
|
6784
|
+
const body2 = await readJsonBody4(req);
|
|
6276
6785
|
const label = typeof body2["label"] === "string" ? body2["label"].trim() : "";
|
|
6277
6786
|
const result = await deps.subscriptionTokenWriter.renameAccount(providerId, accountId, label);
|
|
6278
6787
|
if (!result.ok) return writeJsonError(res, 404, `account '${accountId}' not found`);
|
|
6279
|
-
return
|
|
6788
|
+
return writeJson4(res, 200, { ok: true });
|
|
6280
6789
|
}
|
|
6281
6790
|
if (method === "POST" && rest[2] === "priority") {
|
|
6282
6791
|
const accountId = rest[1];
|
|
6283
|
-
const body2 = await
|
|
6792
|
+
const body2 = await readJsonBody4(req);
|
|
6284
6793
|
const raw = body2["priority"];
|
|
6285
6794
|
const priority = typeof raw === "number" ? raw : Number(raw);
|
|
6286
6795
|
if (!Number.isFinite(priority)) {
|
|
@@ -6288,76 +6797,76 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
6288
6797
|
}
|
|
6289
6798
|
const result = await deps.subscriptionTokenWriter.setAccountPriority(providerId, accountId, priority);
|
|
6290
6799
|
if (!result.ok) return writeJsonError(res, 404, `account '${accountId}' not found`);
|
|
6291
|
-
return
|
|
6800
|
+
return writeJson4(res, 200, { ok: true });
|
|
6292
6801
|
}
|
|
6293
6802
|
if (method === "POST" && rest[2] === "proxy") {
|
|
6294
6803
|
const accountId = rest[1];
|
|
6295
|
-
const body2 = await
|
|
6804
|
+
const body2 = await readJsonBody4(req);
|
|
6296
6805
|
const rawProxy = body2["proxy"];
|
|
6297
6806
|
let proxy;
|
|
6298
6807
|
if (rawProxy !== null && rawProxy !== void 0) {
|
|
6299
|
-
proxy = (0,
|
|
6808
|
+
proxy = (0, import_outbound_api5.normalizeProxyConfig)(rawProxy);
|
|
6300
6809
|
if (!proxy) return writeJsonError(res, 400, "invalid proxy config");
|
|
6301
6810
|
}
|
|
6302
6811
|
const result = await deps.subscriptionTokenWriter.setAccountProxy(providerId, accountId, proxy);
|
|
6303
6812
|
if (!result.ok) return writeJsonError(res, 404, `account '${accountId}' not found`);
|
|
6304
|
-
return
|
|
6813
|
+
return writeJson4(res, 200, { ok: true });
|
|
6305
6814
|
}
|
|
6306
6815
|
if (method === "POST" && rest[2] === "supported-models") {
|
|
6307
6816
|
const accountId = rest[1];
|
|
6308
|
-
const body2 = await
|
|
6817
|
+
const body2 = await readJsonBody4(req);
|
|
6309
6818
|
const parsed = validateSupportedModelsBody(body2["supportedModels"]);
|
|
6310
6819
|
if (!parsed.ok) return writeJsonError(res, 400, "invalid supportedModels");
|
|
6311
6820
|
const result = await deps.subscriptionTokenWriter.setAccountSupportedModels(providerId, accountId, parsed.value);
|
|
6312
6821
|
if (!result.ok) return writeJsonError(res, 404, `account '${accountId}' not found`);
|
|
6313
|
-
return
|
|
6822
|
+
return writeJson4(res, 200, { ok: true });
|
|
6314
6823
|
}
|
|
6315
6824
|
if (method === "PUT" && rest[1] === "active") {
|
|
6316
|
-
const body2 = await
|
|
6825
|
+
const body2 = await readJsonBody4(req);
|
|
6317
6826
|
const id = typeof body2["id"] === "string" ? body2["id"] : "";
|
|
6318
6827
|
if (!id) return writeJsonError(res, 400, "active switch requires { id }");
|
|
6319
6828
|
const result = await deps.subscriptionTokenWriter.setActiveAccount(providerId, id);
|
|
6320
6829
|
if (!result.ok) return writeJsonError(res, 404, `account '${id}' not found`);
|
|
6321
|
-
return
|
|
6830
|
+
return writeJson4(res, 200, { ok: true });
|
|
6322
6831
|
}
|
|
6323
6832
|
if (method === "DELETE" && rest.length === 2) {
|
|
6324
6833
|
const accountId = rest[1];
|
|
6325
6834
|
const result = await deps.subscriptionTokenWriter.removeAccount(providerId, accountId);
|
|
6326
6835
|
if (!result.removed) return writeJsonError(res, 404, `account '${accountId}' not found`);
|
|
6327
6836
|
deps.accountAllowanceService?.removeAccountSnapshot?.(providerId, accountId);
|
|
6328
|
-
return
|
|
6837
|
+
return writeJson4(res, 200, { ok: true });
|
|
6329
6838
|
}
|
|
6330
6839
|
if (method === "DELETE" && rest.length === 1) {
|
|
6331
6840
|
await deps.subscriptionTokenWriter.clearProvider(providerId);
|
|
6332
6841
|
deps.accountAllowanceService?.removeProviderSnapshots?.(providerId);
|
|
6333
|
-
return
|
|
6842
|
+
return writeJson4(res, 200, { ok: true });
|
|
6334
6843
|
}
|
|
6335
6844
|
if (method === "DELETE") {
|
|
6336
6845
|
return writeJsonError(res, 405, "method DELETE not allowed on this accounts path");
|
|
6337
6846
|
}
|
|
6338
|
-
const body = await
|
|
6847
|
+
const body = await readJsonBody4(req);
|
|
6339
6848
|
const config = validateTokenBody(providerId, body);
|
|
6340
6849
|
if (!config) {
|
|
6341
6850
|
return writeJsonError(res, 400, `malformed token body for provider '${providerId}'`);
|
|
6342
6851
|
}
|
|
6343
6852
|
await deps.subscriptionTokenWriter.writeProviderTokens(providerId, config);
|
|
6344
6853
|
const status = await statusEntryFor(deps.subscriptionAccounts, providerId);
|
|
6345
|
-
return
|
|
6854
|
+
return writeJson4(res, 200, status ? { account: status } : { ok: true });
|
|
6346
6855
|
}
|
|
6347
6856
|
return writeJsonError(res, 405, `method ${method} not allowed on accounts`);
|
|
6348
6857
|
}
|
|
6349
6858
|
async function handleCli(req, res, method, rest, deps) {
|
|
6350
6859
|
if (method === "GET" && rest.length === 0) {
|
|
6351
6860
|
const result = handleCliList(process.platform, deps.cliPathProbe);
|
|
6352
|
-
return
|
|
6861
|
+
return writeJson4(res, result.status, result.body);
|
|
6353
6862
|
}
|
|
6354
6863
|
if (method === "GET" && rest[0] === "sessions") {
|
|
6355
6864
|
const result = handleCliSessions();
|
|
6356
|
-
return
|
|
6865
|
+
return writeJson4(res, result.status, result.body);
|
|
6357
6866
|
}
|
|
6358
6867
|
if (method === "DELETE" && rest[0] === "sessions" && rest[1]) {
|
|
6359
6868
|
const result = handleCliStop(rest[1]);
|
|
6360
|
-
return
|
|
6869
|
+
return writeJson4(res, result.status, result.body);
|
|
6361
6870
|
}
|
|
6362
6871
|
if (method === "POST" && rest[1] === "install") {
|
|
6363
6872
|
const cli = rest[0];
|
|
@@ -6365,14 +6874,14 @@ async function handleCli(req, res, method, rest, deps) {
|
|
|
6365
6874
|
return writeJsonError(res, 400, `unknown cli '${cli ?? ""}'`);
|
|
6366
6875
|
}
|
|
6367
6876
|
const result = await handleCliInstall(cli, deps.cliCommandRunner);
|
|
6368
|
-
return
|
|
6877
|
+
return writeJson4(res, result.status, result.body);
|
|
6369
6878
|
}
|
|
6370
6879
|
if (method === "POST" && rest[1] === "launch") {
|
|
6371
6880
|
const cli = rest[0];
|
|
6372
6881
|
if (!isLaunchCliId(cli)) {
|
|
6373
6882
|
return writeJsonError(res, 400, `unknown cli '${cli ?? ""}'`);
|
|
6374
6883
|
}
|
|
6375
|
-
const body = await
|
|
6884
|
+
const body = await readJsonBody4(req);
|
|
6376
6885
|
const providers = loadConfig(deps.configPath).providers ?? [];
|
|
6377
6886
|
const result = await handleCliLaunch(cli, body, {
|
|
6378
6887
|
llmConfig: deps.llmConfig,
|
|
@@ -6381,7 +6890,7 @@ async function handleCli(req, res, method, rest, deps) {
|
|
|
6381
6890
|
opener: deps.cliTerminalOpener,
|
|
6382
6891
|
probe: deps.cliPathProbe
|
|
6383
6892
|
});
|
|
6384
|
-
return
|
|
6893
|
+
return writeJson4(res, result.status, result.body);
|
|
6385
6894
|
}
|
|
6386
6895
|
return writeJsonError(res, 405, `method ${method} not allowed on cli`);
|
|
6387
6896
|
}
|
|
@@ -6391,52 +6900,52 @@ async function handleIntegrations(req, res, method, rest, deps) {
|
|
|
6391
6900
|
const manager = factory();
|
|
6392
6901
|
try {
|
|
6393
6902
|
if (method === "GET" && rest.length === 0) {
|
|
6394
|
-
return
|
|
6903
|
+
return writeJson4(res, 200, {
|
|
6395
6904
|
integrations: await manager.listStatus(),
|
|
6396
6905
|
gateway: deps.outboundApiServer.getStatus()
|
|
6397
6906
|
});
|
|
6398
6907
|
}
|
|
6399
6908
|
if (method === "POST" && rest.length === 1 && rest[0] === "rotate") {
|
|
6400
6909
|
await manager.rotateGatewayKey();
|
|
6401
|
-
return
|
|
6910
|
+
return writeJson4(res, 200, { ok: true, integrations: await manager.listStatus() });
|
|
6402
6911
|
}
|
|
6403
6912
|
const client = rest[0];
|
|
6404
6913
|
if (!isIntegrationClient(client)) {
|
|
6405
6914
|
return writeJsonError(res, 400, `unknown integration client '${client ?? ""}'`);
|
|
6406
6915
|
}
|
|
6407
6916
|
if (method === "POST" && rest[1] === "key") {
|
|
6408
|
-
const body = await
|
|
6917
|
+
const body = await readJsonBody4(req);
|
|
6409
6918
|
if (Object.keys(body).length !== 1 || typeof body.keyId !== "string" || !body.keyId.trim()) {
|
|
6410
6919
|
return writeJsonError(res, 400, "body must contain a non-empty keyId string");
|
|
6411
6920
|
}
|
|
6412
6921
|
const status = await manager.bindIntegrationKey(client, body.keyId.trim());
|
|
6413
|
-
return
|
|
6922
|
+
return writeJson4(res, 200, { integration: status });
|
|
6414
6923
|
}
|
|
6415
6924
|
if (method === "POST" && rest[1] === "plan") {
|
|
6416
|
-
const body = await
|
|
6925
|
+
const body = await readJsonBody4(req);
|
|
6417
6926
|
const configPath = body.configPath;
|
|
6418
6927
|
if (configPath !== void 0 && typeof configPath !== "string") {
|
|
6419
6928
|
return writeJsonError(res, 400, "configPath must be a string");
|
|
6420
6929
|
}
|
|
6421
6930
|
const plan = await manager.plan(client, configPath);
|
|
6422
|
-
return
|
|
6931
|
+
return writeJson4(res, 200, { plan });
|
|
6423
6932
|
}
|
|
6424
6933
|
if (method === "POST" && (rest[1] === "install" || rest[1] === "apply")) {
|
|
6425
|
-
const body = await
|
|
6934
|
+
const body = await readJsonBody4(req);
|
|
6426
6935
|
const configPath = body.configPath;
|
|
6427
6936
|
if (configPath !== void 0 && typeof configPath !== "string") {
|
|
6428
6937
|
return writeJsonError(res, 400, "configPath must be a string");
|
|
6429
6938
|
}
|
|
6430
6939
|
const status = await manager.install(client, configPath);
|
|
6431
|
-
return
|
|
6940
|
+
return writeJson4(res, 200, { integration: status });
|
|
6432
6941
|
}
|
|
6433
6942
|
if (method === "POST" && rest[1] === "repair") {
|
|
6434
6943
|
const status = await manager.repair(client);
|
|
6435
|
-
return
|
|
6944
|
+
return writeJson4(res, 200, { integration: status });
|
|
6436
6945
|
}
|
|
6437
6946
|
if (method === "DELETE" && rest.length === 1 || method === "POST" && rest[1] === "remove") {
|
|
6438
6947
|
const status = await manager.remove(client);
|
|
6439
|
-
return
|
|
6948
|
+
return writeJson4(res, 200, { integration: status });
|
|
6440
6949
|
}
|
|
6441
6950
|
return writeJsonError(res, 405, `method ${method} not allowed on integrations`);
|
|
6442
6951
|
} catch (error) {
|
|
@@ -6579,8 +7088,8 @@ async function handleImages(res, method, rest, deps) {
|
|
|
6579
7088
|
}
|
|
6580
7089
|
const reader = deps.imageRuntimeStatus;
|
|
6581
7090
|
if (!reader) return writeJsonError(res, 501, "Images runtime status is not available");
|
|
6582
|
-
const serverConfig = await (0,
|
|
6583
|
-
const images = serverConfig.images ??
|
|
7091
|
+
const serverConfig = await (0, import_outbound_api5.loadServerConfig)(deps.settingsStore);
|
|
7092
|
+
const images = serverConfig.images ?? import_outbound_api5.DEFAULT_IMAGES_SERVER_CONFIG;
|
|
6584
7093
|
const lifecycle = reader.status();
|
|
6585
7094
|
const capability = await reader.inspectCapability(IMAGE_ADMIN_STATUS_TENANT);
|
|
6586
7095
|
const resources = safeRuntimeResources(reader.resourceStatus());
|
|
@@ -6599,7 +7108,7 @@ async function handleImages(res, method, rest, deps) {
|
|
|
6599
7108
|
httpLeases: safeStatusCount(generation.httpLeases),
|
|
6600
7109
|
hostedLeases: safeStatusCount(generation.hostedLeases)
|
|
6601
7110
|
}));
|
|
6602
|
-
return
|
|
7111
|
+
return writeJson4(res, 200, {
|
|
6603
7112
|
configured: {
|
|
6604
7113
|
enabled: images.enabled,
|
|
6605
7114
|
provider: images.provider,
|
|
@@ -6627,11 +7136,11 @@ async function handleImages(res, method, rest, deps) {
|
|
|
6627
7136
|
async function handleStatus(res, method, deps) {
|
|
6628
7137
|
if (method !== "GET") return writeJsonError(res, 405, `method ${method} not allowed on status`);
|
|
6629
7138
|
const status = deps.outboundApiServer.getStatus();
|
|
6630
|
-
const serverConfig = await (0,
|
|
7139
|
+
const serverConfig = await (0, import_outbound_api5.loadServerConfig)(deps.settingsStore);
|
|
6631
7140
|
const endpoints = ["chat", "responses", "messages", "gemini"].map((endpoint) => {
|
|
6632
|
-
const routes = (serverConfig.bindings ?? []).filter((binding) => binding.enabled && binding.endpoint === endpoint).map((binding) => (0,
|
|
7141
|
+
const routes = (serverConfig.bindings ?? []).filter((binding) => binding.enabled && binding.endpoint === endpoint).map((binding) => (0, import_outbound_api5.gatewayBindingToEndpointConfig)(binding));
|
|
6633
7142
|
const useSubscription = routes.some((route) => route.useSubscription);
|
|
6634
|
-
if ((0,
|
|
7143
|
+
if ((0, import_outbound_api5.isKindMappedEndpoint)(endpoint)) {
|
|
6635
7144
|
const kinds = {};
|
|
6636
7145
|
for (const route of routes) {
|
|
6637
7146
|
for (const [kind, ref] of Object.entries(route.modelMap ?? {})) {
|
|
@@ -6665,14 +7174,14 @@ async function handleStatus(res, method, deps) {
|
|
|
6665
7174
|
})() : void 0;
|
|
6666
7175
|
if (status.running) {
|
|
6667
7176
|
const queueStatus = deps.outboundApiServer.getQueueStatus();
|
|
6668
|
-
return
|
|
7177
|
+
return writeJson4(res, 200, {
|
|
6669
7178
|
...status,
|
|
6670
7179
|
endpoints,
|
|
6671
7180
|
queueStatus,
|
|
6672
7181
|
...imageRuntime ? { imageRuntime } : {}
|
|
6673
7182
|
});
|
|
6674
7183
|
}
|
|
6675
|
-
return
|
|
7184
|
+
return writeJson4(res, 200, {
|
|
6676
7185
|
...status,
|
|
6677
7186
|
endpoints,
|
|
6678
7187
|
...imageRuntime ? { imageRuntime } : {}
|
|
@@ -6696,18 +7205,18 @@ function resolvePlaygroundPath(endpoint, body) {
|
|
|
6696
7205
|
}
|
|
6697
7206
|
async function handlePlayground(req, res, method, deps) {
|
|
6698
7207
|
if (method !== "POST") return writeJsonError(res, 405, `method ${method} not allowed on playground`);
|
|
6699
|
-
const body = await
|
|
7208
|
+
const body = await readJsonBody4(req);
|
|
6700
7209
|
const endpoint = typeof body["endpoint"] === "string" ? body["endpoint"] : "";
|
|
6701
7210
|
const key = typeof body["key"] === "string" ? body["key"] : "";
|
|
6702
7211
|
const payload = body["body"];
|
|
6703
7212
|
const status = deps.outboundApiServer.getStatus();
|
|
6704
7213
|
if (!status.running || !status.port) return writeJsonError(res, 503, "outbound server not running");
|
|
6705
|
-
const path2 = resolvePlaygroundPath(endpoint,
|
|
7214
|
+
const path2 = resolvePlaygroundPath(endpoint, isRecord3(payload) ? payload : {});
|
|
6706
7215
|
if (!path2) return writeJsonError(res, 400, `unknown endpoint '${endpoint}'`);
|
|
6707
7216
|
const upstreamBody = typeof payload === "string" ? payload : JSON.stringify(payload ?? {});
|
|
6708
7217
|
await proxyToOutbound(res, status.port, path2, key, upstreamBody);
|
|
6709
7218
|
}
|
|
6710
|
-
function
|
|
7219
|
+
function isRecord3(v) {
|
|
6711
7220
|
return !!v && typeof v === "object" && !Array.isArray(v);
|
|
6712
7221
|
}
|
|
6713
7222
|
function proxyToOutbound(res, outboundPort, path2, key, body) {
|
|
@@ -6843,7 +7352,7 @@ async function handleUiStatic(req, res, urlPath, uiDist) {
|
|
|
6843
7352
|
}
|
|
6844
7353
|
|
|
6845
7354
|
// src/admin/version.ts
|
|
6846
|
-
var DAEMON_VERSION = true ? "0.
|
|
7355
|
+
var DAEMON_VERSION = true ? "0.3.0" : "0.0.0-dev";
|
|
6847
7356
|
|
|
6848
7357
|
// src/admin/AdminServer.ts
|
|
6849
7358
|
var LOOPBACK_ADDR = "127.0.0.1";
|
|
@@ -7296,14 +7805,14 @@ function defaultBillingDir(configPath) {
|
|
|
7296
7805
|
|
|
7297
7806
|
// src/image-generation/ImageDoctorService.ts
|
|
7298
7807
|
var import_image_generation = require("@omnicross/core/image-generation");
|
|
7299
|
-
var
|
|
7808
|
+
var import_outbound_api7 = require("@omnicross/core/outbound-api");
|
|
7300
7809
|
var import_subscriptions3 = require("@omnicross/subscriptions");
|
|
7301
7810
|
|
|
7302
7811
|
// src/image-generation/FileCodexImageCapabilityEvidenceSource.ts
|
|
7303
7812
|
var import_node_crypto13 = require("crypto");
|
|
7304
7813
|
var import_node_fs10 = require("fs");
|
|
7305
7814
|
var import_node_path11 = require("path");
|
|
7306
|
-
var
|
|
7815
|
+
var import_outbound_api6 = require("@omnicross/core/outbound-api");
|
|
7307
7816
|
|
|
7308
7817
|
// src/image-generation/imageTenantHmac.ts
|
|
7309
7818
|
var import_node_crypto12 = require("crypto");
|
|
@@ -7376,7 +7885,7 @@ var ACCOUNT_DOMAIN = Buffer.from("omnicross:codex-image-evidence:account:v1\0",
|
|
|
7376
7885
|
var ACCOUNT_KEY = /^[a-f0-9]{64}$/u;
|
|
7377
7886
|
var SIZE = /^(?:auto|[1-9][0-9]{1,4}x[1-9][0-9]{1,4})$/u;
|
|
7378
7887
|
var MAX_MANIFEST_BYTES = 4 * 1024 * 1024;
|
|
7379
|
-
var PHYSICAL_RETENTION_TTL_MS =
|
|
7888
|
+
var PHYSICAL_RETENTION_TTL_MS = import_outbound_api6.IMAGE_SERVER_HARD_CEILINGS.evidenceTtlMs;
|
|
7380
7889
|
function exactKeys(value, allowed) {
|
|
7381
7890
|
const allow = new Set(allowed);
|
|
7382
7891
|
return Object.keys(value).every((key) => allow.has(key));
|
|
@@ -7750,7 +8259,7 @@ function createImageDoctorService(options) {
|
|
|
7750
8259
|
const activePaths = () => options.storageCatalog.active().resolver;
|
|
7751
8260
|
return Object.freeze({
|
|
7752
8261
|
inspectLocal: async (config) => {
|
|
7753
|
-
const configErrors = (0,
|
|
8262
|
+
const configErrors = (0, import_outbound_api7.validateImagesServerConfig)(config);
|
|
7754
8263
|
let verifiedAreas = 0;
|
|
7755
8264
|
try {
|
|
7756
8265
|
const paths = activePaths();
|
|
@@ -7792,7 +8301,7 @@ function createImageDoctorService(options) {
|
|
|
7792
8301
|
continue;
|
|
7793
8302
|
}
|
|
7794
8303
|
try {
|
|
7795
|
-
const permissions = (0,
|
|
8304
|
+
const permissions = (0, import_outbound_api7.validateOutboundPermissions)(row.allowedEndpoints);
|
|
7796
8305
|
if (permissions.includes("images")) imagesAuthorizedRows += 1;
|
|
7797
8306
|
} catch {
|
|
7798
8307
|
invalidRows += 1;
|
|
@@ -8096,7 +8605,7 @@ var ImageCleanupService = class {
|
|
|
8096
8605
|
// src/image-generation/ImageRuntimeGenerationFactory.ts
|
|
8097
8606
|
var import_node_crypto16 = require("crypto");
|
|
8098
8607
|
var import_image_generation5 = require("@omnicross/core/image-generation");
|
|
8099
|
-
var
|
|
8608
|
+
var import_outbound_api8 = require("@omnicross/core/outbound-api");
|
|
8100
8609
|
var import_subscriptions4 = require("@omnicross/subscriptions");
|
|
8101
8610
|
|
|
8102
8611
|
// src/image-generation/ImageApiRuntimeResolver.ts
|
|
@@ -8564,7 +9073,7 @@ function createImageRuntimeGeneration(options) {
|
|
|
8564
9073
|
throw new TypeError("image runtime generation id is invalid");
|
|
8565
9074
|
}
|
|
8566
9075
|
const config = snapshotConfig(options.config);
|
|
8567
|
-
const configErrors = (0,
|
|
9076
|
+
const configErrors = (0, import_outbound_api8.validateImagesServerConfig)(config);
|
|
8568
9077
|
if (configErrors.length > 0) {
|
|
8569
9078
|
throw new TypeError(`image runtime configuration is invalid: ${configErrors[0]}`);
|
|
8570
9079
|
}
|
|
@@ -12156,7 +12665,7 @@ function safeStringify(value) {
|
|
|
12156
12665
|
var import_node_crypto20 = require("crypto");
|
|
12157
12666
|
var import_node_fs16 = require("fs");
|
|
12158
12667
|
var import_node_path17 = require("path");
|
|
12159
|
-
var
|
|
12668
|
+
var import_outbound_api9 = require("@omnicross/core/outbound-api");
|
|
12160
12669
|
function atomicReplaceDocument(targetPath, contents) {
|
|
12161
12670
|
const tempPath = (0, import_node_path17.join)(
|
|
12162
12671
|
(0, import_node_path17.dirname)(targetPath),
|
|
@@ -12204,13 +12713,13 @@ var JsonApiServerSettingsStore = class {
|
|
|
12204
12713
|
box;
|
|
12205
12714
|
atomicReplace;
|
|
12206
12715
|
async get(key) {
|
|
12207
|
-
if (key !==
|
|
12716
|
+
if (key !== import_outbound_api9.OUTBOUND_API_SERVER_CONFIG_KEY) return void 0;
|
|
12208
12717
|
const file = this.readFile();
|
|
12209
12718
|
if (file.server === void 0) return void 0;
|
|
12210
12719
|
return this.decryptSecrets(file.server);
|
|
12211
12720
|
}
|
|
12212
12721
|
async set(key, value) {
|
|
12213
|
-
if (key !==
|
|
12722
|
+
if (key !== import_outbound_api9.OUTBOUND_API_SERVER_CONFIG_KEY) return;
|
|
12214
12723
|
const file = this.readFile();
|
|
12215
12724
|
file.server = this.encryptSecrets(value);
|
|
12216
12725
|
this.atomicReplace(
|
|
@@ -13849,7 +14358,7 @@ var import_node_fs23 = require("fs");
|
|
|
13849
14358
|
var import_node_path24 = require("path");
|
|
13850
14359
|
var import_SubscriptionAccountHealth2 = require("@omnicross/core/pipeline/SubscriptionAccountHealth");
|
|
13851
14360
|
var import_AccountAllowanceScheduling3 = require("@omnicross/core/pipeline/AccountAllowanceScheduling");
|
|
13852
|
-
var
|
|
14361
|
+
var import_upstreamFetch5 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
13853
14362
|
var import_SubscriptionIdentityStore2 = require("@omnicross/core/provider-proxy/identity/SubscriptionIdentityStore");
|
|
13854
14363
|
var import_subscriptions5 = require("@omnicross/subscriptions");
|
|
13855
14364
|
|
|
@@ -13997,7 +14506,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
13997
14506
|
* a plaintext token pair into `upstream-trace.jsonl`.
|
|
13998
14507
|
*/
|
|
13999
14508
|
buildRefreshFetch(providerId, accountId) {
|
|
14000
|
-
return this.fetchImpl ?? ((url, init) => (0,
|
|
14509
|
+
return this.fetchImpl ?? ((url, init) => (0, import_upstreamFetch5.fetchUpstream)(url, init, { providerId, accountId, redactBodies: true }));
|
|
14001
14510
|
}
|
|
14002
14511
|
/**
|
|
14003
14512
|
* In-flight refresh coalescing. OAuth refresh tokens are
|
|
@@ -14555,7 +15064,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
14555
15064
|
};
|
|
14556
15065
|
|
|
14557
15066
|
// src/AccountHealthProbeScheduler.ts
|
|
14558
|
-
var
|
|
15067
|
+
var import_upstreamFetch6 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
14559
15068
|
|
|
14560
15069
|
// src/probe/CodexGenerationProbe.ts
|
|
14561
15070
|
var import_codexCliHeaders = require("@omnicross/core/provider-proxy/identity/codexCliHeaders");
|
|
@@ -14716,7 +15225,7 @@ var AccountHealthProbeScheduler = class {
|
|
|
14716
15225
|
this.logger = logger;
|
|
14717
15226
|
this.config = config;
|
|
14718
15227
|
this.now = opts.now ?? Date.now;
|
|
14719
|
-
this.fetchImpl = opts.fetchImpl ??
|
|
15228
|
+
this.fetchImpl = opts.fetchImpl ?? import_upstreamFetch6.fetchUpstream;
|
|
14720
15229
|
this.sleep = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
14721
15230
|
this.planFor = opts.planFor ?? probePlanFor;
|
|
14722
15231
|
}
|
|
@@ -16414,7 +16923,7 @@ var AuditWriter = class {
|
|
|
16414
16923
|
var import_node_fs32 = require("fs");
|
|
16415
16924
|
var import_node_crypto24 = require("crypto");
|
|
16416
16925
|
var import_node_path33 = require("path");
|
|
16417
|
-
var
|
|
16926
|
+
var import_upstreamFetch7 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
16418
16927
|
|
|
16419
16928
|
// src/billing/billingFiles.ts
|
|
16420
16929
|
var BILLING_FILE_RE = /^billing-(\d{4})-(\d{2})-(\d{2})\.jsonl$/;
|
|
@@ -16437,7 +16946,7 @@ var BillingPublisher = class {
|
|
|
16437
16946
|
constructor(billingDir, logger, opts = {}) {
|
|
16438
16947
|
this.billingDir = billingDir;
|
|
16439
16948
|
this.logger = logger;
|
|
16440
|
-
this.fetchImpl = opts.fetchImpl ?? ((url, init) => (0,
|
|
16949
|
+
this.fetchImpl = opts.fetchImpl ?? ((url, init) => (0, import_upstreamFetch7.fetchUpstream)(url, init));
|
|
16441
16950
|
this.defer = opts.defer ?? ((fn) => setTimeout(fn, 0));
|
|
16442
16951
|
this.timeoutMs = opts.timeoutMs ?? BILLING_POST_TIMEOUT_MS;
|
|
16443
16952
|
this.now = opts.now ?? Date.now;
|
|
@@ -16846,7 +17355,7 @@ function createRouteLeaseSubscriptionPreflight(credentials) {
|
|
|
16846
17355
|
|
|
16847
17356
|
// src/webhook/WebhookDispatcher.ts
|
|
16848
17357
|
var import_node_crypto25 = require("crypto");
|
|
16849
|
-
var
|
|
17358
|
+
var import_upstreamFetch8 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
16850
17359
|
var WEBHOOK_MAX_ATTEMPTS = 3;
|
|
16851
17360
|
var WEBHOOK_QUEUE_MAX = 1e3;
|
|
16852
17361
|
var WEBHOOK_SEND_TIMEOUT_MS = 1e4;
|
|
@@ -16866,7 +17375,7 @@ var WebhookDispatcher = class {
|
|
|
16866
17375
|
sleep;
|
|
16867
17376
|
now;
|
|
16868
17377
|
constructor(opts = {}) {
|
|
16869
|
-
this.fetchImpl = opts.fetchImpl ?? ((url, init) => (0,
|
|
17378
|
+
this.fetchImpl = opts.fetchImpl ?? ((url, init) => (0, import_upstreamFetch8.fetchUpstream)(url, init));
|
|
16870
17379
|
this.logger = opts.logger;
|
|
16871
17380
|
this.maxAttempts = opts.maxAttempts ?? WEBHOOK_MAX_ATTEMPTS;
|
|
16872
17381
|
this.queueMax = opts.queueMax ?? WEBHOOK_QUEUE_MAX;
|
|
@@ -17102,7 +17611,7 @@ function buildDaemon(config, paths) {
|
|
|
17102
17611
|
);
|
|
17103
17612
|
(0, import_AccountAllowanceStore4.setSharedAccountAllowanceStore)(accountAllowanceStore);
|
|
17104
17613
|
(0, import_AccountAllowanceScheduling5.getSharedAccountAllowanceScheduling)().configure(
|
|
17105
|
-
(0,
|
|
17614
|
+
(0, import_outbound_api10.normalizeServerConfig)(decryptedConfig.server).allowanceScheduling
|
|
17106
17615
|
);
|
|
17107
17616
|
const llmConfig = new ConfigFileProviderConfigSource(decryptedConfig);
|
|
17108
17617
|
const keyDb = new JsonOutboundKeyDb(paths.keysPath, secretBox3);
|
|
@@ -17123,7 +17632,7 @@ function buildDaemon(config, paths) {
|
|
|
17123
17632
|
logger
|
|
17124
17633
|
);
|
|
17125
17634
|
claudeAllowanceRefreshScheduler.configure(
|
|
17126
|
-
(0,
|
|
17635
|
+
(0, import_outbound_api10.normalizeServerConfig)(decryptedConfig.server).allowanceScheduling
|
|
17127
17636
|
);
|
|
17128
17637
|
const subscriptionAccounts = new import_subscriptions6.SubscriptionAccountService(credentialStore);
|
|
17129
17638
|
(0, import_subscriptions6.setSubscriptionAccountService)(subscriptionAccounts);
|
|
@@ -17133,7 +17642,7 @@ function buildDaemon(config, paths) {
|
|
|
17133
17642
|
);
|
|
17134
17643
|
(0, import_subscriptions6.setSubscriptionProviderRegistry)(subscriptionRegistry);
|
|
17135
17644
|
setServerProxyConfig(decryptedConfig.server?.proxy);
|
|
17136
|
-
(0,
|
|
17645
|
+
(0, import_upstreamFetch9.setUpstreamProxyResolver)(
|
|
17137
17646
|
createUpstreamProxyResolver({
|
|
17138
17647
|
getAccountProxy: (providerId, accountId) => credentialStore.getAccountProxy(providerId, accountId)
|
|
17139
17648
|
})
|
|
@@ -17156,7 +17665,7 @@ function buildDaemon(config, paths) {
|
|
|
17156
17665
|
const pricingEngine = new import_usage2.PricingEngine(pricingStore, logger, {
|
|
17157
17666
|
// Catalog egress follows the same global/env proxy policy as every other
|
|
17158
17667
|
// daemon upstream call; no provider/account override applies here.
|
|
17159
|
-
fetchImpl: ((input, init) => (0,
|
|
17668
|
+
fetchImpl: ((input, init) => (0, import_upstreamFetch9.fetchUpstream)(String(input), init ?? {}))
|
|
17160
17669
|
});
|
|
17161
17670
|
const pricingRefreshScheduler = new PricingRefreshScheduler(
|
|
17162
17671
|
pricingEngine,
|
|
@@ -17168,13 +17677,21 @@ function buildDaemon(config, paths) {
|
|
|
17168
17677
|
defaultUsageEventsPath(paths.configPath),
|
|
17169
17678
|
async (providerId, model) => await pricingEngine.getEntry(providerId, model) !== null
|
|
17170
17679
|
);
|
|
17171
|
-
const keySpendTracker = new
|
|
17680
|
+
const keySpendTracker = new import_outbound_api11.KeySpendTracker(usageEventStore);
|
|
17172
17681
|
const usageThroughput = (0, import_usage2.getSharedUsageThroughputTracker)();
|
|
17173
17682
|
const usageRecorder = new import_usage2.UsageRecorder(usageEventStore, pricingEngine, logger, {
|
|
17174
17683
|
onRecord: (apiKeyId, costUsd, at) => keySpendTracker.add(apiKeyId, costUsd, at),
|
|
17175
17684
|
onEvent: (row, at) => usageThroughput.record(row, at)
|
|
17176
17685
|
});
|
|
17177
|
-
const initialImagesConfig = (0,
|
|
17686
|
+
const initialImagesConfig = (0, import_outbound_api10.normalizeServerConfig)(decryptedConfig.server).images;
|
|
17687
|
+
const initialSearchConfig = (0, import_outbound_api10.normalizeServerConfig)(decryptedConfig.server).search;
|
|
17688
|
+
for (const issue of (0, import_outbound_api10.validateSearchServerConfig)(
|
|
17689
|
+
decryptedConfig.server?.search
|
|
17690
|
+
)) {
|
|
17691
|
+
logger.warn("[search] ignoring invalid config: " + issue);
|
|
17692
|
+
}
|
|
17693
|
+
const searchRuntime = buildSearchRuntime(initialSearchConfig, { logger });
|
|
17694
|
+
const searchFrontendModes = initialSearchConfig.modes;
|
|
17178
17695
|
const imageObservability = new ImageObservability();
|
|
17179
17696
|
const imageRuntimeObservability = Object.freeze({
|
|
17180
17697
|
telemetrySink: imageObservability.telemetrySink,
|
|
@@ -17304,7 +17821,9 @@ function buildDaemon(config, paths) {
|
|
|
17304
17821
|
apiKeyPool,
|
|
17305
17822
|
usageRecorder,
|
|
17306
17823
|
openAIOperationRegistry,
|
|
17307
|
-
responsesHostedImageIngress
|
|
17824
|
+
responsesHostedImageIngress,
|
|
17825
|
+
searchRuntime,
|
|
17826
|
+
searchFrontendModes
|
|
17308
17827
|
});
|
|
17309
17828
|
if (providerProxy.getDeps().openAIOperationRegistry !== openAIOperationRegistry) {
|
|
17310
17829
|
throw new Error(
|
|
@@ -17338,7 +17857,7 @@ function buildDaemon(config, paths) {
|
|
|
17338
17857
|
credentialStore,
|
|
17339
17858
|
(0, import_SubscriptionAccountHealth4.getSharedAccountHealth)(),
|
|
17340
17859
|
logger,
|
|
17341
|
-
|
|
17860
|
+
import_outbound_api10.DEFAULT_ACCOUNT_PROBE
|
|
17342
17861
|
);
|
|
17343
17862
|
const getHealthReport = () => buildHealthReport({
|
|
17344
17863
|
version: DAEMON_VERSION,
|
|
@@ -17355,7 +17874,7 @@ function buildDaemon(config, paths) {
|
|
|
17355
17874
|
// `/health` body stays byte-identical (zero regression).
|
|
17356
17875
|
subscriptionAccountsHealthy: () => accountHealthProbeScheduler.enabled ? accountHealthProbeScheduler.probedAccountsHealthy() : void 0
|
|
17357
17876
|
});
|
|
17358
|
-
const outboundApiServer = (0,
|
|
17877
|
+
const outboundApiServer = (0, import_outbound_api10.getOutboundApiServer)({
|
|
17359
17878
|
db: keyDb,
|
|
17360
17879
|
// voucher-redemption #9: the key-authenticated `POST /redeem` endpoint redeems
|
|
17361
17880
|
// cards against the presenting key (gated on `voucher.enabled`).
|
|
@@ -17369,7 +17888,9 @@ function buildDaemon(config, paths) {
|
|
|
17369
17888
|
keySpendTracker,
|
|
17370
17889
|
// configurable-logging: route the server's OWN lifecycle + relay dispatch-error
|
|
17371
17890
|
// lines through the injected logger (honors level/format/file sink).
|
|
17372
|
-
logger
|
|
17891
|
+
logger,
|
|
17892
|
+
// plan 阶段5: the same instance the managed frontends hold.
|
|
17893
|
+
searchRuntime
|
|
17373
17894
|
});
|
|
17374
17895
|
const auditDir = defaultAuditDir(paths.configPath);
|
|
17375
17896
|
const billingDir = defaultBillingDir(paths.configPath);
|
|
@@ -17391,6 +17912,10 @@ function buildDaemon(config, paths) {
|
|
|
17391
17912
|
}),
|
|
17392
17913
|
routeLeaseManager,
|
|
17393
17914
|
subscriptionAccounts,
|
|
17915
|
+
// search-settings-ui D3: the daemon's ONE search runtime + its
|
|
17916
|
+
// bootstrap-captured modes, for `GET /admin/api/search/diagnostics` and
|
|
17917
|
+
// `POST /admin/api/search/test` (501 when a light embedder omits it).
|
|
17918
|
+
searchStatus: { runtime: searchRuntime, modes: searchFrontendModes },
|
|
17394
17919
|
accountAllowanceService,
|
|
17395
17920
|
allowanceRefreshScheduler: claudeAllowanceRefreshScheduler,
|
|
17396
17921
|
accountProbeService: accountHealthProbeScheduler,
|
|
@@ -17420,7 +17945,7 @@ function buildDaemon(config, paths) {
|
|
|
17420
17945
|
// — `server.proxy.byProvider[...]` was silently skipped — and the call was
|
|
17421
17946
|
// excluded from the upstream trace, so a failing login left no evidence.
|
|
17422
17947
|
// `redactBodies` keeps the code/verifier + minted token out of that trace.
|
|
17423
|
-
oauthExchangeFetch: paths.oauthExchangeFetch ? () => paths.oauthExchangeFetch : (providerId) => (url, init) => (0,
|
|
17948
|
+
oauthExchangeFetch: paths.oauthExchangeFetch ? () => paths.oauthExchangeFetch : (providerId) => (url, init) => (0, import_upstreamFetch9.fetchUpstream)(url, init, { providerId, redactBodies: true }),
|
|
17424
17949
|
subscriptionAccountAppender: credentialStore,
|
|
17425
17950
|
// Codex interactive OAuth (app-parity-2 child 5) — the async loopback flow store
|
|
17426
17951
|
// + the one-shot 127.0.0.1:1455 listener. Token captured + persisted daemon-side;
|
|
@@ -17440,7 +17965,7 @@ function buildDaemon(config, paths) {
|
|
|
17440
17965
|
cliCommandRunner: paths.cliCommandRunner,
|
|
17441
17966
|
integrationManagerFactory: () => {
|
|
17442
17967
|
const live = outboundApiServer.getStatus();
|
|
17443
|
-
const port = live.port || decryptedConfig.server?.port ||
|
|
17968
|
+
const port = live.port || decryptedConfig.server?.port || import_outbound_api10.DEFAULT_OUTBOUND_PORT;
|
|
17444
17969
|
return new IntegrationManager({
|
|
17445
17970
|
configPath: paths.configPath,
|
|
17446
17971
|
gatewayBaseUrl: live.loopbackUrl ?? `http://127.0.0.1:${port}`,
|
|
@@ -17486,7 +18011,7 @@ function buildDaemon(config, paths) {
|
|
|
17486
18011
|
});
|
|
17487
18012
|
const webhookDispatcher = new WebhookDispatcher({
|
|
17488
18013
|
logger,
|
|
17489
|
-
fetchImpl: (url, init) => (0,
|
|
18014
|
+
fetchImpl: (url, init) => (0, import_upstreamFetch9.fetchUpstream)(url, init)
|
|
17490
18015
|
});
|
|
17491
18016
|
setWebhookRuntime(webhookDispatcher, (0, import_SubscriptionAccountHealth4.getSharedAccountHealth)());
|
|
17492
18017
|
const auditWriter = new AuditWriter(auditDir, logger);
|
|
@@ -17527,6 +18052,8 @@ function buildDaemon(config, paths) {
|
|
|
17527
18052
|
keyDb,
|
|
17528
18053
|
settingsStore,
|
|
17529
18054
|
openAIOperationRegistry,
|
|
18055
|
+
searchRuntime,
|
|
18056
|
+
searchFrontendModes,
|
|
17530
18057
|
imageRuntimeManager,
|
|
17531
18058
|
imageObservability,
|
|
17532
18059
|
imageCleanupService,
|
|
@@ -17562,11 +18089,11 @@ function buildDaemon(config, paths) {
|
|
|
17562
18089
|
function resetDaemonSingletonsForTests() {
|
|
17563
18090
|
resetImageRuntimeBootstrapSession();
|
|
17564
18091
|
(0, import_provider_proxy4.__resetProviderProxyForTests)();
|
|
17565
|
-
(0,
|
|
18092
|
+
(0, import_outbound_api10.__resetOutboundApiServerForTests)();
|
|
17566
18093
|
(0, import_subscriptionRegistryPort.setSubscriptionRegistryForOutbound)(null);
|
|
17567
18094
|
(0, import_subscriptions6.setSubscriptionProviderRegistry)(null);
|
|
17568
18095
|
(0, import_subscriptions6.setSubscriptionAccountService)(null);
|
|
17569
|
-
(0,
|
|
18096
|
+
(0, import_upstreamFetch9.setUpstreamProxyResolver)(null);
|
|
17570
18097
|
setServerProxyConfig(void 0);
|
|
17571
18098
|
(0, import_gemini_code_assist_resolver.setGeminiCodeAssistResolver)(null);
|
|
17572
18099
|
setSecretBox(null);
|