@omnicross/daemon 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +19 -19
- package/dist/cli.cjs +852 -199
- package/dist/cli.js +826 -160
- package/dist/index.cjs +696 -189
- package/dist/index.d.cts +69 -0
- package/dist/index.d.ts +69 -0
- package/dist/index.js +668 -153
- package/package.json +6 -6
package/dist/index.cjs
CHANGED
|
@@ -65,7 +65,7 @@ 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");
|
|
@@ -75,7 +75,7 @@ var import_SubscriptionIdentityStore3 = require("@omnicross/core/provider-proxy/
|
|
|
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,7 +879,7 @@ 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");
|
|
@@ -3739,6 +3739,468 @@ 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
|
+
|
|
3747
|
+
// src/search/searchDoctorProjection.ts
|
|
3748
|
+
var import_search_types = require("@omnicross/contracts/search-types");
|
|
3749
|
+
var import_api = require("@omnicross/core/search/api");
|
|
3750
|
+
var import_http = require("@omnicross/core/search/http");
|
|
3751
|
+
var API_DOCTOR_PROVIDERS = [
|
|
3752
|
+
{
|
|
3753
|
+
id: "tavily",
|
|
3754
|
+
capabilities: import_api.TAVILY_CAPABILITIES,
|
|
3755
|
+
configured: (configs) => configs.tavily !== void 0,
|
|
3756
|
+
missingReason: "no API key configured"
|
|
3757
|
+
},
|
|
3758
|
+
{
|
|
3759
|
+
id: "jina",
|
|
3760
|
+
capabilities: import_api.JINA_CAPABILITIES,
|
|
3761
|
+
configured: (configs) => configs.jina !== void 0,
|
|
3762
|
+
// Honest about the asymmetry: Jina CAN run keyless, but a provider nobody
|
|
3763
|
+
// asked for is still not enabled.
|
|
3764
|
+
missingReason: "not configured (Jina can run without a key, but must be enabled explicitly)"
|
|
3765
|
+
},
|
|
3766
|
+
{
|
|
3767
|
+
id: "searxng",
|
|
3768
|
+
capabilities: import_api.SEARXNG_CAPABILITIES,
|
|
3769
|
+
configured: (configs) => configs.searxng !== void 0,
|
|
3770
|
+
missingReason: "no API host configured"
|
|
3771
|
+
},
|
|
3772
|
+
{
|
|
3773
|
+
id: "zhipu",
|
|
3774
|
+
capabilities: import_api.ZHIPU_CAPABILITIES,
|
|
3775
|
+
configured: (configs) => configs.zhipu !== void 0,
|
|
3776
|
+
missingReason: "no API key configured"
|
|
3777
|
+
},
|
|
3778
|
+
{
|
|
3779
|
+
id: "z.ai",
|
|
3780
|
+
capabilities: import_api.ZHIPU_CAPABILITIES,
|
|
3781
|
+
configured: (configs) => configs["z.ai"] !== void 0,
|
|
3782
|
+
missingReason: "no API key configured"
|
|
3783
|
+
}
|
|
3784
|
+
];
|
|
3785
|
+
function buildSearchDoctorSnapshot(contributions = (0, import_http.builtinHttpSearchContributions)(), apiConfigs) {
|
|
3786
|
+
const rows = contributions.map((contribution) => ({
|
|
3787
|
+
providerId: contribution.id,
|
|
3788
|
+
source: contribution.source,
|
|
3789
|
+
kind: contribution.kind,
|
|
3790
|
+
capabilities: contribution.capabilities
|
|
3791
|
+
}));
|
|
3792
|
+
if (apiConfigs === void 0) return rows;
|
|
3793
|
+
for (const provider of API_DOCTOR_PROVIDERS) {
|
|
3794
|
+
if (provider.configured(apiConfigs)) continue;
|
|
3795
|
+
rows.push({
|
|
3796
|
+
providerId: provider.id,
|
|
3797
|
+
source: "builtin",
|
|
3798
|
+
kind: "api",
|
|
3799
|
+
capabilities: provider.capabilities,
|
|
3800
|
+
status: "unconfigured",
|
|
3801
|
+
reason: provider.missingReason
|
|
3802
|
+
});
|
|
3803
|
+
}
|
|
3804
|
+
return rows;
|
|
3805
|
+
}
|
|
3806
|
+
var SEARCH_DOCTOR_QUERY = "mozilla developer network http headers";
|
|
3807
|
+
function classifyLiveSearchOutcome(providerId, outcome, checkedAt) {
|
|
3808
|
+
if (outcome.kind === "results") {
|
|
3809
|
+
if (outcome.count > 0) return { providerId, status: "healthy", checkedAt };
|
|
3810
|
+
return {
|
|
3811
|
+
providerId,
|
|
3812
|
+
status: "degraded",
|
|
3813
|
+
checkedAt,
|
|
3814
|
+
reason: "reachable, but the engine returned no usable results (possible partial drift)"
|
|
3815
|
+
};
|
|
3816
|
+
}
|
|
3817
|
+
const error = (0, import_search_types.toSearchErrorShape)(outcome.error);
|
|
3818
|
+
const stage = error.details?.stage;
|
|
3819
|
+
const { status, reason } = classifySearchFailure(stage, error.code);
|
|
3820
|
+
return { providerId, status, checkedAt, reason, error };
|
|
3821
|
+
}
|
|
3822
|
+
function classifySearchFailure(stage, code) {
|
|
3823
|
+
if (stage === "challenge") {
|
|
3824
|
+
return { status: "blocked", reason: "the engine served a bot challenge instead of results" };
|
|
3825
|
+
}
|
|
3826
|
+
if (stage === "trust") {
|
|
3827
|
+
return {
|
|
3828
|
+
status: "blocked",
|
|
3829
|
+
reason: "the engine served a page that failed the anti-decoy trust check"
|
|
3830
|
+
};
|
|
3831
|
+
}
|
|
3832
|
+
if (code === "policy_denied") {
|
|
3833
|
+
return {
|
|
3834
|
+
status: "blocked",
|
|
3835
|
+
reason: "the egress policy refused the request target"
|
|
3836
|
+
};
|
|
3837
|
+
}
|
|
3838
|
+
if (code === "parse_failed") {
|
|
3839
|
+
return {
|
|
3840
|
+
status: "failed",
|
|
3841
|
+
reason: "the response was not recognizable as a search result page (parser drift suspected)"
|
|
3842
|
+
};
|
|
3843
|
+
}
|
|
3844
|
+
if (code === "timeout") {
|
|
3845
|
+
return { status: "failed", reason: "the request exceeded its time budget" };
|
|
3846
|
+
}
|
|
3847
|
+
return { status: "failed", reason: `the request failed (${code})` };
|
|
3848
|
+
}
|
|
3849
|
+
|
|
3850
|
+
// src/search/SearchAssembly.ts
|
|
3851
|
+
var import_search = require("@omnicross/core/search");
|
|
3852
|
+
var import_api2 = require("@omnicross/core/search/api");
|
|
3853
|
+
var import_http2 = require("@omnicross/core/search/http");
|
|
3854
|
+
function searchEgressPolicyFrom(config) {
|
|
3855
|
+
const hosts = config.egress.allowedPrivateHosts;
|
|
3856
|
+
return hosts.length > 0 ? { allowedPrivateHosts: [...hosts] } : {};
|
|
3857
|
+
}
|
|
3858
|
+
function searchPolicyFrom(config) {
|
|
3859
|
+
const { preferred, allowed, fallbackEnabled, maxAttempts } = config.policy;
|
|
3860
|
+
return {
|
|
3861
|
+
...preferred !== void 0 ? { preferred } : {},
|
|
3862
|
+
...allowed !== void 0 ? { allowed: [...allowed] } : {},
|
|
3863
|
+
fallbackEnabled,
|
|
3864
|
+
...maxAttempts !== void 0 ? { maxAttempts } : {}
|
|
3865
|
+
};
|
|
3866
|
+
}
|
|
3867
|
+
function searchContributionsFrom(config) {
|
|
3868
|
+
return [
|
|
3869
|
+
...(0, import_http2.builtinHttpSearchContributions)(),
|
|
3870
|
+
...(0, import_api2.apiSearchContributions)(config.providers, {
|
|
3871
|
+
egressPolicy: searchEgressPolicyFrom(config)
|
|
3872
|
+
})
|
|
3873
|
+
];
|
|
3874
|
+
}
|
|
3875
|
+
function buildSearchRuntime(config, options = {}) {
|
|
3876
|
+
const logger = options.logger ?? null;
|
|
3877
|
+
return (0, import_search.createSearchRuntime)({
|
|
3878
|
+
contributions: options.contributions ?? searchContributionsFrom(config),
|
|
3879
|
+
policy: searchPolicyFrom(config),
|
|
3880
|
+
...logger ? {
|
|
3881
|
+
onEvent: (event) => {
|
|
3882
|
+
logger.debug(`[search] ${formatSearchEvent(event)}`);
|
|
3883
|
+
}
|
|
3884
|
+
} : {}
|
|
3885
|
+
});
|
|
3886
|
+
}
|
|
3887
|
+
function formatSearchEvent(event) {
|
|
3888
|
+
const parts = [
|
|
3889
|
+
`type=${event.type}`,
|
|
3890
|
+
`request=${event.requestId}`,
|
|
3891
|
+
`queryHash=${event.queryHash}`,
|
|
3892
|
+
`durationMs=${event.durationMs}`
|
|
3893
|
+
];
|
|
3894
|
+
if ("providerId" in event && event.providerId !== void 0) {
|
|
3895
|
+
parts.push(`provider=${event.providerId}`);
|
|
3896
|
+
}
|
|
3897
|
+
if ("outcome" in event && event.outcome !== void 0) parts.push(`outcome=${event.outcome}`);
|
|
3898
|
+
if ("errorCode" in event && event.errorCode !== void 0) parts.push(`error=${event.errorCode}`);
|
|
3899
|
+
if ("resultCount" in event && event.resultCount !== void 0) {
|
|
3900
|
+
parts.push(`results=${event.resultCount}`);
|
|
3901
|
+
}
|
|
3902
|
+
if ("fallbackCount" in event && event.fallbackCount !== void 0) {
|
|
3903
|
+
parts.push(`fallbacks=${event.fallbackCount}`);
|
|
3904
|
+
}
|
|
3905
|
+
return parts.join(" ");
|
|
3906
|
+
}
|
|
3907
|
+
|
|
3908
|
+
// src/admin/searchAdminApi.ts
|
|
3909
|
+
var KEYLESS_HTTP_PROVIDER_IDS = /* @__PURE__ */ new Set(["http-bing", "http-duckduckgo"]);
|
|
3910
|
+
var API_PROVIDER_IDS = /* @__PURE__ */ new Set([
|
|
3911
|
+
"tavily",
|
|
3912
|
+
"jina",
|
|
3913
|
+
"searxng",
|
|
3914
|
+
"zhipu",
|
|
3915
|
+
"z.ai"
|
|
3916
|
+
]);
|
|
3917
|
+
var SEARCH_QUERY_MAX_CODE_UNITS = 256;
|
|
3918
|
+
var QUERY_CONTROL_CHARS = /[\u0000-\u001f\u007f]/u;
|
|
3919
|
+
var SEARCH_RESULT_FIELD_CAPS = { title: 512, url: 2048, content: 1024 };
|
|
3920
|
+
var SEARCH_QUERY_MAX_RESULTS = 5;
|
|
3921
|
+
function sanitizeResultField(value, cap) {
|
|
3922
|
+
const text = typeof value === "string" ? value : value === null || value === void 0 ? "" : String(value);
|
|
3923
|
+
return text.replace(/[\u0000-\u001f\u007f]/gu, "").slice(0, cap);
|
|
3924
|
+
}
|
|
3925
|
+
function writeJson(res, status, body) {
|
|
3926
|
+
res.writeHead(status, { "Content-Type": "application/json" });
|
|
3927
|
+
res.end(JSON.stringify(body));
|
|
3928
|
+
}
|
|
3929
|
+
function writeErr(res, status, message) {
|
|
3930
|
+
writeJson(res, status, { error: { type: "admin_api_error", message } });
|
|
3931
|
+
}
|
|
3932
|
+
var SEARCH_MAX_BODY_BYTES = 64 * 1024;
|
|
3933
|
+
var SearchBodyTooLargeError = class extends Error {
|
|
3934
|
+
};
|
|
3935
|
+
async function readJsonBody2(req) {
|
|
3936
|
+
const chunks = [];
|
|
3937
|
+
let bytes = 0;
|
|
3938
|
+
for await (const chunk of req) {
|
|
3939
|
+
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
3940
|
+
bytes += buffer.length;
|
|
3941
|
+
if (bytes > SEARCH_MAX_BODY_BYTES) throw new SearchBodyTooLargeError("request body is too large");
|
|
3942
|
+
chunks.push(buffer);
|
|
3943
|
+
}
|
|
3944
|
+
const raw = Buffer.concat(chunks).toString("utf8");
|
|
3945
|
+
if (!raw.trim()) return {};
|
|
3946
|
+
try {
|
|
3947
|
+
const parsed = JSON.parse(raw);
|
|
3948
|
+
return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
3949
|
+
} catch {
|
|
3950
|
+
return {};
|
|
3951
|
+
}
|
|
3952
|
+
}
|
|
3953
|
+
async function readBodyOrReject(req, res) {
|
|
3954
|
+
try {
|
|
3955
|
+
return await readJsonBody2(req);
|
|
3956
|
+
} catch (error) {
|
|
3957
|
+
if (error instanceof SearchBodyTooLargeError) {
|
|
3958
|
+
writeErr(res, 400, error.message);
|
|
3959
|
+
return void 0;
|
|
3960
|
+
}
|
|
3961
|
+
throw error;
|
|
3962
|
+
}
|
|
3963
|
+
}
|
|
3964
|
+
async function handleSearchAdmin(req, res, method, rest, deps) {
|
|
3965
|
+
if (rest.length === 1 && rest[0] === "diagnostics") {
|
|
3966
|
+
if (!deps.searchStatus) {
|
|
3967
|
+
return writeErr(res, 501, "Search status is not available in this build");
|
|
3968
|
+
}
|
|
3969
|
+
if (method !== "GET") {
|
|
3970
|
+
return writeErr(res, 405, `method ${method} not allowed on search diagnostics`);
|
|
3971
|
+
}
|
|
3972
|
+
return handleSearchDiagnostics(res, deps);
|
|
3973
|
+
}
|
|
3974
|
+
if (rest.length === 1 && rest[0] === "test") {
|
|
3975
|
+
if (!deps.searchStatus) {
|
|
3976
|
+
return writeErr(res, 501, "Search status is not available in this build");
|
|
3977
|
+
}
|
|
3978
|
+
if (method !== "POST") {
|
|
3979
|
+
return writeErr(res, 405, `method ${method} not allowed on search test`);
|
|
3980
|
+
}
|
|
3981
|
+
return handleSearchTest(req, res, deps);
|
|
3982
|
+
}
|
|
3983
|
+
if (rest.length === 1 && rest[0] === "query") {
|
|
3984
|
+
if (!deps.searchStatus) {
|
|
3985
|
+
return writeErr(res, 501, "Search status is not available in this build");
|
|
3986
|
+
}
|
|
3987
|
+
if (method !== "POST") {
|
|
3988
|
+
return writeErr(res, 405, `method ${method} not allowed on search query`);
|
|
3989
|
+
}
|
|
3990
|
+
return handleSearchQuery(req, res, deps);
|
|
3991
|
+
}
|
|
3992
|
+
return writeErr(res, 404, `unknown search route '/${rest.join("/")}'`);
|
|
3993
|
+
}
|
|
3994
|
+
async function handleSearchDiagnostics(res, deps) {
|
|
3995
|
+
const status = deps.searchStatus;
|
|
3996
|
+
const persisted = await (0, import_outbound_api2.loadServerConfig)(deps.settingsStore);
|
|
3997
|
+
const search = persisted.search ?? import_outbound_api2.DEFAULT_SEARCH_SERVER_CONFIG;
|
|
3998
|
+
const rows = buildSearchDoctorSnapshot(
|
|
3999
|
+
status.runtime.listProviders(),
|
|
4000
|
+
search.providers
|
|
4001
|
+
);
|
|
4002
|
+
const snapshot = {
|
|
4003
|
+
rows,
|
|
4004
|
+
modes: {
|
|
4005
|
+
// codex is read from the LIVE config per request — an admin PUT has
|
|
4006
|
+
// already applied. responses/anthropic were captured at bootstrap.
|
|
4007
|
+
codex: search.modes.codex,
|
|
4008
|
+
responses: status.modes.responses,
|
|
4009
|
+
anthropic: status.modes.anthropic
|
|
4010
|
+
},
|
|
4011
|
+
applySemantics: { codex: "immediate", rest: "restart" }
|
|
4012
|
+
};
|
|
4013
|
+
return writeJson(res, 200, { diagnostics: snapshot });
|
|
4014
|
+
}
|
|
4015
|
+
async function handleSearchTest(req, res, deps) {
|
|
4016
|
+
const status = deps.searchStatus;
|
|
4017
|
+
const body = await readBodyOrReject(req, res);
|
|
4018
|
+
if (body === void 0) return;
|
|
4019
|
+
const providerId = body["providerId"];
|
|
4020
|
+
if (typeof providerId !== "string" || providerId.length === 0) {
|
|
4021
|
+
return writeErr(res, 400, "providerId must be a non-empty string");
|
|
4022
|
+
}
|
|
4023
|
+
if (!KEYLESS_HTTP_PROVIDER_IDS.has(providerId) && !API_PROVIDER_IDS.has(providerId)) {
|
|
4024
|
+
return writeErr(res, 404, `unknown search provider '${providerId}'`);
|
|
4025
|
+
}
|
|
4026
|
+
const persisted = await (0, import_outbound_api2.loadServerConfig)(deps.settingsStore);
|
|
4027
|
+
const search = persisted.search ?? import_outbound_api2.DEFAULT_SEARCH_SERVER_CONFIG;
|
|
4028
|
+
const providers = search.providers;
|
|
4029
|
+
if (!KEYLESS_HTTP_PROVIDER_IDS.has(providerId) && providers[providerId] === void 0) {
|
|
4030
|
+
return writeErr(res, 400, `search provider '${providerId}' is not configured`);
|
|
4031
|
+
}
|
|
4032
|
+
const egressPolicy = searchEgressPolicyFrom(search);
|
|
4033
|
+
const fetchImpl = status.testFetch;
|
|
4034
|
+
const transport = fetchImpl ? (0, import_http3.createSearchHttpTransport)({ fetch: fetchImpl, egressPolicy }) : void 0;
|
|
4035
|
+
const contributions = [
|
|
4036
|
+
...(0, import_http3.builtinHttpSearchContributions)(transport),
|
|
4037
|
+
...(0, import_api3.apiSearchContributions)(search.providers, {
|
|
4038
|
+
egressPolicy,
|
|
4039
|
+
...fetchImpl ? { fetchImpl } : {}
|
|
4040
|
+
})
|
|
4041
|
+
];
|
|
4042
|
+
const contribution = contributions.find((c) => c.id === providerId);
|
|
4043
|
+
if (!contribution) {
|
|
4044
|
+
return writeErr(res, 400, `search provider '${providerId}' is not configured`);
|
|
4045
|
+
}
|
|
4046
|
+
const checkedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4047
|
+
try {
|
|
4048
|
+
const results = await contribution.provider.search(SEARCH_DOCTOR_QUERY, { maxResults: 5 });
|
|
4049
|
+
const diagnostic = classifyLiveSearchOutcome(
|
|
4050
|
+
contribution.id,
|
|
4051
|
+
{ kind: "results", count: results.length },
|
|
4052
|
+
checkedAt
|
|
4053
|
+
);
|
|
4054
|
+
const response = { diagnostic, resultCount: results.length };
|
|
4055
|
+
return writeJson(res, 200, { result: response });
|
|
4056
|
+
} catch (error) {
|
|
4057
|
+
const diagnostic = classifyLiveSearchOutcome(contribution.id, { kind: "failure", error }, checkedAt);
|
|
4058
|
+
const response = { diagnostic };
|
|
4059
|
+
return writeJson(res, 200, { result: response });
|
|
4060
|
+
}
|
|
4061
|
+
}
|
|
4062
|
+
async function handleSearchQuery(req, res, deps) {
|
|
4063
|
+
const status = deps.searchStatus;
|
|
4064
|
+
const body = await readBodyOrReject(req, res);
|
|
4065
|
+
if (body === void 0) return;
|
|
4066
|
+
const providerId = body["providerId"];
|
|
4067
|
+
if (typeof providerId !== "string" || providerId.length === 0) {
|
|
4068
|
+
return writeErr(res, 400, "providerId must be a non-empty string");
|
|
4069
|
+
}
|
|
4070
|
+
if (!KEYLESS_HTTP_PROVIDER_IDS.has(providerId) && !API_PROVIDER_IDS.has(providerId)) {
|
|
4071
|
+
return writeErr(res, 404, `unknown search provider '${providerId}'`);
|
|
4072
|
+
}
|
|
4073
|
+
const query2 = body["query"];
|
|
4074
|
+
if (typeof query2 !== "string" || query2.trim().length === 0) {
|
|
4075
|
+
return writeErr(res, 400, "query must be a non-empty string");
|
|
4076
|
+
}
|
|
4077
|
+
if (query2.length > SEARCH_QUERY_MAX_CODE_UNITS) {
|
|
4078
|
+
return writeErr(res, 400, `query must be at most ${SEARCH_QUERY_MAX_CODE_UNITS} characters`);
|
|
4079
|
+
}
|
|
4080
|
+
if (QUERY_CONTROL_CHARS.test(query2)) {
|
|
4081
|
+
return writeErr(res, 400, "query must not contain control characters");
|
|
4082
|
+
}
|
|
4083
|
+
const persisted = await (0, import_outbound_api2.loadServerConfig)(deps.settingsStore);
|
|
4084
|
+
const search = persisted.search ?? import_outbound_api2.DEFAULT_SEARCH_SERVER_CONFIG;
|
|
4085
|
+
const providers = search.providers;
|
|
4086
|
+
if (!KEYLESS_HTTP_PROVIDER_IDS.has(providerId) && providers[providerId] === void 0) {
|
|
4087
|
+
return writeErr(res, 400, `search provider '${providerId}' is not configured`);
|
|
4088
|
+
}
|
|
4089
|
+
const egressPolicy = searchEgressPolicyFrom(search);
|
|
4090
|
+
const fetchImpl = status.testFetch;
|
|
4091
|
+
const transport = fetchImpl ? (0, import_http3.createSearchHttpTransport)({ fetch: fetchImpl, egressPolicy }) : void 0;
|
|
4092
|
+
const contributions = [
|
|
4093
|
+
...(0, import_http3.builtinHttpSearchContributions)(transport),
|
|
4094
|
+
...(0, import_api3.apiSearchContributions)(search.providers, {
|
|
4095
|
+
egressPolicy,
|
|
4096
|
+
...fetchImpl ? { fetchImpl } : {}
|
|
4097
|
+
})
|
|
4098
|
+
];
|
|
4099
|
+
const contribution = contributions.find((c) => c.id === providerId);
|
|
4100
|
+
if (!contribution) {
|
|
4101
|
+
return writeErr(res, 400, `search provider '${providerId}' is not configured`);
|
|
4102
|
+
}
|
|
4103
|
+
const checkedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4104
|
+
try {
|
|
4105
|
+
const results = await contribution.provider.search(query2, { maxResults: 5 });
|
|
4106
|
+
const sanitized = results.slice(0, SEARCH_QUERY_MAX_RESULTS).map((result) => ({
|
|
4107
|
+
title: sanitizeResultField(result.title, SEARCH_RESULT_FIELD_CAPS.title),
|
|
4108
|
+
url: sanitizeResultField(result.url, SEARCH_RESULT_FIELD_CAPS.url),
|
|
4109
|
+
content: sanitizeResultField(result.content, SEARCH_RESULT_FIELD_CAPS.content)
|
|
4110
|
+
}));
|
|
4111
|
+
const diagnostic = sanitized.length === 0 ? { providerId: contribution.id, status: "healthy", checkedAt } : classifyLiveSearchOutcome(
|
|
4112
|
+
contribution.id,
|
|
4113
|
+
{ kind: "results", count: sanitized.length },
|
|
4114
|
+
checkedAt
|
|
4115
|
+
);
|
|
4116
|
+
const response = {
|
|
4117
|
+
diagnostic,
|
|
4118
|
+
resultCount: sanitized.length,
|
|
4119
|
+
results: sanitized
|
|
4120
|
+
};
|
|
4121
|
+
return writeJson(res, 200, { result: response });
|
|
4122
|
+
} catch (error) {
|
|
4123
|
+
const diagnostic = classifyLiveSearchOutcome(contribution.id, { kind: "failure", error }, checkedAt);
|
|
4124
|
+
const response = { diagnostic };
|
|
4125
|
+
return writeJson(res, 200, { result: response });
|
|
4126
|
+
}
|
|
4127
|
+
}
|
|
4128
|
+
|
|
4129
|
+
// src/admin/searchAdminView.ts
|
|
4130
|
+
var API_KEY_PROVIDERS = /* @__PURE__ */ new Set(["tavily", "jina", "zhipu", "z.ai"]);
|
|
4131
|
+
var BASIC_AUTH_PROVIDERS = /* @__PURE__ */ new Set(["searxng"]);
|
|
4132
|
+
function isRecord(value) {
|
|
4133
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
4134
|
+
}
|
|
4135
|
+
function redactSearchServerConfig(search) {
|
|
4136
|
+
const providers = {};
|
|
4137
|
+
for (const [id, raw] of Object.entries(search.providers)) {
|
|
4138
|
+
const entry = raw;
|
|
4139
|
+
const view = {};
|
|
4140
|
+
if (entry.apiHost !== void 0) view.apiHost = entry.apiHost;
|
|
4141
|
+
if (entry.basicAuthUsername !== void 0) view.basicAuthUsername = entry.basicAuthUsername;
|
|
4142
|
+
if (API_KEY_PROVIDERS.has(id)) {
|
|
4143
|
+
view.apiKeyConfigured = typeof entry.apiKey === "string" && entry.apiKey.length > 0;
|
|
4144
|
+
}
|
|
4145
|
+
if (BASIC_AUTH_PROVIDERS.has(id)) {
|
|
4146
|
+
view.basicAuthPasswordConfigured = typeof entry.basicAuthPassword === "string" && entry.basicAuthPassword.length > 0;
|
|
4147
|
+
}
|
|
4148
|
+
providers[id] = view;
|
|
4149
|
+
}
|
|
4150
|
+
return {
|
|
4151
|
+
modes: search.modes,
|
|
4152
|
+
providers,
|
|
4153
|
+
egress: { allowedPrivateHosts: [...search.egress.allowedPrivateHosts] },
|
|
4154
|
+
policy: { ...search.policy, ...search.policy.allowed ? { allowed: [...search.policy.allowed] } : {} }
|
|
4155
|
+
};
|
|
4156
|
+
}
|
|
4157
|
+
function storedSecrets(current, id) {
|
|
4158
|
+
const entry = current.providers[id];
|
|
4159
|
+
if (!entry) return {};
|
|
4160
|
+
const out = {};
|
|
4161
|
+
if (typeof entry.apiKey === "string" && entry.apiKey.length > 0) out.apiKey = entry.apiKey;
|
|
4162
|
+
if (typeof entry.basicAuthPassword === "string" && entry.basicAuthPassword.length > 0) {
|
|
4163
|
+
out.basicAuthPassword = entry.basicAuthPassword;
|
|
4164
|
+
}
|
|
4165
|
+
return out;
|
|
4166
|
+
}
|
|
4167
|
+
function resolveSecretField(entry, field, stored) {
|
|
4168
|
+
if (!(field in entry)) {
|
|
4169
|
+
if (stored !== void 0) entry[field] = stored;
|
|
4170
|
+
return;
|
|
4171
|
+
}
|
|
4172
|
+
const value = entry[field];
|
|
4173
|
+
if (value === null) {
|
|
4174
|
+
delete entry[field];
|
|
4175
|
+
return;
|
|
4176
|
+
}
|
|
4177
|
+
if (typeof value === "string" && value.trim().length > 0) return;
|
|
4178
|
+
if (stored !== void 0) entry[field] = stored;
|
|
4179
|
+
else delete entry[field];
|
|
4180
|
+
}
|
|
4181
|
+
function preserveSearchSecrets(incoming, current) {
|
|
4182
|
+
if (!isRecord(incoming)) return incoming;
|
|
4183
|
+
const section = { ...incoming };
|
|
4184
|
+
const providersValue = section["providers"];
|
|
4185
|
+
if (!isRecord(providersValue)) return section;
|
|
4186
|
+
const providers = {};
|
|
4187
|
+
for (const [id, entryValue] of Object.entries(providersValue)) {
|
|
4188
|
+
if (!isRecord(entryValue)) {
|
|
4189
|
+
providers[id] = entryValue;
|
|
4190
|
+
continue;
|
|
4191
|
+
}
|
|
4192
|
+
const entry = { ...entryValue };
|
|
4193
|
+
delete entry["apiKeyConfigured"];
|
|
4194
|
+
delete entry["basicAuthPasswordConfigured"];
|
|
4195
|
+
const stored = storedSecrets(current, id);
|
|
4196
|
+
resolveSecretField(entry, "apiKey", stored.apiKey);
|
|
4197
|
+
resolveSecretField(entry, "basicAuthPassword", stored.basicAuthPassword);
|
|
4198
|
+
providers[id] = entry;
|
|
4199
|
+
}
|
|
4200
|
+
section["providers"] = providers;
|
|
4201
|
+
return section;
|
|
4202
|
+
}
|
|
4203
|
+
|
|
3742
4204
|
// src/admin/keyPolicyBody.ts
|
|
3743
4205
|
function parseKeyPolicyBody(body) {
|
|
3744
4206
|
const policy = {};
|
|
@@ -3801,7 +4263,7 @@ function parseKeyPolicyBody(body) {
|
|
|
3801
4263
|
var ENDPOINTS = /* @__PURE__ */ new Set(["chat", "responses", "messages", "gemini"]);
|
|
3802
4264
|
var TARGET_KINDS = /* @__PURE__ */ new Set(["account", "account-group", "account-pool", "provider"]);
|
|
3803
4265
|
var FALLBACKS = /* @__PURE__ */ new Set(["next", "fail", "global"]);
|
|
3804
|
-
function
|
|
4266
|
+
function isRecord2(value) {
|
|
3805
4267
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
3806
4268
|
}
|
|
3807
4269
|
function nonBlank(value) {
|
|
@@ -3821,7 +4283,7 @@ function validateGatewayBindingsSegment(patch) {
|
|
|
3821
4283
|
const ids = /* @__PURE__ */ new Set();
|
|
3822
4284
|
raw.forEach((entry, index) => {
|
|
3823
4285
|
const path2 = `bindings[${index}]`;
|
|
3824
|
-
if (!
|
|
4286
|
+
if (!isRecord2(entry)) {
|
|
3825
4287
|
errors.push(`${path2} must be an object`);
|
|
3826
4288
|
return;
|
|
3827
4289
|
}
|
|
@@ -3850,12 +4312,12 @@ function validateGatewayBindingsSegment(patch) {
|
|
|
3850
4312
|
} else if (entry.modelMappings.length > 100) {
|
|
3851
4313
|
errors.push(`${path2}.modelMappings cannot contain more than 100 entries`);
|
|
3852
4314
|
} else if (entry.modelMappings.some(
|
|
3853
|
-
(mapping) => !
|
|
4315
|
+
(mapping) => !isRecord2(mapping) || !nonBlank(mapping.source) || !nonBlank(mapping.target)
|
|
3854
4316
|
)) {
|
|
3855
4317
|
errors.push(`${path2}.modelMappings must contain non-empty source and target strings`);
|
|
3856
4318
|
}
|
|
3857
4319
|
}
|
|
3858
|
-
if (!
|
|
4320
|
+
if (!isRecord2(entry.target) || !TARGET_KINDS.has(String(entry.target.kind))) {
|
|
3859
4321
|
errors.push(`${path2}.target is invalid`);
|
|
3860
4322
|
} else {
|
|
3861
4323
|
if (!nonBlank(entry.target.providerId)) errors.push(`${path2}.target.providerId is required`);
|
|
@@ -3870,7 +4332,7 @@ function validateGatewayBindingsSegment(patch) {
|
|
|
3870
4332
|
}
|
|
3871
4333
|
}
|
|
3872
4334
|
if (entry.modelMap !== void 0) {
|
|
3873
|
-
if (!
|
|
4335
|
+
if (!isRecord2(entry.modelMap) || Object.values(entry.modelMap).some((value) => typeof value !== "string")) {
|
|
3874
4336
|
errors.push(`${path2}.modelMap must contain string values`);
|
|
3875
4337
|
}
|
|
3876
4338
|
}
|
|
@@ -3888,15 +4350,15 @@ function validateGatewayBindingsSegment(patch) {
|
|
|
3888
4350
|
}
|
|
3889
4351
|
|
|
3890
4352
|
// src/admin/voucherAdmin.ts
|
|
3891
|
-
var
|
|
3892
|
-
function
|
|
4353
|
+
var import_outbound_api3 = require("@omnicross/core/outbound-api");
|
|
4354
|
+
function writeJson2(res, status, body) {
|
|
3893
4355
|
res.writeHead(status, { "Content-Type": "application/json" });
|
|
3894
4356
|
res.end(JSON.stringify(body));
|
|
3895
4357
|
}
|
|
3896
|
-
function
|
|
3897
|
-
|
|
4358
|
+
function writeErr2(res, status, message) {
|
|
4359
|
+
writeJson2(res, status, { error: { type: "voucher_error", message } });
|
|
3898
4360
|
}
|
|
3899
|
-
function
|
|
4361
|
+
function readJsonBody3(req) {
|
|
3900
4362
|
return new Promise((resolve10, reject) => {
|
|
3901
4363
|
const chunks = [];
|
|
3902
4364
|
req.on("data", (c) => chunks.push(c));
|
|
@@ -3947,34 +4409,34 @@ function parseVoucherCreateBody(body) {
|
|
|
3947
4409
|
return { ok: true, input };
|
|
3948
4410
|
}
|
|
3949
4411
|
async function voucherEnabled(deps) {
|
|
3950
|
-
const config = await (0,
|
|
4412
|
+
const config = await (0, import_outbound_api3.loadServerConfig)(deps.settingsStore);
|
|
3951
4413
|
return config.voucher?.enabled === true;
|
|
3952
4414
|
}
|
|
3953
4415
|
async function handleVoucher(req, res, method, rest, deps) {
|
|
3954
4416
|
const voucherDb = deps.voucherDb;
|
|
3955
|
-
if (!voucherDb) return
|
|
4417
|
+
if (!voucherDb) return writeErr2(res, 501, "Voucher feature is not available");
|
|
3956
4418
|
if (method === "GET" && rest.length === 0) {
|
|
3957
4419
|
const rows = await voucherDb.voucherList();
|
|
3958
|
-
return
|
|
4420
|
+
return writeJson2(res, 200, { vouchers: rows.map(import_outbound_api3.toVoucherInfo) });
|
|
3959
4421
|
}
|
|
3960
4422
|
if (method === "POST" && rest.length === 0) {
|
|
3961
|
-
if (!await voucherEnabled(deps)) return
|
|
4423
|
+
if (!await voucherEnabled(deps)) return writeErr2(res, 403, "Voucher feature is disabled");
|
|
3962
4424
|
let body;
|
|
3963
4425
|
try {
|
|
3964
|
-
body = await
|
|
4426
|
+
body = await readJsonBody3(req);
|
|
3965
4427
|
} catch {
|
|
3966
|
-
return
|
|
4428
|
+
return writeErr2(res, 400, "Invalid JSON in request body");
|
|
3967
4429
|
}
|
|
3968
4430
|
const parsed = parseVoucherCreateBody(body);
|
|
3969
|
-
if (!parsed.ok) return
|
|
3970
|
-
const code = (0,
|
|
4431
|
+
if (!parsed.ok) return writeErr2(res, 400, parsed.message);
|
|
4432
|
+
const code = (0, import_outbound_api3.generateVoucherCode)();
|
|
3971
4433
|
const created = await voucherDb.voucherCreate({
|
|
3972
|
-
id: (0,
|
|
3973
|
-
codeHash: (0,
|
|
3974
|
-
codePrefix: (0,
|
|
4434
|
+
id: (0, import_outbound_api3.newVoucherId)(),
|
|
4435
|
+
codeHash: (0, import_outbound_api3.hashVoucherCode)(code),
|
|
4436
|
+
codePrefix: (0, import_outbound_api3.voucherCodePrefix)(code),
|
|
3975
4437
|
...parsed.input
|
|
3976
4438
|
});
|
|
3977
|
-
return
|
|
4439
|
+
return writeJson2(res, 201, {
|
|
3978
4440
|
id: created.id,
|
|
3979
4441
|
codePrefix: created.codePrefix,
|
|
3980
4442
|
type: created.type,
|
|
@@ -3985,11 +4447,11 @@ async function handleVoucher(req, res, method, rest, deps) {
|
|
|
3985
4447
|
}
|
|
3986
4448
|
const id = rest[0];
|
|
3987
4449
|
if (method === "POST" && id && rest[1] === "revoke") {
|
|
3988
|
-
if (!await voucherEnabled(deps)) return
|
|
4450
|
+
if (!await voucherEnabled(deps)) return writeErr2(res, 403, "Voucher feature is disabled");
|
|
3989
4451
|
const ok = await voucherDb.voucherRevokeCas(id, Date.now());
|
|
3990
|
-
return
|
|
4452
|
+
return writeJson2(res, ok ? 200 : 409, { ok });
|
|
3991
4453
|
}
|
|
3992
|
-
return
|
|
4454
|
+
return writeErr2(res, 405, `method ${method} not allowed on voucher`);
|
|
3993
4455
|
}
|
|
3994
4456
|
|
|
3995
4457
|
// src/admin/webhookConfigBody.ts
|
|
@@ -4156,7 +4618,7 @@ function resetBillingRuntimeForTests() {
|
|
|
4156
4618
|
}
|
|
4157
4619
|
|
|
4158
4620
|
// src/migration/migration.ts
|
|
4159
|
-
var
|
|
4621
|
+
var import_outbound_api4 = require("@omnicross/core/outbound-api");
|
|
4160
4622
|
|
|
4161
4623
|
// src/ports/account-multi.ts
|
|
4162
4624
|
var import_node_crypto8 = require("crypto");
|
|
@@ -4584,7 +5046,7 @@ async function gatherExport(deps, passphrase) {
|
|
|
4584
5046
|
v: BUNDLE_VERSION,
|
|
4585
5047
|
providers: cfg.providers,
|
|
4586
5048
|
tokens,
|
|
4587
|
-
server: (0,
|
|
5049
|
+
server: (0, import_outbound_api4.normalizeServerConfig)(cfg.server)
|
|
4588
5050
|
};
|
|
4589
5051
|
return sealPack(JSON.stringify(bundle), passphrase);
|
|
4590
5052
|
}
|
|
@@ -4633,7 +5095,7 @@ async function applyImport(packString, passphrase, mode, deps, parseProviderInpu
|
|
|
4633
5095
|
const imageErrors = validateImagesAdminConfig(rawServer["images"]);
|
|
4634
5096
|
if (imageErrors.length > 0) throw new Error("migration pack has an invalid Images config");
|
|
4635
5097
|
}
|
|
4636
|
-
validatedServer = (0,
|
|
5098
|
+
validatedServer = (0, import_outbound_api4.normalizeServerConfig)(bundle.server);
|
|
4637
5099
|
}
|
|
4638
5100
|
const validatedProviders = [];
|
|
4639
5101
|
for (const raw of rawProviders) {
|
|
@@ -4914,12 +5376,12 @@ async function handlePricingResolveConflicts(body, deps) {
|
|
|
4914
5376
|
}
|
|
4915
5377
|
|
|
4916
5378
|
// src/admin/accountAllowanceApi.ts
|
|
4917
|
-
function
|
|
5379
|
+
function writeJson3(res, status, body) {
|
|
4918
5380
|
res.writeHead(status, { "Content-Type": "application/json" });
|
|
4919
5381
|
res.end(JSON.stringify(body));
|
|
4920
5382
|
}
|
|
4921
5383
|
function writeError2(res, status, message) {
|
|
4922
|
-
|
|
5384
|
+
writeJson3(res, status, { error: { type: "account_allowance_error", message } });
|
|
4923
5385
|
}
|
|
4924
5386
|
function readJson2(req) {
|
|
4925
5387
|
return new Promise((resolve10, reject) => {
|
|
@@ -4952,7 +5414,7 @@ async function handleAccountAllowanceApi(req, res, method, rest, service) {
|
|
|
4952
5414
|
if (!service.getSchedulingStatus) {
|
|
4953
5415
|
return writeError2(res, 501, "allowance scheduling diagnostics are not available");
|
|
4954
5416
|
}
|
|
4955
|
-
return
|
|
5417
|
+
return writeJson3(res, 200, { scheduling: service.getSchedulingStatus() });
|
|
4956
5418
|
}
|
|
4957
5419
|
if (method === "GET") {
|
|
4958
5420
|
const params = query(req);
|
|
@@ -4961,7 +5423,7 @@ async function handleAccountAllowanceApi(req, res, method, rest, service) {
|
|
|
4961
5423
|
if (providerId === null) return writeError2(res, 400, "providerId must be claude or codex");
|
|
4962
5424
|
const accountId = rest.length >= 2 ? rest[1] : params.get("accountId") ?? void 0;
|
|
4963
5425
|
const allowances = await service.list({ providerId, accountId });
|
|
4964
|
-
return
|
|
5426
|
+
return writeJson3(res, 200, { allowances });
|
|
4965
5427
|
}
|
|
4966
5428
|
if (method === "POST" && rest[0] === "refresh") {
|
|
4967
5429
|
const body = await readJson2(req);
|
|
@@ -4976,7 +5438,7 @@ async function handleAccountAllowanceApi(req, res, method, rest, service) {
|
|
|
4976
5438
|
if (accountId && allowances.length === 0) {
|
|
4977
5439
|
return writeError2(res, 404, `Claude account '${accountId}' not found`);
|
|
4978
5440
|
}
|
|
4979
|
-
return
|
|
5441
|
+
return writeJson3(res, 200, { allowances });
|
|
4980
5442
|
}
|
|
4981
5443
|
return writeError2(res, 405, `method ${method} not allowed on account allowances`);
|
|
4982
5444
|
}
|
|
@@ -4992,7 +5454,7 @@ function readBody(req) {
|
|
|
4992
5454
|
req.on("error", reject);
|
|
4993
5455
|
});
|
|
4994
5456
|
}
|
|
4995
|
-
async function
|
|
5457
|
+
async function readJsonBody4(req) {
|
|
4996
5458
|
const raw = await readBody(req);
|
|
4997
5459
|
if (!raw.trim()) return {};
|
|
4998
5460
|
try {
|
|
@@ -5002,12 +5464,12 @@ async function readJsonBody3(req) {
|
|
|
5002
5464
|
return {};
|
|
5003
5465
|
}
|
|
5004
5466
|
}
|
|
5005
|
-
function
|
|
5467
|
+
function writeJson4(res, status, body) {
|
|
5006
5468
|
res.writeHead(status, { "Content-Type": "application/json" });
|
|
5007
5469
|
res.end(JSON.stringify(body));
|
|
5008
5470
|
}
|
|
5009
5471
|
function writeJsonError(res, status, message) {
|
|
5010
|
-
|
|
5472
|
+
writeJson4(res, status, { error: { type: "admin_api_error", message } });
|
|
5011
5473
|
}
|
|
5012
5474
|
function maskProviderApiKey(apiKey) {
|
|
5013
5475
|
if (!apiKey) return "";
|
|
@@ -5028,7 +5490,7 @@ function toKeyInfo(row) {
|
|
|
5028
5490
|
lastUsedAt: row.lastUsedAt,
|
|
5029
5491
|
revoked: row.revokedAt !== null,
|
|
5030
5492
|
kind: row.kind,
|
|
5031
|
-
allowedEndpoints: [...(0,
|
|
5493
|
+
allowedEndpoints: [...(0, import_outbound_api5.effectiveOutboundPermissions)(row.allowedEndpoints)],
|
|
5032
5494
|
legacyPermissions: row.allowedEndpoints === void 0,
|
|
5033
5495
|
loopbackOnly: row.loopbackOnly,
|
|
5034
5496
|
maxConcurrency: row.maxConcurrency,
|
|
@@ -5114,6 +5576,8 @@ async function handleAdminApi(req, res, path2, deps) {
|
|
|
5114
5576
|
return await handleServer(req, res, method, deps);
|
|
5115
5577
|
case "images":
|
|
5116
5578
|
return await handleImages(res, method, rest, deps);
|
|
5579
|
+
case "search":
|
|
5580
|
+
return await handleSearchAdmin(req, res, method, rest, deps);
|
|
5117
5581
|
case "accounts":
|
|
5118
5582
|
return await handleAccounts(req, res, method, rest, deps);
|
|
5119
5583
|
case "cli":
|
|
@@ -5147,7 +5611,7 @@ function requestQuery(req) {
|
|
|
5147
5611
|
return new URLSearchParams(qIdx >= 0 ? raw.slice(qIdx + 1) : "");
|
|
5148
5612
|
}
|
|
5149
5613
|
function writeResult(res, result) {
|
|
5150
|
-
|
|
5614
|
+
writeJson4(res, result.status, result.body);
|
|
5151
5615
|
}
|
|
5152
5616
|
async function handleUsage(req, res, method, rest, deps) {
|
|
5153
5617
|
if (method !== "GET") return writeJsonError(res, 405, `method ${method} not allowed on usage`);
|
|
@@ -5156,13 +5620,13 @@ async function handleUsage(req, res, method, rest, deps) {
|
|
|
5156
5620
|
async function handleDashboardRoute(res, method, deps) {
|
|
5157
5621
|
if (method !== "GET") return writeJsonError(res, 405, `method ${method} not allowed on dashboard`);
|
|
5158
5622
|
const result = await handleDashboard(deps);
|
|
5159
|
-
return
|
|
5623
|
+
return writeJson4(res, result.status, result.body);
|
|
5160
5624
|
}
|
|
5161
5625
|
async function handlePricing(req, res, method, rest, deps) {
|
|
5162
5626
|
if (rest.length === 0) {
|
|
5163
5627
|
if (method === "GET") return writeResult(res, await handlePricingList(deps));
|
|
5164
5628
|
if (method === "PUT") {
|
|
5165
|
-
return writeResult(res, await handlePricingUpsert(await
|
|
5629
|
+
return writeResult(res, await handlePricingUpsert(await readJsonBody4(req), deps));
|
|
5166
5630
|
}
|
|
5167
5631
|
if (method === "DELETE") {
|
|
5168
5632
|
return writeResult(res, await handlePricingDelete(requestQuery(req), deps));
|
|
@@ -5173,7 +5637,7 @@ async function handlePricing(req, res, method, rest, deps) {
|
|
|
5173
5637
|
return writeResult(res, await handlePricingFetchLatest(deps));
|
|
5174
5638
|
}
|
|
5175
5639
|
if (method === "POST" && rest.length === 1 && rest[0] === "resolve-conflicts") {
|
|
5176
|
-
return writeResult(res, await handlePricingResolveConflicts(await
|
|
5640
|
+
return writeResult(res, await handlePricingResolveConflicts(await readJsonBody4(req), deps));
|
|
5177
5641
|
}
|
|
5178
5642
|
return writeJsonError(res, 404, `unknown pricing route '${rest.join("/")}'`);
|
|
5179
5643
|
}
|
|
@@ -5187,15 +5651,15 @@ function migrationDeps(deps) {
|
|
|
5187
5651
|
}
|
|
5188
5652
|
async function handleMigrationExport(req, res, method, deps) {
|
|
5189
5653
|
if (method !== "POST") return writeJsonError(res, 405, `method ${method} not allowed on export`);
|
|
5190
|
-
const body = await
|
|
5654
|
+
const body = await readJsonBody4(req);
|
|
5191
5655
|
const result = await handleExport(body, migrationDeps(deps));
|
|
5192
|
-
return
|
|
5656
|
+
return writeJson4(res, result.status, result.body);
|
|
5193
5657
|
}
|
|
5194
5658
|
async function handleMigrationImport(req, res, method, deps) {
|
|
5195
5659
|
if (method !== "POST") return writeJsonError(res, 405, `method ${method} not allowed on import`);
|
|
5196
|
-
const body = await
|
|
5660
|
+
const body = await readJsonBody4(req);
|
|
5197
5661
|
const result = await handleImport(body, migrationDeps(deps));
|
|
5198
|
-
return
|
|
5662
|
+
return writeJson4(res, result.status, result.body);
|
|
5199
5663
|
}
|
|
5200
5664
|
async function handleProviders(req, res, method, rest, deps) {
|
|
5201
5665
|
const cfg = loadConfig(deps.configPath);
|
|
@@ -5226,13 +5690,13 @@ async function handleProviders(req, res, method, rest, deps) {
|
|
|
5226
5690
|
if (method === "GET" && rest.length === 2 && rest[1] === "reveal-key") {
|
|
5227
5691
|
const row = cfg.providers.find((p) => p.id === rest[0]);
|
|
5228
5692
|
if (!row) return writeJsonError(res, 404, `provider '${rest[0]}' not found`);
|
|
5229
|
-
return
|
|
5693
|
+
return writeJson4(res, 200, { apiKey: row.apiKey ?? "" });
|
|
5230
5694
|
}
|
|
5231
5695
|
if (method === "GET") {
|
|
5232
|
-
return
|
|
5696
|
+
return writeJson4(res, 200, { providers: cfg.providers.map(toProviderView) });
|
|
5233
5697
|
}
|
|
5234
5698
|
if (method === "POST") {
|
|
5235
|
-
const body = await
|
|
5699
|
+
const body = await readJsonBody4(req);
|
|
5236
5700
|
const provider = parseProviderInput(body, void 0);
|
|
5237
5701
|
if (!provider) return writeJsonError(res, 400, "invalid provider (id, apiFormat, baseUrl required)");
|
|
5238
5702
|
if (cfg.providers.some((p) => p.id === provider.id)) {
|
|
@@ -5240,25 +5704,25 @@ async function handleProviders(req, res, method, rest, deps) {
|
|
|
5240
5704
|
}
|
|
5241
5705
|
cfg.providers.push(provider);
|
|
5242
5706
|
persistProviders(cfg, deps);
|
|
5243
|
-
return
|
|
5707
|
+
return writeJson4(res, 201, { provider: toProviderView(provider) });
|
|
5244
5708
|
}
|
|
5245
5709
|
const id = rest[0];
|
|
5246
5710
|
if (!id) return writeJsonError(res, 400, "provider id required in path");
|
|
5247
5711
|
const idx = cfg.providers.findIndex((p) => p.id === id);
|
|
5248
5712
|
if (idx < 0) return writeJsonError(res, 404, `provider '${id}' not found`);
|
|
5249
5713
|
if (method === "PUT") {
|
|
5250
|
-
const body = await
|
|
5714
|
+
const body = await readJsonBody4(req);
|
|
5251
5715
|
const existing = cfg.providers[idx];
|
|
5252
5716
|
const updated = parseProviderInput(body, existing);
|
|
5253
5717
|
if (!updated) return writeJsonError(res, 400, "invalid provider (apiFormat, baseUrl required)");
|
|
5254
5718
|
cfg.providers[idx] = updated;
|
|
5255
5719
|
persistProviders(cfg, deps);
|
|
5256
|
-
return
|
|
5720
|
+
return writeJson4(res, 200, { provider: toProviderView(updated) });
|
|
5257
5721
|
}
|
|
5258
5722
|
if (method === "DELETE") {
|
|
5259
5723
|
cfg.providers.splice(idx, 1);
|
|
5260
5724
|
persistProviders(cfg, deps);
|
|
5261
|
-
return
|
|
5725
|
+
return writeJson4(res, 200, { ok: true });
|
|
5262
5726
|
}
|
|
5263
5727
|
return writeJsonError(res, 405, `method ${method} not allowed on providers`);
|
|
5264
5728
|
}
|
|
@@ -5267,7 +5731,7 @@ function persistProviders(cfg, deps) {
|
|
|
5267
5731
|
deps.llmConfig.reload(cfg);
|
|
5268
5732
|
}
|
|
5269
5733
|
async function handleProviderReorder(req, res, cfg, deps) {
|
|
5270
|
-
const body = await
|
|
5734
|
+
const body = await readJsonBody4(req);
|
|
5271
5735
|
const rawOrder = body["order"];
|
|
5272
5736
|
if (!Array.isArray(rawOrder)) {
|
|
5273
5737
|
return writeJsonError(res, 400, "reorder requires { order: string[] }");
|
|
@@ -5291,14 +5755,14 @@ async function handleProviderReorder(req, res, cfg, deps) {
|
|
|
5291
5755
|
}
|
|
5292
5756
|
cfg.providers = reordered;
|
|
5293
5757
|
persistProviders(cfg, deps);
|
|
5294
|
-
return
|
|
5758
|
+
return writeJson4(res, 200, { ok: true, providers: cfg.providers.map(toProviderView) });
|
|
5295
5759
|
}
|
|
5296
5760
|
async function handleDiscoverModels(res, id, cfg) {
|
|
5297
5761
|
if (!id) return writeJsonError(res, 400, "provider id required in path");
|
|
5298
5762
|
const row = cfg.providers.find((p) => p.id === id);
|
|
5299
5763
|
if (!row) return writeJsonError(res, 404, `provider '${id}' not found`);
|
|
5300
5764
|
if (row.apiFormat !== "openai" && row.apiFormat !== "openai-response") {
|
|
5301
|
-
return
|
|
5765
|
+
return writeJson4(res, 200, { models: [], unsupportedFormat: true });
|
|
5302
5766
|
}
|
|
5303
5767
|
const resolvedKey = resolveEnvKey(row.apiKey);
|
|
5304
5768
|
const base = row.baseUrl.replace(/\/+$/, "");
|
|
@@ -5315,32 +5779,32 @@ async function handleDiscoverModels(res, id, cfg) {
|
|
|
5315
5779
|
message = parsed?.error?.message || parsed?.message || message;
|
|
5316
5780
|
} catch {
|
|
5317
5781
|
}
|
|
5318
|
-
return
|
|
5782
|
+
return writeJson4(res, 200, {
|
|
5319
5783
|
models: [],
|
|
5320
5784
|
error: `discovery failed (${response.status})${message ? `: ${message}` : ""}`
|
|
5321
5785
|
});
|
|
5322
5786
|
}
|
|
5323
5787
|
const data = await response.json();
|
|
5324
5788
|
const models = Array.isArray(data?.data) ? data.data.map((m) => typeof m?.id === "string" ? m.id : "").filter((m) => m.length > 0) : [];
|
|
5325
|
-
return
|
|
5789
|
+
return writeJson4(res, 200, { models });
|
|
5326
5790
|
} catch (err5) {
|
|
5327
5791
|
const message = err5 instanceof Error ? err5.message : String(err5);
|
|
5328
|
-
return
|
|
5792
|
+
return writeJson4(res, 200, { models: [], error: `discovery failed: ${message}` });
|
|
5329
5793
|
}
|
|
5330
5794
|
}
|
|
5331
5795
|
async function handleTestModel(req, res, id, cfg) {
|
|
5332
5796
|
if (!id) return writeJsonError(res, 400, "provider id required in path");
|
|
5333
5797
|
const row = cfg.providers.find((p) => p.id === id);
|
|
5334
5798
|
if (!row) return writeJsonError(res, 404, `provider '${id}' not found`);
|
|
5335
|
-
const body = await
|
|
5799
|
+
const body = await readJsonBody4(req);
|
|
5336
5800
|
const model = typeof body["model"] === "string" ? body["model"].trim() : "";
|
|
5337
5801
|
if (!model) return writeJsonError(res, 400, "test requires a { model } string");
|
|
5338
5802
|
if (row.apiFormat === "gemini") {
|
|
5339
|
-
return
|
|
5803
|
+
return writeJson4(res, 200, { ok: false, unsupportedFormat: true });
|
|
5340
5804
|
}
|
|
5341
5805
|
const resolvedKey = resolveEnvKey(row.apiKey);
|
|
5342
5806
|
if (!resolvedKey) {
|
|
5343
|
-
return
|
|
5807
|
+
return writeJson4(res, 200, { ok: false, message: "no API key configured for this provider" });
|
|
5344
5808
|
}
|
|
5345
5809
|
let url = row.baseUrl.replace(/\/+$/, "");
|
|
5346
5810
|
const prompt = "Reply with the single word: OK.";
|
|
@@ -5379,9 +5843,9 @@ async function handleTestModel(req, res, id, cfg) {
|
|
|
5379
5843
|
message = parsed?.error?.message || parsed?.message || message;
|
|
5380
5844
|
} catch {
|
|
5381
5845
|
}
|
|
5382
|
-
return
|
|
5846
|
+
return writeJson4(res, 200, { ok: false, status: response.status, latencyMs, message });
|
|
5383
5847
|
}
|
|
5384
|
-
return
|
|
5848
|
+
return writeJson4(res, 200, {
|
|
5385
5849
|
ok: true,
|
|
5386
5850
|
status: response.status,
|
|
5387
5851
|
latencyMs,
|
|
@@ -5389,7 +5853,7 @@ async function handleTestModel(req, res, id, cfg) {
|
|
|
5389
5853
|
});
|
|
5390
5854
|
} catch (err5) {
|
|
5391
5855
|
const message = err5 instanceof Error ? err5.message : String(err5);
|
|
5392
|
-
return
|
|
5856
|
+
return writeJson4(res, 200, { ok: false, latencyMs: Date.now() - startedAt, message });
|
|
5393
5857
|
}
|
|
5394
5858
|
}
|
|
5395
5859
|
function extractSampleText(text, apiFormat) {
|
|
@@ -5430,7 +5894,7 @@ async function handleProviderKeys(res, id, cfg, deps) {
|
|
|
5430
5894
|
const row = cfg.providers.find((p) => p.id === id);
|
|
5431
5895
|
if (!row) return writeJsonError(res, 404, `provider '${id}' not found`);
|
|
5432
5896
|
const cooldown = await deps.apiKeyPool.getKeyHealth(id);
|
|
5433
|
-
return
|
|
5897
|
+
return writeJson4(res, 200, { keys: toPoolKeyView(row, cooldown, deps) });
|
|
5434
5898
|
}
|
|
5435
5899
|
function parsePoolKeyInput(body, existing) {
|
|
5436
5900
|
const out = {};
|
|
@@ -5449,7 +5913,7 @@ async function handleAddProviderKey(req, res, id, cfg, deps) {
|
|
|
5449
5913
|
if (!id) return writeJsonError(res, 400, "provider id required in path");
|
|
5450
5914
|
const idx = cfg.providers.findIndex((p) => p.id === id);
|
|
5451
5915
|
if (idx < 0) return writeJsonError(res, 404, `provider '${id}' not found`);
|
|
5452
|
-
const body = await
|
|
5916
|
+
const body = await readJsonBody4(req);
|
|
5453
5917
|
const parsed = parsePoolKeyInput(body);
|
|
5454
5918
|
if (!parsed.apiKey) return writeJsonError(res, 400, "add requires a non-empty apiKey");
|
|
5455
5919
|
const keyId = `key-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
@@ -5461,7 +5925,7 @@ async function handleAddProviderKey(req, res, id, cfg, deps) {
|
|
|
5461
5925
|
row.apiKeys = [...row.apiKeys ?? [], entry];
|
|
5462
5926
|
persistProviders(cfg, deps);
|
|
5463
5927
|
const cooldown = await deps.apiKeyPool.getKeyHealth(id);
|
|
5464
|
-
return
|
|
5928
|
+
return writeJson4(res, 201, { keys: toPoolKeyView(row, cooldown, deps) });
|
|
5465
5929
|
}
|
|
5466
5930
|
async function handleUpdateProviderKey(req, res, id, keyId, cfg, deps) {
|
|
5467
5931
|
if (!id) return writeJsonError(res, 400, "provider id required in path");
|
|
@@ -5471,7 +5935,7 @@ async function handleUpdateProviderKey(req, res, id, keyId, cfg, deps) {
|
|
|
5471
5935
|
const row = cfg.providers[idx];
|
|
5472
5936
|
const keyIdx = (row.apiKeys ?? []).findIndex((k) => k.id === keyId);
|
|
5473
5937
|
if (keyIdx < 0) return writeJsonError(res, 404, `pool key '${keyId}' not found`);
|
|
5474
|
-
const body = await
|
|
5938
|
+
const body = await readJsonBody4(req);
|
|
5475
5939
|
const existing = row.apiKeys[keyIdx];
|
|
5476
5940
|
const parsed = parsePoolKeyInput(body, existing);
|
|
5477
5941
|
const entry = { id: keyId, apiKey: parsed.apiKey || existing.apiKey };
|
|
@@ -5481,7 +5945,7 @@ async function handleUpdateProviderKey(req, res, id, keyId, cfg, deps) {
|
|
|
5481
5945
|
row.apiKeys[keyIdx] = entry;
|
|
5482
5946
|
persistProviders(cfg, deps);
|
|
5483
5947
|
const cooldown = await deps.apiKeyPool.getKeyHealth(id);
|
|
5484
|
-
return
|
|
5948
|
+
return writeJson4(res, 200, { keys: toPoolKeyView(row, cooldown, deps) });
|
|
5485
5949
|
}
|
|
5486
5950
|
async function handleDeleteProviderKey(res, id, keyId, cfg, deps) {
|
|
5487
5951
|
if (!id) return writeJsonError(res, 400, "provider id required in path");
|
|
@@ -5495,7 +5959,7 @@ async function handleDeleteProviderKey(res, id, keyId, cfg, deps) {
|
|
|
5495
5959
|
if (row.apiKeys.length === 0) row.apiKeys = void 0;
|
|
5496
5960
|
persistProviders(cfg, deps);
|
|
5497
5961
|
const cooldown = await deps.apiKeyPool.getKeyHealth(id);
|
|
5498
|
-
return
|
|
5962
|
+
return writeJson4(res, 200, { keys: toPoolKeyView(row, cooldown, deps) });
|
|
5499
5963
|
}
|
|
5500
5964
|
async function handleToggleProviderKey(req, res, id, keyId, cfg, deps) {
|
|
5501
5965
|
if (!id) return writeJsonError(res, 400, "provider id required in path");
|
|
@@ -5505,11 +5969,11 @@ async function handleToggleProviderKey(req, res, id, keyId, cfg, deps) {
|
|
|
5505
5969
|
const row = cfg.providers[idx];
|
|
5506
5970
|
const keyIdx = (row.apiKeys ?? []).findIndex((k) => k.id === keyId);
|
|
5507
5971
|
if (keyIdx < 0) return writeJsonError(res, 404, `pool key '${keyId}' not found`);
|
|
5508
|
-
const body = await
|
|
5972
|
+
const body = await readJsonBody4(req);
|
|
5509
5973
|
row.apiKeys[keyIdx].enabled = Boolean(body["enabled"]);
|
|
5510
5974
|
persistProviders(cfg, deps);
|
|
5511
5975
|
const cooldown = await deps.apiKeyPool.getKeyHealth(id);
|
|
5512
|
-
return
|
|
5976
|
+
return writeJson4(res, 200, { keys: toPoolKeyView(row, cooldown, deps) });
|
|
5513
5977
|
}
|
|
5514
5978
|
function parseApiKeysInput(raw, existing) {
|
|
5515
5979
|
if (!Array.isArray(raw)) return existing;
|
|
@@ -5695,13 +6159,13 @@ function handlePresets(res, method) {
|
|
|
5695
6159
|
website: p.website,
|
|
5696
6160
|
modelsEndpoint: p.modelsEndpoint
|
|
5697
6161
|
}));
|
|
5698
|
-
return
|
|
6162
|
+
return writeJson4(res, 200, { presets, excluded });
|
|
5699
6163
|
}
|
|
5700
6164
|
async function handleKeys(req, res, method, rest, deps) {
|
|
5701
6165
|
if (method === "GET" && rest.length === 0) {
|
|
5702
6166
|
const rows = await deps.keyDb.outboundApiKeysList();
|
|
5703
6167
|
const reader = deps.keySpendReader;
|
|
5704
|
-
if (!reader) return
|
|
6168
|
+
if (!reader) return writeJson4(res, 200, { keys: rows.map(toKeyInfo) });
|
|
5705
6169
|
const now = Date.now();
|
|
5706
6170
|
const keys = await Promise.all(
|
|
5707
6171
|
rows.map(async (row) => {
|
|
@@ -5713,13 +6177,13 @@ async function handleKeys(req, res, method, rest, deps) {
|
|
|
5713
6177
|
return info;
|
|
5714
6178
|
})
|
|
5715
6179
|
);
|
|
5716
|
-
return
|
|
6180
|
+
return writeJson4(res, 200, { keys });
|
|
5717
6181
|
}
|
|
5718
6182
|
if (method === "POST" && rest.length === 0) {
|
|
5719
|
-
const body = await
|
|
6183
|
+
const body = await readJsonBody4(req);
|
|
5720
6184
|
const name = typeof body["name"] === "string" && body["name"].trim() ? body["name"].trim() : "key";
|
|
5721
|
-
const created = await (0,
|
|
5722
|
-
return
|
|
6185
|
+
const created = await (0, import_outbound_api5.createNamedKey)(deps.keyDb, name);
|
|
6186
|
+
return writeJson4(res, 201, {
|
|
5723
6187
|
id: created.id,
|
|
5724
6188
|
name: created.name,
|
|
5725
6189
|
keyPrefix: created.keyPrefix,
|
|
@@ -5730,7 +6194,7 @@ async function handleKeys(req, res, method, rest, deps) {
|
|
|
5730
6194
|
}
|
|
5731
6195
|
if (method === "GET" && rest.length === 2 && rest[1] === "reveal") {
|
|
5732
6196
|
const revealed = await deps.keyDb.outboundApiKeysReveal(rest[0]);
|
|
5733
|
-
if (revealed !== null) return
|
|
6197
|
+
if (revealed !== null) return writeJson4(res, 200, { key: revealed });
|
|
5734
6198
|
const exists = (await deps.keyDb.outboundApiKeysList()).some((r) => r.id === rest[0]);
|
|
5735
6199
|
if (!exists) return writeJsonError(res, 404, `key '${rest[0]}' not found`);
|
|
5736
6200
|
return writeJsonError(
|
|
@@ -5745,32 +6209,32 @@ async function handleKeys(req, res, method, rest, deps) {
|
|
|
5745
6209
|
const bound = await integrationKeyRequirement(deps, id);
|
|
5746
6210
|
if (bound) return writeJsonError(res, 409, bound);
|
|
5747
6211
|
const ok = await deps.keyDb.outboundApiKeysRevoke(id);
|
|
5748
|
-
return
|
|
6212
|
+
return writeJson4(res, ok ? 200 : 404, { ok });
|
|
5749
6213
|
}
|
|
5750
6214
|
if (method === "DELETE" && id && !action) {
|
|
5751
6215
|
const bound = await integrationKeyRequirement(deps, id);
|
|
5752
6216
|
if (bound) return writeJsonError(res, 409, bound);
|
|
5753
6217
|
const ok = await deps.keyDb.outboundApiKeysDelete(id);
|
|
5754
|
-
return
|
|
6218
|
+
return writeJson4(res, ok ? 200 : 404, { ok });
|
|
5755
6219
|
}
|
|
5756
6220
|
if (method === "POST" && id && action === "enabled") {
|
|
5757
|
-
const body = await
|
|
6221
|
+
const body = await readJsonBody4(req);
|
|
5758
6222
|
const enabled = body["enabled"] === true;
|
|
5759
6223
|
if (!enabled) {
|
|
5760
6224
|
const bound = await integrationKeyRequirement(deps, id);
|
|
5761
6225
|
if (bound) return writeJsonError(res, 409, bound);
|
|
5762
6226
|
}
|
|
5763
6227
|
const ok = await deps.keyDb.outboundApiKeysSetEnabled(id, enabled);
|
|
5764
|
-
return
|
|
6228
|
+
return writeJson4(res, ok ? 200 : 404, { ok, enabled });
|
|
5765
6229
|
}
|
|
5766
6230
|
if (method === "POST" && id && action === "permissions") {
|
|
5767
|
-
const body = await
|
|
6231
|
+
const body = await readJsonBody4(req);
|
|
5768
6232
|
if (Object.keys(body).length !== 1 || !Object.prototype.hasOwnProperty.call(body, "permissions")) {
|
|
5769
6233
|
return writeJsonError(res, 400, "body must contain only permissions");
|
|
5770
6234
|
}
|
|
5771
6235
|
let permissions;
|
|
5772
6236
|
try {
|
|
5773
|
-
permissions = (0,
|
|
6237
|
+
permissions = (0, import_outbound_api5.validateOutboundPermissions)(body["permissions"]);
|
|
5774
6238
|
} catch {
|
|
5775
6239
|
return writeJsonError(
|
|
5776
6240
|
res,
|
|
@@ -5779,19 +6243,19 @@ async function handleKeys(req, res, method, rest, deps) {
|
|
|
5779
6243
|
);
|
|
5780
6244
|
}
|
|
5781
6245
|
const before = (await deps.keyDb.outboundApiKeysList()).find((row) => row.id === id);
|
|
5782
|
-
if (!before) return
|
|
5783
|
-
if (before.revokedAt !== null) return
|
|
6246
|
+
if (!before) return writeJson4(res, 404, { ok: false });
|
|
6247
|
+
if (before.revokedAt !== null) return writeJson4(res, 409, { ok: false });
|
|
5784
6248
|
const required = await integrationKeyRequirement(deps, id, permissions);
|
|
5785
6249
|
if (required) return writeJsonError(res, 409, required);
|
|
5786
6250
|
const ok = await deps.keyDb.outboundApiKeysSetPermissions(id, permissions);
|
|
5787
6251
|
if (!ok) {
|
|
5788
6252
|
const current = (await deps.keyDb.outboundApiKeysList()).find((row) => row.id === id);
|
|
5789
|
-
return
|
|
6253
|
+
return writeJson4(res, current?.revokedAt !== null ? 409 : 404, { ok: false });
|
|
5790
6254
|
}
|
|
5791
|
-
return
|
|
6255
|
+
return writeJson4(res, 200, { ok: true, allowedEndpoints: permissions });
|
|
5792
6256
|
}
|
|
5793
6257
|
if (method === "POST" && id && action === "max-concurrency") {
|
|
5794
|
-
const body = await
|
|
6258
|
+
const body = await readJsonBody4(req);
|
|
5795
6259
|
const raw = body["maxConcurrency"];
|
|
5796
6260
|
let value;
|
|
5797
6261
|
if (raw === null) {
|
|
@@ -5806,14 +6270,14 @@ async function handleKeys(req, res, method, rest, deps) {
|
|
|
5806
6270
|
);
|
|
5807
6271
|
}
|
|
5808
6272
|
const ok = await deps.keyDb.outboundApiKeysSetMaxConcurrency(id, value);
|
|
5809
|
-
return
|
|
6273
|
+
return writeJson4(res, ok ? 200 : 404, { ok, maxConcurrency: value });
|
|
5810
6274
|
}
|
|
5811
6275
|
if (method === "POST" && id && action === "policy") {
|
|
5812
|
-
const body = await
|
|
6276
|
+
const body = await readJsonBody4(req);
|
|
5813
6277
|
const parsed = parseKeyPolicyBody(body);
|
|
5814
6278
|
if (!parsed.ok) return writeJsonError(res, 400, parsed.message);
|
|
5815
6279
|
const ok = await deps.keyDb.outboundApiKeysSetPolicy(id, parsed.policy);
|
|
5816
|
-
return
|
|
6280
|
+
return writeJson4(res, ok ? 200 : 404, { ok });
|
|
5817
6281
|
}
|
|
5818
6282
|
return writeJsonError(res, 405, `method ${method} not allowed on keys`);
|
|
5819
6283
|
}
|
|
@@ -5904,7 +6368,8 @@ function outboundServerConfigInput(config) {
|
|
|
5904
6368
|
userMessageQueue: config.userMessageQueue,
|
|
5905
6369
|
concurrencyQueue: config.concurrencyQueue,
|
|
5906
6370
|
voucher: config.voucher,
|
|
5907
|
-
anthropic: config.anthropic
|
|
6371
|
+
anthropic: config.anthropic,
|
|
6372
|
+
search: config.search
|
|
5908
6373
|
};
|
|
5909
6374
|
}
|
|
5910
6375
|
function projectImagesConfigForAdmin(config) {
|
|
@@ -5940,8 +6405,8 @@ function sameConfigValue(left, right) {
|
|
|
5940
6405
|
return JSON.stringify(left) === JSON.stringify(right);
|
|
5941
6406
|
}
|
|
5942
6407
|
function imageConfigurationAuditFields(previous, next) {
|
|
5943
|
-
const before = previous ??
|
|
5944
|
-
const after = next ??
|
|
6408
|
+
const before = previous ?? import_outbound_api5.DEFAULT_IMAGES_SERVER_CONFIG;
|
|
6409
|
+
const after = next ?? import_outbound_api5.DEFAULT_IMAGES_SERVER_CONFIG;
|
|
5945
6410
|
const fields = [];
|
|
5946
6411
|
if (before.enabled !== after.enabled) fields.push("enablement");
|
|
5947
6412
|
if (before.provider !== after.provider) fields.push("provider");
|
|
@@ -5967,15 +6432,21 @@ function currentImageGenerationId(deps) {
|
|
|
5967
6432
|
}
|
|
5968
6433
|
async function handleServer(req, res, method, deps) {
|
|
5969
6434
|
if (method === "GET") {
|
|
5970
|
-
const config = await (0,
|
|
6435
|
+
const config = await (0, import_outbound_api5.loadServerConfig)(deps.settingsStore);
|
|
5971
6436
|
let server = config;
|
|
5972
6437
|
if (config.proxy) server = { ...server, proxy: redactOutboundProxy(config.proxy) };
|
|
5973
6438
|
if (config.webhook) server = { ...server, webhook: redactWebhookConfig(config.webhook) };
|
|
5974
6439
|
if (config.billing) server = { ...server, billing: redactBillingConfig(config.billing) };
|
|
5975
|
-
|
|
6440
|
+
if (config.search) {
|
|
6441
|
+
server = {
|
|
6442
|
+
...server,
|
|
6443
|
+
search: redactSearchServerConfig(config.search)
|
|
6444
|
+
};
|
|
6445
|
+
}
|
|
6446
|
+
return writeJson4(res, 200, { server: projectImagesConfigForAdmin(server) });
|
|
5976
6447
|
}
|
|
5977
6448
|
if (method === "PUT") {
|
|
5978
|
-
const patch = await
|
|
6449
|
+
const patch = await readJsonBody4(req);
|
|
5979
6450
|
const queueErrors = validateQueueSegments(patch);
|
|
5980
6451
|
if (queueErrors.length > 0) {
|
|
5981
6452
|
return writeJsonError(res, 400, `invalid queue config: ${queueErrors.join("; ")}`);
|
|
@@ -6004,7 +6475,7 @@ async function handleServer(req, res, method, deps) {
|
|
|
6004
6475
|
if (billingErrors.length > 0) {
|
|
6005
6476
|
return writeJsonError(res, 400, `invalid billing config: ${billingErrors.join("; ")}`);
|
|
6006
6477
|
}
|
|
6007
|
-
const current = await (0,
|
|
6478
|
+
const current = await (0, import_outbound_api5.loadServerConfig)(deps.settingsStore);
|
|
6008
6479
|
let effectivePatch = patch;
|
|
6009
6480
|
if (patch.proxy) {
|
|
6010
6481
|
effectivePatch = { ...effectivePatch, proxy: preserveOutboundProxySecrets(patch.proxy, current.proxy) };
|
|
@@ -6025,7 +6496,21 @@ async function handleServer(req, res, method, deps) {
|
|
|
6025
6496
|
}
|
|
6026
6497
|
effectivePatch = { ...effectivePatch, images };
|
|
6027
6498
|
}
|
|
6028
|
-
|
|
6499
|
+
if (patch.search !== void 0) {
|
|
6500
|
+
const searchPatch = preserveSearchSecrets(
|
|
6501
|
+
patch.search,
|
|
6502
|
+
current.search ?? import_outbound_api5.DEFAULT_SEARCH_SERVER_CONFIG
|
|
6503
|
+
);
|
|
6504
|
+
const searchErrors = (0, import_outbound_api5.validateSearchServerConfig)(searchPatch);
|
|
6505
|
+
if (searchErrors.length > 0) {
|
|
6506
|
+
return writeJsonError(res, 400, `invalid search config: ${searchErrors.join("; ")}`);
|
|
6507
|
+
}
|
|
6508
|
+
effectivePatch = {
|
|
6509
|
+
...effectivePatch,
|
|
6510
|
+
search: searchPatch
|
|
6511
|
+
};
|
|
6512
|
+
}
|
|
6513
|
+
const merged = (0, import_outbound_api5.mergeServerConfig)(current, effectivePatch);
|
|
6029
6514
|
const priorImageGenerationId = currentImageGenerationId(deps);
|
|
6030
6515
|
try {
|
|
6031
6516
|
await applyServerConfigTransaction(current, merged, {
|
|
@@ -6055,7 +6540,7 @@ async function handleServer(req, res, method, deps) {
|
|
|
6055
6540
|
throw error;
|
|
6056
6541
|
}
|
|
6057
6542
|
},
|
|
6058
|
-
persist: (next) => (0,
|
|
6543
|
+
persist: (next) => (0, import_outbound_api5.saveServerConfig)(deps.settingsStore, next),
|
|
6059
6544
|
restorePersisted: (snapshot) => deps.settingsStore.restoreDocumentSnapshot(snapshot)
|
|
6060
6545
|
});
|
|
6061
6546
|
} catch (error) {
|
|
@@ -6077,7 +6562,11 @@ async function handleServer(req, res, method, deps) {
|
|
|
6077
6562
|
} catch {
|
|
6078
6563
|
}
|
|
6079
6564
|
}
|
|
6080
|
-
|
|
6565
|
+
const mergedForAdmin = merged.search ? {
|
|
6566
|
+
...merged,
|
|
6567
|
+
search: redactSearchServerConfig(merged.search)
|
|
6568
|
+
} : merged;
|
|
6569
|
+
return writeJson4(res, 200, { server: projectImagesConfigForAdmin(mergedForAdmin) });
|
|
6081
6570
|
}
|
|
6082
6571
|
return writeJsonError(res, 405, `method ${method} not allowed on server`);
|
|
6083
6572
|
}
|
|
@@ -6094,7 +6583,7 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
6094
6583
|
sessionKey: query2.get("sessionKey") ?? void 0,
|
|
6095
6584
|
limit: Number.isFinite(parsedLimit) ? parsedLimit : 100
|
|
6096
6585
|
});
|
|
6097
|
-
return
|
|
6586
|
+
return writeJson4(res, 200, {
|
|
6098
6587
|
available: true,
|
|
6099
6588
|
records,
|
|
6100
6589
|
capacity: import_AccountRouteActivity.ACCOUNT_ROUTE_ACTIVITY_LIMIT,
|
|
@@ -6110,7 +6599,7 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
6110
6599
|
providerId: query2.get("providerId") ?? void 0,
|
|
6111
6600
|
accountId: query2.get("accountId") ?? void 0
|
|
6112
6601
|
});
|
|
6113
|
-
return
|
|
6602
|
+
return writeJson4(res, 200, {
|
|
6114
6603
|
available: true,
|
|
6115
6604
|
entries,
|
|
6116
6605
|
collectedAt: Date.now()
|
|
@@ -6129,10 +6618,10 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
6129
6618
|
const accounts = await deps.subscriptionAccounts.listAll();
|
|
6130
6619
|
const providerAccounts = await deps.subscriptionTokenWriter.listSanitizedAccounts();
|
|
6131
6620
|
const externalCli = await deps.subscriptionTokenWriter.listExternalCliAvailability();
|
|
6132
|
-
return
|
|
6621
|
+
return writeJson4(res, 200, { accounts, providerAccounts, externalCli });
|
|
6133
6622
|
}
|
|
6134
6623
|
if (method === "POST" && rest[0] === "batch" && rest.length === 1) {
|
|
6135
|
-
const body = await
|
|
6624
|
+
const body = await readJsonBody4(req);
|
|
6136
6625
|
const parsed = validateAccountBatchBody(body);
|
|
6137
6626
|
if (!parsed) return writeJsonError(res, 400, "invalid account batch request");
|
|
6138
6627
|
const result = await deps.subscriptionTokenWriter.batchManageAccounts(parsed.refs, parsed.mutation);
|
|
@@ -6148,15 +6637,15 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
6148
6637
|
deps.accountAllowanceService?.removeAccountSnapshot?.(ref.providerId, ref.accountId);
|
|
6149
6638
|
}
|
|
6150
6639
|
}
|
|
6151
|
-
return
|
|
6640
|
+
return writeJson4(res, 200, { ok: true, affected: result.affected });
|
|
6152
6641
|
}
|
|
6153
6642
|
if (method === "GET" && rest[0] === "codex" && rest[1] === "oauth" && rest[3] === "status") {
|
|
6154
6643
|
const result = handleCodexOAuthStatus(rest[2], deps);
|
|
6155
|
-
return
|
|
6644
|
+
return writeJson4(res, result.status, result.body);
|
|
6156
6645
|
}
|
|
6157
6646
|
if (method === "DELETE" && rest[0] === "codex" && rest[1] === "oauth" && rest[2]) {
|
|
6158
6647
|
const result = handleCodexOAuthCancel(rest[2], deps);
|
|
6159
|
-
return
|
|
6648
|
+
return writeJson4(res, result.status, result.body);
|
|
6160
6649
|
}
|
|
6161
6650
|
if (method === "GET" && rest.length === 3 && rest[2] === "diagnostics") {
|
|
6162
6651
|
const providerId = asSubscriptionProviderId(rest[0]);
|
|
@@ -6178,7 +6667,7 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
6178
6667
|
resumeAt: entry.resumeAt
|
|
6179
6668
|
})) ?? [];
|
|
6180
6669
|
const diagnostics = [...health2, ...allowance].sort((left, right) => right.at - left.at).slice(0, 200);
|
|
6181
|
-
return
|
|
6670
|
+
return writeJson4(res, 200, { diagnostics });
|
|
6182
6671
|
}
|
|
6183
6672
|
if (method === "GET" && rest.length === 3 && rest[2] === "events") {
|
|
6184
6673
|
const providerId = asSubscriptionProviderId(rest[0]);
|
|
@@ -6190,17 +6679,17 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
6190
6679
|
}
|
|
6191
6680
|
const snapshot = deps.accountProbeService?.getAllHistory().find((entry) => entry.providerId === providerId && entry.accountId === accountId);
|
|
6192
6681
|
const diagnostics = (0, import_SubscriptionAccountHealth.getSharedAccountHealth)().getDiagnostics({ providerId, accountId });
|
|
6193
|
-
return
|
|
6682
|
+
return writeJson4(res, 200, { events: snapshot?.records ?? [], diagnostics });
|
|
6194
6683
|
}
|
|
6195
6684
|
if (method === "PATCH" && rest.length === 2) {
|
|
6196
6685
|
const providerId = asSubscriptionProviderId(rest[0]);
|
|
6197
6686
|
if (!providerId) return writeJsonError(res, 400, `unknown subscription provider '${rest[0] ?? ""}'`);
|
|
6198
|
-
const body = await
|
|
6687
|
+
const body = await readJsonBody4(req);
|
|
6199
6688
|
const patch = validateAccountMetadataPatch(body);
|
|
6200
6689
|
if (!patch) return writeJsonError(res, 400, "invalid account metadata patch");
|
|
6201
6690
|
const result = await deps.subscriptionTokenWriter.patchAccountMetadata(providerId, rest[1], patch);
|
|
6202
6691
|
if (!result.ok) return writeJsonError(res, 404, `account '${rest[1]}' not found`);
|
|
6203
|
-
return
|
|
6692
|
+
return writeJson4(res, 200, { ok: true });
|
|
6204
6693
|
}
|
|
6205
6694
|
if (method === "PUT" || method === "POST" || method === "DELETE") {
|
|
6206
6695
|
const providerId = asSubscriptionProviderId(rest[0]);
|
|
@@ -6209,15 +6698,15 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
6209
6698
|
}
|
|
6210
6699
|
if (method === "POST" && rest[1] === "oauth" && rest[2] === "start") {
|
|
6211
6700
|
const result = providerId === "codex" ? handleCodexOAuthStart(deps) : handleOAuthStart(providerId, deps);
|
|
6212
|
-
return
|
|
6701
|
+
return writeJson4(res, result.status, result.body);
|
|
6213
6702
|
}
|
|
6214
6703
|
if (method === "POST" && rest[1] === "oauth" && rest[2] === "complete") {
|
|
6215
|
-
const body2 = await
|
|
6704
|
+
const body2 = await readJsonBody4(req);
|
|
6216
6705
|
const result = await handleOAuthComplete(providerId, body2, deps);
|
|
6217
|
-
return
|
|
6706
|
+
return writeJson4(res, result.status, result.body);
|
|
6218
6707
|
}
|
|
6219
6708
|
if (method === "POST" && rest[1] === "accounts") {
|
|
6220
|
-
const body2 = await
|
|
6709
|
+
const body2 = await readJsonBody4(req);
|
|
6221
6710
|
const block = validateTokenBody(providerId, body2);
|
|
6222
6711
|
if (!block) {
|
|
6223
6712
|
return writeJsonError(res, 400, `malformed token body for provider '${providerId}'`);
|
|
@@ -6225,20 +6714,20 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
6225
6714
|
const label = typeof body2["label"] === "string" && body2["label"].trim() ? body2["label"].trim() : void 0;
|
|
6226
6715
|
await deps.subscriptionAccountAppender.appendProviderAccount(providerId, block, label);
|
|
6227
6716
|
const status2 = await statusEntryFor(deps.subscriptionAccounts, providerId);
|
|
6228
|
-
return
|
|
6717
|
+
return writeJson4(res, 200, status2 ? { account: status2 } : { ok: true });
|
|
6229
6718
|
}
|
|
6230
6719
|
if (method === "POST" && rest[1] === "import-external") {
|
|
6231
6720
|
if (providerId !== "claude" && providerId !== "codex") {
|
|
6232
6721
|
return writeJsonError(res, 400, `provider '${providerId}' has no external CLI store`);
|
|
6233
6722
|
}
|
|
6234
|
-
const body2 = await
|
|
6723
|
+
const body2 = await readJsonBody4(req);
|
|
6235
6724
|
const label = typeof body2["label"] === "string" && body2["label"].trim() ? body2["label"].trim() : void 0;
|
|
6236
6725
|
const result = await deps.subscriptionTokenWriter.importExternalCliAccount(providerId, label);
|
|
6237
6726
|
if (!result.ok) {
|
|
6238
6727
|
return writeJsonError(res, 409, `no usable external ${providerId} CLI credential found`);
|
|
6239
6728
|
}
|
|
6240
6729
|
const status2 = await statusEntryFor(deps.subscriptionAccounts, providerId);
|
|
6241
|
-
return
|
|
6730
|
+
return writeJson4(res, 200, {
|
|
6242
6731
|
ok: true,
|
|
6243
6732
|
account: status2 ?? void 0,
|
|
6244
6733
|
nativeCredentialMode: result.nativeCredentialMode,
|
|
@@ -6253,7 +6742,7 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
6253
6742
|
const writer2 = deps.subscriptionTokenWriter;
|
|
6254
6743
|
const ok = providerId === "claude" ? await writer2.refreshClaudeToken() : providerId === "codex" ? await writer2.refreshCodexToken() : await writer2.refreshGeminiToken();
|
|
6255
6744
|
const status2 = await statusEntryFor(deps.subscriptionAccounts, providerId);
|
|
6256
|
-
return
|
|
6745
|
+
return writeJson4(res, 200, { ok, account: status2 ?? void 0 });
|
|
6257
6746
|
}
|
|
6258
6747
|
if (method === "POST" && rest.length === 3 && rest[2] === "test") {
|
|
6259
6748
|
const accountId = rest[1];
|
|
@@ -6263,7 +6752,7 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
6263
6752
|
return writeJsonError(res, 404, `account '${accountId}' not found`);
|
|
6264
6753
|
}
|
|
6265
6754
|
const result = await deps.accountProbeService.testAccountConnection(providerId, accountId);
|
|
6266
|
-
return
|
|
6755
|
+
return writeJson4(res, 200, {
|
|
6267
6756
|
ok: result.ok,
|
|
6268
6757
|
marked: result.marked,
|
|
6269
6758
|
tier: result.tier,
|
|
@@ -6272,15 +6761,15 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
6272
6761
|
}
|
|
6273
6762
|
if (method === "POST" && rest[2] === "label") {
|
|
6274
6763
|
const accountId = rest[1];
|
|
6275
|
-
const body2 = await
|
|
6764
|
+
const body2 = await readJsonBody4(req);
|
|
6276
6765
|
const label = typeof body2["label"] === "string" ? body2["label"].trim() : "";
|
|
6277
6766
|
const result = await deps.subscriptionTokenWriter.renameAccount(providerId, accountId, label);
|
|
6278
6767
|
if (!result.ok) return writeJsonError(res, 404, `account '${accountId}' not found`);
|
|
6279
|
-
return
|
|
6768
|
+
return writeJson4(res, 200, { ok: true });
|
|
6280
6769
|
}
|
|
6281
6770
|
if (method === "POST" && rest[2] === "priority") {
|
|
6282
6771
|
const accountId = rest[1];
|
|
6283
|
-
const body2 = await
|
|
6772
|
+
const body2 = await readJsonBody4(req);
|
|
6284
6773
|
const raw = body2["priority"];
|
|
6285
6774
|
const priority = typeof raw === "number" ? raw : Number(raw);
|
|
6286
6775
|
if (!Number.isFinite(priority)) {
|
|
@@ -6288,76 +6777,76 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
6288
6777
|
}
|
|
6289
6778
|
const result = await deps.subscriptionTokenWriter.setAccountPriority(providerId, accountId, priority);
|
|
6290
6779
|
if (!result.ok) return writeJsonError(res, 404, `account '${accountId}' not found`);
|
|
6291
|
-
return
|
|
6780
|
+
return writeJson4(res, 200, { ok: true });
|
|
6292
6781
|
}
|
|
6293
6782
|
if (method === "POST" && rest[2] === "proxy") {
|
|
6294
6783
|
const accountId = rest[1];
|
|
6295
|
-
const body2 = await
|
|
6784
|
+
const body2 = await readJsonBody4(req);
|
|
6296
6785
|
const rawProxy = body2["proxy"];
|
|
6297
6786
|
let proxy;
|
|
6298
6787
|
if (rawProxy !== null && rawProxy !== void 0) {
|
|
6299
|
-
proxy = (0,
|
|
6788
|
+
proxy = (0, import_outbound_api5.normalizeProxyConfig)(rawProxy);
|
|
6300
6789
|
if (!proxy) return writeJsonError(res, 400, "invalid proxy config");
|
|
6301
6790
|
}
|
|
6302
6791
|
const result = await deps.subscriptionTokenWriter.setAccountProxy(providerId, accountId, proxy);
|
|
6303
6792
|
if (!result.ok) return writeJsonError(res, 404, `account '${accountId}' not found`);
|
|
6304
|
-
return
|
|
6793
|
+
return writeJson4(res, 200, { ok: true });
|
|
6305
6794
|
}
|
|
6306
6795
|
if (method === "POST" && rest[2] === "supported-models") {
|
|
6307
6796
|
const accountId = rest[1];
|
|
6308
|
-
const body2 = await
|
|
6797
|
+
const body2 = await readJsonBody4(req);
|
|
6309
6798
|
const parsed = validateSupportedModelsBody(body2["supportedModels"]);
|
|
6310
6799
|
if (!parsed.ok) return writeJsonError(res, 400, "invalid supportedModels");
|
|
6311
6800
|
const result = await deps.subscriptionTokenWriter.setAccountSupportedModels(providerId, accountId, parsed.value);
|
|
6312
6801
|
if (!result.ok) return writeJsonError(res, 404, `account '${accountId}' not found`);
|
|
6313
|
-
return
|
|
6802
|
+
return writeJson4(res, 200, { ok: true });
|
|
6314
6803
|
}
|
|
6315
6804
|
if (method === "PUT" && rest[1] === "active") {
|
|
6316
|
-
const body2 = await
|
|
6805
|
+
const body2 = await readJsonBody4(req);
|
|
6317
6806
|
const id = typeof body2["id"] === "string" ? body2["id"] : "";
|
|
6318
6807
|
if (!id) return writeJsonError(res, 400, "active switch requires { id }");
|
|
6319
6808
|
const result = await deps.subscriptionTokenWriter.setActiveAccount(providerId, id);
|
|
6320
6809
|
if (!result.ok) return writeJsonError(res, 404, `account '${id}' not found`);
|
|
6321
|
-
return
|
|
6810
|
+
return writeJson4(res, 200, { ok: true });
|
|
6322
6811
|
}
|
|
6323
6812
|
if (method === "DELETE" && rest.length === 2) {
|
|
6324
6813
|
const accountId = rest[1];
|
|
6325
6814
|
const result = await deps.subscriptionTokenWriter.removeAccount(providerId, accountId);
|
|
6326
6815
|
if (!result.removed) return writeJsonError(res, 404, `account '${accountId}' not found`);
|
|
6327
6816
|
deps.accountAllowanceService?.removeAccountSnapshot?.(providerId, accountId);
|
|
6328
|
-
return
|
|
6817
|
+
return writeJson4(res, 200, { ok: true });
|
|
6329
6818
|
}
|
|
6330
6819
|
if (method === "DELETE" && rest.length === 1) {
|
|
6331
6820
|
await deps.subscriptionTokenWriter.clearProvider(providerId);
|
|
6332
6821
|
deps.accountAllowanceService?.removeProviderSnapshots?.(providerId);
|
|
6333
|
-
return
|
|
6822
|
+
return writeJson4(res, 200, { ok: true });
|
|
6334
6823
|
}
|
|
6335
6824
|
if (method === "DELETE") {
|
|
6336
6825
|
return writeJsonError(res, 405, "method DELETE not allowed on this accounts path");
|
|
6337
6826
|
}
|
|
6338
|
-
const body = await
|
|
6827
|
+
const body = await readJsonBody4(req);
|
|
6339
6828
|
const config = validateTokenBody(providerId, body);
|
|
6340
6829
|
if (!config) {
|
|
6341
6830
|
return writeJsonError(res, 400, `malformed token body for provider '${providerId}'`);
|
|
6342
6831
|
}
|
|
6343
6832
|
await deps.subscriptionTokenWriter.writeProviderTokens(providerId, config);
|
|
6344
6833
|
const status = await statusEntryFor(deps.subscriptionAccounts, providerId);
|
|
6345
|
-
return
|
|
6834
|
+
return writeJson4(res, 200, status ? { account: status } : { ok: true });
|
|
6346
6835
|
}
|
|
6347
6836
|
return writeJsonError(res, 405, `method ${method} not allowed on accounts`);
|
|
6348
6837
|
}
|
|
6349
6838
|
async function handleCli(req, res, method, rest, deps) {
|
|
6350
6839
|
if (method === "GET" && rest.length === 0) {
|
|
6351
6840
|
const result = handleCliList(process.platform, deps.cliPathProbe);
|
|
6352
|
-
return
|
|
6841
|
+
return writeJson4(res, result.status, result.body);
|
|
6353
6842
|
}
|
|
6354
6843
|
if (method === "GET" && rest[0] === "sessions") {
|
|
6355
6844
|
const result = handleCliSessions();
|
|
6356
|
-
return
|
|
6845
|
+
return writeJson4(res, result.status, result.body);
|
|
6357
6846
|
}
|
|
6358
6847
|
if (method === "DELETE" && rest[0] === "sessions" && rest[1]) {
|
|
6359
6848
|
const result = handleCliStop(rest[1]);
|
|
6360
|
-
return
|
|
6849
|
+
return writeJson4(res, result.status, result.body);
|
|
6361
6850
|
}
|
|
6362
6851
|
if (method === "POST" && rest[1] === "install") {
|
|
6363
6852
|
const cli = rest[0];
|
|
@@ -6365,14 +6854,14 @@ async function handleCli(req, res, method, rest, deps) {
|
|
|
6365
6854
|
return writeJsonError(res, 400, `unknown cli '${cli ?? ""}'`);
|
|
6366
6855
|
}
|
|
6367
6856
|
const result = await handleCliInstall(cli, deps.cliCommandRunner);
|
|
6368
|
-
return
|
|
6857
|
+
return writeJson4(res, result.status, result.body);
|
|
6369
6858
|
}
|
|
6370
6859
|
if (method === "POST" && rest[1] === "launch") {
|
|
6371
6860
|
const cli = rest[0];
|
|
6372
6861
|
if (!isLaunchCliId(cli)) {
|
|
6373
6862
|
return writeJsonError(res, 400, `unknown cli '${cli ?? ""}'`);
|
|
6374
6863
|
}
|
|
6375
|
-
const body = await
|
|
6864
|
+
const body = await readJsonBody4(req);
|
|
6376
6865
|
const providers = loadConfig(deps.configPath).providers ?? [];
|
|
6377
6866
|
const result = await handleCliLaunch(cli, body, {
|
|
6378
6867
|
llmConfig: deps.llmConfig,
|
|
@@ -6381,7 +6870,7 @@ async function handleCli(req, res, method, rest, deps) {
|
|
|
6381
6870
|
opener: deps.cliTerminalOpener,
|
|
6382
6871
|
probe: deps.cliPathProbe
|
|
6383
6872
|
});
|
|
6384
|
-
return
|
|
6873
|
+
return writeJson4(res, result.status, result.body);
|
|
6385
6874
|
}
|
|
6386
6875
|
return writeJsonError(res, 405, `method ${method} not allowed on cli`);
|
|
6387
6876
|
}
|
|
@@ -6391,52 +6880,52 @@ async function handleIntegrations(req, res, method, rest, deps) {
|
|
|
6391
6880
|
const manager = factory();
|
|
6392
6881
|
try {
|
|
6393
6882
|
if (method === "GET" && rest.length === 0) {
|
|
6394
|
-
return
|
|
6883
|
+
return writeJson4(res, 200, {
|
|
6395
6884
|
integrations: await manager.listStatus(),
|
|
6396
6885
|
gateway: deps.outboundApiServer.getStatus()
|
|
6397
6886
|
});
|
|
6398
6887
|
}
|
|
6399
6888
|
if (method === "POST" && rest.length === 1 && rest[0] === "rotate") {
|
|
6400
6889
|
await manager.rotateGatewayKey();
|
|
6401
|
-
return
|
|
6890
|
+
return writeJson4(res, 200, { ok: true, integrations: await manager.listStatus() });
|
|
6402
6891
|
}
|
|
6403
6892
|
const client = rest[0];
|
|
6404
6893
|
if (!isIntegrationClient(client)) {
|
|
6405
6894
|
return writeJsonError(res, 400, `unknown integration client '${client ?? ""}'`);
|
|
6406
6895
|
}
|
|
6407
6896
|
if (method === "POST" && rest[1] === "key") {
|
|
6408
|
-
const body = await
|
|
6897
|
+
const body = await readJsonBody4(req);
|
|
6409
6898
|
if (Object.keys(body).length !== 1 || typeof body.keyId !== "string" || !body.keyId.trim()) {
|
|
6410
6899
|
return writeJsonError(res, 400, "body must contain a non-empty keyId string");
|
|
6411
6900
|
}
|
|
6412
6901
|
const status = await manager.bindIntegrationKey(client, body.keyId.trim());
|
|
6413
|
-
return
|
|
6902
|
+
return writeJson4(res, 200, { integration: status });
|
|
6414
6903
|
}
|
|
6415
6904
|
if (method === "POST" && rest[1] === "plan") {
|
|
6416
|
-
const body = await
|
|
6905
|
+
const body = await readJsonBody4(req);
|
|
6417
6906
|
const configPath = body.configPath;
|
|
6418
6907
|
if (configPath !== void 0 && typeof configPath !== "string") {
|
|
6419
6908
|
return writeJsonError(res, 400, "configPath must be a string");
|
|
6420
6909
|
}
|
|
6421
6910
|
const plan = await manager.plan(client, configPath);
|
|
6422
|
-
return
|
|
6911
|
+
return writeJson4(res, 200, { plan });
|
|
6423
6912
|
}
|
|
6424
6913
|
if (method === "POST" && (rest[1] === "install" || rest[1] === "apply")) {
|
|
6425
|
-
const body = await
|
|
6914
|
+
const body = await readJsonBody4(req);
|
|
6426
6915
|
const configPath = body.configPath;
|
|
6427
6916
|
if (configPath !== void 0 && typeof configPath !== "string") {
|
|
6428
6917
|
return writeJsonError(res, 400, "configPath must be a string");
|
|
6429
6918
|
}
|
|
6430
6919
|
const status = await manager.install(client, configPath);
|
|
6431
|
-
return
|
|
6920
|
+
return writeJson4(res, 200, { integration: status });
|
|
6432
6921
|
}
|
|
6433
6922
|
if (method === "POST" && rest[1] === "repair") {
|
|
6434
6923
|
const status = await manager.repair(client);
|
|
6435
|
-
return
|
|
6924
|
+
return writeJson4(res, 200, { integration: status });
|
|
6436
6925
|
}
|
|
6437
6926
|
if (method === "DELETE" && rest.length === 1 || method === "POST" && rest[1] === "remove") {
|
|
6438
6927
|
const status = await manager.remove(client);
|
|
6439
|
-
return
|
|
6928
|
+
return writeJson4(res, 200, { integration: status });
|
|
6440
6929
|
}
|
|
6441
6930
|
return writeJsonError(res, 405, `method ${method} not allowed on integrations`);
|
|
6442
6931
|
} catch (error) {
|
|
@@ -6579,8 +7068,8 @@ async function handleImages(res, method, rest, deps) {
|
|
|
6579
7068
|
}
|
|
6580
7069
|
const reader = deps.imageRuntimeStatus;
|
|
6581
7070
|
if (!reader) return writeJsonError(res, 501, "Images runtime status is not available");
|
|
6582
|
-
const serverConfig = await (0,
|
|
6583
|
-
const images = serverConfig.images ??
|
|
7071
|
+
const serverConfig = await (0, import_outbound_api5.loadServerConfig)(deps.settingsStore);
|
|
7072
|
+
const images = serverConfig.images ?? import_outbound_api5.DEFAULT_IMAGES_SERVER_CONFIG;
|
|
6584
7073
|
const lifecycle = reader.status();
|
|
6585
7074
|
const capability = await reader.inspectCapability(IMAGE_ADMIN_STATUS_TENANT);
|
|
6586
7075
|
const resources = safeRuntimeResources(reader.resourceStatus());
|
|
@@ -6599,7 +7088,7 @@ async function handleImages(res, method, rest, deps) {
|
|
|
6599
7088
|
httpLeases: safeStatusCount(generation.httpLeases),
|
|
6600
7089
|
hostedLeases: safeStatusCount(generation.hostedLeases)
|
|
6601
7090
|
}));
|
|
6602
|
-
return
|
|
7091
|
+
return writeJson4(res, 200, {
|
|
6603
7092
|
configured: {
|
|
6604
7093
|
enabled: images.enabled,
|
|
6605
7094
|
provider: images.provider,
|
|
@@ -6627,11 +7116,11 @@ async function handleImages(res, method, rest, deps) {
|
|
|
6627
7116
|
async function handleStatus(res, method, deps) {
|
|
6628
7117
|
if (method !== "GET") return writeJsonError(res, 405, `method ${method} not allowed on status`);
|
|
6629
7118
|
const status = deps.outboundApiServer.getStatus();
|
|
6630
|
-
const serverConfig = await (0,
|
|
7119
|
+
const serverConfig = await (0, import_outbound_api5.loadServerConfig)(deps.settingsStore);
|
|
6631
7120
|
const endpoints = ["chat", "responses", "messages", "gemini"].map((endpoint) => {
|
|
6632
|
-
const routes = (serverConfig.bindings ?? []).filter((binding) => binding.enabled && binding.endpoint === endpoint).map((binding) => (0,
|
|
7121
|
+
const routes = (serverConfig.bindings ?? []).filter((binding) => binding.enabled && binding.endpoint === endpoint).map((binding) => (0, import_outbound_api5.gatewayBindingToEndpointConfig)(binding));
|
|
6633
7122
|
const useSubscription = routes.some((route) => route.useSubscription);
|
|
6634
|
-
if ((0,
|
|
7123
|
+
if ((0, import_outbound_api5.isKindMappedEndpoint)(endpoint)) {
|
|
6635
7124
|
const kinds = {};
|
|
6636
7125
|
for (const route of routes) {
|
|
6637
7126
|
for (const [kind, ref] of Object.entries(route.modelMap ?? {})) {
|
|
@@ -6665,14 +7154,14 @@ async function handleStatus(res, method, deps) {
|
|
|
6665
7154
|
})() : void 0;
|
|
6666
7155
|
if (status.running) {
|
|
6667
7156
|
const queueStatus = deps.outboundApiServer.getQueueStatus();
|
|
6668
|
-
return
|
|
7157
|
+
return writeJson4(res, 200, {
|
|
6669
7158
|
...status,
|
|
6670
7159
|
endpoints,
|
|
6671
7160
|
queueStatus,
|
|
6672
7161
|
...imageRuntime ? { imageRuntime } : {}
|
|
6673
7162
|
});
|
|
6674
7163
|
}
|
|
6675
|
-
return
|
|
7164
|
+
return writeJson4(res, 200, {
|
|
6676
7165
|
...status,
|
|
6677
7166
|
endpoints,
|
|
6678
7167
|
...imageRuntime ? { imageRuntime } : {}
|
|
@@ -6696,18 +7185,18 @@ function resolvePlaygroundPath(endpoint, body) {
|
|
|
6696
7185
|
}
|
|
6697
7186
|
async function handlePlayground(req, res, method, deps) {
|
|
6698
7187
|
if (method !== "POST") return writeJsonError(res, 405, `method ${method} not allowed on playground`);
|
|
6699
|
-
const body = await
|
|
7188
|
+
const body = await readJsonBody4(req);
|
|
6700
7189
|
const endpoint = typeof body["endpoint"] === "string" ? body["endpoint"] : "";
|
|
6701
7190
|
const key = typeof body["key"] === "string" ? body["key"] : "";
|
|
6702
7191
|
const payload = body["body"];
|
|
6703
7192
|
const status = deps.outboundApiServer.getStatus();
|
|
6704
7193
|
if (!status.running || !status.port) return writeJsonError(res, 503, "outbound server not running");
|
|
6705
|
-
const path2 = resolvePlaygroundPath(endpoint,
|
|
7194
|
+
const path2 = resolvePlaygroundPath(endpoint, isRecord3(payload) ? payload : {});
|
|
6706
7195
|
if (!path2) return writeJsonError(res, 400, `unknown endpoint '${endpoint}'`);
|
|
6707
7196
|
const upstreamBody = typeof payload === "string" ? payload : JSON.stringify(payload ?? {});
|
|
6708
7197
|
await proxyToOutbound(res, status.port, path2, key, upstreamBody);
|
|
6709
7198
|
}
|
|
6710
|
-
function
|
|
7199
|
+
function isRecord3(v) {
|
|
6711
7200
|
return !!v && typeof v === "object" && !Array.isArray(v);
|
|
6712
7201
|
}
|
|
6713
7202
|
function proxyToOutbound(res, outboundPort, path2, key, body) {
|
|
@@ -6843,7 +7332,7 @@ async function handleUiStatic(req, res, urlPath, uiDist) {
|
|
|
6843
7332
|
}
|
|
6844
7333
|
|
|
6845
7334
|
// src/admin/version.ts
|
|
6846
|
-
var DAEMON_VERSION = true ? "0.2.
|
|
7335
|
+
var DAEMON_VERSION = true ? "0.2.1" : "0.0.0-dev";
|
|
6847
7336
|
|
|
6848
7337
|
// src/admin/AdminServer.ts
|
|
6849
7338
|
var LOOPBACK_ADDR = "127.0.0.1";
|
|
@@ -7296,14 +7785,14 @@ function defaultBillingDir(configPath) {
|
|
|
7296
7785
|
|
|
7297
7786
|
// src/image-generation/ImageDoctorService.ts
|
|
7298
7787
|
var import_image_generation = require("@omnicross/core/image-generation");
|
|
7299
|
-
var
|
|
7788
|
+
var import_outbound_api7 = require("@omnicross/core/outbound-api");
|
|
7300
7789
|
var import_subscriptions3 = require("@omnicross/subscriptions");
|
|
7301
7790
|
|
|
7302
7791
|
// src/image-generation/FileCodexImageCapabilityEvidenceSource.ts
|
|
7303
7792
|
var import_node_crypto13 = require("crypto");
|
|
7304
7793
|
var import_node_fs10 = require("fs");
|
|
7305
7794
|
var import_node_path11 = require("path");
|
|
7306
|
-
var
|
|
7795
|
+
var import_outbound_api6 = require("@omnicross/core/outbound-api");
|
|
7307
7796
|
|
|
7308
7797
|
// src/image-generation/imageTenantHmac.ts
|
|
7309
7798
|
var import_node_crypto12 = require("crypto");
|
|
@@ -7376,7 +7865,7 @@ var ACCOUNT_DOMAIN = Buffer.from("omnicross:codex-image-evidence:account:v1\0",
|
|
|
7376
7865
|
var ACCOUNT_KEY = /^[a-f0-9]{64}$/u;
|
|
7377
7866
|
var SIZE = /^(?:auto|[1-9][0-9]{1,4}x[1-9][0-9]{1,4})$/u;
|
|
7378
7867
|
var MAX_MANIFEST_BYTES = 4 * 1024 * 1024;
|
|
7379
|
-
var PHYSICAL_RETENTION_TTL_MS =
|
|
7868
|
+
var PHYSICAL_RETENTION_TTL_MS = import_outbound_api6.IMAGE_SERVER_HARD_CEILINGS.evidenceTtlMs;
|
|
7380
7869
|
function exactKeys(value, allowed) {
|
|
7381
7870
|
const allow = new Set(allowed);
|
|
7382
7871
|
return Object.keys(value).every((key) => allow.has(key));
|
|
@@ -7750,7 +8239,7 @@ function createImageDoctorService(options) {
|
|
|
7750
8239
|
const activePaths = () => options.storageCatalog.active().resolver;
|
|
7751
8240
|
return Object.freeze({
|
|
7752
8241
|
inspectLocal: async (config) => {
|
|
7753
|
-
const configErrors = (0,
|
|
8242
|
+
const configErrors = (0, import_outbound_api7.validateImagesServerConfig)(config);
|
|
7754
8243
|
let verifiedAreas = 0;
|
|
7755
8244
|
try {
|
|
7756
8245
|
const paths = activePaths();
|
|
@@ -7792,7 +8281,7 @@ function createImageDoctorService(options) {
|
|
|
7792
8281
|
continue;
|
|
7793
8282
|
}
|
|
7794
8283
|
try {
|
|
7795
|
-
const permissions = (0,
|
|
8284
|
+
const permissions = (0, import_outbound_api7.validateOutboundPermissions)(row.allowedEndpoints);
|
|
7796
8285
|
if (permissions.includes("images")) imagesAuthorizedRows += 1;
|
|
7797
8286
|
} catch {
|
|
7798
8287
|
invalidRows += 1;
|
|
@@ -8096,7 +8585,7 @@ var ImageCleanupService = class {
|
|
|
8096
8585
|
// src/image-generation/ImageRuntimeGenerationFactory.ts
|
|
8097
8586
|
var import_node_crypto16 = require("crypto");
|
|
8098
8587
|
var import_image_generation5 = require("@omnicross/core/image-generation");
|
|
8099
|
-
var
|
|
8588
|
+
var import_outbound_api8 = require("@omnicross/core/outbound-api");
|
|
8100
8589
|
var import_subscriptions4 = require("@omnicross/subscriptions");
|
|
8101
8590
|
|
|
8102
8591
|
// src/image-generation/ImageApiRuntimeResolver.ts
|
|
@@ -8564,7 +9053,7 @@ function createImageRuntimeGeneration(options) {
|
|
|
8564
9053
|
throw new TypeError("image runtime generation id is invalid");
|
|
8565
9054
|
}
|
|
8566
9055
|
const config = snapshotConfig(options.config);
|
|
8567
|
-
const configErrors = (0,
|
|
9056
|
+
const configErrors = (0, import_outbound_api8.validateImagesServerConfig)(config);
|
|
8568
9057
|
if (configErrors.length > 0) {
|
|
8569
9058
|
throw new TypeError(`image runtime configuration is invalid: ${configErrors[0]}`);
|
|
8570
9059
|
}
|
|
@@ -12156,7 +12645,7 @@ function safeStringify(value) {
|
|
|
12156
12645
|
var import_node_crypto20 = require("crypto");
|
|
12157
12646
|
var import_node_fs16 = require("fs");
|
|
12158
12647
|
var import_node_path17 = require("path");
|
|
12159
|
-
var
|
|
12648
|
+
var import_outbound_api9 = require("@omnicross/core/outbound-api");
|
|
12160
12649
|
function atomicReplaceDocument(targetPath, contents) {
|
|
12161
12650
|
const tempPath = (0, import_node_path17.join)(
|
|
12162
12651
|
(0, import_node_path17.dirname)(targetPath),
|
|
@@ -12204,13 +12693,13 @@ var JsonApiServerSettingsStore = class {
|
|
|
12204
12693
|
box;
|
|
12205
12694
|
atomicReplace;
|
|
12206
12695
|
async get(key) {
|
|
12207
|
-
if (key !==
|
|
12696
|
+
if (key !== import_outbound_api9.OUTBOUND_API_SERVER_CONFIG_KEY) return void 0;
|
|
12208
12697
|
const file = this.readFile();
|
|
12209
12698
|
if (file.server === void 0) return void 0;
|
|
12210
12699
|
return this.decryptSecrets(file.server);
|
|
12211
12700
|
}
|
|
12212
12701
|
async set(key, value) {
|
|
12213
|
-
if (key !==
|
|
12702
|
+
if (key !== import_outbound_api9.OUTBOUND_API_SERVER_CONFIG_KEY) return;
|
|
12214
12703
|
const file = this.readFile();
|
|
12215
12704
|
file.server = this.encryptSecrets(value);
|
|
12216
12705
|
this.atomicReplace(
|
|
@@ -17102,7 +17591,7 @@ function buildDaemon(config, paths) {
|
|
|
17102
17591
|
);
|
|
17103
17592
|
(0, import_AccountAllowanceStore4.setSharedAccountAllowanceStore)(accountAllowanceStore);
|
|
17104
17593
|
(0, import_AccountAllowanceScheduling5.getSharedAccountAllowanceScheduling)().configure(
|
|
17105
|
-
(0,
|
|
17594
|
+
(0, import_outbound_api10.normalizeServerConfig)(decryptedConfig.server).allowanceScheduling
|
|
17106
17595
|
);
|
|
17107
17596
|
const llmConfig = new ConfigFileProviderConfigSource(decryptedConfig);
|
|
17108
17597
|
const keyDb = new JsonOutboundKeyDb(paths.keysPath, secretBox3);
|
|
@@ -17123,7 +17612,7 @@ function buildDaemon(config, paths) {
|
|
|
17123
17612
|
logger
|
|
17124
17613
|
);
|
|
17125
17614
|
claudeAllowanceRefreshScheduler.configure(
|
|
17126
|
-
(0,
|
|
17615
|
+
(0, import_outbound_api10.normalizeServerConfig)(decryptedConfig.server).allowanceScheduling
|
|
17127
17616
|
);
|
|
17128
17617
|
const subscriptionAccounts = new import_subscriptions6.SubscriptionAccountService(credentialStore);
|
|
17129
17618
|
(0, import_subscriptions6.setSubscriptionAccountService)(subscriptionAccounts);
|
|
@@ -17168,13 +17657,21 @@ function buildDaemon(config, paths) {
|
|
|
17168
17657
|
defaultUsageEventsPath(paths.configPath),
|
|
17169
17658
|
async (providerId, model) => await pricingEngine.getEntry(providerId, model) !== null
|
|
17170
17659
|
);
|
|
17171
|
-
const keySpendTracker = new
|
|
17660
|
+
const keySpendTracker = new import_outbound_api11.KeySpendTracker(usageEventStore);
|
|
17172
17661
|
const usageThroughput = (0, import_usage2.getSharedUsageThroughputTracker)();
|
|
17173
17662
|
const usageRecorder = new import_usage2.UsageRecorder(usageEventStore, pricingEngine, logger, {
|
|
17174
17663
|
onRecord: (apiKeyId, costUsd, at) => keySpendTracker.add(apiKeyId, costUsd, at),
|
|
17175
17664
|
onEvent: (row, at) => usageThroughput.record(row, at)
|
|
17176
17665
|
});
|
|
17177
|
-
const initialImagesConfig = (0,
|
|
17666
|
+
const initialImagesConfig = (0, import_outbound_api10.normalizeServerConfig)(decryptedConfig.server).images;
|
|
17667
|
+
const initialSearchConfig = (0, import_outbound_api10.normalizeServerConfig)(decryptedConfig.server).search;
|
|
17668
|
+
for (const issue of (0, import_outbound_api10.validateSearchServerConfig)(
|
|
17669
|
+
decryptedConfig.server?.search
|
|
17670
|
+
)) {
|
|
17671
|
+
logger.warn("[search] ignoring invalid config: " + issue);
|
|
17672
|
+
}
|
|
17673
|
+
const searchRuntime = buildSearchRuntime(initialSearchConfig, { logger });
|
|
17674
|
+
const searchFrontendModes = initialSearchConfig.modes;
|
|
17178
17675
|
const imageObservability = new ImageObservability();
|
|
17179
17676
|
const imageRuntimeObservability = Object.freeze({
|
|
17180
17677
|
telemetrySink: imageObservability.telemetrySink,
|
|
@@ -17304,7 +17801,9 @@ function buildDaemon(config, paths) {
|
|
|
17304
17801
|
apiKeyPool,
|
|
17305
17802
|
usageRecorder,
|
|
17306
17803
|
openAIOperationRegistry,
|
|
17307
|
-
responsesHostedImageIngress
|
|
17804
|
+
responsesHostedImageIngress,
|
|
17805
|
+
searchRuntime,
|
|
17806
|
+
searchFrontendModes
|
|
17308
17807
|
});
|
|
17309
17808
|
if (providerProxy.getDeps().openAIOperationRegistry !== openAIOperationRegistry) {
|
|
17310
17809
|
throw new Error(
|
|
@@ -17338,7 +17837,7 @@ function buildDaemon(config, paths) {
|
|
|
17338
17837
|
credentialStore,
|
|
17339
17838
|
(0, import_SubscriptionAccountHealth4.getSharedAccountHealth)(),
|
|
17340
17839
|
logger,
|
|
17341
|
-
|
|
17840
|
+
import_outbound_api10.DEFAULT_ACCOUNT_PROBE
|
|
17342
17841
|
);
|
|
17343
17842
|
const getHealthReport = () => buildHealthReport({
|
|
17344
17843
|
version: DAEMON_VERSION,
|
|
@@ -17355,7 +17854,7 @@ function buildDaemon(config, paths) {
|
|
|
17355
17854
|
// `/health` body stays byte-identical (zero regression).
|
|
17356
17855
|
subscriptionAccountsHealthy: () => accountHealthProbeScheduler.enabled ? accountHealthProbeScheduler.probedAccountsHealthy() : void 0
|
|
17357
17856
|
});
|
|
17358
|
-
const outboundApiServer = (0,
|
|
17857
|
+
const outboundApiServer = (0, import_outbound_api10.getOutboundApiServer)({
|
|
17359
17858
|
db: keyDb,
|
|
17360
17859
|
// voucher-redemption #9: the key-authenticated `POST /redeem` endpoint redeems
|
|
17361
17860
|
// cards against the presenting key (gated on `voucher.enabled`).
|
|
@@ -17369,7 +17868,9 @@ function buildDaemon(config, paths) {
|
|
|
17369
17868
|
keySpendTracker,
|
|
17370
17869
|
// configurable-logging: route the server's OWN lifecycle + relay dispatch-error
|
|
17371
17870
|
// lines through the injected logger (honors level/format/file sink).
|
|
17372
|
-
logger
|
|
17871
|
+
logger,
|
|
17872
|
+
// plan 阶段5: the same instance the managed frontends hold.
|
|
17873
|
+
searchRuntime
|
|
17373
17874
|
});
|
|
17374
17875
|
const auditDir = defaultAuditDir(paths.configPath);
|
|
17375
17876
|
const billingDir = defaultBillingDir(paths.configPath);
|
|
@@ -17391,6 +17892,10 @@ function buildDaemon(config, paths) {
|
|
|
17391
17892
|
}),
|
|
17392
17893
|
routeLeaseManager,
|
|
17393
17894
|
subscriptionAccounts,
|
|
17895
|
+
// search-settings-ui D3: the daemon's ONE search runtime + its
|
|
17896
|
+
// bootstrap-captured modes, for `GET /admin/api/search/diagnostics` and
|
|
17897
|
+
// `POST /admin/api/search/test` (501 when a light embedder omits it).
|
|
17898
|
+
searchStatus: { runtime: searchRuntime, modes: searchFrontendModes },
|
|
17394
17899
|
accountAllowanceService,
|
|
17395
17900
|
allowanceRefreshScheduler: claudeAllowanceRefreshScheduler,
|
|
17396
17901
|
accountProbeService: accountHealthProbeScheduler,
|
|
@@ -17440,7 +17945,7 @@ function buildDaemon(config, paths) {
|
|
|
17440
17945
|
cliCommandRunner: paths.cliCommandRunner,
|
|
17441
17946
|
integrationManagerFactory: () => {
|
|
17442
17947
|
const live = outboundApiServer.getStatus();
|
|
17443
|
-
const port = live.port || decryptedConfig.server?.port ||
|
|
17948
|
+
const port = live.port || decryptedConfig.server?.port || import_outbound_api10.DEFAULT_OUTBOUND_PORT;
|
|
17444
17949
|
return new IntegrationManager({
|
|
17445
17950
|
configPath: paths.configPath,
|
|
17446
17951
|
gatewayBaseUrl: live.loopbackUrl ?? `http://127.0.0.1:${port}`,
|
|
@@ -17527,6 +18032,8 @@ function buildDaemon(config, paths) {
|
|
|
17527
18032
|
keyDb,
|
|
17528
18033
|
settingsStore,
|
|
17529
18034
|
openAIOperationRegistry,
|
|
18035
|
+
searchRuntime,
|
|
18036
|
+
searchFrontendModes,
|
|
17530
18037
|
imageRuntimeManager,
|
|
17531
18038
|
imageObservability,
|
|
17532
18039
|
imageCleanupService,
|
|
@@ -17562,7 +18069,7 @@ function buildDaemon(config, paths) {
|
|
|
17562
18069
|
function resetDaemonSingletonsForTests() {
|
|
17563
18070
|
resetImageRuntimeBootstrapSession();
|
|
17564
18071
|
(0, import_provider_proxy4.__resetProviderProxyForTests)();
|
|
17565
|
-
(0,
|
|
18072
|
+
(0, import_outbound_api10.__resetOutboundApiServerForTests)();
|
|
17566
18073
|
(0, import_subscriptionRegistryPort.setSubscriptionRegistryForOutbound)(null);
|
|
17567
18074
|
(0, import_subscriptions6.setSubscriptionProviderRegistry)(null);
|
|
17568
18075
|
(0, import_subscriptions6.setSubscriptionAccountService)(null);
|