@omnicross/daemon 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +892 -219
- package/dist/cli.js +849 -160
- package/dist/index.cjs +733 -206
- package/dist/index.d.cts +69 -0
- package/dist/index.d.ts +69 -0
- package/dist/index.js +691 -153
- package/package.json +6 -6
package/dist/cli.cjs
CHANGED
|
@@ -1160,7 +1160,10 @@ ${turn.responseBody}`);
|
|
|
1160
1160
|
|
|
1161
1161
|
// src/commands/doctor.ts
|
|
1162
1162
|
var import_node_util2 = require("util");
|
|
1163
|
-
var
|
|
1163
|
+
var import_outbound_api12 = require("@omnicross/core/outbound-api");
|
|
1164
|
+
var import_api4 = require("@omnicross/core/search/api");
|
|
1165
|
+
var import_http4 = require("@omnicross/core/search/http");
|
|
1166
|
+
var import_search3 = require("@omnicross/core/search");
|
|
1164
1167
|
|
|
1165
1168
|
// src/bootstrap.ts
|
|
1166
1169
|
var import_node_fs35 = require("fs");
|
|
@@ -1170,17 +1173,17 @@ var import_billing_types = require("@omnicross/contracts/billing-types");
|
|
|
1170
1173
|
var import_core4 = require("@omnicross/core");
|
|
1171
1174
|
var import_GeminiCodeAssistProjectResolver = require("@omnicross/core/auth/GeminiCodeAssistProjectResolver");
|
|
1172
1175
|
var import_ApiKeyPoolService = require("@omnicross/core/completion/ApiKeyPoolService");
|
|
1173
|
-
var
|
|
1176
|
+
var import_outbound_api10 = require("@omnicross/core/outbound-api");
|
|
1174
1177
|
var import_subscriptionRegistryPort = require("@omnicross/core/outbound-api/subscriptionRegistryPort");
|
|
1175
1178
|
var import_SubscriptionAccountHealth4 = require("@omnicross/core/pipeline/SubscriptionAccountHealth");
|
|
1176
1179
|
var import_AccountAllowanceStore4 = require("@omnicross/core/pipeline/AccountAllowanceStore");
|
|
1177
1180
|
var import_AccountAllowanceScheduling5 = require("@omnicross/core/pipeline/AccountAllowanceScheduling");
|
|
1178
|
-
var
|
|
1181
|
+
var import_upstreamFetch9 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
1179
1182
|
var import_SubscriptionIdentityStore3 = require("@omnicross/core/provider-proxy/identity/SubscriptionIdentityStore");
|
|
1180
1183
|
var import_gemini_code_assist_resolver = require("@omnicross/core/ports/gemini-code-assist-resolver");
|
|
1181
1184
|
var import_provider_proxy4 = require("@omnicross/core/provider-proxy");
|
|
1182
1185
|
var import_cli_launcher2 = require("@omnicross/cli-launcher");
|
|
1183
|
-
var
|
|
1186
|
+
var import_outbound_api11 = require("@omnicross/core/outbound-api");
|
|
1184
1187
|
var import_usage2 = require("@omnicross/core/usage");
|
|
1185
1188
|
var import_subscriptions6 = require("@omnicross/subscriptions");
|
|
1186
1189
|
|
|
@@ -1977,11 +1980,11 @@ async function handleRouteLeaseApi(req, res, path2, deps) {
|
|
|
1977
1980
|
|
|
1978
1981
|
// src/admin/adminApi.ts
|
|
1979
1982
|
var import_node_http = __toESM(require("http"), 1);
|
|
1980
|
-
var
|
|
1983
|
+
var import_outbound_api5 = require("@omnicross/core/outbound-api");
|
|
1981
1984
|
var import_image_generation_types = require("@omnicross/contracts/image-generation-types");
|
|
1982
1985
|
var import_AccountAllowanceScheduling2 = require("@omnicross/core/pipeline/AccountAllowanceScheduling");
|
|
1983
1986
|
var import_SubscriptionAccountHealth = require("@omnicross/core/pipeline/SubscriptionAccountHealth");
|
|
1984
|
-
var
|
|
1987
|
+
var import_upstreamFetch4 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
1985
1988
|
|
|
1986
1989
|
// src/image-generation/imagesConfigValidation.ts
|
|
1987
1990
|
var import_outbound_api = require("@omnicross/core/outbound-api");
|
|
@@ -4502,6 +4505,502 @@ async function handleDashboard(deps) {
|
|
|
4502
4505
|
return { status: 200, body: summary };
|
|
4503
4506
|
}
|
|
4504
4507
|
|
|
4508
|
+
// src/admin/searchAdminApi.ts
|
|
4509
|
+
var import_outbound_api2 = require("@omnicross/core/outbound-api");
|
|
4510
|
+
var import_api3 = require("@omnicross/core/search/api");
|
|
4511
|
+
var import_http3 = require("@omnicross/core/search/http");
|
|
4512
|
+
var import_search2 = require("@omnicross/core/search");
|
|
4513
|
+
|
|
4514
|
+
// src/search/searchDoctorProjection.ts
|
|
4515
|
+
var import_search_types = require("@omnicross/contracts/search-types");
|
|
4516
|
+
var import_api = require("@omnicross/core/search/api");
|
|
4517
|
+
var import_http = require("@omnicross/core/search/http");
|
|
4518
|
+
var API_DOCTOR_PROVIDERS = [
|
|
4519
|
+
{
|
|
4520
|
+
id: "tavily",
|
|
4521
|
+
capabilities: import_api.TAVILY_CAPABILITIES,
|
|
4522
|
+
configured: (configs) => configs.tavily !== void 0,
|
|
4523
|
+
missingReason: "no API key configured"
|
|
4524
|
+
},
|
|
4525
|
+
{
|
|
4526
|
+
id: "jina",
|
|
4527
|
+
capabilities: import_api.JINA_CAPABILITIES,
|
|
4528
|
+
configured: (configs) => configs.jina !== void 0,
|
|
4529
|
+
// Honest about the asymmetry: Jina CAN run keyless, but a provider nobody
|
|
4530
|
+
// asked for is still not enabled.
|
|
4531
|
+
missingReason: "not configured (Jina can run without a key, but must be enabled explicitly)"
|
|
4532
|
+
},
|
|
4533
|
+
{
|
|
4534
|
+
id: "searxng",
|
|
4535
|
+
capabilities: import_api.SEARXNG_CAPABILITIES,
|
|
4536
|
+
configured: (configs) => configs.searxng !== void 0,
|
|
4537
|
+
missingReason: "no API host configured"
|
|
4538
|
+
},
|
|
4539
|
+
{
|
|
4540
|
+
id: "zhipu",
|
|
4541
|
+
capabilities: import_api.ZHIPU_CAPABILITIES,
|
|
4542
|
+
configured: (configs) => configs.zhipu !== void 0,
|
|
4543
|
+
missingReason: "no API key configured"
|
|
4544
|
+
},
|
|
4545
|
+
{
|
|
4546
|
+
id: "z.ai",
|
|
4547
|
+
capabilities: import_api.ZHIPU_CAPABILITIES,
|
|
4548
|
+
configured: (configs) => configs["z.ai"] !== void 0,
|
|
4549
|
+
missingReason: "no API key configured"
|
|
4550
|
+
}
|
|
4551
|
+
];
|
|
4552
|
+
function buildSearchDoctorSnapshot(contributions = (0, import_http.builtinHttpSearchContributions)(), apiConfigs) {
|
|
4553
|
+
const rows = contributions.map((contribution) => ({
|
|
4554
|
+
providerId: contribution.id,
|
|
4555
|
+
source: contribution.source,
|
|
4556
|
+
kind: contribution.kind,
|
|
4557
|
+
capabilities: contribution.capabilities
|
|
4558
|
+
}));
|
|
4559
|
+
if (apiConfigs === void 0) return rows;
|
|
4560
|
+
for (const provider of API_DOCTOR_PROVIDERS) {
|
|
4561
|
+
if (provider.configured(apiConfigs)) continue;
|
|
4562
|
+
rows.push({
|
|
4563
|
+
providerId: provider.id,
|
|
4564
|
+
source: "builtin",
|
|
4565
|
+
kind: "api",
|
|
4566
|
+
capabilities: provider.capabilities,
|
|
4567
|
+
status: "unconfigured",
|
|
4568
|
+
reason: provider.missingReason
|
|
4569
|
+
});
|
|
4570
|
+
}
|
|
4571
|
+
return rows;
|
|
4572
|
+
}
|
|
4573
|
+
var SEARCH_DOCTOR_QUERY = "MDN HTTP headers documentation";
|
|
4574
|
+
function classifyLiveSearchOutcome(providerId, outcome, checkedAt) {
|
|
4575
|
+
if (outcome.kind === "results") {
|
|
4576
|
+
if (outcome.count > 0) return { providerId, status: "healthy", checkedAt };
|
|
4577
|
+
return {
|
|
4578
|
+
providerId,
|
|
4579
|
+
status: "degraded",
|
|
4580
|
+
checkedAt,
|
|
4581
|
+
reason: "reachable, but the engine returned no usable results (possible partial drift)"
|
|
4582
|
+
};
|
|
4583
|
+
}
|
|
4584
|
+
const error = (0, import_search_types.toSearchErrorShape)(outcome.error);
|
|
4585
|
+
const stage = error.details?.stage;
|
|
4586
|
+
const { status, reason } = classifySearchFailure(stage, error.code);
|
|
4587
|
+
return { providerId, status, checkedAt, reason, error };
|
|
4588
|
+
}
|
|
4589
|
+
function classifySearchFailure(stage, code) {
|
|
4590
|
+
if (stage === "challenge") {
|
|
4591
|
+
return { status: "blocked", reason: "the engine served a bot challenge instead of results" };
|
|
4592
|
+
}
|
|
4593
|
+
if (stage === "trust") {
|
|
4594
|
+
return {
|
|
4595
|
+
status: "blocked",
|
|
4596
|
+
reason: "the engine served a page that failed the anti-decoy trust check"
|
|
4597
|
+
};
|
|
4598
|
+
}
|
|
4599
|
+
if (code === "policy_denied") {
|
|
4600
|
+
return {
|
|
4601
|
+
status: "blocked",
|
|
4602
|
+
reason: "the egress policy refused the request target"
|
|
4603
|
+
};
|
|
4604
|
+
}
|
|
4605
|
+
if (code === "parse_failed") {
|
|
4606
|
+
return {
|
|
4607
|
+
status: "failed",
|
|
4608
|
+
reason: "the response was not recognizable as a search result page (parser drift suspected)"
|
|
4609
|
+
};
|
|
4610
|
+
}
|
|
4611
|
+
if (code === "timeout") {
|
|
4612
|
+
return { status: "failed", reason: "the request exceeded its time budget" };
|
|
4613
|
+
}
|
|
4614
|
+
return { status: "failed", reason: `the request failed (${code})` };
|
|
4615
|
+
}
|
|
4616
|
+
async function runSearchLiveChecks(contributions, now = () => (/* @__PURE__ */ new Date()).toISOString()) {
|
|
4617
|
+
const diagnostics = [];
|
|
4618
|
+
for (const contribution of contributions) {
|
|
4619
|
+
try {
|
|
4620
|
+
const results = await contribution.provider.search(SEARCH_DOCTOR_QUERY, { maxResults: 5 });
|
|
4621
|
+
diagnostics.push(
|
|
4622
|
+
classifyLiveSearchOutcome(contribution.id, { kind: "results", count: results.length }, now())
|
|
4623
|
+
);
|
|
4624
|
+
} catch (error) {
|
|
4625
|
+
diagnostics.push(classifyLiveSearchOutcome(contribution.id, { kind: "failure", error }, now()));
|
|
4626
|
+
}
|
|
4627
|
+
}
|
|
4628
|
+
return diagnostics;
|
|
4629
|
+
}
|
|
4630
|
+
|
|
4631
|
+
// src/search/SearchAssembly.ts
|
|
4632
|
+
var import_upstreamFetch3 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
4633
|
+
var import_search = require("@omnicross/core/search");
|
|
4634
|
+
var import_api2 = require("@omnicross/core/search/api");
|
|
4635
|
+
var import_http2 = require("@omnicross/core/search/http");
|
|
4636
|
+
function searchEgressPolicyFrom(config) {
|
|
4637
|
+
const hosts = config.egress.allowedPrivateHosts;
|
|
4638
|
+
return hosts.length > 0 ? { allowedPrivateHosts: [...hosts] } : {};
|
|
4639
|
+
}
|
|
4640
|
+
function searchPolicyFrom(config) {
|
|
4641
|
+
const { preferred, allowed, fallbackEnabled, maxAttempts } = config.policy;
|
|
4642
|
+
return {
|
|
4643
|
+
...preferred !== void 0 ? { preferred } : {},
|
|
4644
|
+
...allowed !== void 0 ? { allowed: [...allowed] } : {},
|
|
4645
|
+
fallbackEnabled,
|
|
4646
|
+
...maxAttempts !== void 0 ? { maxAttempts } : {}
|
|
4647
|
+
};
|
|
4648
|
+
}
|
|
4649
|
+
function resolveSearchUpstreamDispatcher(url) {
|
|
4650
|
+
return (0, import_upstreamFetch3.resolveUpstreamDispatcher)({ url });
|
|
4651
|
+
}
|
|
4652
|
+
var searchUpstreamProxyConfig = createUpstreamProxyResolver();
|
|
4653
|
+
function resolveSearchUpstreamProxyConfig(url) {
|
|
4654
|
+
return searchUpstreamProxyConfig({ url });
|
|
4655
|
+
}
|
|
4656
|
+
function searchContributionsFrom(config) {
|
|
4657
|
+
return [
|
|
4658
|
+
...(0, import_http2.builtinHttpSearchContributions)(
|
|
4659
|
+
(0, import_http2.createSearchHttpTransport)({
|
|
4660
|
+
resolveProxyDispatcher: resolveSearchUpstreamDispatcher,
|
|
4661
|
+
resolveProxyConfig: resolveSearchUpstreamProxyConfig
|
|
4662
|
+
})
|
|
4663
|
+
),
|
|
4664
|
+
...(0, import_api2.apiSearchContributions)(config.providers, {
|
|
4665
|
+
egressPolicy: searchEgressPolicyFrom(config),
|
|
4666
|
+
resolveProxyDispatcher: resolveSearchUpstreamDispatcher
|
|
4667
|
+
})
|
|
4668
|
+
];
|
|
4669
|
+
}
|
|
4670
|
+
function buildSearchRuntime(config, options = {}) {
|
|
4671
|
+
const logger = options.logger ?? null;
|
|
4672
|
+
return (0, import_search.createSearchRuntime)({
|
|
4673
|
+
contributions: options.contributions ?? searchContributionsFrom(config),
|
|
4674
|
+
policy: searchPolicyFrom(config),
|
|
4675
|
+
...logger ? {
|
|
4676
|
+
onEvent: (event) => {
|
|
4677
|
+
logger.debug(`[search] ${formatSearchEvent(event)}`);
|
|
4678
|
+
}
|
|
4679
|
+
} : {}
|
|
4680
|
+
});
|
|
4681
|
+
}
|
|
4682
|
+
function formatSearchEvent(event) {
|
|
4683
|
+
const parts = [
|
|
4684
|
+
`type=${event.type}`,
|
|
4685
|
+
`request=${event.requestId}`,
|
|
4686
|
+
`queryHash=${event.queryHash}`,
|
|
4687
|
+
`durationMs=${event.durationMs}`
|
|
4688
|
+
];
|
|
4689
|
+
if ("providerId" in event && event.providerId !== void 0) {
|
|
4690
|
+
parts.push(`provider=${event.providerId}`);
|
|
4691
|
+
}
|
|
4692
|
+
if ("outcome" in event && event.outcome !== void 0) parts.push(`outcome=${event.outcome}`);
|
|
4693
|
+
if ("errorCode" in event && event.errorCode !== void 0) parts.push(`error=${event.errorCode}`);
|
|
4694
|
+
if ("resultCount" in event && event.resultCount !== void 0) {
|
|
4695
|
+
parts.push(`results=${event.resultCount}`);
|
|
4696
|
+
}
|
|
4697
|
+
if ("fallbackCount" in event && event.fallbackCount !== void 0) {
|
|
4698
|
+
parts.push(`fallbacks=${event.fallbackCount}`);
|
|
4699
|
+
}
|
|
4700
|
+
return parts.join(" ");
|
|
4701
|
+
}
|
|
4702
|
+
|
|
4703
|
+
// src/admin/searchAdminApi.ts
|
|
4704
|
+
var KEYLESS_HTTP_PROVIDER_IDS = /* @__PURE__ */ new Set(["http-bing", "http-duckduckgo"]);
|
|
4705
|
+
var API_PROVIDER_IDS = /* @__PURE__ */ new Set([
|
|
4706
|
+
"tavily",
|
|
4707
|
+
"jina",
|
|
4708
|
+
"searxng",
|
|
4709
|
+
"zhipu",
|
|
4710
|
+
"z.ai"
|
|
4711
|
+
]);
|
|
4712
|
+
var SEARCH_QUERY_MAX_CODE_UNITS = 256;
|
|
4713
|
+
var QUERY_CONTROL_CHARS = /[\u0000-\u001f\u007f]/u;
|
|
4714
|
+
var SEARCH_RESULT_FIELD_CAPS = { title: 512, url: 2048, content: 1024 };
|
|
4715
|
+
var SEARCH_QUERY_MAX_RESULTS = 5;
|
|
4716
|
+
function sanitizeResultField(value, cap) {
|
|
4717
|
+
const text = typeof value === "string" ? value : value === null || value === void 0 ? "" : String(value);
|
|
4718
|
+
return text.replace(/[\u0000-\u001f\u007f]/gu, "").slice(0, cap);
|
|
4719
|
+
}
|
|
4720
|
+
function writeJson(res, status, body) {
|
|
4721
|
+
res.writeHead(status, { "Content-Type": "application/json" });
|
|
4722
|
+
res.end(JSON.stringify(body));
|
|
4723
|
+
}
|
|
4724
|
+
function writeErr(res, status, message) {
|
|
4725
|
+
writeJson(res, status, { error: { type: "admin_api_error", message } });
|
|
4726
|
+
}
|
|
4727
|
+
var SEARCH_MAX_BODY_BYTES = 64 * 1024;
|
|
4728
|
+
var SearchBodyTooLargeError = class extends Error {
|
|
4729
|
+
};
|
|
4730
|
+
async function readJsonBody2(req) {
|
|
4731
|
+
const chunks = [];
|
|
4732
|
+
let bytes = 0;
|
|
4733
|
+
for await (const chunk of req) {
|
|
4734
|
+
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
4735
|
+
bytes += buffer.length;
|
|
4736
|
+
if (bytes > SEARCH_MAX_BODY_BYTES) throw new SearchBodyTooLargeError("request body is too large");
|
|
4737
|
+
chunks.push(buffer);
|
|
4738
|
+
}
|
|
4739
|
+
const raw = Buffer.concat(chunks).toString("utf8");
|
|
4740
|
+
if (!raw.trim()) return {};
|
|
4741
|
+
try {
|
|
4742
|
+
const parsed = JSON.parse(raw);
|
|
4743
|
+
return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
4744
|
+
} catch {
|
|
4745
|
+
return {};
|
|
4746
|
+
}
|
|
4747
|
+
}
|
|
4748
|
+
async function readBodyOrReject(req, res) {
|
|
4749
|
+
try {
|
|
4750
|
+
return await readJsonBody2(req);
|
|
4751
|
+
} catch (error) {
|
|
4752
|
+
if (error instanceof SearchBodyTooLargeError) {
|
|
4753
|
+
writeErr(res, 400, error.message);
|
|
4754
|
+
return void 0;
|
|
4755
|
+
}
|
|
4756
|
+
throw error;
|
|
4757
|
+
}
|
|
4758
|
+
}
|
|
4759
|
+
async function handleSearchAdmin(req, res, method, rest, deps) {
|
|
4760
|
+
if (rest.length === 1 && rest[0] === "diagnostics") {
|
|
4761
|
+
if (!deps.searchStatus) {
|
|
4762
|
+
return writeErr(res, 501, "Search status is not available in this build");
|
|
4763
|
+
}
|
|
4764
|
+
if (method !== "GET") {
|
|
4765
|
+
return writeErr(res, 405, `method ${method} not allowed on search diagnostics`);
|
|
4766
|
+
}
|
|
4767
|
+
return handleSearchDiagnostics(res, deps);
|
|
4768
|
+
}
|
|
4769
|
+
if (rest.length === 1 && rest[0] === "test") {
|
|
4770
|
+
if (!deps.searchStatus) {
|
|
4771
|
+
return writeErr(res, 501, "Search status is not available in this build");
|
|
4772
|
+
}
|
|
4773
|
+
if (method !== "POST") {
|
|
4774
|
+
return writeErr(res, 405, `method ${method} not allowed on search test`);
|
|
4775
|
+
}
|
|
4776
|
+
return handleSearchTest(req, res, deps);
|
|
4777
|
+
}
|
|
4778
|
+
if (rest.length === 1 && rest[0] === "query") {
|
|
4779
|
+
if (!deps.searchStatus) {
|
|
4780
|
+
return writeErr(res, 501, "Search status is not available in this build");
|
|
4781
|
+
}
|
|
4782
|
+
if (method !== "POST") {
|
|
4783
|
+
return writeErr(res, 405, `method ${method} not allowed on search query`);
|
|
4784
|
+
}
|
|
4785
|
+
return handleSearchQuery(req, res, deps);
|
|
4786
|
+
}
|
|
4787
|
+
return writeErr(res, 404, `unknown search route '/${rest.join("/")}'`);
|
|
4788
|
+
}
|
|
4789
|
+
async function handleSearchDiagnostics(res, deps) {
|
|
4790
|
+
const status = deps.searchStatus;
|
|
4791
|
+
const persisted = await (0, import_outbound_api2.loadServerConfig)(deps.settingsStore);
|
|
4792
|
+
const search = persisted.search ?? import_outbound_api2.DEFAULT_SEARCH_SERVER_CONFIG;
|
|
4793
|
+
const rows = buildSearchDoctorSnapshot(
|
|
4794
|
+
status.runtime.listProviders(),
|
|
4795
|
+
search.providers
|
|
4796
|
+
);
|
|
4797
|
+
const snapshot = {
|
|
4798
|
+
rows,
|
|
4799
|
+
modes: {
|
|
4800
|
+
// codex is read from the LIVE config per request — an admin PUT has
|
|
4801
|
+
// already applied. responses/anthropic were captured at bootstrap.
|
|
4802
|
+
codex: search.modes.codex,
|
|
4803
|
+
responses: status.modes.responses,
|
|
4804
|
+
anthropic: status.modes.anthropic
|
|
4805
|
+
},
|
|
4806
|
+
applySemantics: { codex: "immediate", rest: "restart" }
|
|
4807
|
+
};
|
|
4808
|
+
return writeJson(res, 200, { diagnostics: snapshot });
|
|
4809
|
+
}
|
|
4810
|
+
function persistedSearchContributions(search, fetchImpl) {
|
|
4811
|
+
if (fetchImpl) {
|
|
4812
|
+
const egressPolicy = searchEgressPolicyFrom(search);
|
|
4813
|
+
return [
|
|
4814
|
+
...(0, import_http3.builtinHttpSearchContributions)(
|
|
4815
|
+
(0, import_http3.createSearchHttpTransport)({ fetch: fetchImpl, egressPolicy })
|
|
4816
|
+
),
|
|
4817
|
+
...(0, import_api3.apiSearchContributions)(search.providers, { egressPolicy, fetchImpl })
|
|
4818
|
+
];
|
|
4819
|
+
}
|
|
4820
|
+
return searchContributionsFrom(search);
|
|
4821
|
+
}
|
|
4822
|
+
async function handleSearchTest(req, res, deps) {
|
|
4823
|
+
const status = deps.searchStatus;
|
|
4824
|
+
const body = await readBodyOrReject(req, res);
|
|
4825
|
+
if (body === void 0) return;
|
|
4826
|
+
const providerId = body["providerId"];
|
|
4827
|
+
if (typeof providerId !== "string" || providerId.length === 0) {
|
|
4828
|
+
return writeErr(res, 400, "providerId must be a non-empty string");
|
|
4829
|
+
}
|
|
4830
|
+
if (!KEYLESS_HTTP_PROVIDER_IDS.has(providerId) && !API_PROVIDER_IDS.has(providerId)) {
|
|
4831
|
+
return writeErr(res, 404, `unknown search provider '${providerId}'`);
|
|
4832
|
+
}
|
|
4833
|
+
const persisted = await (0, import_outbound_api2.loadServerConfig)(deps.settingsStore);
|
|
4834
|
+
const search = persisted.search ?? import_outbound_api2.DEFAULT_SEARCH_SERVER_CONFIG;
|
|
4835
|
+
const providers = search.providers;
|
|
4836
|
+
if (!KEYLESS_HTTP_PROVIDER_IDS.has(providerId) && providers[providerId] === void 0) {
|
|
4837
|
+
return writeErr(res, 400, `search provider '${providerId}' is not configured`);
|
|
4838
|
+
}
|
|
4839
|
+
const fetchImpl = status.testFetch;
|
|
4840
|
+
const contributions = persistedSearchContributions(search, fetchImpl);
|
|
4841
|
+
const contribution = contributions.find((c) => c.id === providerId);
|
|
4842
|
+
if (!contribution) {
|
|
4843
|
+
return writeErr(res, 400, `search provider '${providerId}' is not configured`);
|
|
4844
|
+
}
|
|
4845
|
+
const checkedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4846
|
+
try {
|
|
4847
|
+
const results = await contribution.provider.search(SEARCH_DOCTOR_QUERY, { maxResults: 5 });
|
|
4848
|
+
const diagnostic = classifyLiveSearchOutcome(
|
|
4849
|
+
contribution.id,
|
|
4850
|
+
{ kind: "results", count: results.length },
|
|
4851
|
+
checkedAt
|
|
4852
|
+
);
|
|
4853
|
+
const response = { diagnostic, resultCount: results.length };
|
|
4854
|
+
return writeJson(res, 200, { result: response });
|
|
4855
|
+
} catch (error) {
|
|
4856
|
+
const diagnostic = classifyLiveSearchOutcome(contribution.id, { kind: "failure", error }, checkedAt);
|
|
4857
|
+
const response = { diagnostic };
|
|
4858
|
+
return writeJson(res, 200, { result: response });
|
|
4859
|
+
}
|
|
4860
|
+
}
|
|
4861
|
+
async function handleSearchQuery(req, res, deps) {
|
|
4862
|
+
const status = deps.searchStatus;
|
|
4863
|
+
const body = await readBodyOrReject(req, res);
|
|
4864
|
+
if (body === void 0) return;
|
|
4865
|
+
const providerId = body["providerId"];
|
|
4866
|
+
if (typeof providerId !== "string" || providerId.length === 0) {
|
|
4867
|
+
return writeErr(res, 400, "providerId must be a non-empty string");
|
|
4868
|
+
}
|
|
4869
|
+
if (!KEYLESS_HTTP_PROVIDER_IDS.has(providerId) && !API_PROVIDER_IDS.has(providerId)) {
|
|
4870
|
+
return writeErr(res, 404, `unknown search provider '${providerId}'`);
|
|
4871
|
+
}
|
|
4872
|
+
const query2 = body["query"];
|
|
4873
|
+
if (typeof query2 !== "string" || query2.trim().length === 0) {
|
|
4874
|
+
return writeErr(res, 400, "query must be a non-empty string");
|
|
4875
|
+
}
|
|
4876
|
+
if (query2.length > SEARCH_QUERY_MAX_CODE_UNITS) {
|
|
4877
|
+
return writeErr(res, 400, `query must be at most ${SEARCH_QUERY_MAX_CODE_UNITS} characters`);
|
|
4878
|
+
}
|
|
4879
|
+
if (QUERY_CONTROL_CHARS.test(query2)) {
|
|
4880
|
+
return writeErr(res, 400, "query must not contain control characters");
|
|
4881
|
+
}
|
|
4882
|
+
const persisted = await (0, import_outbound_api2.loadServerConfig)(deps.settingsStore);
|
|
4883
|
+
const search = persisted.search ?? import_outbound_api2.DEFAULT_SEARCH_SERVER_CONFIG;
|
|
4884
|
+
const providers = search.providers;
|
|
4885
|
+
if (!KEYLESS_HTTP_PROVIDER_IDS.has(providerId) && providers[providerId] === void 0) {
|
|
4886
|
+
return writeErr(res, 400, `search provider '${providerId}' is not configured`);
|
|
4887
|
+
}
|
|
4888
|
+
const fetchImpl = status.testFetch;
|
|
4889
|
+
const runtime = (0, import_search2.createSearchRuntime)({
|
|
4890
|
+
contributions: persistedSearchContributions(search, fetchImpl),
|
|
4891
|
+
policy: {
|
|
4892
|
+
...searchPolicyFrom(search),
|
|
4893
|
+
// The panel always walks: it answers "does a search WORK for this
|
|
4894
|
+
// operator", not "does this one provider behave" — that is `/test`'s
|
|
4895
|
+
// job. The persisted policy's allowlist still bounds the walk.
|
|
4896
|
+
fallbackEnabled: true,
|
|
4897
|
+
preferred: providerId
|
|
4898
|
+
}
|
|
4899
|
+
});
|
|
4900
|
+
const checkedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4901
|
+
try {
|
|
4902
|
+
const orchestrated = await runtime.search({ query: query2, options: { maxResults: 5 } });
|
|
4903
|
+
const results = orchestrated.results;
|
|
4904
|
+
const sanitized = results.slice(0, SEARCH_QUERY_MAX_RESULTS).map((result) => ({
|
|
4905
|
+
title: sanitizeResultField(result.title, SEARCH_RESULT_FIELD_CAPS.title),
|
|
4906
|
+
url: sanitizeResultField(result.url, SEARCH_RESULT_FIELD_CAPS.url),
|
|
4907
|
+
content: sanitizeResultField(result.content, SEARCH_RESULT_FIELD_CAPS.content)
|
|
4908
|
+
}));
|
|
4909
|
+
const diagnostic = sanitized.length === 0 ? { providerId: orchestrated.providerId, status: "healthy", checkedAt } : classifyLiveSearchOutcome(
|
|
4910
|
+
orchestrated.providerId,
|
|
4911
|
+
{ kind: "results", count: sanitized.length },
|
|
4912
|
+
checkedAt
|
|
4913
|
+
);
|
|
4914
|
+
const response = {
|
|
4915
|
+
diagnostic,
|
|
4916
|
+
providerUsed: orchestrated.providerId,
|
|
4917
|
+
fallbackCount: orchestrated.fallbackCount,
|
|
4918
|
+
resultCount: sanitized.length,
|
|
4919
|
+
results: sanitized
|
|
4920
|
+
};
|
|
4921
|
+
return writeJson(res, 200, { result: response });
|
|
4922
|
+
} catch (error) {
|
|
4923
|
+
const diagnostic = classifyLiveSearchOutcome(providerId, { kind: "failure", error }, checkedAt);
|
|
4924
|
+
const response = { diagnostic };
|
|
4925
|
+
return writeJson(res, 200, { result: response });
|
|
4926
|
+
}
|
|
4927
|
+
}
|
|
4928
|
+
|
|
4929
|
+
// src/admin/searchAdminView.ts
|
|
4930
|
+
var API_KEY_PROVIDERS = /* @__PURE__ */ new Set(["tavily", "jina", "zhipu", "z.ai"]);
|
|
4931
|
+
var BASIC_AUTH_PROVIDERS = /* @__PURE__ */ new Set(["searxng"]);
|
|
4932
|
+
function isRecord(value) {
|
|
4933
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
4934
|
+
}
|
|
4935
|
+
function redactSearchServerConfig(search) {
|
|
4936
|
+
const providers = {};
|
|
4937
|
+
for (const [id, raw] of Object.entries(search.providers)) {
|
|
4938
|
+
const entry = raw;
|
|
4939
|
+
const view = {};
|
|
4940
|
+
if (entry.apiHost !== void 0) view.apiHost = entry.apiHost;
|
|
4941
|
+
if (entry.basicAuthUsername !== void 0) view.basicAuthUsername = entry.basicAuthUsername;
|
|
4942
|
+
if (API_KEY_PROVIDERS.has(id)) {
|
|
4943
|
+
view.apiKeyConfigured = typeof entry.apiKey === "string" && entry.apiKey.length > 0;
|
|
4944
|
+
}
|
|
4945
|
+
if (BASIC_AUTH_PROVIDERS.has(id)) {
|
|
4946
|
+
view.basicAuthPasswordConfigured = typeof entry.basicAuthPassword === "string" && entry.basicAuthPassword.length > 0;
|
|
4947
|
+
}
|
|
4948
|
+
providers[id] = view;
|
|
4949
|
+
}
|
|
4950
|
+
return {
|
|
4951
|
+
modes: search.modes,
|
|
4952
|
+
providers,
|
|
4953
|
+
egress: { allowedPrivateHosts: [...search.egress.allowedPrivateHosts] },
|
|
4954
|
+
policy: { ...search.policy, ...search.policy.allowed ? { allowed: [...search.policy.allowed] } : {} }
|
|
4955
|
+
};
|
|
4956
|
+
}
|
|
4957
|
+
function storedSecrets(current, id) {
|
|
4958
|
+
const entry = current.providers[id];
|
|
4959
|
+
if (!entry) return {};
|
|
4960
|
+
const out = {};
|
|
4961
|
+
if (typeof entry.apiKey === "string" && entry.apiKey.length > 0) out.apiKey = entry.apiKey;
|
|
4962
|
+
if (typeof entry.basicAuthPassword === "string" && entry.basicAuthPassword.length > 0) {
|
|
4963
|
+
out.basicAuthPassword = entry.basicAuthPassword;
|
|
4964
|
+
}
|
|
4965
|
+
return out;
|
|
4966
|
+
}
|
|
4967
|
+
function resolveSecretField(entry, field, stored) {
|
|
4968
|
+
if (!(field in entry)) {
|
|
4969
|
+
if (stored !== void 0) entry[field] = stored;
|
|
4970
|
+
return;
|
|
4971
|
+
}
|
|
4972
|
+
const value = entry[field];
|
|
4973
|
+
if (value === null) {
|
|
4974
|
+
delete entry[field];
|
|
4975
|
+
return;
|
|
4976
|
+
}
|
|
4977
|
+
if (typeof value === "string" && value.trim().length > 0) return;
|
|
4978
|
+
if (stored !== void 0) entry[field] = stored;
|
|
4979
|
+
else delete entry[field];
|
|
4980
|
+
}
|
|
4981
|
+
function preserveSearchSecrets(incoming, current) {
|
|
4982
|
+
if (!isRecord(incoming)) return incoming;
|
|
4983
|
+
const section = { ...incoming };
|
|
4984
|
+
const providersValue = section["providers"];
|
|
4985
|
+
if (!isRecord(providersValue)) return section;
|
|
4986
|
+
const providers = {};
|
|
4987
|
+
for (const [id, entryValue] of Object.entries(providersValue)) {
|
|
4988
|
+
if (!isRecord(entryValue)) {
|
|
4989
|
+
providers[id] = entryValue;
|
|
4990
|
+
continue;
|
|
4991
|
+
}
|
|
4992
|
+
const entry = { ...entryValue };
|
|
4993
|
+
delete entry["apiKeyConfigured"];
|
|
4994
|
+
delete entry["basicAuthPasswordConfigured"];
|
|
4995
|
+
const stored = storedSecrets(current, id);
|
|
4996
|
+
resolveSecretField(entry, "apiKey", stored.apiKey);
|
|
4997
|
+
resolveSecretField(entry, "basicAuthPassword", stored.basicAuthPassword);
|
|
4998
|
+
providers[id] = entry;
|
|
4999
|
+
}
|
|
5000
|
+
section["providers"] = providers;
|
|
5001
|
+
return section;
|
|
5002
|
+
}
|
|
5003
|
+
|
|
4505
5004
|
// src/admin/keyPolicyBody.ts
|
|
4506
5005
|
function parseKeyPolicyBody(body) {
|
|
4507
5006
|
const policy = {};
|
|
@@ -4564,7 +5063,7 @@ function parseKeyPolicyBody(body) {
|
|
|
4564
5063
|
var ENDPOINTS = /* @__PURE__ */ new Set(["chat", "responses", "messages", "gemini"]);
|
|
4565
5064
|
var TARGET_KINDS = /* @__PURE__ */ new Set(["account", "account-group", "account-pool", "provider"]);
|
|
4566
5065
|
var FALLBACKS = /* @__PURE__ */ new Set(["next", "fail", "global"]);
|
|
4567
|
-
function
|
|
5066
|
+
function isRecord2(value) {
|
|
4568
5067
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
4569
5068
|
}
|
|
4570
5069
|
function nonBlank(value) {
|
|
@@ -4584,7 +5083,7 @@ function validateGatewayBindingsSegment(patch) {
|
|
|
4584
5083
|
const ids = /* @__PURE__ */ new Set();
|
|
4585
5084
|
raw.forEach((entry, index) => {
|
|
4586
5085
|
const path2 = `bindings[${index}]`;
|
|
4587
|
-
if (!
|
|
5086
|
+
if (!isRecord2(entry)) {
|
|
4588
5087
|
errors.push(`${path2} must be an object`);
|
|
4589
5088
|
return;
|
|
4590
5089
|
}
|
|
@@ -4613,12 +5112,12 @@ function validateGatewayBindingsSegment(patch) {
|
|
|
4613
5112
|
} else if (entry.modelMappings.length > 100) {
|
|
4614
5113
|
errors.push(`${path2}.modelMappings cannot contain more than 100 entries`);
|
|
4615
5114
|
} else if (entry.modelMappings.some(
|
|
4616
|
-
(mapping) => !
|
|
5115
|
+
(mapping) => !isRecord2(mapping) || !nonBlank(mapping.source) || !nonBlank(mapping.target)
|
|
4617
5116
|
)) {
|
|
4618
5117
|
errors.push(`${path2}.modelMappings must contain non-empty source and target strings`);
|
|
4619
5118
|
}
|
|
4620
5119
|
}
|
|
4621
|
-
if (!
|
|
5120
|
+
if (!isRecord2(entry.target) || !TARGET_KINDS.has(String(entry.target.kind))) {
|
|
4622
5121
|
errors.push(`${path2}.target is invalid`);
|
|
4623
5122
|
} else {
|
|
4624
5123
|
if (!nonBlank(entry.target.providerId)) errors.push(`${path2}.target.providerId is required`);
|
|
@@ -4633,7 +5132,7 @@ function validateGatewayBindingsSegment(patch) {
|
|
|
4633
5132
|
}
|
|
4634
5133
|
}
|
|
4635
5134
|
if (entry.modelMap !== void 0) {
|
|
4636
|
-
if (!
|
|
5135
|
+
if (!isRecord2(entry.modelMap) || Object.values(entry.modelMap).some((value) => typeof value !== "string")) {
|
|
4637
5136
|
errors.push(`${path2}.modelMap must contain string values`);
|
|
4638
5137
|
}
|
|
4639
5138
|
}
|
|
@@ -4651,15 +5150,15 @@ function validateGatewayBindingsSegment(patch) {
|
|
|
4651
5150
|
}
|
|
4652
5151
|
|
|
4653
5152
|
// src/admin/voucherAdmin.ts
|
|
4654
|
-
var
|
|
4655
|
-
function
|
|
5153
|
+
var import_outbound_api3 = require("@omnicross/core/outbound-api");
|
|
5154
|
+
function writeJson2(res, status, body) {
|
|
4656
5155
|
res.writeHead(status, { "Content-Type": "application/json" });
|
|
4657
5156
|
res.end(JSON.stringify(body));
|
|
4658
5157
|
}
|
|
4659
|
-
function
|
|
4660
|
-
|
|
5158
|
+
function writeErr2(res, status, message) {
|
|
5159
|
+
writeJson2(res, status, { error: { type: "voucher_error", message } });
|
|
4661
5160
|
}
|
|
4662
|
-
function
|
|
5161
|
+
function readJsonBody3(req) {
|
|
4663
5162
|
return new Promise((resolve11, reject) => {
|
|
4664
5163
|
const chunks = [];
|
|
4665
5164
|
req.on("data", (c) => chunks.push(c));
|
|
@@ -4710,34 +5209,34 @@ function parseVoucherCreateBody(body) {
|
|
|
4710
5209
|
return { ok: true, input };
|
|
4711
5210
|
}
|
|
4712
5211
|
async function voucherEnabled(deps) {
|
|
4713
|
-
const config = await (0,
|
|
5212
|
+
const config = await (0, import_outbound_api3.loadServerConfig)(deps.settingsStore);
|
|
4714
5213
|
return config.voucher?.enabled === true;
|
|
4715
5214
|
}
|
|
4716
5215
|
async function handleVoucher(req, res, method, rest, deps) {
|
|
4717
5216
|
const voucherDb = deps.voucherDb;
|
|
4718
|
-
if (!voucherDb) return
|
|
5217
|
+
if (!voucherDb) return writeErr2(res, 501, "Voucher feature is not available");
|
|
4719
5218
|
if (method === "GET" && rest.length === 0) {
|
|
4720
5219
|
const rows = await voucherDb.voucherList();
|
|
4721
|
-
return
|
|
5220
|
+
return writeJson2(res, 200, { vouchers: rows.map(import_outbound_api3.toVoucherInfo) });
|
|
4722
5221
|
}
|
|
4723
5222
|
if (method === "POST" && rest.length === 0) {
|
|
4724
|
-
if (!await voucherEnabled(deps)) return
|
|
5223
|
+
if (!await voucherEnabled(deps)) return writeErr2(res, 403, "Voucher feature is disabled");
|
|
4725
5224
|
let body;
|
|
4726
5225
|
try {
|
|
4727
|
-
body = await
|
|
5226
|
+
body = await readJsonBody3(req);
|
|
4728
5227
|
} catch {
|
|
4729
|
-
return
|
|
5228
|
+
return writeErr2(res, 400, "Invalid JSON in request body");
|
|
4730
5229
|
}
|
|
4731
5230
|
const parsed = parseVoucherCreateBody(body);
|
|
4732
|
-
if (!parsed.ok) return
|
|
4733
|
-
const code = (0,
|
|
5231
|
+
if (!parsed.ok) return writeErr2(res, 400, parsed.message);
|
|
5232
|
+
const code = (0, import_outbound_api3.generateVoucherCode)();
|
|
4734
5233
|
const created = await voucherDb.voucherCreate({
|
|
4735
|
-
id: (0,
|
|
4736
|
-
codeHash: (0,
|
|
4737
|
-
codePrefix: (0,
|
|
5234
|
+
id: (0, import_outbound_api3.newVoucherId)(),
|
|
5235
|
+
codeHash: (0, import_outbound_api3.hashVoucherCode)(code),
|
|
5236
|
+
codePrefix: (0, import_outbound_api3.voucherCodePrefix)(code),
|
|
4738
5237
|
...parsed.input
|
|
4739
5238
|
});
|
|
4740
|
-
return
|
|
5239
|
+
return writeJson2(res, 201, {
|
|
4741
5240
|
id: created.id,
|
|
4742
5241
|
codePrefix: created.codePrefix,
|
|
4743
5242
|
type: created.type,
|
|
@@ -4748,11 +5247,11 @@ async function handleVoucher(req, res, method, rest, deps) {
|
|
|
4748
5247
|
}
|
|
4749
5248
|
const id = rest[0];
|
|
4750
5249
|
if (method === "POST" && id && rest[1] === "revoke") {
|
|
4751
|
-
if (!await voucherEnabled(deps)) return
|
|
5250
|
+
if (!await voucherEnabled(deps)) return writeErr2(res, 403, "Voucher feature is disabled");
|
|
4752
5251
|
const ok = await voucherDb.voucherRevokeCas(id, Date.now());
|
|
4753
|
-
return
|
|
5252
|
+
return writeJson2(res, ok ? 200 : 409, { ok });
|
|
4754
5253
|
}
|
|
4755
|
-
return
|
|
5254
|
+
return writeErr2(res, 405, `method ${method} not allowed on voucher`);
|
|
4756
5255
|
}
|
|
4757
5256
|
|
|
4758
5257
|
// src/admin/webhookConfigBody.ts
|
|
@@ -4903,7 +5402,7 @@ function applyBillingConfig(config) {
|
|
|
4903
5402
|
}
|
|
4904
5403
|
|
|
4905
5404
|
// src/migration/migration.ts
|
|
4906
|
-
var
|
|
5405
|
+
var import_outbound_api4 = require("@omnicross/core/outbound-api");
|
|
4907
5406
|
|
|
4908
5407
|
// src/ports/account-multi.ts
|
|
4909
5408
|
var import_node_crypto8 = require("crypto");
|
|
@@ -5331,7 +5830,7 @@ async function gatherExport(deps, passphrase) {
|
|
|
5331
5830
|
v: BUNDLE_VERSION,
|
|
5332
5831
|
providers: cfg.providers,
|
|
5333
5832
|
tokens,
|
|
5334
|
-
server: (0,
|
|
5833
|
+
server: (0, import_outbound_api4.normalizeServerConfig)(cfg.server)
|
|
5335
5834
|
};
|
|
5336
5835
|
return sealPack(JSON.stringify(bundle), passphrase);
|
|
5337
5836
|
}
|
|
@@ -5380,7 +5879,7 @@ async function applyImport(packString, passphrase, mode, deps, parseProviderInpu
|
|
|
5380
5879
|
const imageErrors = validateImagesAdminConfig(rawServer["images"]);
|
|
5381
5880
|
if (imageErrors.length > 0) throw new Error("migration pack has an invalid Images config");
|
|
5382
5881
|
}
|
|
5383
|
-
validatedServer = (0,
|
|
5882
|
+
validatedServer = (0, import_outbound_api4.normalizeServerConfig)(bundle.server);
|
|
5384
5883
|
}
|
|
5385
5884
|
const validatedProviders = [];
|
|
5386
5885
|
for (const raw of rawProviders) {
|
|
@@ -5661,12 +6160,12 @@ async function handlePricingResolveConflicts(body, deps) {
|
|
|
5661
6160
|
}
|
|
5662
6161
|
|
|
5663
6162
|
// src/admin/accountAllowanceApi.ts
|
|
5664
|
-
function
|
|
6163
|
+
function writeJson3(res, status, body) {
|
|
5665
6164
|
res.writeHead(status, { "Content-Type": "application/json" });
|
|
5666
6165
|
res.end(JSON.stringify(body));
|
|
5667
6166
|
}
|
|
5668
6167
|
function writeError2(res, status, message) {
|
|
5669
|
-
|
|
6168
|
+
writeJson3(res, status, { error: { type: "account_allowance_error", message } });
|
|
5670
6169
|
}
|
|
5671
6170
|
function readJson2(req) {
|
|
5672
6171
|
return new Promise((resolve11, reject) => {
|
|
@@ -5699,7 +6198,7 @@ async function handleAccountAllowanceApi(req, res, method, rest, service) {
|
|
|
5699
6198
|
if (!service.getSchedulingStatus) {
|
|
5700
6199
|
return writeError2(res, 501, "allowance scheduling diagnostics are not available");
|
|
5701
6200
|
}
|
|
5702
|
-
return
|
|
6201
|
+
return writeJson3(res, 200, { scheduling: service.getSchedulingStatus() });
|
|
5703
6202
|
}
|
|
5704
6203
|
if (method === "GET") {
|
|
5705
6204
|
const params = query(req);
|
|
@@ -5708,7 +6207,7 @@ async function handleAccountAllowanceApi(req, res, method, rest, service) {
|
|
|
5708
6207
|
if (providerId === null) return writeError2(res, 400, "providerId must be claude or codex");
|
|
5709
6208
|
const accountId = rest.length >= 2 ? rest[1] : params.get("accountId") ?? void 0;
|
|
5710
6209
|
const allowances = await service.list({ providerId, accountId });
|
|
5711
|
-
return
|
|
6210
|
+
return writeJson3(res, 200, { allowances });
|
|
5712
6211
|
}
|
|
5713
6212
|
if (method === "POST" && rest[0] === "refresh") {
|
|
5714
6213
|
const body = await readJson2(req);
|
|
@@ -5723,7 +6222,7 @@ async function handleAccountAllowanceApi(req, res, method, rest, service) {
|
|
|
5723
6222
|
if (accountId && allowances.length === 0) {
|
|
5724
6223
|
return writeError2(res, 404, `Claude account '${accountId}' not found`);
|
|
5725
6224
|
}
|
|
5726
|
-
return
|
|
6225
|
+
return writeJson3(res, 200, { allowances });
|
|
5727
6226
|
}
|
|
5728
6227
|
return writeError2(res, 405, `method ${method} not allowed on account allowances`);
|
|
5729
6228
|
}
|
|
@@ -5739,7 +6238,7 @@ function readBody(req) {
|
|
|
5739
6238
|
req.on("error", reject);
|
|
5740
6239
|
});
|
|
5741
6240
|
}
|
|
5742
|
-
async function
|
|
6241
|
+
async function readJsonBody4(req) {
|
|
5743
6242
|
const raw = await readBody(req);
|
|
5744
6243
|
if (!raw.trim()) return {};
|
|
5745
6244
|
try {
|
|
@@ -5749,12 +6248,12 @@ async function readJsonBody3(req) {
|
|
|
5749
6248
|
return {};
|
|
5750
6249
|
}
|
|
5751
6250
|
}
|
|
5752
|
-
function
|
|
6251
|
+
function writeJson4(res, status, body) {
|
|
5753
6252
|
res.writeHead(status, { "Content-Type": "application/json" });
|
|
5754
6253
|
res.end(JSON.stringify(body));
|
|
5755
6254
|
}
|
|
5756
6255
|
function writeJsonError(res, status, message) {
|
|
5757
|
-
|
|
6256
|
+
writeJson4(res, status, { error: { type: "admin_api_error", message } });
|
|
5758
6257
|
}
|
|
5759
6258
|
function maskProviderApiKey(apiKey) {
|
|
5760
6259
|
if (!apiKey) return "";
|
|
@@ -5775,7 +6274,7 @@ function toKeyInfo(row) {
|
|
|
5775
6274
|
lastUsedAt: row.lastUsedAt,
|
|
5776
6275
|
revoked: row.revokedAt !== null,
|
|
5777
6276
|
kind: row.kind,
|
|
5778
|
-
allowedEndpoints: [...(0,
|
|
6277
|
+
allowedEndpoints: [...(0, import_outbound_api5.effectiveOutboundPermissions)(row.allowedEndpoints)],
|
|
5779
6278
|
legacyPermissions: row.allowedEndpoints === void 0,
|
|
5780
6279
|
loopbackOnly: row.loopbackOnly,
|
|
5781
6280
|
maxConcurrency: row.maxConcurrency,
|
|
@@ -5861,6 +6360,8 @@ async function handleAdminApi(req, res, path2, deps) {
|
|
|
5861
6360
|
return await handleServer(req, res, method, deps);
|
|
5862
6361
|
case "images":
|
|
5863
6362
|
return await handleImages(res, method, rest, deps);
|
|
6363
|
+
case "search":
|
|
6364
|
+
return await handleSearchAdmin(req, res, method, rest, deps);
|
|
5864
6365
|
case "accounts":
|
|
5865
6366
|
return await handleAccounts(req, res, method, rest, deps);
|
|
5866
6367
|
case "cli":
|
|
@@ -5894,7 +6395,7 @@ function requestQuery(req) {
|
|
|
5894
6395
|
return new URLSearchParams(qIdx >= 0 ? raw.slice(qIdx + 1) : "");
|
|
5895
6396
|
}
|
|
5896
6397
|
function writeResult(res, result) {
|
|
5897
|
-
|
|
6398
|
+
writeJson4(res, result.status, result.body);
|
|
5898
6399
|
}
|
|
5899
6400
|
async function handleUsage(req, res, method, rest, deps) {
|
|
5900
6401
|
if (method !== "GET") return writeJsonError(res, 405, `method ${method} not allowed on usage`);
|
|
@@ -5903,13 +6404,13 @@ async function handleUsage(req, res, method, rest, deps) {
|
|
|
5903
6404
|
async function handleDashboardRoute(res, method, deps) {
|
|
5904
6405
|
if (method !== "GET") return writeJsonError(res, 405, `method ${method} not allowed on dashboard`);
|
|
5905
6406
|
const result = await handleDashboard(deps);
|
|
5906
|
-
return
|
|
6407
|
+
return writeJson4(res, result.status, result.body);
|
|
5907
6408
|
}
|
|
5908
6409
|
async function handlePricing(req, res, method, rest, deps) {
|
|
5909
6410
|
if (rest.length === 0) {
|
|
5910
6411
|
if (method === "GET") return writeResult(res, await handlePricingList(deps));
|
|
5911
6412
|
if (method === "PUT") {
|
|
5912
|
-
return writeResult(res, await handlePricingUpsert(await
|
|
6413
|
+
return writeResult(res, await handlePricingUpsert(await readJsonBody4(req), deps));
|
|
5913
6414
|
}
|
|
5914
6415
|
if (method === "DELETE") {
|
|
5915
6416
|
return writeResult(res, await handlePricingDelete(requestQuery(req), deps));
|
|
@@ -5920,7 +6421,7 @@ async function handlePricing(req, res, method, rest, deps) {
|
|
|
5920
6421
|
return writeResult(res, await handlePricingFetchLatest(deps));
|
|
5921
6422
|
}
|
|
5922
6423
|
if (method === "POST" && rest.length === 1 && rest[0] === "resolve-conflicts") {
|
|
5923
|
-
return writeResult(res, await handlePricingResolveConflicts(await
|
|
6424
|
+
return writeResult(res, await handlePricingResolveConflicts(await readJsonBody4(req), deps));
|
|
5924
6425
|
}
|
|
5925
6426
|
return writeJsonError(res, 404, `unknown pricing route '${rest.join("/")}'`);
|
|
5926
6427
|
}
|
|
@@ -5934,15 +6435,15 @@ function migrationDeps(deps) {
|
|
|
5934
6435
|
}
|
|
5935
6436
|
async function handleMigrationExport(req, res, method, deps) {
|
|
5936
6437
|
if (method !== "POST") return writeJsonError(res, 405, `method ${method} not allowed on export`);
|
|
5937
|
-
const body = await
|
|
6438
|
+
const body = await readJsonBody4(req);
|
|
5938
6439
|
const result = await handleExport(body, migrationDeps(deps));
|
|
5939
|
-
return
|
|
6440
|
+
return writeJson4(res, result.status, result.body);
|
|
5940
6441
|
}
|
|
5941
6442
|
async function handleMigrationImport(req, res, method, deps) {
|
|
5942
6443
|
if (method !== "POST") return writeJsonError(res, 405, `method ${method} not allowed on import`);
|
|
5943
|
-
const body = await
|
|
6444
|
+
const body = await readJsonBody4(req);
|
|
5944
6445
|
const result = await handleImport(body, migrationDeps(deps));
|
|
5945
|
-
return
|
|
6446
|
+
return writeJson4(res, result.status, result.body);
|
|
5946
6447
|
}
|
|
5947
6448
|
async function handleProviders(req, res, method, rest, deps) {
|
|
5948
6449
|
const cfg = loadConfig(deps.configPath);
|
|
@@ -5973,13 +6474,13 @@ async function handleProviders(req, res, method, rest, deps) {
|
|
|
5973
6474
|
if (method === "GET" && rest.length === 2 && rest[1] === "reveal-key") {
|
|
5974
6475
|
const row = cfg.providers.find((p) => p.id === rest[0]);
|
|
5975
6476
|
if (!row) return writeJsonError(res, 404, `provider '${rest[0]}' not found`);
|
|
5976
|
-
return
|
|
6477
|
+
return writeJson4(res, 200, { apiKey: row.apiKey ?? "" });
|
|
5977
6478
|
}
|
|
5978
6479
|
if (method === "GET") {
|
|
5979
|
-
return
|
|
6480
|
+
return writeJson4(res, 200, { providers: cfg.providers.map(toProviderView) });
|
|
5980
6481
|
}
|
|
5981
6482
|
if (method === "POST") {
|
|
5982
|
-
const body = await
|
|
6483
|
+
const body = await readJsonBody4(req);
|
|
5983
6484
|
const provider = parseProviderInput(body, void 0);
|
|
5984
6485
|
if (!provider) return writeJsonError(res, 400, "invalid provider (id, apiFormat, baseUrl required)");
|
|
5985
6486
|
if (cfg.providers.some((p) => p.id === provider.id)) {
|
|
@@ -5987,25 +6488,25 @@ async function handleProviders(req, res, method, rest, deps) {
|
|
|
5987
6488
|
}
|
|
5988
6489
|
cfg.providers.push(provider);
|
|
5989
6490
|
persistProviders(cfg, deps);
|
|
5990
|
-
return
|
|
6491
|
+
return writeJson4(res, 201, { provider: toProviderView(provider) });
|
|
5991
6492
|
}
|
|
5992
6493
|
const id = rest[0];
|
|
5993
6494
|
if (!id) return writeJsonError(res, 400, "provider id required in path");
|
|
5994
6495
|
const idx = cfg.providers.findIndex((p) => p.id === id);
|
|
5995
6496
|
if (idx < 0) return writeJsonError(res, 404, `provider '${id}' not found`);
|
|
5996
6497
|
if (method === "PUT") {
|
|
5997
|
-
const body = await
|
|
6498
|
+
const body = await readJsonBody4(req);
|
|
5998
6499
|
const existing = cfg.providers[idx];
|
|
5999
6500
|
const updated = parseProviderInput(body, existing);
|
|
6000
6501
|
if (!updated) return writeJsonError(res, 400, "invalid provider (apiFormat, baseUrl required)");
|
|
6001
6502
|
cfg.providers[idx] = updated;
|
|
6002
6503
|
persistProviders(cfg, deps);
|
|
6003
|
-
return
|
|
6504
|
+
return writeJson4(res, 200, { provider: toProviderView(updated) });
|
|
6004
6505
|
}
|
|
6005
6506
|
if (method === "DELETE") {
|
|
6006
6507
|
cfg.providers.splice(idx, 1);
|
|
6007
6508
|
persistProviders(cfg, deps);
|
|
6008
|
-
return
|
|
6509
|
+
return writeJson4(res, 200, { ok: true });
|
|
6009
6510
|
}
|
|
6010
6511
|
return writeJsonError(res, 405, `method ${method} not allowed on providers`);
|
|
6011
6512
|
}
|
|
@@ -6014,7 +6515,7 @@ function persistProviders(cfg, deps) {
|
|
|
6014
6515
|
deps.llmConfig.reload(cfg);
|
|
6015
6516
|
}
|
|
6016
6517
|
async function handleProviderReorder(req, res, cfg, deps) {
|
|
6017
|
-
const body = await
|
|
6518
|
+
const body = await readJsonBody4(req);
|
|
6018
6519
|
const rawOrder = body["order"];
|
|
6019
6520
|
if (!Array.isArray(rawOrder)) {
|
|
6020
6521
|
return writeJsonError(res, 400, "reorder requires { order: string[] }");
|
|
@@ -6038,14 +6539,14 @@ async function handleProviderReorder(req, res, cfg, deps) {
|
|
|
6038
6539
|
}
|
|
6039
6540
|
cfg.providers = reordered;
|
|
6040
6541
|
persistProviders(cfg, deps);
|
|
6041
|
-
return
|
|
6542
|
+
return writeJson4(res, 200, { ok: true, providers: cfg.providers.map(toProviderView) });
|
|
6042
6543
|
}
|
|
6043
6544
|
async function handleDiscoverModels(res, id, cfg) {
|
|
6044
6545
|
if (!id) return writeJsonError(res, 400, "provider id required in path");
|
|
6045
6546
|
const row = cfg.providers.find((p) => p.id === id);
|
|
6046
6547
|
if (!row) return writeJsonError(res, 404, `provider '${id}' not found`);
|
|
6047
6548
|
if (row.apiFormat !== "openai" && row.apiFormat !== "openai-response") {
|
|
6048
|
-
return
|
|
6549
|
+
return writeJson4(res, 200, { models: [], unsupportedFormat: true });
|
|
6049
6550
|
}
|
|
6050
6551
|
const resolvedKey = resolveEnvKey(row.apiKey);
|
|
6051
6552
|
const base = row.baseUrl.replace(/\/+$/, "");
|
|
@@ -6053,7 +6554,7 @@ async function handleDiscoverModels(res, id, cfg) {
|
|
|
6053
6554
|
try {
|
|
6054
6555
|
const headers = { Accept: "application/json" };
|
|
6055
6556
|
if (resolvedKey) headers["Authorization"] = `Bearer ${resolvedKey}`;
|
|
6056
|
-
const response = await (0,
|
|
6557
|
+
const response = await (0, import_upstreamFetch4.fetchUpstream)(url, { method: "GET", headers }, { providerId: "byo" });
|
|
6057
6558
|
if (!response.ok) {
|
|
6058
6559
|
const text = await response.text().catch(() => "");
|
|
6059
6560
|
let message = text.slice(0, 300);
|
|
@@ -6062,32 +6563,32 @@ async function handleDiscoverModels(res, id, cfg) {
|
|
|
6062
6563
|
message = parsed?.error?.message || parsed?.message || message;
|
|
6063
6564
|
} catch {
|
|
6064
6565
|
}
|
|
6065
|
-
return
|
|
6566
|
+
return writeJson4(res, 200, {
|
|
6066
6567
|
models: [],
|
|
6067
6568
|
error: `discovery failed (${response.status})${message ? `: ${message}` : ""}`
|
|
6068
6569
|
});
|
|
6069
6570
|
}
|
|
6070
6571
|
const data = await response.json();
|
|
6071
6572
|
const models = Array.isArray(data?.data) ? data.data.map((m) => typeof m?.id === "string" ? m.id : "").filter((m) => m.length > 0) : [];
|
|
6072
|
-
return
|
|
6573
|
+
return writeJson4(res, 200, { models });
|
|
6073
6574
|
} catch (err5) {
|
|
6074
6575
|
const message = err5 instanceof Error ? err5.message : String(err5);
|
|
6075
|
-
return
|
|
6576
|
+
return writeJson4(res, 200, { models: [], error: `discovery failed: ${message}` });
|
|
6076
6577
|
}
|
|
6077
6578
|
}
|
|
6078
6579
|
async function handleTestModel(req, res, id, cfg) {
|
|
6079
6580
|
if (!id) return writeJsonError(res, 400, "provider id required in path");
|
|
6080
6581
|
const row = cfg.providers.find((p) => p.id === id);
|
|
6081
6582
|
if (!row) return writeJsonError(res, 404, `provider '${id}' not found`);
|
|
6082
|
-
const body = await
|
|
6583
|
+
const body = await readJsonBody4(req);
|
|
6083
6584
|
const model = typeof body["model"] === "string" ? body["model"].trim() : "";
|
|
6084
6585
|
if (!model) return writeJsonError(res, 400, "test requires a { model } string");
|
|
6085
6586
|
if (row.apiFormat === "gemini") {
|
|
6086
|
-
return
|
|
6587
|
+
return writeJson4(res, 200, { ok: false, unsupportedFormat: true });
|
|
6087
6588
|
}
|
|
6088
6589
|
const resolvedKey = resolveEnvKey(row.apiKey);
|
|
6089
6590
|
if (!resolvedKey) {
|
|
6090
|
-
return
|
|
6591
|
+
return writeJson4(res, 200, { ok: false, message: "no API key configured for this provider" });
|
|
6091
6592
|
}
|
|
6092
6593
|
let url = row.baseUrl.replace(/\/+$/, "");
|
|
6093
6594
|
const prompt = "Reply with the single word: OK.";
|
|
@@ -6112,7 +6613,7 @@ async function handleTestModel(req, res, id, cfg) {
|
|
|
6112
6613
|
}
|
|
6113
6614
|
const startedAt = Date.now();
|
|
6114
6615
|
try {
|
|
6115
|
-
const response = await (0,
|
|
6616
|
+
const response = await (0, import_upstreamFetch4.fetchUpstream)(
|
|
6116
6617
|
url,
|
|
6117
6618
|
{ method: "POST", headers, body: JSON.stringify(payload) },
|
|
6118
6619
|
{ providerId: "byo" }
|
|
@@ -6126,9 +6627,9 @@ async function handleTestModel(req, res, id, cfg) {
|
|
|
6126
6627
|
message = parsed?.error?.message || parsed?.message || message;
|
|
6127
6628
|
} catch {
|
|
6128
6629
|
}
|
|
6129
|
-
return
|
|
6630
|
+
return writeJson4(res, 200, { ok: false, status: response.status, latencyMs, message });
|
|
6130
6631
|
}
|
|
6131
|
-
return
|
|
6632
|
+
return writeJson4(res, 200, {
|
|
6132
6633
|
ok: true,
|
|
6133
6634
|
status: response.status,
|
|
6134
6635
|
latencyMs,
|
|
@@ -6136,7 +6637,7 @@ async function handleTestModel(req, res, id, cfg) {
|
|
|
6136
6637
|
});
|
|
6137
6638
|
} catch (err5) {
|
|
6138
6639
|
const message = err5 instanceof Error ? err5.message : String(err5);
|
|
6139
|
-
return
|
|
6640
|
+
return writeJson4(res, 200, { ok: false, latencyMs: Date.now() - startedAt, message });
|
|
6140
6641
|
}
|
|
6141
6642
|
}
|
|
6142
6643
|
function extractSampleText(text, apiFormat) {
|
|
@@ -6177,7 +6678,7 @@ async function handleProviderKeys(res, id, cfg, deps) {
|
|
|
6177
6678
|
const row = cfg.providers.find((p) => p.id === id);
|
|
6178
6679
|
if (!row) return writeJsonError(res, 404, `provider '${id}' not found`);
|
|
6179
6680
|
const cooldown = await deps.apiKeyPool.getKeyHealth(id);
|
|
6180
|
-
return
|
|
6681
|
+
return writeJson4(res, 200, { keys: toPoolKeyView(row, cooldown, deps) });
|
|
6181
6682
|
}
|
|
6182
6683
|
function parsePoolKeyInput(body, existing) {
|
|
6183
6684
|
const out = {};
|
|
@@ -6196,7 +6697,7 @@ async function handleAddProviderKey(req, res, id, cfg, deps) {
|
|
|
6196
6697
|
if (!id) return writeJsonError(res, 400, "provider id required in path");
|
|
6197
6698
|
const idx = cfg.providers.findIndex((p) => p.id === id);
|
|
6198
6699
|
if (idx < 0) return writeJsonError(res, 404, `provider '${id}' not found`);
|
|
6199
|
-
const body = await
|
|
6700
|
+
const body = await readJsonBody4(req);
|
|
6200
6701
|
const parsed = parsePoolKeyInput(body);
|
|
6201
6702
|
if (!parsed.apiKey) return writeJsonError(res, 400, "add requires a non-empty apiKey");
|
|
6202
6703
|
const keyId = `key-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
@@ -6208,7 +6709,7 @@ async function handleAddProviderKey(req, res, id, cfg, deps) {
|
|
|
6208
6709
|
row.apiKeys = [...row.apiKeys ?? [], entry];
|
|
6209
6710
|
persistProviders(cfg, deps);
|
|
6210
6711
|
const cooldown = await deps.apiKeyPool.getKeyHealth(id);
|
|
6211
|
-
return
|
|
6712
|
+
return writeJson4(res, 201, { keys: toPoolKeyView(row, cooldown, deps) });
|
|
6212
6713
|
}
|
|
6213
6714
|
async function handleUpdateProviderKey(req, res, id, keyId, cfg, deps) {
|
|
6214
6715
|
if (!id) return writeJsonError(res, 400, "provider id required in path");
|
|
@@ -6218,7 +6719,7 @@ async function handleUpdateProviderKey(req, res, id, keyId, cfg, deps) {
|
|
|
6218
6719
|
const row = cfg.providers[idx];
|
|
6219
6720
|
const keyIdx = (row.apiKeys ?? []).findIndex((k) => k.id === keyId);
|
|
6220
6721
|
if (keyIdx < 0) return writeJsonError(res, 404, `pool key '${keyId}' not found`);
|
|
6221
|
-
const body = await
|
|
6722
|
+
const body = await readJsonBody4(req);
|
|
6222
6723
|
const existing = row.apiKeys[keyIdx];
|
|
6223
6724
|
const parsed = parsePoolKeyInput(body, existing);
|
|
6224
6725
|
const entry = { id: keyId, apiKey: parsed.apiKey || existing.apiKey };
|
|
@@ -6228,7 +6729,7 @@ async function handleUpdateProviderKey(req, res, id, keyId, cfg, deps) {
|
|
|
6228
6729
|
row.apiKeys[keyIdx] = entry;
|
|
6229
6730
|
persistProviders(cfg, deps);
|
|
6230
6731
|
const cooldown = await deps.apiKeyPool.getKeyHealth(id);
|
|
6231
|
-
return
|
|
6732
|
+
return writeJson4(res, 200, { keys: toPoolKeyView(row, cooldown, deps) });
|
|
6232
6733
|
}
|
|
6233
6734
|
async function handleDeleteProviderKey(res, id, keyId, cfg, deps) {
|
|
6234
6735
|
if (!id) return writeJsonError(res, 400, "provider id required in path");
|
|
@@ -6242,7 +6743,7 @@ async function handleDeleteProviderKey(res, id, keyId, cfg, deps) {
|
|
|
6242
6743
|
if (row.apiKeys.length === 0) row.apiKeys = void 0;
|
|
6243
6744
|
persistProviders(cfg, deps);
|
|
6244
6745
|
const cooldown = await deps.apiKeyPool.getKeyHealth(id);
|
|
6245
|
-
return
|
|
6746
|
+
return writeJson4(res, 200, { keys: toPoolKeyView(row, cooldown, deps) });
|
|
6246
6747
|
}
|
|
6247
6748
|
async function handleToggleProviderKey(req, res, id, keyId, cfg, deps) {
|
|
6248
6749
|
if (!id) return writeJsonError(res, 400, "provider id required in path");
|
|
@@ -6252,11 +6753,11 @@ async function handleToggleProviderKey(req, res, id, keyId, cfg, deps) {
|
|
|
6252
6753
|
const row = cfg.providers[idx];
|
|
6253
6754
|
const keyIdx = (row.apiKeys ?? []).findIndex((k) => k.id === keyId);
|
|
6254
6755
|
if (keyIdx < 0) return writeJsonError(res, 404, `pool key '${keyId}' not found`);
|
|
6255
|
-
const body = await
|
|
6756
|
+
const body = await readJsonBody4(req);
|
|
6256
6757
|
row.apiKeys[keyIdx].enabled = Boolean(body["enabled"]);
|
|
6257
6758
|
persistProviders(cfg, deps);
|
|
6258
6759
|
const cooldown = await deps.apiKeyPool.getKeyHealth(id);
|
|
6259
|
-
return
|
|
6760
|
+
return writeJson4(res, 200, { keys: toPoolKeyView(row, cooldown, deps) });
|
|
6260
6761
|
}
|
|
6261
6762
|
function parseApiKeysInput(raw, existing) {
|
|
6262
6763
|
if (!Array.isArray(raw)) return existing;
|
|
@@ -6442,13 +6943,13 @@ function handlePresets(res, method) {
|
|
|
6442
6943
|
website: p.website,
|
|
6443
6944
|
modelsEndpoint: p.modelsEndpoint
|
|
6444
6945
|
}));
|
|
6445
|
-
return
|
|
6946
|
+
return writeJson4(res, 200, { presets, excluded });
|
|
6446
6947
|
}
|
|
6447
6948
|
async function handleKeys(req, res, method, rest, deps) {
|
|
6448
6949
|
if (method === "GET" && rest.length === 0) {
|
|
6449
6950
|
const rows = await deps.keyDb.outboundApiKeysList();
|
|
6450
6951
|
const reader = deps.keySpendReader;
|
|
6451
|
-
if (!reader) return
|
|
6952
|
+
if (!reader) return writeJson4(res, 200, { keys: rows.map(toKeyInfo) });
|
|
6452
6953
|
const now = Date.now();
|
|
6453
6954
|
const keys = await Promise.all(
|
|
6454
6955
|
rows.map(async (row) => {
|
|
@@ -6460,13 +6961,13 @@ async function handleKeys(req, res, method, rest, deps) {
|
|
|
6460
6961
|
return info;
|
|
6461
6962
|
})
|
|
6462
6963
|
);
|
|
6463
|
-
return
|
|
6964
|
+
return writeJson4(res, 200, { keys });
|
|
6464
6965
|
}
|
|
6465
6966
|
if (method === "POST" && rest.length === 0) {
|
|
6466
|
-
const body = await
|
|
6967
|
+
const body = await readJsonBody4(req);
|
|
6467
6968
|
const name = typeof body["name"] === "string" && body["name"].trim() ? body["name"].trim() : "key";
|
|
6468
|
-
const created = await (0,
|
|
6469
|
-
return
|
|
6969
|
+
const created = await (0, import_outbound_api5.createNamedKey)(deps.keyDb, name);
|
|
6970
|
+
return writeJson4(res, 201, {
|
|
6470
6971
|
id: created.id,
|
|
6471
6972
|
name: created.name,
|
|
6472
6973
|
keyPrefix: created.keyPrefix,
|
|
@@ -6477,7 +6978,7 @@ async function handleKeys(req, res, method, rest, deps) {
|
|
|
6477
6978
|
}
|
|
6478
6979
|
if (method === "GET" && rest.length === 2 && rest[1] === "reveal") {
|
|
6479
6980
|
const revealed = await deps.keyDb.outboundApiKeysReveal(rest[0]);
|
|
6480
|
-
if (revealed !== null) return
|
|
6981
|
+
if (revealed !== null) return writeJson4(res, 200, { key: revealed });
|
|
6481
6982
|
const exists = (await deps.keyDb.outboundApiKeysList()).some((r) => r.id === rest[0]);
|
|
6482
6983
|
if (!exists) return writeJsonError(res, 404, `key '${rest[0]}' not found`);
|
|
6483
6984
|
return writeJsonError(
|
|
@@ -6492,32 +6993,32 @@ async function handleKeys(req, res, method, rest, deps) {
|
|
|
6492
6993
|
const bound = await integrationKeyRequirement(deps, id);
|
|
6493
6994
|
if (bound) return writeJsonError(res, 409, bound);
|
|
6494
6995
|
const ok = await deps.keyDb.outboundApiKeysRevoke(id);
|
|
6495
|
-
return
|
|
6996
|
+
return writeJson4(res, ok ? 200 : 404, { ok });
|
|
6496
6997
|
}
|
|
6497
6998
|
if (method === "DELETE" && id && !action) {
|
|
6498
6999
|
const bound = await integrationKeyRequirement(deps, id);
|
|
6499
7000
|
if (bound) return writeJsonError(res, 409, bound);
|
|
6500
7001
|
const ok = await deps.keyDb.outboundApiKeysDelete(id);
|
|
6501
|
-
return
|
|
7002
|
+
return writeJson4(res, ok ? 200 : 404, { ok });
|
|
6502
7003
|
}
|
|
6503
7004
|
if (method === "POST" && id && action === "enabled") {
|
|
6504
|
-
const body = await
|
|
7005
|
+
const body = await readJsonBody4(req);
|
|
6505
7006
|
const enabled = body["enabled"] === true;
|
|
6506
7007
|
if (!enabled) {
|
|
6507
7008
|
const bound = await integrationKeyRequirement(deps, id);
|
|
6508
7009
|
if (bound) return writeJsonError(res, 409, bound);
|
|
6509
7010
|
}
|
|
6510
7011
|
const ok = await deps.keyDb.outboundApiKeysSetEnabled(id, enabled);
|
|
6511
|
-
return
|
|
7012
|
+
return writeJson4(res, ok ? 200 : 404, { ok, enabled });
|
|
6512
7013
|
}
|
|
6513
7014
|
if (method === "POST" && id && action === "permissions") {
|
|
6514
|
-
const body = await
|
|
7015
|
+
const body = await readJsonBody4(req);
|
|
6515
7016
|
if (Object.keys(body).length !== 1 || !Object.prototype.hasOwnProperty.call(body, "permissions")) {
|
|
6516
7017
|
return writeJsonError(res, 400, "body must contain only permissions");
|
|
6517
7018
|
}
|
|
6518
7019
|
let permissions;
|
|
6519
7020
|
try {
|
|
6520
|
-
permissions = (0,
|
|
7021
|
+
permissions = (0, import_outbound_api5.validateOutboundPermissions)(body["permissions"]);
|
|
6521
7022
|
} catch {
|
|
6522
7023
|
return writeJsonError(
|
|
6523
7024
|
res,
|
|
@@ -6526,19 +7027,19 @@ async function handleKeys(req, res, method, rest, deps) {
|
|
|
6526
7027
|
);
|
|
6527
7028
|
}
|
|
6528
7029
|
const before = (await deps.keyDb.outboundApiKeysList()).find((row) => row.id === id);
|
|
6529
|
-
if (!before) return
|
|
6530
|
-
if (before.revokedAt !== null) return
|
|
7030
|
+
if (!before) return writeJson4(res, 404, { ok: false });
|
|
7031
|
+
if (before.revokedAt !== null) return writeJson4(res, 409, { ok: false });
|
|
6531
7032
|
const required = await integrationKeyRequirement(deps, id, permissions);
|
|
6532
7033
|
if (required) return writeJsonError(res, 409, required);
|
|
6533
7034
|
const ok = await deps.keyDb.outboundApiKeysSetPermissions(id, permissions);
|
|
6534
7035
|
if (!ok) {
|
|
6535
7036
|
const current = (await deps.keyDb.outboundApiKeysList()).find((row) => row.id === id);
|
|
6536
|
-
return
|
|
7037
|
+
return writeJson4(res, current?.revokedAt !== null ? 409 : 404, { ok: false });
|
|
6537
7038
|
}
|
|
6538
|
-
return
|
|
7039
|
+
return writeJson4(res, 200, { ok: true, allowedEndpoints: permissions });
|
|
6539
7040
|
}
|
|
6540
7041
|
if (method === "POST" && id && action === "max-concurrency") {
|
|
6541
|
-
const body = await
|
|
7042
|
+
const body = await readJsonBody4(req);
|
|
6542
7043
|
const raw = body["maxConcurrency"];
|
|
6543
7044
|
let value;
|
|
6544
7045
|
if (raw === null) {
|
|
@@ -6553,14 +7054,14 @@ async function handleKeys(req, res, method, rest, deps) {
|
|
|
6553
7054
|
);
|
|
6554
7055
|
}
|
|
6555
7056
|
const ok = await deps.keyDb.outboundApiKeysSetMaxConcurrency(id, value);
|
|
6556
|
-
return
|
|
7057
|
+
return writeJson4(res, ok ? 200 : 404, { ok, maxConcurrency: value });
|
|
6557
7058
|
}
|
|
6558
7059
|
if (method === "POST" && id && action === "policy") {
|
|
6559
|
-
const body = await
|
|
7060
|
+
const body = await readJsonBody4(req);
|
|
6560
7061
|
const parsed = parseKeyPolicyBody(body);
|
|
6561
7062
|
if (!parsed.ok) return writeJsonError(res, 400, parsed.message);
|
|
6562
7063
|
const ok = await deps.keyDb.outboundApiKeysSetPolicy(id, parsed.policy);
|
|
6563
|
-
return
|
|
7064
|
+
return writeJson4(res, ok ? 200 : 404, { ok });
|
|
6564
7065
|
}
|
|
6565
7066
|
return writeJsonError(res, 405, `method ${method} not allowed on keys`);
|
|
6566
7067
|
}
|
|
@@ -6651,7 +7152,8 @@ function outboundServerConfigInput(config) {
|
|
|
6651
7152
|
userMessageQueue: config.userMessageQueue,
|
|
6652
7153
|
concurrencyQueue: config.concurrencyQueue,
|
|
6653
7154
|
voucher: config.voucher,
|
|
6654
|
-
anthropic: config.anthropic
|
|
7155
|
+
anthropic: config.anthropic,
|
|
7156
|
+
search: config.search
|
|
6655
7157
|
};
|
|
6656
7158
|
}
|
|
6657
7159
|
function projectImagesConfigForAdmin(config) {
|
|
@@ -6687,8 +7189,8 @@ function sameConfigValue(left, right) {
|
|
|
6687
7189
|
return JSON.stringify(left) === JSON.stringify(right);
|
|
6688
7190
|
}
|
|
6689
7191
|
function imageConfigurationAuditFields(previous, next) {
|
|
6690
|
-
const before = previous ??
|
|
6691
|
-
const after = next ??
|
|
7192
|
+
const before = previous ?? import_outbound_api5.DEFAULT_IMAGES_SERVER_CONFIG;
|
|
7193
|
+
const after = next ?? import_outbound_api5.DEFAULT_IMAGES_SERVER_CONFIG;
|
|
6692
7194
|
const fields = [];
|
|
6693
7195
|
if (before.enabled !== after.enabled) fields.push("enablement");
|
|
6694
7196
|
if (before.provider !== after.provider) fields.push("provider");
|
|
@@ -6714,15 +7216,21 @@ function currentImageGenerationId(deps) {
|
|
|
6714
7216
|
}
|
|
6715
7217
|
async function handleServer(req, res, method, deps) {
|
|
6716
7218
|
if (method === "GET") {
|
|
6717
|
-
const config = await (0,
|
|
7219
|
+
const config = await (0, import_outbound_api5.loadServerConfig)(deps.settingsStore);
|
|
6718
7220
|
let server = config;
|
|
6719
7221
|
if (config.proxy) server = { ...server, proxy: redactOutboundProxy(config.proxy) };
|
|
6720
7222
|
if (config.webhook) server = { ...server, webhook: redactWebhookConfig(config.webhook) };
|
|
6721
7223
|
if (config.billing) server = { ...server, billing: redactBillingConfig(config.billing) };
|
|
6722
|
-
|
|
7224
|
+
if (config.search) {
|
|
7225
|
+
server = {
|
|
7226
|
+
...server,
|
|
7227
|
+
search: redactSearchServerConfig(config.search)
|
|
7228
|
+
};
|
|
7229
|
+
}
|
|
7230
|
+
return writeJson4(res, 200, { server: projectImagesConfigForAdmin(server) });
|
|
6723
7231
|
}
|
|
6724
7232
|
if (method === "PUT") {
|
|
6725
|
-
const patch = await
|
|
7233
|
+
const patch = await readJsonBody4(req);
|
|
6726
7234
|
const queueErrors = validateQueueSegments(patch);
|
|
6727
7235
|
if (queueErrors.length > 0) {
|
|
6728
7236
|
return writeJsonError(res, 400, `invalid queue config: ${queueErrors.join("; ")}`);
|
|
@@ -6751,7 +7259,7 @@ async function handleServer(req, res, method, deps) {
|
|
|
6751
7259
|
if (billingErrors.length > 0) {
|
|
6752
7260
|
return writeJsonError(res, 400, `invalid billing config: ${billingErrors.join("; ")}`);
|
|
6753
7261
|
}
|
|
6754
|
-
const current = await (0,
|
|
7262
|
+
const current = await (0, import_outbound_api5.loadServerConfig)(deps.settingsStore);
|
|
6755
7263
|
let effectivePatch = patch;
|
|
6756
7264
|
if (patch.proxy) {
|
|
6757
7265
|
effectivePatch = { ...effectivePatch, proxy: preserveOutboundProxySecrets(patch.proxy, current.proxy) };
|
|
@@ -6772,7 +7280,21 @@ async function handleServer(req, res, method, deps) {
|
|
|
6772
7280
|
}
|
|
6773
7281
|
effectivePatch = { ...effectivePatch, images };
|
|
6774
7282
|
}
|
|
6775
|
-
|
|
7283
|
+
if (patch.search !== void 0) {
|
|
7284
|
+
const searchPatch = preserveSearchSecrets(
|
|
7285
|
+
patch.search,
|
|
7286
|
+
current.search ?? import_outbound_api5.DEFAULT_SEARCH_SERVER_CONFIG
|
|
7287
|
+
);
|
|
7288
|
+
const searchErrors = (0, import_outbound_api5.validateSearchServerConfig)(searchPatch);
|
|
7289
|
+
if (searchErrors.length > 0) {
|
|
7290
|
+
return writeJsonError(res, 400, `invalid search config: ${searchErrors.join("; ")}`);
|
|
7291
|
+
}
|
|
7292
|
+
effectivePatch = {
|
|
7293
|
+
...effectivePatch,
|
|
7294
|
+
search: searchPatch
|
|
7295
|
+
};
|
|
7296
|
+
}
|
|
7297
|
+
const merged = (0, import_outbound_api5.mergeServerConfig)(current, effectivePatch);
|
|
6776
7298
|
const priorImageGenerationId = currentImageGenerationId(deps);
|
|
6777
7299
|
try {
|
|
6778
7300
|
await applyServerConfigTransaction(current, merged, {
|
|
@@ -6802,7 +7324,7 @@ async function handleServer(req, res, method, deps) {
|
|
|
6802
7324
|
throw error;
|
|
6803
7325
|
}
|
|
6804
7326
|
},
|
|
6805
|
-
persist: (next) => (0,
|
|
7327
|
+
persist: (next) => (0, import_outbound_api5.saveServerConfig)(deps.settingsStore, next),
|
|
6806
7328
|
restorePersisted: (snapshot) => deps.settingsStore.restoreDocumentSnapshot(snapshot)
|
|
6807
7329
|
});
|
|
6808
7330
|
} catch (error) {
|
|
@@ -6824,7 +7346,11 @@ async function handleServer(req, res, method, deps) {
|
|
|
6824
7346
|
} catch {
|
|
6825
7347
|
}
|
|
6826
7348
|
}
|
|
6827
|
-
|
|
7349
|
+
const mergedForAdmin = merged.search ? {
|
|
7350
|
+
...merged,
|
|
7351
|
+
search: redactSearchServerConfig(merged.search)
|
|
7352
|
+
} : merged;
|
|
7353
|
+
return writeJson4(res, 200, { server: projectImagesConfigForAdmin(mergedForAdmin) });
|
|
6828
7354
|
}
|
|
6829
7355
|
return writeJsonError(res, 405, `method ${method} not allowed on server`);
|
|
6830
7356
|
}
|
|
@@ -6841,7 +7367,7 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
6841
7367
|
sessionKey: query2.get("sessionKey") ?? void 0,
|
|
6842
7368
|
limit: Number.isFinite(parsedLimit) ? parsedLimit : 100
|
|
6843
7369
|
});
|
|
6844
|
-
return
|
|
7370
|
+
return writeJson4(res, 200, {
|
|
6845
7371
|
available: true,
|
|
6846
7372
|
records,
|
|
6847
7373
|
capacity: import_AccountRouteActivity.ACCOUNT_ROUTE_ACTIVITY_LIMIT,
|
|
@@ -6857,7 +7383,7 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
6857
7383
|
providerId: query2.get("providerId") ?? void 0,
|
|
6858
7384
|
accountId: query2.get("accountId") ?? void 0
|
|
6859
7385
|
});
|
|
6860
|
-
return
|
|
7386
|
+
return writeJson4(res, 200, {
|
|
6861
7387
|
available: true,
|
|
6862
7388
|
entries,
|
|
6863
7389
|
collectedAt: Date.now()
|
|
@@ -6876,10 +7402,10 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
6876
7402
|
const accounts = await deps.subscriptionAccounts.listAll();
|
|
6877
7403
|
const providerAccounts = await deps.subscriptionTokenWriter.listSanitizedAccounts();
|
|
6878
7404
|
const externalCli = await deps.subscriptionTokenWriter.listExternalCliAvailability();
|
|
6879
|
-
return
|
|
7405
|
+
return writeJson4(res, 200, { accounts, providerAccounts, externalCli });
|
|
6880
7406
|
}
|
|
6881
7407
|
if (method === "POST" && rest[0] === "batch" && rest.length === 1) {
|
|
6882
|
-
const body = await
|
|
7408
|
+
const body = await readJsonBody4(req);
|
|
6883
7409
|
const parsed = validateAccountBatchBody(body);
|
|
6884
7410
|
if (!parsed) return writeJsonError(res, 400, "invalid account batch request");
|
|
6885
7411
|
const result = await deps.subscriptionTokenWriter.batchManageAccounts(parsed.refs, parsed.mutation);
|
|
@@ -6895,15 +7421,15 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
6895
7421
|
deps.accountAllowanceService?.removeAccountSnapshot?.(ref.providerId, ref.accountId);
|
|
6896
7422
|
}
|
|
6897
7423
|
}
|
|
6898
|
-
return
|
|
7424
|
+
return writeJson4(res, 200, { ok: true, affected: result.affected });
|
|
6899
7425
|
}
|
|
6900
7426
|
if (method === "GET" && rest[0] === "codex" && rest[1] === "oauth" && rest[3] === "status") {
|
|
6901
7427
|
const result = handleCodexOAuthStatus(rest[2], deps);
|
|
6902
|
-
return
|
|
7428
|
+
return writeJson4(res, result.status, result.body);
|
|
6903
7429
|
}
|
|
6904
7430
|
if (method === "DELETE" && rest[0] === "codex" && rest[1] === "oauth" && rest[2]) {
|
|
6905
7431
|
const result = handleCodexOAuthCancel(rest[2], deps);
|
|
6906
|
-
return
|
|
7432
|
+
return writeJson4(res, result.status, result.body);
|
|
6907
7433
|
}
|
|
6908
7434
|
if (method === "GET" && rest.length === 3 && rest[2] === "diagnostics") {
|
|
6909
7435
|
const providerId = asSubscriptionProviderId(rest[0]);
|
|
@@ -6925,7 +7451,7 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
6925
7451
|
resumeAt: entry.resumeAt
|
|
6926
7452
|
})) ?? [];
|
|
6927
7453
|
const diagnostics = [...health2, ...allowance].sort((left, right) => right.at - left.at).slice(0, 200);
|
|
6928
|
-
return
|
|
7454
|
+
return writeJson4(res, 200, { diagnostics });
|
|
6929
7455
|
}
|
|
6930
7456
|
if (method === "GET" && rest.length === 3 && rest[2] === "events") {
|
|
6931
7457
|
const providerId = asSubscriptionProviderId(rest[0]);
|
|
@@ -6937,17 +7463,17 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
6937
7463
|
}
|
|
6938
7464
|
const snapshot = deps.accountProbeService?.getAllHistory().find((entry) => entry.providerId === providerId && entry.accountId === accountId);
|
|
6939
7465
|
const diagnostics = (0, import_SubscriptionAccountHealth.getSharedAccountHealth)().getDiagnostics({ providerId, accountId });
|
|
6940
|
-
return
|
|
7466
|
+
return writeJson4(res, 200, { events: snapshot?.records ?? [], diagnostics });
|
|
6941
7467
|
}
|
|
6942
7468
|
if (method === "PATCH" && rest.length === 2) {
|
|
6943
7469
|
const providerId = asSubscriptionProviderId(rest[0]);
|
|
6944
7470
|
if (!providerId) return writeJsonError(res, 400, `unknown subscription provider '${rest[0] ?? ""}'`);
|
|
6945
|
-
const body = await
|
|
7471
|
+
const body = await readJsonBody4(req);
|
|
6946
7472
|
const patch = validateAccountMetadataPatch(body);
|
|
6947
7473
|
if (!patch) return writeJsonError(res, 400, "invalid account metadata patch");
|
|
6948
7474
|
const result = await deps.subscriptionTokenWriter.patchAccountMetadata(providerId, rest[1], patch);
|
|
6949
7475
|
if (!result.ok) return writeJsonError(res, 404, `account '${rest[1]}' not found`);
|
|
6950
|
-
return
|
|
7476
|
+
return writeJson4(res, 200, { ok: true });
|
|
6951
7477
|
}
|
|
6952
7478
|
if (method === "PUT" || method === "POST" || method === "DELETE") {
|
|
6953
7479
|
const providerId = asSubscriptionProviderId(rest[0]);
|
|
@@ -6956,15 +7482,15 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
6956
7482
|
}
|
|
6957
7483
|
if (method === "POST" && rest[1] === "oauth" && rest[2] === "start") {
|
|
6958
7484
|
const result = providerId === "codex" ? handleCodexOAuthStart(deps) : handleOAuthStart(providerId, deps);
|
|
6959
|
-
return
|
|
7485
|
+
return writeJson4(res, result.status, result.body);
|
|
6960
7486
|
}
|
|
6961
7487
|
if (method === "POST" && rest[1] === "oauth" && rest[2] === "complete") {
|
|
6962
|
-
const body2 = await
|
|
7488
|
+
const body2 = await readJsonBody4(req);
|
|
6963
7489
|
const result = await handleOAuthComplete(providerId, body2, deps);
|
|
6964
|
-
return
|
|
7490
|
+
return writeJson4(res, result.status, result.body);
|
|
6965
7491
|
}
|
|
6966
7492
|
if (method === "POST" && rest[1] === "accounts") {
|
|
6967
|
-
const body2 = await
|
|
7493
|
+
const body2 = await readJsonBody4(req);
|
|
6968
7494
|
const block = validateTokenBody(providerId, body2);
|
|
6969
7495
|
if (!block) {
|
|
6970
7496
|
return writeJsonError(res, 400, `malformed token body for provider '${providerId}'`);
|
|
@@ -6972,20 +7498,20 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
6972
7498
|
const label = typeof body2["label"] === "string" && body2["label"].trim() ? body2["label"].trim() : void 0;
|
|
6973
7499
|
await deps.subscriptionAccountAppender.appendProviderAccount(providerId, block, label);
|
|
6974
7500
|
const status2 = await statusEntryFor(deps.subscriptionAccounts, providerId);
|
|
6975
|
-
return
|
|
7501
|
+
return writeJson4(res, 200, status2 ? { account: status2 } : { ok: true });
|
|
6976
7502
|
}
|
|
6977
7503
|
if (method === "POST" && rest[1] === "import-external") {
|
|
6978
7504
|
if (providerId !== "claude" && providerId !== "codex") {
|
|
6979
7505
|
return writeJsonError(res, 400, `provider '${providerId}' has no external CLI store`);
|
|
6980
7506
|
}
|
|
6981
|
-
const body2 = await
|
|
7507
|
+
const body2 = await readJsonBody4(req);
|
|
6982
7508
|
const label = typeof body2["label"] === "string" && body2["label"].trim() ? body2["label"].trim() : void 0;
|
|
6983
7509
|
const result = await deps.subscriptionTokenWriter.importExternalCliAccount(providerId, label);
|
|
6984
7510
|
if (!result.ok) {
|
|
6985
7511
|
return writeJsonError(res, 409, `no usable external ${providerId} CLI credential found`);
|
|
6986
7512
|
}
|
|
6987
7513
|
const status2 = await statusEntryFor(deps.subscriptionAccounts, providerId);
|
|
6988
|
-
return
|
|
7514
|
+
return writeJson4(res, 200, {
|
|
6989
7515
|
ok: true,
|
|
6990
7516
|
account: status2 ?? void 0,
|
|
6991
7517
|
nativeCredentialMode: result.nativeCredentialMode,
|
|
@@ -7000,7 +7526,7 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
7000
7526
|
const writer2 = deps.subscriptionTokenWriter;
|
|
7001
7527
|
const ok = providerId === "claude" ? await writer2.refreshClaudeToken() : providerId === "codex" ? await writer2.refreshCodexToken() : await writer2.refreshGeminiToken();
|
|
7002
7528
|
const status2 = await statusEntryFor(deps.subscriptionAccounts, providerId);
|
|
7003
|
-
return
|
|
7529
|
+
return writeJson4(res, 200, { ok, account: status2 ?? void 0 });
|
|
7004
7530
|
}
|
|
7005
7531
|
if (method === "POST" && rest.length === 3 && rest[2] === "test") {
|
|
7006
7532
|
const accountId = rest[1];
|
|
@@ -7010,7 +7536,7 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
7010
7536
|
return writeJsonError(res, 404, `account '${accountId}' not found`);
|
|
7011
7537
|
}
|
|
7012
7538
|
const result = await deps.accountProbeService.testAccountConnection(providerId, accountId);
|
|
7013
|
-
return
|
|
7539
|
+
return writeJson4(res, 200, {
|
|
7014
7540
|
ok: result.ok,
|
|
7015
7541
|
marked: result.marked,
|
|
7016
7542
|
tier: result.tier,
|
|
@@ -7019,15 +7545,15 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
7019
7545
|
}
|
|
7020
7546
|
if (method === "POST" && rest[2] === "label") {
|
|
7021
7547
|
const accountId = rest[1];
|
|
7022
|
-
const body2 = await
|
|
7548
|
+
const body2 = await readJsonBody4(req);
|
|
7023
7549
|
const label = typeof body2["label"] === "string" ? body2["label"].trim() : "";
|
|
7024
7550
|
const result = await deps.subscriptionTokenWriter.renameAccount(providerId, accountId, label);
|
|
7025
7551
|
if (!result.ok) return writeJsonError(res, 404, `account '${accountId}' not found`);
|
|
7026
|
-
return
|
|
7552
|
+
return writeJson4(res, 200, { ok: true });
|
|
7027
7553
|
}
|
|
7028
7554
|
if (method === "POST" && rest[2] === "priority") {
|
|
7029
7555
|
const accountId = rest[1];
|
|
7030
|
-
const body2 = await
|
|
7556
|
+
const body2 = await readJsonBody4(req);
|
|
7031
7557
|
const raw = body2["priority"];
|
|
7032
7558
|
const priority = typeof raw === "number" ? raw : Number(raw);
|
|
7033
7559
|
if (!Number.isFinite(priority)) {
|
|
@@ -7035,76 +7561,76 @@ async function handleAccounts(req, res, method, rest, deps) {
|
|
|
7035
7561
|
}
|
|
7036
7562
|
const result = await deps.subscriptionTokenWriter.setAccountPriority(providerId, accountId, priority);
|
|
7037
7563
|
if (!result.ok) return writeJsonError(res, 404, `account '${accountId}' not found`);
|
|
7038
|
-
return
|
|
7564
|
+
return writeJson4(res, 200, { ok: true });
|
|
7039
7565
|
}
|
|
7040
7566
|
if (method === "POST" && rest[2] === "proxy") {
|
|
7041
7567
|
const accountId = rest[1];
|
|
7042
|
-
const body2 = await
|
|
7568
|
+
const body2 = await readJsonBody4(req);
|
|
7043
7569
|
const rawProxy = body2["proxy"];
|
|
7044
7570
|
let proxy;
|
|
7045
7571
|
if (rawProxy !== null && rawProxy !== void 0) {
|
|
7046
|
-
proxy = (0,
|
|
7572
|
+
proxy = (0, import_outbound_api5.normalizeProxyConfig)(rawProxy);
|
|
7047
7573
|
if (!proxy) return writeJsonError(res, 400, "invalid proxy config");
|
|
7048
7574
|
}
|
|
7049
7575
|
const result = await deps.subscriptionTokenWriter.setAccountProxy(providerId, accountId, proxy);
|
|
7050
7576
|
if (!result.ok) return writeJsonError(res, 404, `account '${accountId}' not found`);
|
|
7051
|
-
return
|
|
7577
|
+
return writeJson4(res, 200, { ok: true });
|
|
7052
7578
|
}
|
|
7053
7579
|
if (method === "POST" && rest[2] === "supported-models") {
|
|
7054
7580
|
const accountId = rest[1];
|
|
7055
|
-
const body2 = await
|
|
7581
|
+
const body2 = await readJsonBody4(req);
|
|
7056
7582
|
const parsed = validateSupportedModelsBody(body2["supportedModels"]);
|
|
7057
7583
|
if (!parsed.ok) return writeJsonError(res, 400, "invalid supportedModels");
|
|
7058
7584
|
const result = await deps.subscriptionTokenWriter.setAccountSupportedModels(providerId, accountId, parsed.value);
|
|
7059
7585
|
if (!result.ok) return writeJsonError(res, 404, `account '${accountId}' not found`);
|
|
7060
|
-
return
|
|
7586
|
+
return writeJson4(res, 200, { ok: true });
|
|
7061
7587
|
}
|
|
7062
7588
|
if (method === "PUT" && rest[1] === "active") {
|
|
7063
|
-
const body2 = await
|
|
7589
|
+
const body2 = await readJsonBody4(req);
|
|
7064
7590
|
const id = typeof body2["id"] === "string" ? body2["id"] : "";
|
|
7065
7591
|
if (!id) return writeJsonError(res, 400, "active switch requires { id }");
|
|
7066
7592
|
const result = await deps.subscriptionTokenWriter.setActiveAccount(providerId, id);
|
|
7067
7593
|
if (!result.ok) return writeJsonError(res, 404, `account '${id}' not found`);
|
|
7068
|
-
return
|
|
7594
|
+
return writeJson4(res, 200, { ok: true });
|
|
7069
7595
|
}
|
|
7070
7596
|
if (method === "DELETE" && rest.length === 2) {
|
|
7071
7597
|
const accountId = rest[1];
|
|
7072
7598
|
const result = await deps.subscriptionTokenWriter.removeAccount(providerId, accountId);
|
|
7073
7599
|
if (!result.removed) return writeJsonError(res, 404, `account '${accountId}' not found`);
|
|
7074
7600
|
deps.accountAllowanceService?.removeAccountSnapshot?.(providerId, accountId);
|
|
7075
|
-
return
|
|
7601
|
+
return writeJson4(res, 200, { ok: true });
|
|
7076
7602
|
}
|
|
7077
7603
|
if (method === "DELETE" && rest.length === 1) {
|
|
7078
7604
|
await deps.subscriptionTokenWriter.clearProvider(providerId);
|
|
7079
7605
|
deps.accountAllowanceService?.removeProviderSnapshots?.(providerId);
|
|
7080
|
-
return
|
|
7606
|
+
return writeJson4(res, 200, { ok: true });
|
|
7081
7607
|
}
|
|
7082
7608
|
if (method === "DELETE") {
|
|
7083
7609
|
return writeJsonError(res, 405, "method DELETE not allowed on this accounts path");
|
|
7084
7610
|
}
|
|
7085
|
-
const body = await
|
|
7611
|
+
const body = await readJsonBody4(req);
|
|
7086
7612
|
const config = validateTokenBody(providerId, body);
|
|
7087
7613
|
if (!config) {
|
|
7088
7614
|
return writeJsonError(res, 400, `malformed token body for provider '${providerId}'`);
|
|
7089
7615
|
}
|
|
7090
7616
|
await deps.subscriptionTokenWriter.writeProviderTokens(providerId, config);
|
|
7091
7617
|
const status = await statusEntryFor(deps.subscriptionAccounts, providerId);
|
|
7092
|
-
return
|
|
7618
|
+
return writeJson4(res, 200, status ? { account: status } : { ok: true });
|
|
7093
7619
|
}
|
|
7094
7620
|
return writeJsonError(res, 405, `method ${method} not allowed on accounts`);
|
|
7095
7621
|
}
|
|
7096
7622
|
async function handleCli(req, res, method, rest, deps) {
|
|
7097
7623
|
if (method === "GET" && rest.length === 0) {
|
|
7098
7624
|
const result = handleCliList(process.platform, deps.cliPathProbe);
|
|
7099
|
-
return
|
|
7625
|
+
return writeJson4(res, result.status, result.body);
|
|
7100
7626
|
}
|
|
7101
7627
|
if (method === "GET" && rest[0] === "sessions") {
|
|
7102
7628
|
const result = handleCliSessions();
|
|
7103
|
-
return
|
|
7629
|
+
return writeJson4(res, result.status, result.body);
|
|
7104
7630
|
}
|
|
7105
7631
|
if (method === "DELETE" && rest[0] === "sessions" && rest[1]) {
|
|
7106
7632
|
const result = handleCliStop(rest[1]);
|
|
7107
|
-
return
|
|
7633
|
+
return writeJson4(res, result.status, result.body);
|
|
7108
7634
|
}
|
|
7109
7635
|
if (method === "POST" && rest[1] === "install") {
|
|
7110
7636
|
const cli = rest[0];
|
|
@@ -7112,14 +7638,14 @@ async function handleCli(req, res, method, rest, deps) {
|
|
|
7112
7638
|
return writeJsonError(res, 400, `unknown cli '${cli ?? ""}'`);
|
|
7113
7639
|
}
|
|
7114
7640
|
const result = await handleCliInstall(cli, deps.cliCommandRunner);
|
|
7115
|
-
return
|
|
7641
|
+
return writeJson4(res, result.status, result.body);
|
|
7116
7642
|
}
|
|
7117
7643
|
if (method === "POST" && rest[1] === "launch") {
|
|
7118
7644
|
const cli = rest[0];
|
|
7119
7645
|
if (!isLaunchCliId(cli)) {
|
|
7120
7646
|
return writeJsonError(res, 400, `unknown cli '${cli ?? ""}'`);
|
|
7121
7647
|
}
|
|
7122
|
-
const body = await
|
|
7648
|
+
const body = await readJsonBody4(req);
|
|
7123
7649
|
const providers = loadConfig(deps.configPath).providers ?? [];
|
|
7124
7650
|
const result = await handleCliLaunch(cli, body, {
|
|
7125
7651
|
llmConfig: deps.llmConfig,
|
|
@@ -7128,7 +7654,7 @@ async function handleCli(req, res, method, rest, deps) {
|
|
|
7128
7654
|
opener: deps.cliTerminalOpener,
|
|
7129
7655
|
probe: deps.cliPathProbe
|
|
7130
7656
|
});
|
|
7131
|
-
return
|
|
7657
|
+
return writeJson4(res, result.status, result.body);
|
|
7132
7658
|
}
|
|
7133
7659
|
return writeJsonError(res, 405, `method ${method} not allowed on cli`);
|
|
7134
7660
|
}
|
|
@@ -7138,52 +7664,52 @@ async function handleIntegrations(req, res, method, rest, deps) {
|
|
|
7138
7664
|
const manager = factory();
|
|
7139
7665
|
try {
|
|
7140
7666
|
if (method === "GET" && rest.length === 0) {
|
|
7141
|
-
return
|
|
7667
|
+
return writeJson4(res, 200, {
|
|
7142
7668
|
integrations: await manager.listStatus(),
|
|
7143
7669
|
gateway: deps.outboundApiServer.getStatus()
|
|
7144
7670
|
});
|
|
7145
7671
|
}
|
|
7146
7672
|
if (method === "POST" && rest.length === 1 && rest[0] === "rotate") {
|
|
7147
7673
|
await manager.rotateGatewayKey();
|
|
7148
|
-
return
|
|
7674
|
+
return writeJson4(res, 200, { ok: true, integrations: await manager.listStatus() });
|
|
7149
7675
|
}
|
|
7150
7676
|
const client = rest[0];
|
|
7151
7677
|
if (!isIntegrationClient(client)) {
|
|
7152
7678
|
return writeJsonError(res, 400, `unknown integration client '${client ?? ""}'`);
|
|
7153
7679
|
}
|
|
7154
7680
|
if (method === "POST" && rest[1] === "key") {
|
|
7155
|
-
const body = await
|
|
7681
|
+
const body = await readJsonBody4(req);
|
|
7156
7682
|
if (Object.keys(body).length !== 1 || typeof body.keyId !== "string" || !body.keyId.trim()) {
|
|
7157
7683
|
return writeJsonError(res, 400, "body must contain a non-empty keyId string");
|
|
7158
7684
|
}
|
|
7159
7685
|
const status = await manager.bindIntegrationKey(client, body.keyId.trim());
|
|
7160
|
-
return
|
|
7686
|
+
return writeJson4(res, 200, { integration: status });
|
|
7161
7687
|
}
|
|
7162
7688
|
if (method === "POST" && rest[1] === "plan") {
|
|
7163
|
-
const body = await
|
|
7689
|
+
const body = await readJsonBody4(req);
|
|
7164
7690
|
const configPath = body.configPath;
|
|
7165
7691
|
if (configPath !== void 0 && typeof configPath !== "string") {
|
|
7166
7692
|
return writeJsonError(res, 400, "configPath must be a string");
|
|
7167
7693
|
}
|
|
7168
7694
|
const plan = await manager.plan(client, configPath);
|
|
7169
|
-
return
|
|
7695
|
+
return writeJson4(res, 200, { plan });
|
|
7170
7696
|
}
|
|
7171
7697
|
if (method === "POST" && (rest[1] === "install" || rest[1] === "apply")) {
|
|
7172
|
-
const body = await
|
|
7698
|
+
const body = await readJsonBody4(req);
|
|
7173
7699
|
const configPath = body.configPath;
|
|
7174
7700
|
if (configPath !== void 0 && typeof configPath !== "string") {
|
|
7175
7701
|
return writeJsonError(res, 400, "configPath must be a string");
|
|
7176
7702
|
}
|
|
7177
7703
|
const status = await manager.install(client, configPath);
|
|
7178
|
-
return
|
|
7704
|
+
return writeJson4(res, 200, { integration: status });
|
|
7179
7705
|
}
|
|
7180
7706
|
if (method === "POST" && rest[1] === "repair") {
|
|
7181
7707
|
const status = await manager.repair(client);
|
|
7182
|
-
return
|
|
7708
|
+
return writeJson4(res, 200, { integration: status });
|
|
7183
7709
|
}
|
|
7184
7710
|
if (method === "DELETE" && rest.length === 1 || method === "POST" && rest[1] === "remove") {
|
|
7185
7711
|
const status = await manager.remove(client);
|
|
7186
|
-
return
|
|
7712
|
+
return writeJson4(res, 200, { integration: status });
|
|
7187
7713
|
}
|
|
7188
7714
|
return writeJsonError(res, 405, `method ${method} not allowed on integrations`);
|
|
7189
7715
|
} catch (error) {
|
|
@@ -7326,8 +7852,8 @@ async function handleImages(res, method, rest, deps) {
|
|
|
7326
7852
|
}
|
|
7327
7853
|
const reader = deps.imageRuntimeStatus;
|
|
7328
7854
|
if (!reader) return writeJsonError(res, 501, "Images runtime status is not available");
|
|
7329
|
-
const serverConfig = await (0,
|
|
7330
|
-
const images = serverConfig.images ??
|
|
7855
|
+
const serverConfig = await (0, import_outbound_api5.loadServerConfig)(deps.settingsStore);
|
|
7856
|
+
const images = serverConfig.images ?? import_outbound_api5.DEFAULT_IMAGES_SERVER_CONFIG;
|
|
7331
7857
|
const lifecycle = reader.status();
|
|
7332
7858
|
const capability = await reader.inspectCapability(IMAGE_ADMIN_STATUS_TENANT);
|
|
7333
7859
|
const resources = safeRuntimeResources(reader.resourceStatus());
|
|
@@ -7346,7 +7872,7 @@ async function handleImages(res, method, rest, deps) {
|
|
|
7346
7872
|
httpLeases: safeStatusCount(generation.httpLeases),
|
|
7347
7873
|
hostedLeases: safeStatusCount(generation.hostedLeases)
|
|
7348
7874
|
}));
|
|
7349
|
-
return
|
|
7875
|
+
return writeJson4(res, 200, {
|
|
7350
7876
|
configured: {
|
|
7351
7877
|
enabled: images.enabled,
|
|
7352
7878
|
provider: images.provider,
|
|
@@ -7374,11 +7900,11 @@ async function handleImages(res, method, rest, deps) {
|
|
|
7374
7900
|
async function handleStatus(res, method, deps) {
|
|
7375
7901
|
if (method !== "GET") return writeJsonError(res, 405, `method ${method} not allowed on status`);
|
|
7376
7902
|
const status = deps.outboundApiServer.getStatus();
|
|
7377
|
-
const serverConfig = await (0,
|
|
7903
|
+
const serverConfig = await (0, import_outbound_api5.loadServerConfig)(deps.settingsStore);
|
|
7378
7904
|
const endpoints = ["chat", "responses", "messages", "gemini"].map((endpoint) => {
|
|
7379
|
-
const routes = (serverConfig.bindings ?? []).filter((binding) => binding.enabled && binding.endpoint === endpoint).map((binding) => (0,
|
|
7905
|
+
const routes = (serverConfig.bindings ?? []).filter((binding) => binding.enabled && binding.endpoint === endpoint).map((binding) => (0, import_outbound_api5.gatewayBindingToEndpointConfig)(binding));
|
|
7380
7906
|
const useSubscription = routes.some((route) => route.useSubscription);
|
|
7381
|
-
if ((0,
|
|
7907
|
+
if ((0, import_outbound_api5.isKindMappedEndpoint)(endpoint)) {
|
|
7382
7908
|
const kinds = {};
|
|
7383
7909
|
for (const route of routes) {
|
|
7384
7910
|
for (const [kind, ref] of Object.entries(route.modelMap ?? {})) {
|
|
@@ -7412,14 +7938,14 @@ async function handleStatus(res, method, deps) {
|
|
|
7412
7938
|
})() : void 0;
|
|
7413
7939
|
if (status.running) {
|
|
7414
7940
|
const queueStatus = deps.outboundApiServer.getQueueStatus();
|
|
7415
|
-
return
|
|
7941
|
+
return writeJson4(res, 200, {
|
|
7416
7942
|
...status,
|
|
7417
7943
|
endpoints,
|
|
7418
7944
|
queueStatus,
|
|
7419
7945
|
...imageRuntime ? { imageRuntime } : {}
|
|
7420
7946
|
});
|
|
7421
7947
|
}
|
|
7422
|
-
return
|
|
7948
|
+
return writeJson4(res, 200, {
|
|
7423
7949
|
...status,
|
|
7424
7950
|
endpoints,
|
|
7425
7951
|
...imageRuntime ? { imageRuntime } : {}
|
|
@@ -7443,18 +7969,18 @@ function resolvePlaygroundPath(endpoint, body) {
|
|
|
7443
7969
|
}
|
|
7444
7970
|
async function handlePlayground(req, res, method, deps) {
|
|
7445
7971
|
if (method !== "POST") return writeJsonError(res, 405, `method ${method} not allowed on playground`);
|
|
7446
|
-
const body = await
|
|
7972
|
+
const body = await readJsonBody4(req);
|
|
7447
7973
|
const endpoint = typeof body["endpoint"] === "string" ? body["endpoint"] : "";
|
|
7448
7974
|
const key = typeof body["key"] === "string" ? body["key"] : "";
|
|
7449
7975
|
const payload = body["body"];
|
|
7450
7976
|
const status = deps.outboundApiServer.getStatus();
|
|
7451
7977
|
if (!status.running || !status.port) return writeJsonError(res, 503, "outbound server not running");
|
|
7452
|
-
const path2 = resolvePlaygroundPath(endpoint,
|
|
7978
|
+
const path2 = resolvePlaygroundPath(endpoint, isRecord3(payload) ? payload : {});
|
|
7453
7979
|
if (!path2) return writeJsonError(res, 400, `unknown endpoint '${endpoint}'`);
|
|
7454
7980
|
const upstreamBody = typeof payload === "string" ? payload : JSON.stringify(payload ?? {});
|
|
7455
7981
|
await proxyToOutbound(res, status.port, path2, key, upstreamBody);
|
|
7456
7982
|
}
|
|
7457
|
-
function
|
|
7983
|
+
function isRecord3(v) {
|
|
7458
7984
|
return !!v && typeof v === "object" && !Array.isArray(v);
|
|
7459
7985
|
}
|
|
7460
7986
|
function proxyToOutbound(res, outboundPort, path2, key, body) {
|
|
@@ -7590,7 +8116,7 @@ async function handleUiStatic(req, res, urlPath, uiDist) {
|
|
|
7590
8116
|
}
|
|
7591
8117
|
|
|
7592
8118
|
// src/admin/version.ts
|
|
7593
|
-
var DAEMON_VERSION = true ? "0.
|
|
8119
|
+
var DAEMON_VERSION = true ? "0.3.0" : "0.0.0-dev";
|
|
7594
8120
|
|
|
7595
8121
|
// src/admin/AdminServer.ts
|
|
7596
8122
|
var LOOPBACK_ADDR = "127.0.0.1";
|
|
@@ -8010,14 +8536,14 @@ function createPoolKeysLoader(getProviderRow, autoDisabled) {
|
|
|
8010
8536
|
|
|
8011
8537
|
// src/image-generation/ImageDoctorService.ts
|
|
8012
8538
|
var import_image_generation = require("@omnicross/core/image-generation");
|
|
8013
|
-
var
|
|
8539
|
+
var import_outbound_api7 = require("@omnicross/core/outbound-api");
|
|
8014
8540
|
var import_subscriptions3 = require("@omnicross/subscriptions");
|
|
8015
8541
|
|
|
8016
8542
|
// src/image-generation/FileCodexImageCapabilityEvidenceSource.ts
|
|
8017
8543
|
var import_node_crypto13 = require("crypto");
|
|
8018
8544
|
var import_node_fs14 = require("fs");
|
|
8019
8545
|
var import_node_path14 = require("path");
|
|
8020
|
-
var
|
|
8546
|
+
var import_outbound_api6 = require("@omnicross/core/outbound-api");
|
|
8021
8547
|
|
|
8022
8548
|
// src/image-generation/imageTenantHmac.ts
|
|
8023
8549
|
var import_node_crypto12 = require("crypto");
|
|
@@ -8090,7 +8616,7 @@ var ACCOUNT_DOMAIN = Buffer.from("omnicross:codex-image-evidence:account:v1\0",
|
|
|
8090
8616
|
var ACCOUNT_KEY = /^[a-f0-9]{64}$/u;
|
|
8091
8617
|
var SIZE = /^(?:auto|[1-9][0-9]{1,4}x[1-9][0-9]{1,4})$/u;
|
|
8092
8618
|
var MAX_MANIFEST_BYTES = 4 * 1024 * 1024;
|
|
8093
|
-
var PHYSICAL_RETENTION_TTL_MS =
|
|
8619
|
+
var PHYSICAL_RETENTION_TTL_MS = import_outbound_api6.IMAGE_SERVER_HARD_CEILINGS.evidenceTtlMs;
|
|
8094
8620
|
function exactKeys(value, allowed) {
|
|
8095
8621
|
const allow = new Set(allowed);
|
|
8096
8622
|
return Object.keys(value).every((key) => allow.has(key));
|
|
@@ -8464,7 +8990,7 @@ function createImageDoctorService(options) {
|
|
|
8464
8990
|
const activePaths = () => options.storageCatalog.active().resolver;
|
|
8465
8991
|
return Object.freeze({
|
|
8466
8992
|
inspectLocal: async (config) => {
|
|
8467
|
-
const configErrors = (0,
|
|
8993
|
+
const configErrors = (0, import_outbound_api7.validateImagesServerConfig)(config);
|
|
8468
8994
|
let verifiedAreas = 0;
|
|
8469
8995
|
try {
|
|
8470
8996
|
const paths = activePaths();
|
|
@@ -8506,7 +9032,7 @@ function createImageDoctorService(options) {
|
|
|
8506
9032
|
continue;
|
|
8507
9033
|
}
|
|
8508
9034
|
try {
|
|
8509
|
-
const permissions = (0,
|
|
9035
|
+
const permissions = (0, import_outbound_api7.validateOutboundPermissions)(row.allowedEndpoints);
|
|
8510
9036
|
if (permissions.includes("images")) imagesAuthorizedRows += 1;
|
|
8511
9037
|
} catch {
|
|
8512
9038
|
invalidRows += 1;
|
|
@@ -8810,7 +9336,7 @@ var ImageCleanupService = class {
|
|
|
8810
9336
|
// src/image-generation/ImageRuntimeGenerationFactory.ts
|
|
8811
9337
|
var import_node_crypto16 = require("crypto");
|
|
8812
9338
|
var import_image_generation5 = require("@omnicross/core/image-generation");
|
|
8813
|
-
var
|
|
9339
|
+
var import_outbound_api8 = require("@omnicross/core/outbound-api");
|
|
8814
9340
|
var import_subscriptions4 = require("@omnicross/subscriptions");
|
|
8815
9341
|
|
|
8816
9342
|
// src/image-generation/ImageApiRuntimeResolver.ts
|
|
@@ -9278,7 +9804,7 @@ function createImageRuntimeGeneration(options) {
|
|
|
9278
9804
|
throw new TypeError("image runtime generation id is invalid");
|
|
9279
9805
|
}
|
|
9280
9806
|
const config = snapshotConfig(options.config);
|
|
9281
|
-
const configErrors = (0,
|
|
9807
|
+
const configErrors = (0, import_outbound_api8.validateImagesServerConfig)(config);
|
|
9282
9808
|
if (configErrors.length > 0) {
|
|
9283
9809
|
throw new TypeError(`image runtime configuration is invalid: ${configErrors[0]}`);
|
|
9284
9810
|
}
|
|
@@ -12870,7 +13396,7 @@ function safeStringify(value) {
|
|
|
12870
13396
|
var import_node_crypto20 = require("crypto");
|
|
12871
13397
|
var import_node_fs20 = require("fs");
|
|
12872
13398
|
var import_node_path20 = require("path");
|
|
12873
|
-
var
|
|
13399
|
+
var import_outbound_api9 = require("@omnicross/core/outbound-api");
|
|
12874
13400
|
function atomicReplaceDocument(targetPath, contents) {
|
|
12875
13401
|
const tempPath = (0, import_node_path20.join)(
|
|
12876
13402
|
(0, import_node_path20.dirname)(targetPath),
|
|
@@ -12918,13 +13444,13 @@ var JsonApiServerSettingsStore = class {
|
|
|
12918
13444
|
box;
|
|
12919
13445
|
atomicReplace;
|
|
12920
13446
|
async get(key) {
|
|
12921
|
-
if (key !==
|
|
13447
|
+
if (key !== import_outbound_api9.OUTBOUND_API_SERVER_CONFIG_KEY) return void 0;
|
|
12922
13448
|
const file = this.readFile();
|
|
12923
13449
|
if (file.server === void 0) return void 0;
|
|
12924
13450
|
return this.decryptSecrets(file.server);
|
|
12925
13451
|
}
|
|
12926
13452
|
async set(key, value) {
|
|
12927
|
-
if (key !==
|
|
13453
|
+
if (key !== import_outbound_api9.OUTBOUND_API_SERVER_CONFIG_KEY) return;
|
|
12928
13454
|
const file = this.readFile();
|
|
12929
13455
|
file.server = this.encryptSecrets(value);
|
|
12930
13456
|
this.atomicReplace(
|
|
@@ -14563,7 +15089,7 @@ var import_node_fs27 = require("fs");
|
|
|
14563
15089
|
var import_node_path27 = require("path");
|
|
14564
15090
|
var import_SubscriptionAccountHealth2 = require("@omnicross/core/pipeline/SubscriptionAccountHealth");
|
|
14565
15091
|
var import_AccountAllowanceScheduling3 = require("@omnicross/core/pipeline/AccountAllowanceScheduling");
|
|
14566
|
-
var
|
|
15092
|
+
var import_upstreamFetch5 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
14567
15093
|
var import_SubscriptionIdentityStore2 = require("@omnicross/core/provider-proxy/identity/SubscriptionIdentityStore");
|
|
14568
15094
|
var import_subscriptions5 = require("@omnicross/subscriptions");
|
|
14569
15095
|
|
|
@@ -14711,7 +15237,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
14711
15237
|
* a plaintext token pair into `upstream-trace.jsonl`.
|
|
14712
15238
|
*/
|
|
14713
15239
|
buildRefreshFetch(providerId, accountId) {
|
|
14714
|
-
return this.fetchImpl ?? ((url, init) => (0,
|
|
15240
|
+
return this.fetchImpl ?? ((url, init) => (0, import_upstreamFetch5.fetchUpstream)(url, init, { providerId, accountId, redactBodies: true }));
|
|
14715
15241
|
}
|
|
14716
15242
|
/**
|
|
14717
15243
|
* In-flight refresh coalescing. OAuth refresh tokens are
|
|
@@ -15269,7 +15795,7 @@ var JsonSubscriptionCredentialStore = class {
|
|
|
15269
15795
|
};
|
|
15270
15796
|
|
|
15271
15797
|
// src/AccountHealthProbeScheduler.ts
|
|
15272
|
-
var
|
|
15798
|
+
var import_upstreamFetch6 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
15273
15799
|
|
|
15274
15800
|
// src/probe/CodexGenerationProbe.ts
|
|
15275
15801
|
var import_codexCliHeaders = require("@omnicross/core/provider-proxy/identity/codexCliHeaders");
|
|
@@ -15430,7 +15956,7 @@ var AccountHealthProbeScheduler = class {
|
|
|
15430
15956
|
this.logger = logger;
|
|
15431
15957
|
this.config = config;
|
|
15432
15958
|
this.now = opts.now ?? Date.now;
|
|
15433
|
-
this.fetchImpl = opts.fetchImpl ??
|
|
15959
|
+
this.fetchImpl = opts.fetchImpl ?? import_upstreamFetch6.fetchUpstream;
|
|
15434
15960
|
this.sleep = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
15435
15961
|
this.planFor = opts.planFor ?? probePlanFor;
|
|
15436
15962
|
}
|
|
@@ -16586,7 +17112,7 @@ var AuditWriter = class {
|
|
|
16586
17112
|
var import_node_fs33 = require("fs");
|
|
16587
17113
|
var import_node_crypto24 = require("crypto");
|
|
16588
17114
|
var import_node_path34 = require("path");
|
|
16589
|
-
var
|
|
17115
|
+
var import_upstreamFetch7 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
16590
17116
|
|
|
16591
17117
|
// src/billing/billingFiles.ts
|
|
16592
17118
|
var BILLING_FILE_RE = /^billing-(\d{4})-(\d{2})-(\d{2})\.jsonl$/;
|
|
@@ -16609,7 +17135,7 @@ var BillingPublisher = class {
|
|
|
16609
17135
|
constructor(billingDir, logger, opts = {}) {
|
|
16610
17136
|
this.billingDir = billingDir;
|
|
16611
17137
|
this.logger = logger;
|
|
16612
|
-
this.fetchImpl = opts.fetchImpl ?? ((url, init) => (0,
|
|
17138
|
+
this.fetchImpl = opts.fetchImpl ?? ((url, init) => (0, import_upstreamFetch7.fetchUpstream)(url, init));
|
|
16613
17139
|
this.defer = opts.defer ?? ((fn) => setTimeout(fn, 0));
|
|
16614
17140
|
this.timeoutMs = opts.timeoutMs ?? BILLING_POST_TIMEOUT_MS;
|
|
16615
17141
|
this.now = opts.now ?? Date.now;
|
|
@@ -17018,7 +17544,7 @@ function createRouteLeaseSubscriptionPreflight(credentials) {
|
|
|
17018
17544
|
|
|
17019
17545
|
// src/webhook/WebhookDispatcher.ts
|
|
17020
17546
|
var import_node_crypto25 = require("crypto");
|
|
17021
|
-
var
|
|
17547
|
+
var import_upstreamFetch8 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
17022
17548
|
var WEBHOOK_MAX_ATTEMPTS = 3;
|
|
17023
17549
|
var WEBHOOK_QUEUE_MAX = 1e3;
|
|
17024
17550
|
var WEBHOOK_SEND_TIMEOUT_MS = 1e4;
|
|
@@ -17038,7 +17564,7 @@ var WebhookDispatcher = class {
|
|
|
17038
17564
|
sleep;
|
|
17039
17565
|
now;
|
|
17040
17566
|
constructor(opts = {}) {
|
|
17041
|
-
this.fetchImpl = opts.fetchImpl ?? ((url, init) => (0,
|
|
17567
|
+
this.fetchImpl = opts.fetchImpl ?? ((url, init) => (0, import_upstreamFetch8.fetchUpstream)(url, init));
|
|
17042
17568
|
this.logger = opts.logger;
|
|
17043
17569
|
this.maxAttempts = opts.maxAttempts ?? WEBHOOK_MAX_ATTEMPTS;
|
|
17044
17570
|
this.queueMax = opts.queueMax ?? WEBHOOK_QUEUE_MAX;
|
|
@@ -17269,7 +17795,7 @@ function buildDaemon(config, paths) {
|
|
|
17269
17795
|
);
|
|
17270
17796
|
(0, import_AccountAllowanceStore4.setSharedAccountAllowanceStore)(accountAllowanceStore);
|
|
17271
17797
|
(0, import_AccountAllowanceScheduling5.getSharedAccountAllowanceScheduling)().configure(
|
|
17272
|
-
(0,
|
|
17798
|
+
(0, import_outbound_api10.normalizeServerConfig)(decryptedConfig.server).allowanceScheduling
|
|
17273
17799
|
);
|
|
17274
17800
|
const llmConfig = new ConfigFileProviderConfigSource(decryptedConfig);
|
|
17275
17801
|
const keyDb = new JsonOutboundKeyDb(paths.keysPath, secretBox3);
|
|
@@ -17290,7 +17816,7 @@ function buildDaemon(config, paths) {
|
|
|
17290
17816
|
logger
|
|
17291
17817
|
);
|
|
17292
17818
|
claudeAllowanceRefreshScheduler.configure(
|
|
17293
|
-
(0,
|
|
17819
|
+
(0, import_outbound_api10.normalizeServerConfig)(decryptedConfig.server).allowanceScheduling
|
|
17294
17820
|
);
|
|
17295
17821
|
const subscriptionAccounts = new import_subscriptions6.SubscriptionAccountService(credentialStore);
|
|
17296
17822
|
(0, import_subscriptions6.setSubscriptionAccountService)(subscriptionAccounts);
|
|
@@ -17300,7 +17826,7 @@ function buildDaemon(config, paths) {
|
|
|
17300
17826
|
);
|
|
17301
17827
|
(0, import_subscriptions6.setSubscriptionProviderRegistry)(subscriptionRegistry);
|
|
17302
17828
|
setServerProxyConfig(decryptedConfig.server?.proxy);
|
|
17303
|
-
(0,
|
|
17829
|
+
(0, import_upstreamFetch9.setUpstreamProxyResolver)(
|
|
17304
17830
|
createUpstreamProxyResolver({
|
|
17305
17831
|
getAccountProxy: (providerId, accountId) => credentialStore.getAccountProxy(providerId, accountId)
|
|
17306
17832
|
})
|
|
@@ -17323,7 +17849,7 @@ function buildDaemon(config, paths) {
|
|
|
17323
17849
|
const pricingEngine = new import_usage2.PricingEngine(pricingStore, logger, {
|
|
17324
17850
|
// Catalog egress follows the same global/env proxy policy as every other
|
|
17325
17851
|
// daemon upstream call; no provider/account override applies here.
|
|
17326
|
-
fetchImpl: ((input, init) => (0,
|
|
17852
|
+
fetchImpl: ((input, init) => (0, import_upstreamFetch9.fetchUpstream)(String(input), init ?? {}))
|
|
17327
17853
|
});
|
|
17328
17854
|
const pricingRefreshScheduler = new PricingRefreshScheduler(
|
|
17329
17855
|
pricingEngine,
|
|
@@ -17335,13 +17861,21 @@ function buildDaemon(config, paths) {
|
|
|
17335
17861
|
defaultUsageEventsPath(paths.configPath),
|
|
17336
17862
|
async (providerId, model) => await pricingEngine.getEntry(providerId, model) !== null
|
|
17337
17863
|
);
|
|
17338
|
-
const keySpendTracker = new
|
|
17864
|
+
const keySpendTracker = new import_outbound_api11.KeySpendTracker(usageEventStore);
|
|
17339
17865
|
const usageThroughput = (0, import_usage2.getSharedUsageThroughputTracker)();
|
|
17340
17866
|
const usageRecorder = new import_usage2.UsageRecorder(usageEventStore, pricingEngine, logger, {
|
|
17341
17867
|
onRecord: (apiKeyId, costUsd, at) => keySpendTracker.add(apiKeyId, costUsd, at),
|
|
17342
17868
|
onEvent: (row, at) => usageThroughput.record(row, at)
|
|
17343
17869
|
});
|
|
17344
|
-
const initialImagesConfig = (0,
|
|
17870
|
+
const initialImagesConfig = (0, import_outbound_api10.normalizeServerConfig)(decryptedConfig.server).images;
|
|
17871
|
+
const initialSearchConfig = (0, import_outbound_api10.normalizeServerConfig)(decryptedConfig.server).search;
|
|
17872
|
+
for (const issue of (0, import_outbound_api10.validateSearchServerConfig)(
|
|
17873
|
+
decryptedConfig.server?.search
|
|
17874
|
+
)) {
|
|
17875
|
+
logger.warn("[search] ignoring invalid config: " + issue);
|
|
17876
|
+
}
|
|
17877
|
+
const searchRuntime = buildSearchRuntime(initialSearchConfig, { logger });
|
|
17878
|
+
const searchFrontendModes = initialSearchConfig.modes;
|
|
17345
17879
|
const imageObservability = new ImageObservability();
|
|
17346
17880
|
const imageRuntimeObservability = Object.freeze({
|
|
17347
17881
|
telemetrySink: imageObservability.telemetrySink,
|
|
@@ -17471,7 +18005,9 @@ function buildDaemon(config, paths) {
|
|
|
17471
18005
|
apiKeyPool,
|
|
17472
18006
|
usageRecorder,
|
|
17473
18007
|
openAIOperationRegistry,
|
|
17474
|
-
responsesHostedImageIngress
|
|
18008
|
+
responsesHostedImageIngress,
|
|
18009
|
+
searchRuntime,
|
|
18010
|
+
searchFrontendModes
|
|
17475
18011
|
});
|
|
17476
18012
|
if (providerProxy.getDeps().openAIOperationRegistry !== openAIOperationRegistry) {
|
|
17477
18013
|
throw new Error(
|
|
@@ -17505,7 +18041,7 @@ function buildDaemon(config, paths) {
|
|
|
17505
18041
|
credentialStore,
|
|
17506
18042
|
(0, import_SubscriptionAccountHealth4.getSharedAccountHealth)(),
|
|
17507
18043
|
logger,
|
|
17508
|
-
|
|
18044
|
+
import_outbound_api10.DEFAULT_ACCOUNT_PROBE
|
|
17509
18045
|
);
|
|
17510
18046
|
const getHealthReport = () => buildHealthReport({
|
|
17511
18047
|
version: DAEMON_VERSION,
|
|
@@ -17522,7 +18058,7 @@ function buildDaemon(config, paths) {
|
|
|
17522
18058
|
// `/health` body stays byte-identical (zero regression).
|
|
17523
18059
|
subscriptionAccountsHealthy: () => accountHealthProbeScheduler.enabled ? accountHealthProbeScheduler.probedAccountsHealthy() : void 0
|
|
17524
18060
|
});
|
|
17525
|
-
const outboundApiServer = (0,
|
|
18061
|
+
const outboundApiServer = (0, import_outbound_api10.getOutboundApiServer)({
|
|
17526
18062
|
db: keyDb,
|
|
17527
18063
|
// voucher-redemption #9: the key-authenticated `POST /redeem` endpoint redeems
|
|
17528
18064
|
// cards against the presenting key (gated on `voucher.enabled`).
|
|
@@ -17536,7 +18072,9 @@ function buildDaemon(config, paths) {
|
|
|
17536
18072
|
keySpendTracker,
|
|
17537
18073
|
// configurable-logging: route the server's OWN lifecycle + relay dispatch-error
|
|
17538
18074
|
// lines through the injected logger (honors level/format/file sink).
|
|
17539
|
-
logger
|
|
18075
|
+
logger,
|
|
18076
|
+
// plan 阶段5: the same instance the managed frontends hold.
|
|
18077
|
+
searchRuntime
|
|
17540
18078
|
});
|
|
17541
18079
|
const auditDir = defaultAuditDir(paths.configPath);
|
|
17542
18080
|
const billingDir = defaultBillingDir(paths.configPath);
|
|
@@ -17558,6 +18096,10 @@ function buildDaemon(config, paths) {
|
|
|
17558
18096
|
}),
|
|
17559
18097
|
routeLeaseManager,
|
|
17560
18098
|
subscriptionAccounts,
|
|
18099
|
+
// search-settings-ui D3: the daemon's ONE search runtime + its
|
|
18100
|
+
// bootstrap-captured modes, for `GET /admin/api/search/diagnostics` and
|
|
18101
|
+
// `POST /admin/api/search/test` (501 when a light embedder omits it).
|
|
18102
|
+
searchStatus: { runtime: searchRuntime, modes: searchFrontendModes },
|
|
17561
18103
|
accountAllowanceService,
|
|
17562
18104
|
allowanceRefreshScheduler: claudeAllowanceRefreshScheduler,
|
|
17563
18105
|
accountProbeService: accountHealthProbeScheduler,
|
|
@@ -17587,7 +18129,7 @@ function buildDaemon(config, paths) {
|
|
|
17587
18129
|
// — `server.proxy.byProvider[...]` was silently skipped — and the call was
|
|
17588
18130
|
// excluded from the upstream trace, so a failing login left no evidence.
|
|
17589
18131
|
// `redactBodies` keeps the code/verifier + minted token out of that trace.
|
|
17590
|
-
oauthExchangeFetch: paths.oauthExchangeFetch ? () => paths.oauthExchangeFetch : (providerId) => (url, init) => (0,
|
|
18132
|
+
oauthExchangeFetch: paths.oauthExchangeFetch ? () => paths.oauthExchangeFetch : (providerId) => (url, init) => (0, import_upstreamFetch9.fetchUpstream)(url, init, { providerId, redactBodies: true }),
|
|
17591
18133
|
subscriptionAccountAppender: credentialStore,
|
|
17592
18134
|
// Codex interactive OAuth (app-parity-2 child 5) — the async loopback flow store
|
|
17593
18135
|
// + the one-shot 127.0.0.1:1455 listener. Token captured + persisted daemon-side;
|
|
@@ -17607,7 +18149,7 @@ function buildDaemon(config, paths) {
|
|
|
17607
18149
|
cliCommandRunner: paths.cliCommandRunner,
|
|
17608
18150
|
integrationManagerFactory: () => {
|
|
17609
18151
|
const live = outboundApiServer.getStatus();
|
|
17610
|
-
const port = live.port || decryptedConfig.server?.port ||
|
|
18152
|
+
const port = live.port || decryptedConfig.server?.port || import_outbound_api10.DEFAULT_OUTBOUND_PORT;
|
|
17611
18153
|
return new IntegrationManager({
|
|
17612
18154
|
configPath: paths.configPath,
|
|
17613
18155
|
gatewayBaseUrl: live.loopbackUrl ?? `http://127.0.0.1:${port}`,
|
|
@@ -17653,7 +18195,7 @@ function buildDaemon(config, paths) {
|
|
|
17653
18195
|
});
|
|
17654
18196
|
const webhookDispatcher = new WebhookDispatcher({
|
|
17655
18197
|
logger,
|
|
17656
|
-
fetchImpl: (url, init) => (0,
|
|
18198
|
+
fetchImpl: (url, init) => (0, import_upstreamFetch9.fetchUpstream)(url, init)
|
|
17657
18199
|
});
|
|
17658
18200
|
setWebhookRuntime(webhookDispatcher, (0, import_SubscriptionAccountHealth4.getSharedAccountHealth)());
|
|
17659
18201
|
const auditWriter = new AuditWriter(auditDir, logger);
|
|
@@ -17694,6 +18236,8 @@ function buildDaemon(config, paths) {
|
|
|
17694
18236
|
keyDb,
|
|
17695
18237
|
settingsStore,
|
|
17696
18238
|
openAIOperationRegistry,
|
|
18239
|
+
searchRuntime,
|
|
18240
|
+
searchFrontendModes,
|
|
17697
18241
|
imageRuntimeManager,
|
|
17698
18242
|
imageObservability,
|
|
17699
18243
|
imageCleanupService,
|
|
@@ -17830,7 +18374,7 @@ async function runImagesLiveDoctor(config, doctor, signal = new AbortController(
|
|
|
17830
18374
|
console.info(
|
|
17831
18375
|
" [\u26A0] live Images verification may consume subscription quota; one minimal low-quality PNG request will be sent"
|
|
17832
18376
|
);
|
|
17833
|
-
const result = await doctor.verifyLive(config.images ??
|
|
18377
|
+
const result = await doctor.verifyLive(config.images ?? import_outbound_api12.DEFAULT_IMAGES_SERVER_CONFIG, signal);
|
|
17834
18378
|
if (!result.ok) {
|
|
17835
18379
|
console.info(` [\u2717] live Images verification: ${result.code}`);
|
|
17836
18380
|
return false;
|
|
@@ -17840,6 +18384,116 @@ async function runImagesLiveDoctor(config, doctor, signal = new AbortController(
|
|
|
17840
18384
|
);
|
|
17841
18385
|
return true;
|
|
17842
18386
|
}
|
|
18387
|
+
function readSearchApiConfigFromEnv(env = process.env) {
|
|
18388
|
+
const configs = {};
|
|
18389
|
+
const read = (name) => {
|
|
18390
|
+
const value = env[`OMNICROSS_SEARCH_${name}`]?.trim();
|
|
18391
|
+
return value ? value : void 0;
|
|
18392
|
+
};
|
|
18393
|
+
const tavilyKey = read("TAVILY_API_KEY");
|
|
18394
|
+
if (tavilyKey) {
|
|
18395
|
+
configs.tavily = { apiKey: tavilyKey, ...optionalHost(read("TAVILY_API_HOST")) };
|
|
18396
|
+
}
|
|
18397
|
+
const jinaKey = read("JINA_API_KEY");
|
|
18398
|
+
const jinaHost = read("JINA_API_HOST");
|
|
18399
|
+
if (jinaKey || jinaHost) {
|
|
18400
|
+
configs.jina = { ...jinaKey ? { apiKey: jinaKey } : {}, ...optionalHost(jinaHost) };
|
|
18401
|
+
}
|
|
18402
|
+
const searxngHost = read("SEARXNG_API_HOST");
|
|
18403
|
+
if (searxngHost) {
|
|
18404
|
+
const username = read("SEARXNG_BASIC_AUTH_USERNAME");
|
|
18405
|
+
const password = read("SEARXNG_BASIC_AUTH_PASSWORD");
|
|
18406
|
+
configs.searxng = {
|
|
18407
|
+
apiHost: searxngHost,
|
|
18408
|
+
...username ? { basicAuthUsername: username } : {},
|
|
18409
|
+
...password ? { basicAuthPassword: password } : {}
|
|
18410
|
+
};
|
|
18411
|
+
}
|
|
18412
|
+
const zhipuKey = read("ZHIPU_API_KEY");
|
|
18413
|
+
if (zhipuKey) {
|
|
18414
|
+
configs.zhipu = { apiKey: zhipuKey, ...optionalHost(read("ZHIPU_API_HOST")) };
|
|
18415
|
+
}
|
|
18416
|
+
const zaiKey = read("Z_AI_API_KEY");
|
|
18417
|
+
if (zaiKey) {
|
|
18418
|
+
configs["z.ai"] = { apiKey: zaiKey, ...optionalHost(read("Z_AI_API_HOST")) };
|
|
18419
|
+
}
|
|
18420
|
+
return configs;
|
|
18421
|
+
}
|
|
18422
|
+
function optionalHost(apiHost) {
|
|
18423
|
+
return apiHost ? { apiHost } : {};
|
|
18424
|
+
}
|
|
18425
|
+
function resolveSearchApiConfigs(configured, env = process.env) {
|
|
18426
|
+
const fromEnv = readSearchApiConfigFromEnv(env);
|
|
18427
|
+
const resolved = {};
|
|
18428
|
+
const tavily = configured?.tavily ?? fromEnv.tavily;
|
|
18429
|
+
if (tavily) resolved.tavily = tavily;
|
|
18430
|
+
const jina = configured?.jina ?? fromEnv.jina;
|
|
18431
|
+
if (jina) resolved.jina = jina;
|
|
18432
|
+
const searxng = configured?.searxng ?? fromEnv.searxng;
|
|
18433
|
+
if (searxng) resolved.searxng = searxng;
|
|
18434
|
+
const zhipu = configured?.zhipu ?? fromEnv.zhipu;
|
|
18435
|
+
if (zhipu) resolved.zhipu = zhipu;
|
|
18436
|
+
const zai = configured?.["z.ai"] ?? fromEnv["z.ai"];
|
|
18437
|
+
if (zai) resolved["z.ai"] = zai;
|
|
18438
|
+
return resolved;
|
|
18439
|
+
}
|
|
18440
|
+
function formatCapabilities(capabilities) {
|
|
18441
|
+
return [
|
|
18442
|
+
`apiKey=${capabilities.requiresApiKey}`,
|
|
18443
|
+
`cancellation=${capabilities.supportsCancellation}`,
|
|
18444
|
+
`urlRead=${capabilities.supportsUrlRead}`,
|
|
18445
|
+
`region=${capabilities.supportsRegion}`,
|
|
18446
|
+
`language=${capabilities.supportsLanguage}`,
|
|
18447
|
+
`timeRange=${capabilities.supportsTimeRange}`,
|
|
18448
|
+
`maxResults=${capabilities.maxResults ?? "unbounded"}`
|
|
18449
|
+
].join(", ");
|
|
18450
|
+
}
|
|
18451
|
+
function formatDiagnostic(diagnostic) {
|
|
18452
|
+
const parts = [diagnostic.status];
|
|
18453
|
+
if (diagnostic.reason) parts.push(diagnostic.reason);
|
|
18454
|
+
if (diagnostic.error) {
|
|
18455
|
+
const { code, details } = diagnostic.error;
|
|
18456
|
+
parts.push(
|
|
18457
|
+
`code=${code}, transport=${details?.transport ?? "unknown"}, stage=${details?.stage ?? "unknown"}`
|
|
18458
|
+
);
|
|
18459
|
+
}
|
|
18460
|
+
return parts.join(" \u2014 ");
|
|
18461
|
+
}
|
|
18462
|
+
async function runSearchDoctor(live, env = process.env, source = {}) {
|
|
18463
|
+
const apiConfigs = resolveSearchApiConfigs(source.config?.providers, env);
|
|
18464
|
+
const egressPolicy = source.config && source.config.egress.allowedPrivateHosts.length > 0 ? { allowedPrivateHosts: [...source.config.egress.allowedPrivateHosts] } : void 0;
|
|
18465
|
+
const contributions = [
|
|
18466
|
+
...(0, import_http4.builtinHttpSearchContributions)(),
|
|
18467
|
+
...(0, import_api4.apiSearchContributions)(apiConfigs, { ...egressPolicy ? { egressPolicy } : {} })
|
|
18468
|
+
];
|
|
18469
|
+
const declarations = source.runtime?.listProviders() ?? contributions;
|
|
18470
|
+
console.info("omnicross doctor search \u2014 builtin search contributions (offline, no network)");
|
|
18471
|
+
const modes = source.config?.modes ?? import_search3.DEFAULT_SEARCH_FRONTEND_MODES;
|
|
18472
|
+
console.info(
|
|
18473
|
+
` [i] frontend modes: ${import_search3.SEARCH_FRONTEND_NAMES.map((name) => `${name}=${modes[name]}`).join(", ")}`
|
|
18474
|
+
);
|
|
18475
|
+
console.info(
|
|
18476
|
+
" [i] codex mode applies immediately; responses/anthropic modes, provider config, egress allowlist and policy apply on daemon restart"
|
|
18477
|
+
);
|
|
18478
|
+
for (const row of buildSearchDoctorSnapshot(declarations, apiConfigs)) {
|
|
18479
|
+
const mark = row.status === "unconfigured" ? "\u2013" : "\u2713";
|
|
18480
|
+
const suffix = row.status ? ` \u2014 ${row.status}: ${row.reason ?? ""}` : "";
|
|
18481
|
+
console.info(
|
|
18482
|
+
` [${mark}] ${row.providerId}: source=${row.source}, kind=${row.kind}, ${formatCapabilities(row.capabilities)}${suffix}`
|
|
18483
|
+
);
|
|
18484
|
+
}
|
|
18485
|
+
if (!live) return 0;
|
|
18486
|
+
console.info(
|
|
18487
|
+
` [\u26A0] live search sends ONE fixed public query per provider ("${SEARCH_DOCTOR_QUERY}") to the engine itself`
|
|
18488
|
+
);
|
|
18489
|
+
let hardFailure = false;
|
|
18490
|
+
for (const diagnostic of await runSearchLiveChecks(contributions)) {
|
|
18491
|
+
const mark = diagnostic.status === "healthy" ? "\u2713" : diagnostic.status === "failed" ? "\u2717" : "\u26A0";
|
|
18492
|
+
if (diagnostic.status === "failed") hardFailure = true;
|
|
18493
|
+
console.info(` [${mark}] ${diagnostic.providerId}: ${formatDiagnostic(diagnostic)}`);
|
|
18494
|
+
}
|
|
18495
|
+
return hardFailure ? 1 : 0;
|
|
18496
|
+
}
|
|
17843
18497
|
async function runLiveProbe(url, key, fetchImpl = fetch) {
|
|
17844
18498
|
try {
|
|
17845
18499
|
const res = await fetchImpl(`${url.replace(/\/+$/, "")}/v1/messages/count_tokens`, {
|
|
@@ -17876,11 +18530,12 @@ async function runDoctor(argv, fetchImpl = fetch) {
|
|
|
17876
18530
|
allowPositionals: true
|
|
17877
18531
|
});
|
|
17878
18532
|
const subject = positionals[0] ?? "claude";
|
|
17879
|
-
if (subject !== "claude" && subject !== "images") {
|
|
17880
|
-
throw new Error(`doctor: unknown subject '${subject}' (supported: 'claude', 'images')`);
|
|
18533
|
+
if (subject !== "claude" && subject !== "images" && subject !== "search") {
|
|
18534
|
+
throw new Error(`doctor: unknown subject '${subject}' (supported: 'claude', 'images', 'search')`);
|
|
17881
18535
|
}
|
|
17882
18536
|
const configPath = values.config;
|
|
17883
18537
|
if (!configPath) {
|
|
18538
|
+
if (subject === "search") return runSearchDoctor(values.live === true);
|
|
17884
18539
|
throw new Error("doctor: --config <path> is required (the same config `omnicross start` uses)");
|
|
17885
18540
|
}
|
|
17886
18541
|
const config = loadConfig(configPath);
|
|
@@ -17892,9 +18547,15 @@ async function runDoctor(argv, fetchImpl = fetch) {
|
|
|
17892
18547
|
};
|
|
17893
18548
|
const daemon = await buildDaemon(config, paths);
|
|
17894
18549
|
try {
|
|
17895
|
-
const serverConfig = await (0,
|
|
18550
|
+
const serverConfig = await (0, import_outbound_api12.loadServerConfig)(daemon.settingsStore);
|
|
18551
|
+
if (subject === "search") {
|
|
18552
|
+
return await runSearchDoctor(values.live === true, process.env, {
|
|
18553
|
+
...serverConfig.search ? { config: serverConfig.search } : {},
|
|
18554
|
+
runtime: daemon.searchRuntime
|
|
18555
|
+
});
|
|
18556
|
+
}
|
|
17896
18557
|
const checks = subject === "images" ? buildImagesDoctorChecks(await daemon.imageDoctor.inspectLocal(
|
|
17897
|
-
serverConfig.images ??
|
|
18558
|
+
serverConfig.images ?? import_outbound_api12.DEFAULT_IMAGES_SERVER_CONFIG
|
|
17898
18559
|
)) : buildClaudeDoctorChecks(serverConfig);
|
|
17899
18560
|
let hardFailure = false;
|
|
17900
18561
|
console.info(subject === "images" ? "omnicross doctor images \u2014 local metadata only" : `omnicross doctor ${subject} \u2014 config: ${configPath}`);
|
|
@@ -18117,7 +18778,7 @@ function isClient(value) {
|
|
|
18117
18778
|
|
|
18118
18779
|
// src/commands/keys.ts
|
|
18119
18780
|
var import_node_util5 = require("util");
|
|
18120
|
-
var
|
|
18781
|
+
var import_outbound_api13 = require("@omnicross/core/outbound-api");
|
|
18121
18782
|
async function runKeys(argv) {
|
|
18122
18783
|
const { values, positionals } = (0, import_node_util5.parseArgs)({
|
|
18123
18784
|
args: argv,
|
|
@@ -18143,7 +18804,7 @@ async function runKeys(argv) {
|
|
|
18143
18804
|
}
|
|
18144
18805
|
async function keysAdd(db, name) {
|
|
18145
18806
|
if (!name) throw new Error("keys add: a <name> is required");
|
|
18146
|
-
const created = await (0,
|
|
18807
|
+
const created = await (0, import_outbound_api13.createNamedKey)(db, name);
|
|
18147
18808
|
console.info(`Created key '${created.name}' (id: ${created.id}).`);
|
|
18148
18809
|
console.info("");
|
|
18149
18810
|
console.info(` ${created.plaintextOnce}`);
|
|
@@ -18399,7 +19060,7 @@ function spawnCliInherit(plan) {
|
|
|
18399
19060
|
var import_node_child_process3 = require("child_process");
|
|
18400
19061
|
var import_node_readline2 = require("readline");
|
|
18401
19062
|
var import_node_util7 = require("util");
|
|
18402
|
-
var
|
|
19063
|
+
var import_upstreamFetch10 = require("@omnicross/core/pipeline/upstreamFetch");
|
|
18403
19064
|
var import_subscriptions7 = require("@omnicross/subscriptions");
|
|
18404
19065
|
var PROVIDERS2 = ["claude", "codex", "gemini"];
|
|
18405
19066
|
async function runLogin(argv, deps) {
|
|
@@ -18431,10 +19092,10 @@ async function runLogin(argv, deps) {
|
|
|
18431
19092
|
};
|
|
18432
19093
|
const box = resolveSecretBox(values["master-key-file"]);
|
|
18433
19094
|
setSecretBox(box);
|
|
18434
|
-
(0,
|
|
19095
|
+
(0, import_upstreamFetch10.setUpstreamProxyResolver)(createUpstreamProxyResolver());
|
|
18435
19096
|
try {
|
|
18436
19097
|
const tokensPath = defaultTokensPath(values.config);
|
|
18437
|
-
const exchangeFetch = resolved.tokensFetch ?? ((url, init) => (0,
|
|
19098
|
+
const exchangeFetch = resolved.tokensFetch ?? ((url, init) => (0, import_upstreamFetch10.fetchUpstream)(url, init, { providerId: provider, redactBodies: true }));
|
|
18438
19099
|
const store = new JsonSubscriptionCredentialStore(tokensPath, box, exchangeFetch);
|
|
18439
19100
|
const expiresAt = await runProviderLogin(
|
|
18440
19101
|
provider,
|
|
@@ -18447,7 +19108,7 @@ async function runLogin(argv, deps) {
|
|
|
18447
19108
|
console.info(` token: [stored, encrypted] expiresAt: ${expiresAt ?? "n/a"}`);
|
|
18448
19109
|
} finally {
|
|
18449
19110
|
setSecretBox(null);
|
|
18450
|
-
(0,
|
|
19111
|
+
(0, import_upstreamFetch10.setUpstreamProxyResolver)(null);
|
|
18451
19112
|
}
|
|
18452
19113
|
}
|
|
18453
19114
|
async function runProviderLogin(provider, store, deps, exchangeFetch, label) {
|
|
@@ -18966,7 +19627,7 @@ function tokensSuffix(configPath) {
|
|
|
18966
19627
|
|
|
18967
19628
|
// src/commands/start.ts
|
|
18968
19629
|
var import_node_util10 = require("util");
|
|
18969
|
-
var
|
|
19630
|
+
var import_outbound_api14 = require("@omnicross/core/outbound-api");
|
|
18970
19631
|
var import_SubscriptionAccountHealth5 = require("@omnicross/core/pipeline/SubscriptionAccountHealth");
|
|
18971
19632
|
var import_AccountAllowanceScheduling6 = require("@omnicross/core/pipeline/AccountAllowanceScheduling");
|
|
18972
19633
|
|
|
@@ -19028,7 +19689,7 @@ async function runStart(argv) {
|
|
|
19028
19689
|
await daemon.llmConfig.ready();
|
|
19029
19690
|
await daemon.migrateUsageStore();
|
|
19030
19691
|
await daemon.providerProxy.start();
|
|
19031
|
-
const serverConfig = await (0,
|
|
19692
|
+
const serverConfig = await (0, import_outbound_api14.loadServerConfig)(daemon.settingsStore);
|
|
19032
19693
|
await daemon.imageCleanupService.runOnce();
|
|
19033
19694
|
daemon.imageCleanupService.start();
|
|
19034
19695
|
(0, import_SubscriptionAccountHealth5.getSharedAccountHealth)().configure({
|
|
@@ -19051,7 +19712,9 @@ async function runStart(argv) {
|
|
|
19051
19712
|
voucher: serverConfig.voucher,
|
|
19052
19713
|
// claude-api-protocol-fidelity (§10): count_tokens strategy/budget,
|
|
19053
19714
|
// /v1/models shape, synthetic-ping heartbeat (hot-applied inside applyConfig).
|
|
19054
|
-
anthropic: serverConfig.anthropic
|
|
19715
|
+
anthropic: serverConfig.anthropic,
|
|
19716
|
+
// plan 阶段5: the Codex frontend mode is read live per request from here.
|
|
19717
|
+
search: serverConfig.search
|
|
19055
19718
|
});
|
|
19056
19719
|
let dashboardUrl = null;
|
|
19057
19720
|
if (!values["no-dashboard"]) {
|
|
@@ -19166,6 +19829,16 @@ Usage:
|
|
|
19166
19829
|
Check local Images config, roots, stores, permissions,
|
|
19167
19830
|
account and cached evidence. --live warns, then consumes
|
|
19168
19831
|
at most one minimal subscription image request.
|
|
19832
|
+
omnicross doctor search [--live] List the builtin search providers and their declared
|
|
19833
|
+
capabilities (offline, no config needed). --live sends ONE
|
|
19834
|
+
fixed public query per provider and reports healthy /
|
|
19835
|
+
degraded / blocked / failed.
|
|
19836
|
+
Keyed API providers show as unconfigured unless enabled
|
|
19837
|
+
through the DIAGNOSTIC-ONLY environment variables
|
|
19838
|
+
OMNICROSS_SEARCH_{TAVILY,JINA,SEARXNG,ZHIPU,Z_AI}_API_KEY
|
|
19839
|
+
/ _API_HOST (plus SEARXNG_BASIC_AUTH_USERNAME/_PASSWORD).
|
|
19840
|
+
These are read only by this command and are not the
|
|
19841
|
+
configuration system.
|
|
19169
19842
|
`;
|
|
19170
19843
|
async function main() {
|
|
19171
19844
|
const [, , subcommand, ...rest] = process.argv;
|