@omnicross/daemon 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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 import_outbound_api11 = require("@omnicross/core/outbound-api");
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_search2 = require("@omnicross/core/search");
1164
1167
 
1165
1168
  // src/bootstrap.ts
1166
1169
  var import_node_fs35 = require("fs");
@@ -1170,7 +1173,7 @@ 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 import_outbound_api9 = require("@omnicross/core/outbound-api");
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");
@@ -1180,7 +1183,7 @@ var import_SubscriptionIdentityStore3 = require("@omnicross/core/provider-proxy/
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 import_outbound_api10 = require("@omnicross/core/outbound-api");
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,7 +1980,7 @@ 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 import_outbound_api4 = require("@omnicross/core/outbound-api");
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");
@@ -4502,6 +4505,482 @@ 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
+
4513
+ // src/search/searchDoctorProjection.ts
4514
+ var import_search_types = require("@omnicross/contracts/search-types");
4515
+ var import_api = require("@omnicross/core/search/api");
4516
+ var import_http = require("@omnicross/core/search/http");
4517
+ var API_DOCTOR_PROVIDERS = [
4518
+ {
4519
+ id: "tavily",
4520
+ capabilities: import_api.TAVILY_CAPABILITIES,
4521
+ configured: (configs) => configs.tavily !== void 0,
4522
+ missingReason: "no API key configured"
4523
+ },
4524
+ {
4525
+ id: "jina",
4526
+ capabilities: import_api.JINA_CAPABILITIES,
4527
+ configured: (configs) => configs.jina !== void 0,
4528
+ // Honest about the asymmetry: Jina CAN run keyless, but a provider nobody
4529
+ // asked for is still not enabled.
4530
+ missingReason: "not configured (Jina can run without a key, but must be enabled explicitly)"
4531
+ },
4532
+ {
4533
+ id: "searxng",
4534
+ capabilities: import_api.SEARXNG_CAPABILITIES,
4535
+ configured: (configs) => configs.searxng !== void 0,
4536
+ missingReason: "no API host configured"
4537
+ },
4538
+ {
4539
+ id: "zhipu",
4540
+ capabilities: import_api.ZHIPU_CAPABILITIES,
4541
+ configured: (configs) => configs.zhipu !== void 0,
4542
+ missingReason: "no API key configured"
4543
+ },
4544
+ {
4545
+ id: "z.ai",
4546
+ capabilities: import_api.ZHIPU_CAPABILITIES,
4547
+ configured: (configs) => configs["z.ai"] !== void 0,
4548
+ missingReason: "no API key configured"
4549
+ }
4550
+ ];
4551
+ function buildSearchDoctorSnapshot(contributions = (0, import_http.builtinHttpSearchContributions)(), apiConfigs) {
4552
+ const rows = contributions.map((contribution) => ({
4553
+ providerId: contribution.id,
4554
+ source: contribution.source,
4555
+ kind: contribution.kind,
4556
+ capabilities: contribution.capabilities
4557
+ }));
4558
+ if (apiConfigs === void 0) return rows;
4559
+ for (const provider of API_DOCTOR_PROVIDERS) {
4560
+ if (provider.configured(apiConfigs)) continue;
4561
+ rows.push({
4562
+ providerId: provider.id,
4563
+ source: "builtin",
4564
+ kind: "api",
4565
+ capabilities: provider.capabilities,
4566
+ status: "unconfigured",
4567
+ reason: provider.missingReason
4568
+ });
4569
+ }
4570
+ return rows;
4571
+ }
4572
+ var SEARCH_DOCTOR_QUERY = "mozilla developer network http headers";
4573
+ function classifyLiveSearchOutcome(providerId, outcome, checkedAt) {
4574
+ if (outcome.kind === "results") {
4575
+ if (outcome.count > 0) return { providerId, status: "healthy", checkedAt };
4576
+ return {
4577
+ providerId,
4578
+ status: "degraded",
4579
+ checkedAt,
4580
+ reason: "reachable, but the engine returned no usable results (possible partial drift)"
4581
+ };
4582
+ }
4583
+ const error = (0, import_search_types.toSearchErrorShape)(outcome.error);
4584
+ const stage = error.details?.stage;
4585
+ const { status, reason } = classifySearchFailure(stage, error.code);
4586
+ return { providerId, status, checkedAt, reason, error };
4587
+ }
4588
+ function classifySearchFailure(stage, code) {
4589
+ if (stage === "challenge") {
4590
+ return { status: "blocked", reason: "the engine served a bot challenge instead of results" };
4591
+ }
4592
+ if (stage === "trust") {
4593
+ return {
4594
+ status: "blocked",
4595
+ reason: "the engine served a page that failed the anti-decoy trust check"
4596
+ };
4597
+ }
4598
+ if (code === "policy_denied") {
4599
+ return {
4600
+ status: "blocked",
4601
+ reason: "the egress policy refused the request target"
4602
+ };
4603
+ }
4604
+ if (code === "parse_failed") {
4605
+ return {
4606
+ status: "failed",
4607
+ reason: "the response was not recognizable as a search result page (parser drift suspected)"
4608
+ };
4609
+ }
4610
+ if (code === "timeout") {
4611
+ return { status: "failed", reason: "the request exceeded its time budget" };
4612
+ }
4613
+ return { status: "failed", reason: `the request failed (${code})` };
4614
+ }
4615
+ async function runSearchLiveChecks(contributions, now = () => (/* @__PURE__ */ new Date()).toISOString()) {
4616
+ const diagnostics = [];
4617
+ for (const contribution of contributions) {
4618
+ try {
4619
+ const results = await contribution.provider.search(SEARCH_DOCTOR_QUERY, { maxResults: 5 });
4620
+ diagnostics.push(
4621
+ classifyLiveSearchOutcome(contribution.id, { kind: "results", count: results.length }, now())
4622
+ );
4623
+ } catch (error) {
4624
+ diagnostics.push(classifyLiveSearchOutcome(contribution.id, { kind: "failure", error }, now()));
4625
+ }
4626
+ }
4627
+ return diagnostics;
4628
+ }
4629
+
4630
+ // src/search/SearchAssembly.ts
4631
+ var import_search = require("@omnicross/core/search");
4632
+ var import_api2 = require("@omnicross/core/search/api");
4633
+ var import_http2 = require("@omnicross/core/search/http");
4634
+ function searchEgressPolicyFrom(config) {
4635
+ const hosts = config.egress.allowedPrivateHosts;
4636
+ return hosts.length > 0 ? { allowedPrivateHosts: [...hosts] } : {};
4637
+ }
4638
+ function searchPolicyFrom(config) {
4639
+ const { preferred, allowed, fallbackEnabled, maxAttempts } = config.policy;
4640
+ return {
4641
+ ...preferred !== void 0 ? { preferred } : {},
4642
+ ...allowed !== void 0 ? { allowed: [...allowed] } : {},
4643
+ fallbackEnabled,
4644
+ ...maxAttempts !== void 0 ? { maxAttempts } : {}
4645
+ };
4646
+ }
4647
+ function searchContributionsFrom(config) {
4648
+ return [
4649
+ ...(0, import_http2.builtinHttpSearchContributions)(),
4650
+ ...(0, import_api2.apiSearchContributions)(config.providers, {
4651
+ egressPolicy: searchEgressPolicyFrom(config)
4652
+ })
4653
+ ];
4654
+ }
4655
+ function buildSearchRuntime(config, options = {}) {
4656
+ const logger = options.logger ?? null;
4657
+ return (0, import_search.createSearchRuntime)({
4658
+ contributions: options.contributions ?? searchContributionsFrom(config),
4659
+ policy: searchPolicyFrom(config),
4660
+ ...logger ? {
4661
+ onEvent: (event) => {
4662
+ logger.debug(`[search] ${formatSearchEvent(event)}`);
4663
+ }
4664
+ } : {}
4665
+ });
4666
+ }
4667
+ function formatSearchEvent(event) {
4668
+ const parts = [
4669
+ `type=${event.type}`,
4670
+ `request=${event.requestId}`,
4671
+ `queryHash=${event.queryHash}`,
4672
+ `durationMs=${event.durationMs}`
4673
+ ];
4674
+ if ("providerId" in event && event.providerId !== void 0) {
4675
+ parts.push(`provider=${event.providerId}`);
4676
+ }
4677
+ if ("outcome" in event && event.outcome !== void 0) parts.push(`outcome=${event.outcome}`);
4678
+ if ("errorCode" in event && event.errorCode !== void 0) parts.push(`error=${event.errorCode}`);
4679
+ if ("resultCount" in event && event.resultCount !== void 0) {
4680
+ parts.push(`results=${event.resultCount}`);
4681
+ }
4682
+ if ("fallbackCount" in event && event.fallbackCount !== void 0) {
4683
+ parts.push(`fallbacks=${event.fallbackCount}`);
4684
+ }
4685
+ return parts.join(" ");
4686
+ }
4687
+
4688
+ // src/admin/searchAdminApi.ts
4689
+ var KEYLESS_HTTP_PROVIDER_IDS = /* @__PURE__ */ new Set(["http-bing", "http-duckduckgo"]);
4690
+ var API_PROVIDER_IDS = /* @__PURE__ */ new Set([
4691
+ "tavily",
4692
+ "jina",
4693
+ "searxng",
4694
+ "zhipu",
4695
+ "z.ai"
4696
+ ]);
4697
+ var SEARCH_QUERY_MAX_CODE_UNITS = 256;
4698
+ var QUERY_CONTROL_CHARS = /[\u0000-\u001f\u007f]/u;
4699
+ var SEARCH_RESULT_FIELD_CAPS = { title: 512, url: 2048, content: 1024 };
4700
+ var SEARCH_QUERY_MAX_RESULTS = 5;
4701
+ function sanitizeResultField(value, cap) {
4702
+ const text = typeof value === "string" ? value : value === null || value === void 0 ? "" : String(value);
4703
+ return text.replace(/[\u0000-\u001f\u007f]/gu, "").slice(0, cap);
4704
+ }
4705
+ function writeJson(res, status, body) {
4706
+ res.writeHead(status, { "Content-Type": "application/json" });
4707
+ res.end(JSON.stringify(body));
4708
+ }
4709
+ function writeErr(res, status, message) {
4710
+ writeJson(res, status, { error: { type: "admin_api_error", message } });
4711
+ }
4712
+ var SEARCH_MAX_BODY_BYTES = 64 * 1024;
4713
+ var SearchBodyTooLargeError = class extends Error {
4714
+ };
4715
+ async function readJsonBody2(req) {
4716
+ const chunks = [];
4717
+ let bytes = 0;
4718
+ for await (const chunk of req) {
4719
+ const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
4720
+ bytes += buffer.length;
4721
+ if (bytes > SEARCH_MAX_BODY_BYTES) throw new SearchBodyTooLargeError("request body is too large");
4722
+ chunks.push(buffer);
4723
+ }
4724
+ const raw = Buffer.concat(chunks).toString("utf8");
4725
+ if (!raw.trim()) return {};
4726
+ try {
4727
+ const parsed = JSON.parse(raw);
4728
+ return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
4729
+ } catch {
4730
+ return {};
4731
+ }
4732
+ }
4733
+ async function readBodyOrReject(req, res) {
4734
+ try {
4735
+ return await readJsonBody2(req);
4736
+ } catch (error) {
4737
+ if (error instanceof SearchBodyTooLargeError) {
4738
+ writeErr(res, 400, error.message);
4739
+ return void 0;
4740
+ }
4741
+ throw error;
4742
+ }
4743
+ }
4744
+ async function handleSearchAdmin(req, res, method, rest, deps) {
4745
+ if (rest.length === 1 && rest[0] === "diagnostics") {
4746
+ if (!deps.searchStatus) {
4747
+ return writeErr(res, 501, "Search status is not available in this build");
4748
+ }
4749
+ if (method !== "GET") {
4750
+ return writeErr(res, 405, `method ${method} not allowed on search diagnostics`);
4751
+ }
4752
+ return handleSearchDiagnostics(res, deps);
4753
+ }
4754
+ if (rest.length === 1 && rest[0] === "test") {
4755
+ if (!deps.searchStatus) {
4756
+ return writeErr(res, 501, "Search status is not available in this build");
4757
+ }
4758
+ if (method !== "POST") {
4759
+ return writeErr(res, 405, `method ${method} not allowed on search test`);
4760
+ }
4761
+ return handleSearchTest(req, res, deps);
4762
+ }
4763
+ if (rest.length === 1 && rest[0] === "query") {
4764
+ if (!deps.searchStatus) {
4765
+ return writeErr(res, 501, "Search status is not available in this build");
4766
+ }
4767
+ if (method !== "POST") {
4768
+ return writeErr(res, 405, `method ${method} not allowed on search query`);
4769
+ }
4770
+ return handleSearchQuery(req, res, deps);
4771
+ }
4772
+ return writeErr(res, 404, `unknown search route '/${rest.join("/")}'`);
4773
+ }
4774
+ async function handleSearchDiagnostics(res, deps) {
4775
+ const status = deps.searchStatus;
4776
+ const persisted = await (0, import_outbound_api2.loadServerConfig)(deps.settingsStore);
4777
+ const search = persisted.search ?? import_outbound_api2.DEFAULT_SEARCH_SERVER_CONFIG;
4778
+ const rows = buildSearchDoctorSnapshot(
4779
+ status.runtime.listProviders(),
4780
+ search.providers
4781
+ );
4782
+ const snapshot = {
4783
+ rows,
4784
+ modes: {
4785
+ // codex is read from the LIVE config per request — an admin PUT has
4786
+ // already applied. responses/anthropic were captured at bootstrap.
4787
+ codex: search.modes.codex,
4788
+ responses: status.modes.responses,
4789
+ anthropic: status.modes.anthropic
4790
+ },
4791
+ applySemantics: { codex: "immediate", rest: "restart" }
4792
+ };
4793
+ return writeJson(res, 200, { diagnostics: snapshot });
4794
+ }
4795
+ async function handleSearchTest(req, res, deps) {
4796
+ const status = deps.searchStatus;
4797
+ const body = await readBodyOrReject(req, res);
4798
+ if (body === void 0) return;
4799
+ const providerId = body["providerId"];
4800
+ if (typeof providerId !== "string" || providerId.length === 0) {
4801
+ return writeErr(res, 400, "providerId must be a non-empty string");
4802
+ }
4803
+ if (!KEYLESS_HTTP_PROVIDER_IDS.has(providerId) && !API_PROVIDER_IDS.has(providerId)) {
4804
+ return writeErr(res, 404, `unknown search provider '${providerId}'`);
4805
+ }
4806
+ const persisted = await (0, import_outbound_api2.loadServerConfig)(deps.settingsStore);
4807
+ const search = persisted.search ?? import_outbound_api2.DEFAULT_SEARCH_SERVER_CONFIG;
4808
+ const providers = search.providers;
4809
+ if (!KEYLESS_HTTP_PROVIDER_IDS.has(providerId) && providers[providerId] === void 0) {
4810
+ return writeErr(res, 400, `search provider '${providerId}' is not configured`);
4811
+ }
4812
+ const egressPolicy = searchEgressPolicyFrom(search);
4813
+ const fetchImpl = status.testFetch;
4814
+ const transport = fetchImpl ? (0, import_http3.createSearchHttpTransport)({ fetch: fetchImpl, egressPolicy }) : void 0;
4815
+ const contributions = [
4816
+ ...(0, import_http3.builtinHttpSearchContributions)(transport),
4817
+ ...(0, import_api3.apiSearchContributions)(search.providers, {
4818
+ egressPolicy,
4819
+ ...fetchImpl ? { fetchImpl } : {}
4820
+ })
4821
+ ];
4822
+ const contribution = contributions.find((c) => c.id === providerId);
4823
+ if (!contribution) {
4824
+ return writeErr(res, 400, `search provider '${providerId}' is not configured`);
4825
+ }
4826
+ const checkedAt = (/* @__PURE__ */ new Date()).toISOString();
4827
+ try {
4828
+ const results = await contribution.provider.search(SEARCH_DOCTOR_QUERY, { maxResults: 5 });
4829
+ const diagnostic = classifyLiveSearchOutcome(
4830
+ contribution.id,
4831
+ { kind: "results", count: results.length },
4832
+ checkedAt
4833
+ );
4834
+ const response = { diagnostic, resultCount: results.length };
4835
+ return writeJson(res, 200, { result: response });
4836
+ } catch (error) {
4837
+ const diagnostic = classifyLiveSearchOutcome(contribution.id, { kind: "failure", error }, checkedAt);
4838
+ const response = { diagnostic };
4839
+ return writeJson(res, 200, { result: response });
4840
+ }
4841
+ }
4842
+ async function handleSearchQuery(req, res, deps) {
4843
+ const status = deps.searchStatus;
4844
+ const body = await readBodyOrReject(req, res);
4845
+ if (body === void 0) return;
4846
+ const providerId = body["providerId"];
4847
+ if (typeof providerId !== "string" || providerId.length === 0) {
4848
+ return writeErr(res, 400, "providerId must be a non-empty string");
4849
+ }
4850
+ if (!KEYLESS_HTTP_PROVIDER_IDS.has(providerId) && !API_PROVIDER_IDS.has(providerId)) {
4851
+ return writeErr(res, 404, `unknown search provider '${providerId}'`);
4852
+ }
4853
+ const query2 = body["query"];
4854
+ if (typeof query2 !== "string" || query2.trim().length === 0) {
4855
+ return writeErr(res, 400, "query must be a non-empty string");
4856
+ }
4857
+ if (query2.length > SEARCH_QUERY_MAX_CODE_UNITS) {
4858
+ return writeErr(res, 400, `query must be at most ${SEARCH_QUERY_MAX_CODE_UNITS} characters`);
4859
+ }
4860
+ if (QUERY_CONTROL_CHARS.test(query2)) {
4861
+ return writeErr(res, 400, "query must not contain control characters");
4862
+ }
4863
+ const persisted = await (0, import_outbound_api2.loadServerConfig)(deps.settingsStore);
4864
+ const search = persisted.search ?? import_outbound_api2.DEFAULT_SEARCH_SERVER_CONFIG;
4865
+ const providers = search.providers;
4866
+ if (!KEYLESS_HTTP_PROVIDER_IDS.has(providerId) && providers[providerId] === void 0) {
4867
+ return writeErr(res, 400, `search provider '${providerId}' is not configured`);
4868
+ }
4869
+ const egressPolicy = searchEgressPolicyFrom(search);
4870
+ const fetchImpl = status.testFetch;
4871
+ const transport = fetchImpl ? (0, import_http3.createSearchHttpTransport)({ fetch: fetchImpl, egressPolicy }) : void 0;
4872
+ const contributions = [
4873
+ ...(0, import_http3.builtinHttpSearchContributions)(transport),
4874
+ ...(0, import_api3.apiSearchContributions)(search.providers, {
4875
+ egressPolicy,
4876
+ ...fetchImpl ? { fetchImpl } : {}
4877
+ })
4878
+ ];
4879
+ const contribution = contributions.find((c) => c.id === providerId);
4880
+ if (!contribution) {
4881
+ return writeErr(res, 400, `search provider '${providerId}' is not configured`);
4882
+ }
4883
+ const checkedAt = (/* @__PURE__ */ new Date()).toISOString();
4884
+ try {
4885
+ const results = await contribution.provider.search(query2, { maxResults: 5 });
4886
+ const sanitized = results.slice(0, SEARCH_QUERY_MAX_RESULTS).map((result) => ({
4887
+ title: sanitizeResultField(result.title, SEARCH_RESULT_FIELD_CAPS.title),
4888
+ url: sanitizeResultField(result.url, SEARCH_RESULT_FIELD_CAPS.url),
4889
+ content: sanitizeResultField(result.content, SEARCH_RESULT_FIELD_CAPS.content)
4890
+ }));
4891
+ const diagnostic = sanitized.length === 0 ? { providerId: contribution.id, status: "healthy", checkedAt } : classifyLiveSearchOutcome(
4892
+ contribution.id,
4893
+ { kind: "results", count: sanitized.length },
4894
+ checkedAt
4895
+ );
4896
+ const response = {
4897
+ diagnostic,
4898
+ resultCount: sanitized.length,
4899
+ results: sanitized
4900
+ };
4901
+ return writeJson(res, 200, { result: response });
4902
+ } catch (error) {
4903
+ const diagnostic = classifyLiveSearchOutcome(contribution.id, { kind: "failure", error }, checkedAt);
4904
+ const response = { diagnostic };
4905
+ return writeJson(res, 200, { result: response });
4906
+ }
4907
+ }
4908
+
4909
+ // src/admin/searchAdminView.ts
4910
+ var API_KEY_PROVIDERS = /* @__PURE__ */ new Set(["tavily", "jina", "zhipu", "z.ai"]);
4911
+ var BASIC_AUTH_PROVIDERS = /* @__PURE__ */ new Set(["searxng"]);
4912
+ function isRecord(value) {
4913
+ return value !== null && typeof value === "object" && !Array.isArray(value);
4914
+ }
4915
+ function redactSearchServerConfig(search) {
4916
+ const providers = {};
4917
+ for (const [id, raw] of Object.entries(search.providers)) {
4918
+ const entry = raw;
4919
+ const view = {};
4920
+ if (entry.apiHost !== void 0) view.apiHost = entry.apiHost;
4921
+ if (entry.basicAuthUsername !== void 0) view.basicAuthUsername = entry.basicAuthUsername;
4922
+ if (API_KEY_PROVIDERS.has(id)) {
4923
+ view.apiKeyConfigured = typeof entry.apiKey === "string" && entry.apiKey.length > 0;
4924
+ }
4925
+ if (BASIC_AUTH_PROVIDERS.has(id)) {
4926
+ view.basicAuthPasswordConfigured = typeof entry.basicAuthPassword === "string" && entry.basicAuthPassword.length > 0;
4927
+ }
4928
+ providers[id] = view;
4929
+ }
4930
+ return {
4931
+ modes: search.modes,
4932
+ providers,
4933
+ egress: { allowedPrivateHosts: [...search.egress.allowedPrivateHosts] },
4934
+ policy: { ...search.policy, ...search.policy.allowed ? { allowed: [...search.policy.allowed] } : {} }
4935
+ };
4936
+ }
4937
+ function storedSecrets(current, id) {
4938
+ const entry = current.providers[id];
4939
+ if (!entry) return {};
4940
+ const out = {};
4941
+ if (typeof entry.apiKey === "string" && entry.apiKey.length > 0) out.apiKey = entry.apiKey;
4942
+ if (typeof entry.basicAuthPassword === "string" && entry.basicAuthPassword.length > 0) {
4943
+ out.basicAuthPassword = entry.basicAuthPassword;
4944
+ }
4945
+ return out;
4946
+ }
4947
+ function resolveSecretField(entry, field, stored) {
4948
+ if (!(field in entry)) {
4949
+ if (stored !== void 0) entry[field] = stored;
4950
+ return;
4951
+ }
4952
+ const value = entry[field];
4953
+ if (value === null) {
4954
+ delete entry[field];
4955
+ return;
4956
+ }
4957
+ if (typeof value === "string" && value.trim().length > 0) return;
4958
+ if (stored !== void 0) entry[field] = stored;
4959
+ else delete entry[field];
4960
+ }
4961
+ function preserveSearchSecrets(incoming, current) {
4962
+ if (!isRecord(incoming)) return incoming;
4963
+ const section = { ...incoming };
4964
+ const providersValue = section["providers"];
4965
+ if (!isRecord(providersValue)) return section;
4966
+ const providers = {};
4967
+ for (const [id, entryValue] of Object.entries(providersValue)) {
4968
+ if (!isRecord(entryValue)) {
4969
+ providers[id] = entryValue;
4970
+ continue;
4971
+ }
4972
+ const entry = { ...entryValue };
4973
+ delete entry["apiKeyConfigured"];
4974
+ delete entry["basicAuthPasswordConfigured"];
4975
+ const stored = storedSecrets(current, id);
4976
+ resolveSecretField(entry, "apiKey", stored.apiKey);
4977
+ resolveSecretField(entry, "basicAuthPassword", stored.basicAuthPassword);
4978
+ providers[id] = entry;
4979
+ }
4980
+ section["providers"] = providers;
4981
+ return section;
4982
+ }
4983
+
4505
4984
  // src/admin/keyPolicyBody.ts
4506
4985
  function parseKeyPolicyBody(body) {
4507
4986
  const policy = {};
@@ -4564,7 +5043,7 @@ function parseKeyPolicyBody(body) {
4564
5043
  var ENDPOINTS = /* @__PURE__ */ new Set(["chat", "responses", "messages", "gemini"]);
4565
5044
  var TARGET_KINDS = /* @__PURE__ */ new Set(["account", "account-group", "account-pool", "provider"]);
4566
5045
  var FALLBACKS = /* @__PURE__ */ new Set(["next", "fail", "global"]);
4567
- function isRecord(value) {
5046
+ function isRecord2(value) {
4568
5047
  return !!value && typeof value === "object" && !Array.isArray(value);
4569
5048
  }
4570
5049
  function nonBlank(value) {
@@ -4584,7 +5063,7 @@ function validateGatewayBindingsSegment(patch) {
4584
5063
  const ids = /* @__PURE__ */ new Set();
4585
5064
  raw.forEach((entry, index) => {
4586
5065
  const path2 = `bindings[${index}]`;
4587
- if (!isRecord(entry)) {
5066
+ if (!isRecord2(entry)) {
4588
5067
  errors.push(`${path2} must be an object`);
4589
5068
  return;
4590
5069
  }
@@ -4613,12 +5092,12 @@ function validateGatewayBindingsSegment(patch) {
4613
5092
  } else if (entry.modelMappings.length > 100) {
4614
5093
  errors.push(`${path2}.modelMappings cannot contain more than 100 entries`);
4615
5094
  } else if (entry.modelMappings.some(
4616
- (mapping) => !isRecord(mapping) || !nonBlank(mapping.source) || !nonBlank(mapping.target)
5095
+ (mapping) => !isRecord2(mapping) || !nonBlank(mapping.source) || !nonBlank(mapping.target)
4617
5096
  )) {
4618
5097
  errors.push(`${path2}.modelMappings must contain non-empty source and target strings`);
4619
5098
  }
4620
5099
  }
4621
- if (!isRecord(entry.target) || !TARGET_KINDS.has(String(entry.target.kind))) {
5100
+ if (!isRecord2(entry.target) || !TARGET_KINDS.has(String(entry.target.kind))) {
4622
5101
  errors.push(`${path2}.target is invalid`);
4623
5102
  } else {
4624
5103
  if (!nonBlank(entry.target.providerId)) errors.push(`${path2}.target.providerId is required`);
@@ -4633,7 +5112,7 @@ function validateGatewayBindingsSegment(patch) {
4633
5112
  }
4634
5113
  }
4635
5114
  if (entry.modelMap !== void 0) {
4636
- if (!isRecord(entry.modelMap) || Object.values(entry.modelMap).some((value) => typeof value !== "string")) {
5115
+ if (!isRecord2(entry.modelMap) || Object.values(entry.modelMap).some((value) => typeof value !== "string")) {
4637
5116
  errors.push(`${path2}.modelMap must contain string values`);
4638
5117
  }
4639
5118
  }
@@ -4651,15 +5130,15 @@ function validateGatewayBindingsSegment(patch) {
4651
5130
  }
4652
5131
 
4653
5132
  // src/admin/voucherAdmin.ts
4654
- var import_outbound_api2 = require("@omnicross/core/outbound-api");
4655
- function writeJson(res, status, body) {
5133
+ var import_outbound_api3 = require("@omnicross/core/outbound-api");
5134
+ function writeJson2(res, status, body) {
4656
5135
  res.writeHead(status, { "Content-Type": "application/json" });
4657
5136
  res.end(JSON.stringify(body));
4658
5137
  }
4659
- function writeErr(res, status, message) {
4660
- writeJson(res, status, { error: { type: "voucher_error", message } });
5138
+ function writeErr2(res, status, message) {
5139
+ writeJson2(res, status, { error: { type: "voucher_error", message } });
4661
5140
  }
4662
- function readJsonBody2(req) {
5141
+ function readJsonBody3(req) {
4663
5142
  return new Promise((resolve11, reject) => {
4664
5143
  const chunks = [];
4665
5144
  req.on("data", (c) => chunks.push(c));
@@ -4710,34 +5189,34 @@ function parseVoucherCreateBody(body) {
4710
5189
  return { ok: true, input };
4711
5190
  }
4712
5191
  async function voucherEnabled(deps) {
4713
- const config = await (0, import_outbound_api2.loadServerConfig)(deps.settingsStore);
5192
+ const config = await (0, import_outbound_api3.loadServerConfig)(deps.settingsStore);
4714
5193
  return config.voucher?.enabled === true;
4715
5194
  }
4716
5195
  async function handleVoucher(req, res, method, rest, deps) {
4717
5196
  const voucherDb = deps.voucherDb;
4718
- if (!voucherDb) return writeErr(res, 501, "Voucher feature is not available");
5197
+ if (!voucherDb) return writeErr2(res, 501, "Voucher feature is not available");
4719
5198
  if (method === "GET" && rest.length === 0) {
4720
5199
  const rows = await voucherDb.voucherList();
4721
- return writeJson(res, 200, { vouchers: rows.map(import_outbound_api2.toVoucherInfo) });
5200
+ return writeJson2(res, 200, { vouchers: rows.map(import_outbound_api3.toVoucherInfo) });
4722
5201
  }
4723
5202
  if (method === "POST" && rest.length === 0) {
4724
- if (!await voucherEnabled(deps)) return writeErr(res, 403, "Voucher feature is disabled");
5203
+ if (!await voucherEnabled(deps)) return writeErr2(res, 403, "Voucher feature is disabled");
4725
5204
  let body;
4726
5205
  try {
4727
- body = await readJsonBody2(req);
5206
+ body = await readJsonBody3(req);
4728
5207
  } catch {
4729
- return writeErr(res, 400, "Invalid JSON in request body");
5208
+ return writeErr2(res, 400, "Invalid JSON in request body");
4730
5209
  }
4731
5210
  const parsed = parseVoucherCreateBody(body);
4732
- if (!parsed.ok) return writeErr(res, 400, parsed.message);
4733
- const code = (0, import_outbound_api2.generateVoucherCode)();
5211
+ if (!parsed.ok) return writeErr2(res, 400, parsed.message);
5212
+ const code = (0, import_outbound_api3.generateVoucherCode)();
4734
5213
  const created = await voucherDb.voucherCreate({
4735
- id: (0, import_outbound_api2.newVoucherId)(),
4736
- codeHash: (0, import_outbound_api2.hashVoucherCode)(code),
4737
- codePrefix: (0, import_outbound_api2.voucherCodePrefix)(code),
5214
+ id: (0, import_outbound_api3.newVoucherId)(),
5215
+ codeHash: (0, import_outbound_api3.hashVoucherCode)(code),
5216
+ codePrefix: (0, import_outbound_api3.voucherCodePrefix)(code),
4738
5217
  ...parsed.input
4739
5218
  });
4740
- return writeJson(res, 201, {
5219
+ return writeJson2(res, 201, {
4741
5220
  id: created.id,
4742
5221
  codePrefix: created.codePrefix,
4743
5222
  type: created.type,
@@ -4748,11 +5227,11 @@ async function handleVoucher(req, res, method, rest, deps) {
4748
5227
  }
4749
5228
  const id = rest[0];
4750
5229
  if (method === "POST" && id && rest[1] === "revoke") {
4751
- if (!await voucherEnabled(deps)) return writeErr(res, 403, "Voucher feature is disabled");
5230
+ if (!await voucherEnabled(deps)) return writeErr2(res, 403, "Voucher feature is disabled");
4752
5231
  const ok = await voucherDb.voucherRevokeCas(id, Date.now());
4753
- return writeJson(res, ok ? 200 : 409, { ok });
5232
+ return writeJson2(res, ok ? 200 : 409, { ok });
4754
5233
  }
4755
- return writeErr(res, 405, `method ${method} not allowed on voucher`);
5234
+ return writeErr2(res, 405, `method ${method} not allowed on voucher`);
4756
5235
  }
4757
5236
 
4758
5237
  // src/admin/webhookConfigBody.ts
@@ -4903,7 +5382,7 @@ function applyBillingConfig(config) {
4903
5382
  }
4904
5383
 
4905
5384
  // src/migration/migration.ts
4906
- var import_outbound_api3 = require("@omnicross/core/outbound-api");
5385
+ var import_outbound_api4 = require("@omnicross/core/outbound-api");
4907
5386
 
4908
5387
  // src/ports/account-multi.ts
4909
5388
  var import_node_crypto8 = require("crypto");
@@ -5331,7 +5810,7 @@ async function gatherExport(deps, passphrase) {
5331
5810
  v: BUNDLE_VERSION,
5332
5811
  providers: cfg.providers,
5333
5812
  tokens,
5334
- server: (0, import_outbound_api3.normalizeServerConfig)(cfg.server)
5813
+ server: (0, import_outbound_api4.normalizeServerConfig)(cfg.server)
5335
5814
  };
5336
5815
  return sealPack(JSON.stringify(bundle), passphrase);
5337
5816
  }
@@ -5380,7 +5859,7 @@ async function applyImport(packString, passphrase, mode, deps, parseProviderInpu
5380
5859
  const imageErrors = validateImagesAdminConfig(rawServer["images"]);
5381
5860
  if (imageErrors.length > 0) throw new Error("migration pack has an invalid Images config");
5382
5861
  }
5383
- validatedServer = (0, import_outbound_api3.normalizeServerConfig)(bundle.server);
5862
+ validatedServer = (0, import_outbound_api4.normalizeServerConfig)(bundle.server);
5384
5863
  }
5385
5864
  const validatedProviders = [];
5386
5865
  for (const raw of rawProviders) {
@@ -5661,12 +6140,12 @@ async function handlePricingResolveConflicts(body, deps) {
5661
6140
  }
5662
6141
 
5663
6142
  // src/admin/accountAllowanceApi.ts
5664
- function writeJson2(res, status, body) {
6143
+ function writeJson3(res, status, body) {
5665
6144
  res.writeHead(status, { "Content-Type": "application/json" });
5666
6145
  res.end(JSON.stringify(body));
5667
6146
  }
5668
6147
  function writeError2(res, status, message) {
5669
- writeJson2(res, status, { error: { type: "account_allowance_error", message } });
6148
+ writeJson3(res, status, { error: { type: "account_allowance_error", message } });
5670
6149
  }
5671
6150
  function readJson2(req) {
5672
6151
  return new Promise((resolve11, reject) => {
@@ -5699,7 +6178,7 @@ async function handleAccountAllowanceApi(req, res, method, rest, service) {
5699
6178
  if (!service.getSchedulingStatus) {
5700
6179
  return writeError2(res, 501, "allowance scheduling diagnostics are not available");
5701
6180
  }
5702
- return writeJson2(res, 200, { scheduling: service.getSchedulingStatus() });
6181
+ return writeJson3(res, 200, { scheduling: service.getSchedulingStatus() });
5703
6182
  }
5704
6183
  if (method === "GET") {
5705
6184
  const params = query(req);
@@ -5708,7 +6187,7 @@ async function handleAccountAllowanceApi(req, res, method, rest, service) {
5708
6187
  if (providerId === null) return writeError2(res, 400, "providerId must be claude or codex");
5709
6188
  const accountId = rest.length >= 2 ? rest[1] : params.get("accountId") ?? void 0;
5710
6189
  const allowances = await service.list({ providerId, accountId });
5711
- return writeJson2(res, 200, { allowances });
6190
+ return writeJson3(res, 200, { allowances });
5712
6191
  }
5713
6192
  if (method === "POST" && rest[0] === "refresh") {
5714
6193
  const body = await readJson2(req);
@@ -5723,7 +6202,7 @@ async function handleAccountAllowanceApi(req, res, method, rest, service) {
5723
6202
  if (accountId && allowances.length === 0) {
5724
6203
  return writeError2(res, 404, `Claude account '${accountId}' not found`);
5725
6204
  }
5726
- return writeJson2(res, 200, { allowances });
6205
+ return writeJson3(res, 200, { allowances });
5727
6206
  }
5728
6207
  return writeError2(res, 405, `method ${method} not allowed on account allowances`);
5729
6208
  }
@@ -5739,7 +6218,7 @@ function readBody(req) {
5739
6218
  req.on("error", reject);
5740
6219
  });
5741
6220
  }
5742
- async function readJsonBody3(req) {
6221
+ async function readJsonBody4(req) {
5743
6222
  const raw = await readBody(req);
5744
6223
  if (!raw.trim()) return {};
5745
6224
  try {
@@ -5749,12 +6228,12 @@ async function readJsonBody3(req) {
5749
6228
  return {};
5750
6229
  }
5751
6230
  }
5752
- function writeJson3(res, status, body) {
6231
+ function writeJson4(res, status, body) {
5753
6232
  res.writeHead(status, { "Content-Type": "application/json" });
5754
6233
  res.end(JSON.stringify(body));
5755
6234
  }
5756
6235
  function writeJsonError(res, status, message) {
5757
- writeJson3(res, status, { error: { type: "admin_api_error", message } });
6236
+ writeJson4(res, status, { error: { type: "admin_api_error", message } });
5758
6237
  }
5759
6238
  function maskProviderApiKey(apiKey) {
5760
6239
  if (!apiKey) return "";
@@ -5775,7 +6254,7 @@ function toKeyInfo(row) {
5775
6254
  lastUsedAt: row.lastUsedAt,
5776
6255
  revoked: row.revokedAt !== null,
5777
6256
  kind: row.kind,
5778
- allowedEndpoints: [...(0, import_outbound_api4.effectiveOutboundPermissions)(row.allowedEndpoints)],
6257
+ allowedEndpoints: [...(0, import_outbound_api5.effectiveOutboundPermissions)(row.allowedEndpoints)],
5779
6258
  legacyPermissions: row.allowedEndpoints === void 0,
5780
6259
  loopbackOnly: row.loopbackOnly,
5781
6260
  maxConcurrency: row.maxConcurrency,
@@ -5861,6 +6340,8 @@ async function handleAdminApi(req, res, path2, deps) {
5861
6340
  return await handleServer(req, res, method, deps);
5862
6341
  case "images":
5863
6342
  return await handleImages(res, method, rest, deps);
6343
+ case "search":
6344
+ return await handleSearchAdmin(req, res, method, rest, deps);
5864
6345
  case "accounts":
5865
6346
  return await handleAccounts(req, res, method, rest, deps);
5866
6347
  case "cli":
@@ -5894,7 +6375,7 @@ function requestQuery(req) {
5894
6375
  return new URLSearchParams(qIdx >= 0 ? raw.slice(qIdx + 1) : "");
5895
6376
  }
5896
6377
  function writeResult(res, result) {
5897
- writeJson3(res, result.status, result.body);
6378
+ writeJson4(res, result.status, result.body);
5898
6379
  }
5899
6380
  async function handleUsage(req, res, method, rest, deps) {
5900
6381
  if (method !== "GET") return writeJsonError(res, 405, `method ${method} not allowed on usage`);
@@ -5903,13 +6384,13 @@ async function handleUsage(req, res, method, rest, deps) {
5903
6384
  async function handleDashboardRoute(res, method, deps) {
5904
6385
  if (method !== "GET") return writeJsonError(res, 405, `method ${method} not allowed on dashboard`);
5905
6386
  const result = await handleDashboard(deps);
5906
- return writeJson3(res, result.status, result.body);
6387
+ return writeJson4(res, result.status, result.body);
5907
6388
  }
5908
6389
  async function handlePricing(req, res, method, rest, deps) {
5909
6390
  if (rest.length === 0) {
5910
6391
  if (method === "GET") return writeResult(res, await handlePricingList(deps));
5911
6392
  if (method === "PUT") {
5912
- return writeResult(res, await handlePricingUpsert(await readJsonBody3(req), deps));
6393
+ return writeResult(res, await handlePricingUpsert(await readJsonBody4(req), deps));
5913
6394
  }
5914
6395
  if (method === "DELETE") {
5915
6396
  return writeResult(res, await handlePricingDelete(requestQuery(req), deps));
@@ -5920,7 +6401,7 @@ async function handlePricing(req, res, method, rest, deps) {
5920
6401
  return writeResult(res, await handlePricingFetchLatest(deps));
5921
6402
  }
5922
6403
  if (method === "POST" && rest.length === 1 && rest[0] === "resolve-conflicts") {
5923
- return writeResult(res, await handlePricingResolveConflicts(await readJsonBody3(req), deps));
6404
+ return writeResult(res, await handlePricingResolveConflicts(await readJsonBody4(req), deps));
5924
6405
  }
5925
6406
  return writeJsonError(res, 404, `unknown pricing route '${rest.join("/")}'`);
5926
6407
  }
@@ -5934,15 +6415,15 @@ function migrationDeps(deps) {
5934
6415
  }
5935
6416
  async function handleMigrationExport(req, res, method, deps) {
5936
6417
  if (method !== "POST") return writeJsonError(res, 405, `method ${method} not allowed on export`);
5937
- const body = await readJsonBody3(req);
6418
+ const body = await readJsonBody4(req);
5938
6419
  const result = await handleExport(body, migrationDeps(deps));
5939
- return writeJson3(res, result.status, result.body);
6420
+ return writeJson4(res, result.status, result.body);
5940
6421
  }
5941
6422
  async function handleMigrationImport(req, res, method, deps) {
5942
6423
  if (method !== "POST") return writeJsonError(res, 405, `method ${method} not allowed on import`);
5943
- const body = await readJsonBody3(req);
6424
+ const body = await readJsonBody4(req);
5944
6425
  const result = await handleImport(body, migrationDeps(deps));
5945
- return writeJson3(res, result.status, result.body);
6426
+ return writeJson4(res, result.status, result.body);
5946
6427
  }
5947
6428
  async function handleProviders(req, res, method, rest, deps) {
5948
6429
  const cfg = loadConfig(deps.configPath);
@@ -5973,13 +6454,13 @@ async function handleProviders(req, res, method, rest, deps) {
5973
6454
  if (method === "GET" && rest.length === 2 && rest[1] === "reveal-key") {
5974
6455
  const row = cfg.providers.find((p) => p.id === rest[0]);
5975
6456
  if (!row) return writeJsonError(res, 404, `provider '${rest[0]}' not found`);
5976
- return writeJson3(res, 200, { apiKey: row.apiKey ?? "" });
6457
+ return writeJson4(res, 200, { apiKey: row.apiKey ?? "" });
5977
6458
  }
5978
6459
  if (method === "GET") {
5979
- return writeJson3(res, 200, { providers: cfg.providers.map(toProviderView) });
6460
+ return writeJson4(res, 200, { providers: cfg.providers.map(toProviderView) });
5980
6461
  }
5981
6462
  if (method === "POST") {
5982
- const body = await readJsonBody3(req);
6463
+ const body = await readJsonBody4(req);
5983
6464
  const provider = parseProviderInput(body, void 0);
5984
6465
  if (!provider) return writeJsonError(res, 400, "invalid provider (id, apiFormat, baseUrl required)");
5985
6466
  if (cfg.providers.some((p) => p.id === provider.id)) {
@@ -5987,25 +6468,25 @@ async function handleProviders(req, res, method, rest, deps) {
5987
6468
  }
5988
6469
  cfg.providers.push(provider);
5989
6470
  persistProviders(cfg, deps);
5990
- return writeJson3(res, 201, { provider: toProviderView(provider) });
6471
+ return writeJson4(res, 201, { provider: toProviderView(provider) });
5991
6472
  }
5992
6473
  const id = rest[0];
5993
6474
  if (!id) return writeJsonError(res, 400, "provider id required in path");
5994
6475
  const idx = cfg.providers.findIndex((p) => p.id === id);
5995
6476
  if (idx < 0) return writeJsonError(res, 404, `provider '${id}' not found`);
5996
6477
  if (method === "PUT") {
5997
- const body = await readJsonBody3(req);
6478
+ const body = await readJsonBody4(req);
5998
6479
  const existing = cfg.providers[idx];
5999
6480
  const updated = parseProviderInput(body, existing);
6000
6481
  if (!updated) return writeJsonError(res, 400, "invalid provider (apiFormat, baseUrl required)");
6001
6482
  cfg.providers[idx] = updated;
6002
6483
  persistProviders(cfg, deps);
6003
- return writeJson3(res, 200, { provider: toProviderView(updated) });
6484
+ return writeJson4(res, 200, { provider: toProviderView(updated) });
6004
6485
  }
6005
6486
  if (method === "DELETE") {
6006
6487
  cfg.providers.splice(idx, 1);
6007
6488
  persistProviders(cfg, deps);
6008
- return writeJson3(res, 200, { ok: true });
6489
+ return writeJson4(res, 200, { ok: true });
6009
6490
  }
6010
6491
  return writeJsonError(res, 405, `method ${method} not allowed on providers`);
6011
6492
  }
@@ -6014,7 +6495,7 @@ function persistProviders(cfg, deps) {
6014
6495
  deps.llmConfig.reload(cfg);
6015
6496
  }
6016
6497
  async function handleProviderReorder(req, res, cfg, deps) {
6017
- const body = await readJsonBody3(req);
6498
+ const body = await readJsonBody4(req);
6018
6499
  const rawOrder = body["order"];
6019
6500
  if (!Array.isArray(rawOrder)) {
6020
6501
  return writeJsonError(res, 400, "reorder requires { order: string[] }");
@@ -6038,14 +6519,14 @@ async function handleProviderReorder(req, res, cfg, deps) {
6038
6519
  }
6039
6520
  cfg.providers = reordered;
6040
6521
  persistProviders(cfg, deps);
6041
- return writeJson3(res, 200, { ok: true, providers: cfg.providers.map(toProviderView) });
6522
+ return writeJson4(res, 200, { ok: true, providers: cfg.providers.map(toProviderView) });
6042
6523
  }
6043
6524
  async function handleDiscoverModels(res, id, cfg) {
6044
6525
  if (!id) return writeJsonError(res, 400, "provider id required in path");
6045
6526
  const row = cfg.providers.find((p) => p.id === id);
6046
6527
  if (!row) return writeJsonError(res, 404, `provider '${id}' not found`);
6047
6528
  if (row.apiFormat !== "openai" && row.apiFormat !== "openai-response") {
6048
- return writeJson3(res, 200, { models: [], unsupportedFormat: true });
6529
+ return writeJson4(res, 200, { models: [], unsupportedFormat: true });
6049
6530
  }
6050
6531
  const resolvedKey = resolveEnvKey(row.apiKey);
6051
6532
  const base = row.baseUrl.replace(/\/+$/, "");
@@ -6062,32 +6543,32 @@ async function handleDiscoverModels(res, id, cfg) {
6062
6543
  message = parsed?.error?.message || parsed?.message || message;
6063
6544
  } catch {
6064
6545
  }
6065
- return writeJson3(res, 200, {
6546
+ return writeJson4(res, 200, {
6066
6547
  models: [],
6067
6548
  error: `discovery failed (${response.status})${message ? `: ${message}` : ""}`
6068
6549
  });
6069
6550
  }
6070
6551
  const data = await response.json();
6071
6552
  const models = Array.isArray(data?.data) ? data.data.map((m) => typeof m?.id === "string" ? m.id : "").filter((m) => m.length > 0) : [];
6072
- return writeJson3(res, 200, { models });
6553
+ return writeJson4(res, 200, { models });
6073
6554
  } catch (err5) {
6074
6555
  const message = err5 instanceof Error ? err5.message : String(err5);
6075
- return writeJson3(res, 200, { models: [], error: `discovery failed: ${message}` });
6556
+ return writeJson4(res, 200, { models: [], error: `discovery failed: ${message}` });
6076
6557
  }
6077
6558
  }
6078
6559
  async function handleTestModel(req, res, id, cfg) {
6079
6560
  if (!id) return writeJsonError(res, 400, "provider id required in path");
6080
6561
  const row = cfg.providers.find((p) => p.id === id);
6081
6562
  if (!row) return writeJsonError(res, 404, `provider '${id}' not found`);
6082
- const body = await readJsonBody3(req);
6563
+ const body = await readJsonBody4(req);
6083
6564
  const model = typeof body["model"] === "string" ? body["model"].trim() : "";
6084
6565
  if (!model) return writeJsonError(res, 400, "test requires a { model } string");
6085
6566
  if (row.apiFormat === "gemini") {
6086
- return writeJson3(res, 200, { ok: false, unsupportedFormat: true });
6567
+ return writeJson4(res, 200, { ok: false, unsupportedFormat: true });
6087
6568
  }
6088
6569
  const resolvedKey = resolveEnvKey(row.apiKey);
6089
6570
  if (!resolvedKey) {
6090
- return writeJson3(res, 200, { ok: false, message: "no API key configured for this provider" });
6571
+ return writeJson4(res, 200, { ok: false, message: "no API key configured for this provider" });
6091
6572
  }
6092
6573
  let url = row.baseUrl.replace(/\/+$/, "");
6093
6574
  const prompt = "Reply with the single word: OK.";
@@ -6126,9 +6607,9 @@ async function handleTestModel(req, res, id, cfg) {
6126
6607
  message = parsed?.error?.message || parsed?.message || message;
6127
6608
  } catch {
6128
6609
  }
6129
- return writeJson3(res, 200, { ok: false, status: response.status, latencyMs, message });
6610
+ return writeJson4(res, 200, { ok: false, status: response.status, latencyMs, message });
6130
6611
  }
6131
- return writeJson3(res, 200, {
6612
+ return writeJson4(res, 200, {
6132
6613
  ok: true,
6133
6614
  status: response.status,
6134
6615
  latencyMs,
@@ -6136,7 +6617,7 @@ async function handleTestModel(req, res, id, cfg) {
6136
6617
  });
6137
6618
  } catch (err5) {
6138
6619
  const message = err5 instanceof Error ? err5.message : String(err5);
6139
- return writeJson3(res, 200, { ok: false, latencyMs: Date.now() - startedAt, message });
6620
+ return writeJson4(res, 200, { ok: false, latencyMs: Date.now() - startedAt, message });
6140
6621
  }
6141
6622
  }
6142
6623
  function extractSampleText(text, apiFormat) {
@@ -6177,7 +6658,7 @@ async function handleProviderKeys(res, id, cfg, deps) {
6177
6658
  const row = cfg.providers.find((p) => p.id === id);
6178
6659
  if (!row) return writeJsonError(res, 404, `provider '${id}' not found`);
6179
6660
  const cooldown = await deps.apiKeyPool.getKeyHealth(id);
6180
- return writeJson3(res, 200, { keys: toPoolKeyView(row, cooldown, deps) });
6661
+ return writeJson4(res, 200, { keys: toPoolKeyView(row, cooldown, deps) });
6181
6662
  }
6182
6663
  function parsePoolKeyInput(body, existing) {
6183
6664
  const out = {};
@@ -6196,7 +6677,7 @@ async function handleAddProviderKey(req, res, id, cfg, deps) {
6196
6677
  if (!id) return writeJsonError(res, 400, "provider id required in path");
6197
6678
  const idx = cfg.providers.findIndex((p) => p.id === id);
6198
6679
  if (idx < 0) return writeJsonError(res, 404, `provider '${id}' not found`);
6199
- const body = await readJsonBody3(req);
6680
+ const body = await readJsonBody4(req);
6200
6681
  const parsed = parsePoolKeyInput(body);
6201
6682
  if (!parsed.apiKey) return writeJsonError(res, 400, "add requires a non-empty apiKey");
6202
6683
  const keyId = `key-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
@@ -6208,7 +6689,7 @@ async function handleAddProviderKey(req, res, id, cfg, deps) {
6208
6689
  row.apiKeys = [...row.apiKeys ?? [], entry];
6209
6690
  persistProviders(cfg, deps);
6210
6691
  const cooldown = await deps.apiKeyPool.getKeyHealth(id);
6211
- return writeJson3(res, 201, { keys: toPoolKeyView(row, cooldown, deps) });
6692
+ return writeJson4(res, 201, { keys: toPoolKeyView(row, cooldown, deps) });
6212
6693
  }
6213
6694
  async function handleUpdateProviderKey(req, res, id, keyId, cfg, deps) {
6214
6695
  if (!id) return writeJsonError(res, 400, "provider id required in path");
@@ -6218,7 +6699,7 @@ async function handleUpdateProviderKey(req, res, id, keyId, cfg, deps) {
6218
6699
  const row = cfg.providers[idx];
6219
6700
  const keyIdx = (row.apiKeys ?? []).findIndex((k) => k.id === keyId);
6220
6701
  if (keyIdx < 0) return writeJsonError(res, 404, `pool key '${keyId}' not found`);
6221
- const body = await readJsonBody3(req);
6702
+ const body = await readJsonBody4(req);
6222
6703
  const existing = row.apiKeys[keyIdx];
6223
6704
  const parsed = parsePoolKeyInput(body, existing);
6224
6705
  const entry = { id: keyId, apiKey: parsed.apiKey || existing.apiKey };
@@ -6228,7 +6709,7 @@ async function handleUpdateProviderKey(req, res, id, keyId, cfg, deps) {
6228
6709
  row.apiKeys[keyIdx] = entry;
6229
6710
  persistProviders(cfg, deps);
6230
6711
  const cooldown = await deps.apiKeyPool.getKeyHealth(id);
6231
- return writeJson3(res, 200, { keys: toPoolKeyView(row, cooldown, deps) });
6712
+ return writeJson4(res, 200, { keys: toPoolKeyView(row, cooldown, deps) });
6232
6713
  }
6233
6714
  async function handleDeleteProviderKey(res, id, keyId, cfg, deps) {
6234
6715
  if (!id) return writeJsonError(res, 400, "provider id required in path");
@@ -6242,7 +6723,7 @@ async function handleDeleteProviderKey(res, id, keyId, cfg, deps) {
6242
6723
  if (row.apiKeys.length === 0) row.apiKeys = void 0;
6243
6724
  persistProviders(cfg, deps);
6244
6725
  const cooldown = await deps.apiKeyPool.getKeyHealth(id);
6245
- return writeJson3(res, 200, { keys: toPoolKeyView(row, cooldown, deps) });
6726
+ return writeJson4(res, 200, { keys: toPoolKeyView(row, cooldown, deps) });
6246
6727
  }
6247
6728
  async function handleToggleProviderKey(req, res, id, keyId, cfg, deps) {
6248
6729
  if (!id) return writeJsonError(res, 400, "provider id required in path");
@@ -6252,11 +6733,11 @@ async function handleToggleProviderKey(req, res, id, keyId, cfg, deps) {
6252
6733
  const row = cfg.providers[idx];
6253
6734
  const keyIdx = (row.apiKeys ?? []).findIndex((k) => k.id === keyId);
6254
6735
  if (keyIdx < 0) return writeJsonError(res, 404, `pool key '${keyId}' not found`);
6255
- const body = await readJsonBody3(req);
6736
+ const body = await readJsonBody4(req);
6256
6737
  row.apiKeys[keyIdx].enabled = Boolean(body["enabled"]);
6257
6738
  persistProviders(cfg, deps);
6258
6739
  const cooldown = await deps.apiKeyPool.getKeyHealth(id);
6259
- return writeJson3(res, 200, { keys: toPoolKeyView(row, cooldown, deps) });
6740
+ return writeJson4(res, 200, { keys: toPoolKeyView(row, cooldown, deps) });
6260
6741
  }
6261
6742
  function parseApiKeysInput(raw, existing) {
6262
6743
  if (!Array.isArray(raw)) return existing;
@@ -6442,13 +6923,13 @@ function handlePresets(res, method) {
6442
6923
  website: p.website,
6443
6924
  modelsEndpoint: p.modelsEndpoint
6444
6925
  }));
6445
- return writeJson3(res, 200, { presets, excluded });
6926
+ return writeJson4(res, 200, { presets, excluded });
6446
6927
  }
6447
6928
  async function handleKeys(req, res, method, rest, deps) {
6448
6929
  if (method === "GET" && rest.length === 0) {
6449
6930
  const rows = await deps.keyDb.outboundApiKeysList();
6450
6931
  const reader = deps.keySpendReader;
6451
- if (!reader) return writeJson3(res, 200, { keys: rows.map(toKeyInfo) });
6932
+ if (!reader) return writeJson4(res, 200, { keys: rows.map(toKeyInfo) });
6452
6933
  const now = Date.now();
6453
6934
  const keys = await Promise.all(
6454
6935
  rows.map(async (row) => {
@@ -6460,13 +6941,13 @@ async function handleKeys(req, res, method, rest, deps) {
6460
6941
  return info;
6461
6942
  })
6462
6943
  );
6463
- return writeJson3(res, 200, { keys });
6944
+ return writeJson4(res, 200, { keys });
6464
6945
  }
6465
6946
  if (method === "POST" && rest.length === 0) {
6466
- const body = await readJsonBody3(req);
6947
+ const body = await readJsonBody4(req);
6467
6948
  const name = typeof body["name"] === "string" && body["name"].trim() ? body["name"].trim() : "key";
6468
- const created = await (0, import_outbound_api4.createNamedKey)(deps.keyDb, name);
6469
- return writeJson3(res, 201, {
6949
+ const created = await (0, import_outbound_api5.createNamedKey)(deps.keyDb, name);
6950
+ return writeJson4(res, 201, {
6470
6951
  id: created.id,
6471
6952
  name: created.name,
6472
6953
  keyPrefix: created.keyPrefix,
@@ -6477,7 +6958,7 @@ async function handleKeys(req, res, method, rest, deps) {
6477
6958
  }
6478
6959
  if (method === "GET" && rest.length === 2 && rest[1] === "reveal") {
6479
6960
  const revealed = await deps.keyDb.outboundApiKeysReveal(rest[0]);
6480
- if (revealed !== null) return writeJson3(res, 200, { key: revealed });
6961
+ if (revealed !== null) return writeJson4(res, 200, { key: revealed });
6481
6962
  const exists = (await deps.keyDb.outboundApiKeysList()).some((r) => r.id === rest[0]);
6482
6963
  if (!exists) return writeJsonError(res, 404, `key '${rest[0]}' not found`);
6483
6964
  return writeJsonError(
@@ -6492,32 +6973,32 @@ async function handleKeys(req, res, method, rest, deps) {
6492
6973
  const bound = await integrationKeyRequirement(deps, id);
6493
6974
  if (bound) return writeJsonError(res, 409, bound);
6494
6975
  const ok = await deps.keyDb.outboundApiKeysRevoke(id);
6495
- return writeJson3(res, ok ? 200 : 404, { ok });
6976
+ return writeJson4(res, ok ? 200 : 404, { ok });
6496
6977
  }
6497
6978
  if (method === "DELETE" && id && !action) {
6498
6979
  const bound = await integrationKeyRequirement(deps, id);
6499
6980
  if (bound) return writeJsonError(res, 409, bound);
6500
6981
  const ok = await deps.keyDb.outboundApiKeysDelete(id);
6501
- return writeJson3(res, ok ? 200 : 404, { ok });
6982
+ return writeJson4(res, ok ? 200 : 404, { ok });
6502
6983
  }
6503
6984
  if (method === "POST" && id && action === "enabled") {
6504
- const body = await readJsonBody3(req);
6985
+ const body = await readJsonBody4(req);
6505
6986
  const enabled = body["enabled"] === true;
6506
6987
  if (!enabled) {
6507
6988
  const bound = await integrationKeyRequirement(deps, id);
6508
6989
  if (bound) return writeJsonError(res, 409, bound);
6509
6990
  }
6510
6991
  const ok = await deps.keyDb.outboundApiKeysSetEnabled(id, enabled);
6511
- return writeJson3(res, ok ? 200 : 404, { ok, enabled });
6992
+ return writeJson4(res, ok ? 200 : 404, { ok, enabled });
6512
6993
  }
6513
6994
  if (method === "POST" && id && action === "permissions") {
6514
- const body = await readJsonBody3(req);
6995
+ const body = await readJsonBody4(req);
6515
6996
  if (Object.keys(body).length !== 1 || !Object.prototype.hasOwnProperty.call(body, "permissions")) {
6516
6997
  return writeJsonError(res, 400, "body must contain only permissions");
6517
6998
  }
6518
6999
  let permissions;
6519
7000
  try {
6520
- permissions = (0, import_outbound_api4.validateOutboundPermissions)(body["permissions"]);
7001
+ permissions = (0, import_outbound_api5.validateOutboundPermissions)(body["permissions"]);
6521
7002
  } catch {
6522
7003
  return writeJsonError(
6523
7004
  res,
@@ -6526,19 +7007,19 @@ async function handleKeys(req, res, method, rest, deps) {
6526
7007
  );
6527
7008
  }
6528
7009
  const before = (await deps.keyDb.outboundApiKeysList()).find((row) => row.id === id);
6529
- if (!before) return writeJson3(res, 404, { ok: false });
6530
- if (before.revokedAt !== null) return writeJson3(res, 409, { ok: false });
7010
+ if (!before) return writeJson4(res, 404, { ok: false });
7011
+ if (before.revokedAt !== null) return writeJson4(res, 409, { ok: false });
6531
7012
  const required = await integrationKeyRequirement(deps, id, permissions);
6532
7013
  if (required) return writeJsonError(res, 409, required);
6533
7014
  const ok = await deps.keyDb.outboundApiKeysSetPermissions(id, permissions);
6534
7015
  if (!ok) {
6535
7016
  const current = (await deps.keyDb.outboundApiKeysList()).find((row) => row.id === id);
6536
- return writeJson3(res, current?.revokedAt !== null ? 409 : 404, { ok: false });
7017
+ return writeJson4(res, current?.revokedAt !== null ? 409 : 404, { ok: false });
6537
7018
  }
6538
- return writeJson3(res, 200, { ok: true, allowedEndpoints: permissions });
7019
+ return writeJson4(res, 200, { ok: true, allowedEndpoints: permissions });
6539
7020
  }
6540
7021
  if (method === "POST" && id && action === "max-concurrency") {
6541
- const body = await readJsonBody3(req);
7022
+ const body = await readJsonBody4(req);
6542
7023
  const raw = body["maxConcurrency"];
6543
7024
  let value;
6544
7025
  if (raw === null) {
@@ -6553,14 +7034,14 @@ async function handleKeys(req, res, method, rest, deps) {
6553
7034
  );
6554
7035
  }
6555
7036
  const ok = await deps.keyDb.outboundApiKeysSetMaxConcurrency(id, value);
6556
- return writeJson3(res, ok ? 200 : 404, { ok, maxConcurrency: value });
7037
+ return writeJson4(res, ok ? 200 : 404, { ok, maxConcurrency: value });
6557
7038
  }
6558
7039
  if (method === "POST" && id && action === "policy") {
6559
- const body = await readJsonBody3(req);
7040
+ const body = await readJsonBody4(req);
6560
7041
  const parsed = parseKeyPolicyBody(body);
6561
7042
  if (!parsed.ok) return writeJsonError(res, 400, parsed.message);
6562
7043
  const ok = await deps.keyDb.outboundApiKeysSetPolicy(id, parsed.policy);
6563
- return writeJson3(res, ok ? 200 : 404, { ok });
7044
+ return writeJson4(res, ok ? 200 : 404, { ok });
6564
7045
  }
6565
7046
  return writeJsonError(res, 405, `method ${method} not allowed on keys`);
6566
7047
  }
@@ -6651,7 +7132,8 @@ function outboundServerConfigInput(config) {
6651
7132
  userMessageQueue: config.userMessageQueue,
6652
7133
  concurrencyQueue: config.concurrencyQueue,
6653
7134
  voucher: config.voucher,
6654
- anthropic: config.anthropic
7135
+ anthropic: config.anthropic,
7136
+ search: config.search
6655
7137
  };
6656
7138
  }
6657
7139
  function projectImagesConfigForAdmin(config) {
@@ -6687,8 +7169,8 @@ function sameConfigValue(left, right) {
6687
7169
  return JSON.stringify(left) === JSON.stringify(right);
6688
7170
  }
6689
7171
  function imageConfigurationAuditFields(previous, next) {
6690
- const before = previous ?? import_outbound_api4.DEFAULT_IMAGES_SERVER_CONFIG;
6691
- const after = next ?? import_outbound_api4.DEFAULT_IMAGES_SERVER_CONFIG;
7172
+ const before = previous ?? import_outbound_api5.DEFAULT_IMAGES_SERVER_CONFIG;
7173
+ const after = next ?? import_outbound_api5.DEFAULT_IMAGES_SERVER_CONFIG;
6692
7174
  const fields = [];
6693
7175
  if (before.enabled !== after.enabled) fields.push("enablement");
6694
7176
  if (before.provider !== after.provider) fields.push("provider");
@@ -6714,15 +7196,21 @@ function currentImageGenerationId(deps) {
6714
7196
  }
6715
7197
  async function handleServer(req, res, method, deps) {
6716
7198
  if (method === "GET") {
6717
- const config = await (0, import_outbound_api4.loadServerConfig)(deps.settingsStore);
7199
+ const config = await (0, import_outbound_api5.loadServerConfig)(deps.settingsStore);
6718
7200
  let server = config;
6719
7201
  if (config.proxy) server = { ...server, proxy: redactOutboundProxy(config.proxy) };
6720
7202
  if (config.webhook) server = { ...server, webhook: redactWebhookConfig(config.webhook) };
6721
7203
  if (config.billing) server = { ...server, billing: redactBillingConfig(config.billing) };
6722
- return writeJson3(res, 200, { server: projectImagesConfigForAdmin(server) });
7204
+ if (config.search) {
7205
+ server = {
7206
+ ...server,
7207
+ search: redactSearchServerConfig(config.search)
7208
+ };
7209
+ }
7210
+ return writeJson4(res, 200, { server: projectImagesConfigForAdmin(server) });
6723
7211
  }
6724
7212
  if (method === "PUT") {
6725
- const patch = await readJsonBody3(req);
7213
+ const patch = await readJsonBody4(req);
6726
7214
  const queueErrors = validateQueueSegments(patch);
6727
7215
  if (queueErrors.length > 0) {
6728
7216
  return writeJsonError(res, 400, `invalid queue config: ${queueErrors.join("; ")}`);
@@ -6751,7 +7239,7 @@ async function handleServer(req, res, method, deps) {
6751
7239
  if (billingErrors.length > 0) {
6752
7240
  return writeJsonError(res, 400, `invalid billing config: ${billingErrors.join("; ")}`);
6753
7241
  }
6754
- const current = await (0, import_outbound_api4.loadServerConfig)(deps.settingsStore);
7242
+ const current = await (0, import_outbound_api5.loadServerConfig)(deps.settingsStore);
6755
7243
  let effectivePatch = patch;
6756
7244
  if (patch.proxy) {
6757
7245
  effectivePatch = { ...effectivePatch, proxy: preserveOutboundProxySecrets(patch.proxy, current.proxy) };
@@ -6772,7 +7260,21 @@ async function handleServer(req, res, method, deps) {
6772
7260
  }
6773
7261
  effectivePatch = { ...effectivePatch, images };
6774
7262
  }
6775
- const merged = (0, import_outbound_api4.mergeServerConfig)(current, effectivePatch);
7263
+ if (patch.search !== void 0) {
7264
+ const searchPatch = preserveSearchSecrets(
7265
+ patch.search,
7266
+ current.search ?? import_outbound_api5.DEFAULT_SEARCH_SERVER_CONFIG
7267
+ );
7268
+ const searchErrors = (0, import_outbound_api5.validateSearchServerConfig)(searchPatch);
7269
+ if (searchErrors.length > 0) {
7270
+ return writeJsonError(res, 400, `invalid search config: ${searchErrors.join("; ")}`);
7271
+ }
7272
+ effectivePatch = {
7273
+ ...effectivePatch,
7274
+ search: searchPatch
7275
+ };
7276
+ }
7277
+ const merged = (0, import_outbound_api5.mergeServerConfig)(current, effectivePatch);
6776
7278
  const priorImageGenerationId = currentImageGenerationId(deps);
6777
7279
  try {
6778
7280
  await applyServerConfigTransaction(current, merged, {
@@ -6802,7 +7304,7 @@ async function handleServer(req, res, method, deps) {
6802
7304
  throw error;
6803
7305
  }
6804
7306
  },
6805
- persist: (next) => (0, import_outbound_api4.saveServerConfig)(deps.settingsStore, next),
7307
+ persist: (next) => (0, import_outbound_api5.saveServerConfig)(deps.settingsStore, next),
6806
7308
  restorePersisted: (snapshot) => deps.settingsStore.restoreDocumentSnapshot(snapshot)
6807
7309
  });
6808
7310
  } catch (error) {
@@ -6824,7 +7326,11 @@ async function handleServer(req, res, method, deps) {
6824
7326
  } catch {
6825
7327
  }
6826
7328
  }
6827
- return writeJson3(res, 200, { server: projectImagesConfigForAdmin(merged) });
7329
+ const mergedForAdmin = merged.search ? {
7330
+ ...merged,
7331
+ search: redactSearchServerConfig(merged.search)
7332
+ } : merged;
7333
+ return writeJson4(res, 200, { server: projectImagesConfigForAdmin(mergedForAdmin) });
6828
7334
  }
6829
7335
  return writeJsonError(res, 405, `method ${method} not allowed on server`);
6830
7336
  }
@@ -6841,7 +7347,7 @@ async function handleAccounts(req, res, method, rest, deps) {
6841
7347
  sessionKey: query2.get("sessionKey") ?? void 0,
6842
7348
  limit: Number.isFinite(parsedLimit) ? parsedLimit : 100
6843
7349
  });
6844
- return writeJson3(res, 200, {
7350
+ return writeJson4(res, 200, {
6845
7351
  available: true,
6846
7352
  records,
6847
7353
  capacity: import_AccountRouteActivity.ACCOUNT_ROUTE_ACTIVITY_LIMIT,
@@ -6857,7 +7363,7 @@ async function handleAccounts(req, res, method, rest, deps) {
6857
7363
  providerId: query2.get("providerId") ?? void 0,
6858
7364
  accountId: query2.get("accountId") ?? void 0
6859
7365
  });
6860
- return writeJson3(res, 200, {
7366
+ return writeJson4(res, 200, {
6861
7367
  available: true,
6862
7368
  entries,
6863
7369
  collectedAt: Date.now()
@@ -6876,10 +7382,10 @@ async function handleAccounts(req, res, method, rest, deps) {
6876
7382
  const accounts = await deps.subscriptionAccounts.listAll();
6877
7383
  const providerAccounts = await deps.subscriptionTokenWriter.listSanitizedAccounts();
6878
7384
  const externalCli = await deps.subscriptionTokenWriter.listExternalCliAvailability();
6879
- return writeJson3(res, 200, { accounts, providerAccounts, externalCli });
7385
+ return writeJson4(res, 200, { accounts, providerAccounts, externalCli });
6880
7386
  }
6881
7387
  if (method === "POST" && rest[0] === "batch" && rest.length === 1) {
6882
- const body = await readJsonBody3(req);
7388
+ const body = await readJsonBody4(req);
6883
7389
  const parsed = validateAccountBatchBody(body);
6884
7390
  if (!parsed) return writeJsonError(res, 400, "invalid account batch request");
6885
7391
  const result = await deps.subscriptionTokenWriter.batchManageAccounts(parsed.refs, parsed.mutation);
@@ -6895,15 +7401,15 @@ async function handleAccounts(req, res, method, rest, deps) {
6895
7401
  deps.accountAllowanceService?.removeAccountSnapshot?.(ref.providerId, ref.accountId);
6896
7402
  }
6897
7403
  }
6898
- return writeJson3(res, 200, { ok: true, affected: result.affected });
7404
+ return writeJson4(res, 200, { ok: true, affected: result.affected });
6899
7405
  }
6900
7406
  if (method === "GET" && rest[0] === "codex" && rest[1] === "oauth" && rest[3] === "status") {
6901
7407
  const result = handleCodexOAuthStatus(rest[2], deps);
6902
- return writeJson3(res, result.status, result.body);
7408
+ return writeJson4(res, result.status, result.body);
6903
7409
  }
6904
7410
  if (method === "DELETE" && rest[0] === "codex" && rest[1] === "oauth" && rest[2]) {
6905
7411
  const result = handleCodexOAuthCancel(rest[2], deps);
6906
- return writeJson3(res, result.status, result.body);
7412
+ return writeJson4(res, result.status, result.body);
6907
7413
  }
6908
7414
  if (method === "GET" && rest.length === 3 && rest[2] === "diagnostics") {
6909
7415
  const providerId = asSubscriptionProviderId(rest[0]);
@@ -6925,7 +7431,7 @@ async function handleAccounts(req, res, method, rest, deps) {
6925
7431
  resumeAt: entry.resumeAt
6926
7432
  })) ?? [];
6927
7433
  const diagnostics = [...health2, ...allowance].sort((left, right) => right.at - left.at).slice(0, 200);
6928
- return writeJson3(res, 200, { diagnostics });
7434
+ return writeJson4(res, 200, { diagnostics });
6929
7435
  }
6930
7436
  if (method === "GET" && rest.length === 3 && rest[2] === "events") {
6931
7437
  const providerId = asSubscriptionProviderId(rest[0]);
@@ -6937,17 +7443,17 @@ async function handleAccounts(req, res, method, rest, deps) {
6937
7443
  }
6938
7444
  const snapshot = deps.accountProbeService?.getAllHistory().find((entry) => entry.providerId === providerId && entry.accountId === accountId);
6939
7445
  const diagnostics = (0, import_SubscriptionAccountHealth.getSharedAccountHealth)().getDiagnostics({ providerId, accountId });
6940
- return writeJson3(res, 200, { events: snapshot?.records ?? [], diagnostics });
7446
+ return writeJson4(res, 200, { events: snapshot?.records ?? [], diagnostics });
6941
7447
  }
6942
7448
  if (method === "PATCH" && rest.length === 2) {
6943
7449
  const providerId = asSubscriptionProviderId(rest[0]);
6944
7450
  if (!providerId) return writeJsonError(res, 400, `unknown subscription provider '${rest[0] ?? ""}'`);
6945
- const body = await readJsonBody3(req);
7451
+ const body = await readJsonBody4(req);
6946
7452
  const patch = validateAccountMetadataPatch(body);
6947
7453
  if (!patch) return writeJsonError(res, 400, "invalid account metadata patch");
6948
7454
  const result = await deps.subscriptionTokenWriter.patchAccountMetadata(providerId, rest[1], patch);
6949
7455
  if (!result.ok) return writeJsonError(res, 404, `account '${rest[1]}' not found`);
6950
- return writeJson3(res, 200, { ok: true });
7456
+ return writeJson4(res, 200, { ok: true });
6951
7457
  }
6952
7458
  if (method === "PUT" || method === "POST" || method === "DELETE") {
6953
7459
  const providerId = asSubscriptionProviderId(rest[0]);
@@ -6956,15 +7462,15 @@ async function handleAccounts(req, res, method, rest, deps) {
6956
7462
  }
6957
7463
  if (method === "POST" && rest[1] === "oauth" && rest[2] === "start") {
6958
7464
  const result = providerId === "codex" ? handleCodexOAuthStart(deps) : handleOAuthStart(providerId, deps);
6959
- return writeJson3(res, result.status, result.body);
7465
+ return writeJson4(res, result.status, result.body);
6960
7466
  }
6961
7467
  if (method === "POST" && rest[1] === "oauth" && rest[2] === "complete") {
6962
- const body2 = await readJsonBody3(req);
7468
+ const body2 = await readJsonBody4(req);
6963
7469
  const result = await handleOAuthComplete(providerId, body2, deps);
6964
- return writeJson3(res, result.status, result.body);
7470
+ return writeJson4(res, result.status, result.body);
6965
7471
  }
6966
7472
  if (method === "POST" && rest[1] === "accounts") {
6967
- const body2 = await readJsonBody3(req);
7473
+ const body2 = await readJsonBody4(req);
6968
7474
  const block = validateTokenBody(providerId, body2);
6969
7475
  if (!block) {
6970
7476
  return writeJsonError(res, 400, `malformed token body for provider '${providerId}'`);
@@ -6972,20 +7478,20 @@ async function handleAccounts(req, res, method, rest, deps) {
6972
7478
  const label = typeof body2["label"] === "string" && body2["label"].trim() ? body2["label"].trim() : void 0;
6973
7479
  await deps.subscriptionAccountAppender.appendProviderAccount(providerId, block, label);
6974
7480
  const status2 = await statusEntryFor(deps.subscriptionAccounts, providerId);
6975
- return writeJson3(res, 200, status2 ? { account: status2 } : { ok: true });
7481
+ return writeJson4(res, 200, status2 ? { account: status2 } : { ok: true });
6976
7482
  }
6977
7483
  if (method === "POST" && rest[1] === "import-external") {
6978
7484
  if (providerId !== "claude" && providerId !== "codex") {
6979
7485
  return writeJsonError(res, 400, `provider '${providerId}' has no external CLI store`);
6980
7486
  }
6981
- const body2 = await readJsonBody3(req);
7487
+ const body2 = await readJsonBody4(req);
6982
7488
  const label = typeof body2["label"] === "string" && body2["label"].trim() ? body2["label"].trim() : void 0;
6983
7489
  const result = await deps.subscriptionTokenWriter.importExternalCliAccount(providerId, label);
6984
7490
  if (!result.ok) {
6985
7491
  return writeJsonError(res, 409, `no usable external ${providerId} CLI credential found`);
6986
7492
  }
6987
7493
  const status2 = await statusEntryFor(deps.subscriptionAccounts, providerId);
6988
- return writeJson3(res, 200, {
7494
+ return writeJson4(res, 200, {
6989
7495
  ok: true,
6990
7496
  account: status2 ?? void 0,
6991
7497
  nativeCredentialMode: result.nativeCredentialMode,
@@ -7000,7 +7506,7 @@ async function handleAccounts(req, res, method, rest, deps) {
7000
7506
  const writer2 = deps.subscriptionTokenWriter;
7001
7507
  const ok = providerId === "claude" ? await writer2.refreshClaudeToken() : providerId === "codex" ? await writer2.refreshCodexToken() : await writer2.refreshGeminiToken();
7002
7508
  const status2 = await statusEntryFor(deps.subscriptionAccounts, providerId);
7003
- return writeJson3(res, 200, { ok, account: status2 ?? void 0 });
7509
+ return writeJson4(res, 200, { ok, account: status2 ?? void 0 });
7004
7510
  }
7005
7511
  if (method === "POST" && rest.length === 3 && rest[2] === "test") {
7006
7512
  const accountId = rest[1];
@@ -7010,7 +7516,7 @@ async function handleAccounts(req, res, method, rest, deps) {
7010
7516
  return writeJsonError(res, 404, `account '${accountId}' not found`);
7011
7517
  }
7012
7518
  const result = await deps.accountProbeService.testAccountConnection(providerId, accountId);
7013
- return writeJson3(res, 200, {
7519
+ return writeJson4(res, 200, {
7014
7520
  ok: result.ok,
7015
7521
  marked: result.marked,
7016
7522
  tier: result.tier,
@@ -7019,15 +7525,15 @@ async function handleAccounts(req, res, method, rest, deps) {
7019
7525
  }
7020
7526
  if (method === "POST" && rest[2] === "label") {
7021
7527
  const accountId = rest[1];
7022
- const body2 = await readJsonBody3(req);
7528
+ const body2 = await readJsonBody4(req);
7023
7529
  const label = typeof body2["label"] === "string" ? body2["label"].trim() : "";
7024
7530
  const result = await deps.subscriptionTokenWriter.renameAccount(providerId, accountId, label);
7025
7531
  if (!result.ok) return writeJsonError(res, 404, `account '${accountId}' not found`);
7026
- return writeJson3(res, 200, { ok: true });
7532
+ return writeJson4(res, 200, { ok: true });
7027
7533
  }
7028
7534
  if (method === "POST" && rest[2] === "priority") {
7029
7535
  const accountId = rest[1];
7030
- const body2 = await readJsonBody3(req);
7536
+ const body2 = await readJsonBody4(req);
7031
7537
  const raw = body2["priority"];
7032
7538
  const priority = typeof raw === "number" ? raw : Number(raw);
7033
7539
  if (!Number.isFinite(priority)) {
@@ -7035,76 +7541,76 @@ async function handleAccounts(req, res, method, rest, deps) {
7035
7541
  }
7036
7542
  const result = await deps.subscriptionTokenWriter.setAccountPriority(providerId, accountId, priority);
7037
7543
  if (!result.ok) return writeJsonError(res, 404, `account '${accountId}' not found`);
7038
- return writeJson3(res, 200, { ok: true });
7544
+ return writeJson4(res, 200, { ok: true });
7039
7545
  }
7040
7546
  if (method === "POST" && rest[2] === "proxy") {
7041
7547
  const accountId = rest[1];
7042
- const body2 = await readJsonBody3(req);
7548
+ const body2 = await readJsonBody4(req);
7043
7549
  const rawProxy = body2["proxy"];
7044
7550
  let proxy;
7045
7551
  if (rawProxy !== null && rawProxy !== void 0) {
7046
- proxy = (0, import_outbound_api4.normalizeProxyConfig)(rawProxy);
7552
+ proxy = (0, import_outbound_api5.normalizeProxyConfig)(rawProxy);
7047
7553
  if (!proxy) return writeJsonError(res, 400, "invalid proxy config");
7048
7554
  }
7049
7555
  const result = await deps.subscriptionTokenWriter.setAccountProxy(providerId, accountId, proxy);
7050
7556
  if (!result.ok) return writeJsonError(res, 404, `account '${accountId}' not found`);
7051
- return writeJson3(res, 200, { ok: true });
7557
+ return writeJson4(res, 200, { ok: true });
7052
7558
  }
7053
7559
  if (method === "POST" && rest[2] === "supported-models") {
7054
7560
  const accountId = rest[1];
7055
- const body2 = await readJsonBody3(req);
7561
+ const body2 = await readJsonBody4(req);
7056
7562
  const parsed = validateSupportedModelsBody(body2["supportedModels"]);
7057
7563
  if (!parsed.ok) return writeJsonError(res, 400, "invalid supportedModels");
7058
7564
  const result = await deps.subscriptionTokenWriter.setAccountSupportedModels(providerId, accountId, parsed.value);
7059
7565
  if (!result.ok) return writeJsonError(res, 404, `account '${accountId}' not found`);
7060
- return writeJson3(res, 200, { ok: true });
7566
+ return writeJson4(res, 200, { ok: true });
7061
7567
  }
7062
7568
  if (method === "PUT" && rest[1] === "active") {
7063
- const body2 = await readJsonBody3(req);
7569
+ const body2 = await readJsonBody4(req);
7064
7570
  const id = typeof body2["id"] === "string" ? body2["id"] : "";
7065
7571
  if (!id) return writeJsonError(res, 400, "active switch requires { id }");
7066
7572
  const result = await deps.subscriptionTokenWriter.setActiveAccount(providerId, id);
7067
7573
  if (!result.ok) return writeJsonError(res, 404, `account '${id}' not found`);
7068
- return writeJson3(res, 200, { ok: true });
7574
+ return writeJson4(res, 200, { ok: true });
7069
7575
  }
7070
7576
  if (method === "DELETE" && rest.length === 2) {
7071
7577
  const accountId = rest[1];
7072
7578
  const result = await deps.subscriptionTokenWriter.removeAccount(providerId, accountId);
7073
7579
  if (!result.removed) return writeJsonError(res, 404, `account '${accountId}' not found`);
7074
7580
  deps.accountAllowanceService?.removeAccountSnapshot?.(providerId, accountId);
7075
- return writeJson3(res, 200, { ok: true });
7581
+ return writeJson4(res, 200, { ok: true });
7076
7582
  }
7077
7583
  if (method === "DELETE" && rest.length === 1) {
7078
7584
  await deps.subscriptionTokenWriter.clearProvider(providerId);
7079
7585
  deps.accountAllowanceService?.removeProviderSnapshots?.(providerId);
7080
- return writeJson3(res, 200, { ok: true });
7586
+ return writeJson4(res, 200, { ok: true });
7081
7587
  }
7082
7588
  if (method === "DELETE") {
7083
7589
  return writeJsonError(res, 405, "method DELETE not allowed on this accounts path");
7084
7590
  }
7085
- const body = await readJsonBody3(req);
7591
+ const body = await readJsonBody4(req);
7086
7592
  const config = validateTokenBody(providerId, body);
7087
7593
  if (!config) {
7088
7594
  return writeJsonError(res, 400, `malformed token body for provider '${providerId}'`);
7089
7595
  }
7090
7596
  await deps.subscriptionTokenWriter.writeProviderTokens(providerId, config);
7091
7597
  const status = await statusEntryFor(deps.subscriptionAccounts, providerId);
7092
- return writeJson3(res, 200, status ? { account: status } : { ok: true });
7598
+ return writeJson4(res, 200, status ? { account: status } : { ok: true });
7093
7599
  }
7094
7600
  return writeJsonError(res, 405, `method ${method} not allowed on accounts`);
7095
7601
  }
7096
7602
  async function handleCli(req, res, method, rest, deps) {
7097
7603
  if (method === "GET" && rest.length === 0) {
7098
7604
  const result = handleCliList(process.platform, deps.cliPathProbe);
7099
- return writeJson3(res, result.status, result.body);
7605
+ return writeJson4(res, result.status, result.body);
7100
7606
  }
7101
7607
  if (method === "GET" && rest[0] === "sessions") {
7102
7608
  const result = handleCliSessions();
7103
- return writeJson3(res, result.status, result.body);
7609
+ return writeJson4(res, result.status, result.body);
7104
7610
  }
7105
7611
  if (method === "DELETE" && rest[0] === "sessions" && rest[1]) {
7106
7612
  const result = handleCliStop(rest[1]);
7107
- return writeJson3(res, result.status, result.body);
7613
+ return writeJson4(res, result.status, result.body);
7108
7614
  }
7109
7615
  if (method === "POST" && rest[1] === "install") {
7110
7616
  const cli = rest[0];
@@ -7112,14 +7618,14 @@ async function handleCli(req, res, method, rest, deps) {
7112
7618
  return writeJsonError(res, 400, `unknown cli '${cli ?? ""}'`);
7113
7619
  }
7114
7620
  const result = await handleCliInstall(cli, deps.cliCommandRunner);
7115
- return writeJson3(res, result.status, result.body);
7621
+ return writeJson4(res, result.status, result.body);
7116
7622
  }
7117
7623
  if (method === "POST" && rest[1] === "launch") {
7118
7624
  const cli = rest[0];
7119
7625
  if (!isLaunchCliId(cli)) {
7120
7626
  return writeJsonError(res, 400, `unknown cli '${cli ?? ""}'`);
7121
7627
  }
7122
- const body = await readJsonBody3(req);
7628
+ const body = await readJsonBody4(req);
7123
7629
  const providers = loadConfig(deps.configPath).providers ?? [];
7124
7630
  const result = await handleCliLaunch(cli, body, {
7125
7631
  llmConfig: deps.llmConfig,
@@ -7128,7 +7634,7 @@ async function handleCli(req, res, method, rest, deps) {
7128
7634
  opener: deps.cliTerminalOpener,
7129
7635
  probe: deps.cliPathProbe
7130
7636
  });
7131
- return writeJson3(res, result.status, result.body);
7637
+ return writeJson4(res, result.status, result.body);
7132
7638
  }
7133
7639
  return writeJsonError(res, 405, `method ${method} not allowed on cli`);
7134
7640
  }
@@ -7138,52 +7644,52 @@ async function handleIntegrations(req, res, method, rest, deps) {
7138
7644
  const manager = factory();
7139
7645
  try {
7140
7646
  if (method === "GET" && rest.length === 0) {
7141
- return writeJson3(res, 200, {
7647
+ return writeJson4(res, 200, {
7142
7648
  integrations: await manager.listStatus(),
7143
7649
  gateway: deps.outboundApiServer.getStatus()
7144
7650
  });
7145
7651
  }
7146
7652
  if (method === "POST" && rest.length === 1 && rest[0] === "rotate") {
7147
7653
  await manager.rotateGatewayKey();
7148
- return writeJson3(res, 200, { ok: true, integrations: await manager.listStatus() });
7654
+ return writeJson4(res, 200, { ok: true, integrations: await manager.listStatus() });
7149
7655
  }
7150
7656
  const client = rest[0];
7151
7657
  if (!isIntegrationClient(client)) {
7152
7658
  return writeJsonError(res, 400, `unknown integration client '${client ?? ""}'`);
7153
7659
  }
7154
7660
  if (method === "POST" && rest[1] === "key") {
7155
- const body = await readJsonBody3(req);
7661
+ const body = await readJsonBody4(req);
7156
7662
  if (Object.keys(body).length !== 1 || typeof body.keyId !== "string" || !body.keyId.trim()) {
7157
7663
  return writeJsonError(res, 400, "body must contain a non-empty keyId string");
7158
7664
  }
7159
7665
  const status = await manager.bindIntegrationKey(client, body.keyId.trim());
7160
- return writeJson3(res, 200, { integration: status });
7666
+ return writeJson4(res, 200, { integration: status });
7161
7667
  }
7162
7668
  if (method === "POST" && rest[1] === "plan") {
7163
- const body = await readJsonBody3(req);
7669
+ const body = await readJsonBody4(req);
7164
7670
  const configPath = body.configPath;
7165
7671
  if (configPath !== void 0 && typeof configPath !== "string") {
7166
7672
  return writeJsonError(res, 400, "configPath must be a string");
7167
7673
  }
7168
7674
  const plan = await manager.plan(client, configPath);
7169
- return writeJson3(res, 200, { plan });
7675
+ return writeJson4(res, 200, { plan });
7170
7676
  }
7171
7677
  if (method === "POST" && (rest[1] === "install" || rest[1] === "apply")) {
7172
- const body = await readJsonBody3(req);
7678
+ const body = await readJsonBody4(req);
7173
7679
  const configPath = body.configPath;
7174
7680
  if (configPath !== void 0 && typeof configPath !== "string") {
7175
7681
  return writeJsonError(res, 400, "configPath must be a string");
7176
7682
  }
7177
7683
  const status = await manager.install(client, configPath);
7178
- return writeJson3(res, 200, { integration: status });
7684
+ return writeJson4(res, 200, { integration: status });
7179
7685
  }
7180
7686
  if (method === "POST" && rest[1] === "repair") {
7181
7687
  const status = await manager.repair(client);
7182
- return writeJson3(res, 200, { integration: status });
7688
+ return writeJson4(res, 200, { integration: status });
7183
7689
  }
7184
7690
  if (method === "DELETE" && rest.length === 1 || method === "POST" && rest[1] === "remove") {
7185
7691
  const status = await manager.remove(client);
7186
- return writeJson3(res, 200, { integration: status });
7692
+ return writeJson4(res, 200, { integration: status });
7187
7693
  }
7188
7694
  return writeJsonError(res, 405, `method ${method} not allowed on integrations`);
7189
7695
  } catch (error) {
@@ -7326,8 +7832,8 @@ async function handleImages(res, method, rest, deps) {
7326
7832
  }
7327
7833
  const reader = deps.imageRuntimeStatus;
7328
7834
  if (!reader) return writeJsonError(res, 501, "Images runtime status is not available");
7329
- const serverConfig = await (0, import_outbound_api4.loadServerConfig)(deps.settingsStore);
7330
- const images = serverConfig.images ?? import_outbound_api4.DEFAULT_IMAGES_SERVER_CONFIG;
7835
+ const serverConfig = await (0, import_outbound_api5.loadServerConfig)(deps.settingsStore);
7836
+ const images = serverConfig.images ?? import_outbound_api5.DEFAULT_IMAGES_SERVER_CONFIG;
7331
7837
  const lifecycle = reader.status();
7332
7838
  const capability = await reader.inspectCapability(IMAGE_ADMIN_STATUS_TENANT);
7333
7839
  const resources = safeRuntimeResources(reader.resourceStatus());
@@ -7346,7 +7852,7 @@ async function handleImages(res, method, rest, deps) {
7346
7852
  httpLeases: safeStatusCount(generation.httpLeases),
7347
7853
  hostedLeases: safeStatusCount(generation.hostedLeases)
7348
7854
  }));
7349
- return writeJson3(res, 200, {
7855
+ return writeJson4(res, 200, {
7350
7856
  configured: {
7351
7857
  enabled: images.enabled,
7352
7858
  provider: images.provider,
@@ -7374,11 +7880,11 @@ async function handleImages(res, method, rest, deps) {
7374
7880
  async function handleStatus(res, method, deps) {
7375
7881
  if (method !== "GET") return writeJsonError(res, 405, `method ${method} not allowed on status`);
7376
7882
  const status = deps.outboundApiServer.getStatus();
7377
- const serverConfig = await (0, import_outbound_api4.loadServerConfig)(deps.settingsStore);
7883
+ const serverConfig = await (0, import_outbound_api5.loadServerConfig)(deps.settingsStore);
7378
7884
  const endpoints = ["chat", "responses", "messages", "gemini"].map((endpoint) => {
7379
- const routes = (serverConfig.bindings ?? []).filter((binding) => binding.enabled && binding.endpoint === endpoint).map((binding) => (0, import_outbound_api4.gatewayBindingToEndpointConfig)(binding));
7885
+ const routes = (serverConfig.bindings ?? []).filter((binding) => binding.enabled && binding.endpoint === endpoint).map((binding) => (0, import_outbound_api5.gatewayBindingToEndpointConfig)(binding));
7380
7886
  const useSubscription = routes.some((route) => route.useSubscription);
7381
- if ((0, import_outbound_api4.isKindMappedEndpoint)(endpoint)) {
7887
+ if ((0, import_outbound_api5.isKindMappedEndpoint)(endpoint)) {
7382
7888
  const kinds = {};
7383
7889
  for (const route of routes) {
7384
7890
  for (const [kind, ref] of Object.entries(route.modelMap ?? {})) {
@@ -7412,14 +7918,14 @@ async function handleStatus(res, method, deps) {
7412
7918
  })() : void 0;
7413
7919
  if (status.running) {
7414
7920
  const queueStatus = deps.outboundApiServer.getQueueStatus();
7415
- return writeJson3(res, 200, {
7921
+ return writeJson4(res, 200, {
7416
7922
  ...status,
7417
7923
  endpoints,
7418
7924
  queueStatus,
7419
7925
  ...imageRuntime ? { imageRuntime } : {}
7420
7926
  });
7421
7927
  }
7422
- return writeJson3(res, 200, {
7928
+ return writeJson4(res, 200, {
7423
7929
  ...status,
7424
7930
  endpoints,
7425
7931
  ...imageRuntime ? { imageRuntime } : {}
@@ -7443,18 +7949,18 @@ function resolvePlaygroundPath(endpoint, body) {
7443
7949
  }
7444
7950
  async function handlePlayground(req, res, method, deps) {
7445
7951
  if (method !== "POST") return writeJsonError(res, 405, `method ${method} not allowed on playground`);
7446
- const body = await readJsonBody3(req);
7952
+ const body = await readJsonBody4(req);
7447
7953
  const endpoint = typeof body["endpoint"] === "string" ? body["endpoint"] : "";
7448
7954
  const key = typeof body["key"] === "string" ? body["key"] : "";
7449
7955
  const payload = body["body"];
7450
7956
  const status = deps.outboundApiServer.getStatus();
7451
7957
  if (!status.running || !status.port) return writeJsonError(res, 503, "outbound server not running");
7452
- const path2 = resolvePlaygroundPath(endpoint, isRecord2(payload) ? payload : {});
7958
+ const path2 = resolvePlaygroundPath(endpoint, isRecord3(payload) ? payload : {});
7453
7959
  if (!path2) return writeJsonError(res, 400, `unknown endpoint '${endpoint}'`);
7454
7960
  const upstreamBody = typeof payload === "string" ? payload : JSON.stringify(payload ?? {});
7455
7961
  await proxyToOutbound(res, status.port, path2, key, upstreamBody);
7456
7962
  }
7457
- function isRecord2(v) {
7963
+ function isRecord3(v) {
7458
7964
  return !!v && typeof v === "object" && !Array.isArray(v);
7459
7965
  }
7460
7966
  function proxyToOutbound(res, outboundPort, path2, key, body) {
@@ -7590,7 +8096,7 @@ async function handleUiStatic(req, res, urlPath, uiDist) {
7590
8096
  }
7591
8097
 
7592
8098
  // src/admin/version.ts
7593
- var DAEMON_VERSION = true ? "0.2.0" : "0.0.0-dev";
8099
+ var DAEMON_VERSION = true ? "0.2.1" : "0.0.0-dev";
7594
8100
 
7595
8101
  // src/admin/AdminServer.ts
7596
8102
  var LOOPBACK_ADDR = "127.0.0.1";
@@ -8010,14 +8516,14 @@ function createPoolKeysLoader(getProviderRow, autoDisabled) {
8010
8516
 
8011
8517
  // src/image-generation/ImageDoctorService.ts
8012
8518
  var import_image_generation = require("@omnicross/core/image-generation");
8013
- var import_outbound_api6 = require("@omnicross/core/outbound-api");
8519
+ var import_outbound_api7 = require("@omnicross/core/outbound-api");
8014
8520
  var import_subscriptions3 = require("@omnicross/subscriptions");
8015
8521
 
8016
8522
  // src/image-generation/FileCodexImageCapabilityEvidenceSource.ts
8017
8523
  var import_node_crypto13 = require("crypto");
8018
8524
  var import_node_fs14 = require("fs");
8019
8525
  var import_node_path14 = require("path");
8020
- var import_outbound_api5 = require("@omnicross/core/outbound-api");
8526
+ var import_outbound_api6 = require("@omnicross/core/outbound-api");
8021
8527
 
8022
8528
  // src/image-generation/imageTenantHmac.ts
8023
8529
  var import_node_crypto12 = require("crypto");
@@ -8090,7 +8596,7 @@ var ACCOUNT_DOMAIN = Buffer.from("omnicross:codex-image-evidence:account:v1\0",
8090
8596
  var ACCOUNT_KEY = /^[a-f0-9]{64}$/u;
8091
8597
  var SIZE = /^(?:auto|[1-9][0-9]{1,4}x[1-9][0-9]{1,4})$/u;
8092
8598
  var MAX_MANIFEST_BYTES = 4 * 1024 * 1024;
8093
- var PHYSICAL_RETENTION_TTL_MS = import_outbound_api5.IMAGE_SERVER_HARD_CEILINGS.evidenceTtlMs;
8599
+ var PHYSICAL_RETENTION_TTL_MS = import_outbound_api6.IMAGE_SERVER_HARD_CEILINGS.evidenceTtlMs;
8094
8600
  function exactKeys(value, allowed) {
8095
8601
  const allow = new Set(allowed);
8096
8602
  return Object.keys(value).every((key) => allow.has(key));
@@ -8464,7 +8970,7 @@ function createImageDoctorService(options) {
8464
8970
  const activePaths = () => options.storageCatalog.active().resolver;
8465
8971
  return Object.freeze({
8466
8972
  inspectLocal: async (config) => {
8467
- const configErrors = (0, import_outbound_api6.validateImagesServerConfig)(config);
8973
+ const configErrors = (0, import_outbound_api7.validateImagesServerConfig)(config);
8468
8974
  let verifiedAreas = 0;
8469
8975
  try {
8470
8976
  const paths = activePaths();
@@ -8506,7 +9012,7 @@ function createImageDoctorService(options) {
8506
9012
  continue;
8507
9013
  }
8508
9014
  try {
8509
- const permissions = (0, import_outbound_api6.validateOutboundPermissions)(row.allowedEndpoints);
9015
+ const permissions = (0, import_outbound_api7.validateOutboundPermissions)(row.allowedEndpoints);
8510
9016
  if (permissions.includes("images")) imagesAuthorizedRows += 1;
8511
9017
  } catch {
8512
9018
  invalidRows += 1;
@@ -8810,7 +9316,7 @@ var ImageCleanupService = class {
8810
9316
  // src/image-generation/ImageRuntimeGenerationFactory.ts
8811
9317
  var import_node_crypto16 = require("crypto");
8812
9318
  var import_image_generation5 = require("@omnicross/core/image-generation");
8813
- var import_outbound_api7 = require("@omnicross/core/outbound-api");
9319
+ var import_outbound_api8 = require("@omnicross/core/outbound-api");
8814
9320
  var import_subscriptions4 = require("@omnicross/subscriptions");
8815
9321
 
8816
9322
  // src/image-generation/ImageApiRuntimeResolver.ts
@@ -9278,7 +9784,7 @@ function createImageRuntimeGeneration(options) {
9278
9784
  throw new TypeError("image runtime generation id is invalid");
9279
9785
  }
9280
9786
  const config = snapshotConfig(options.config);
9281
- const configErrors = (0, import_outbound_api7.validateImagesServerConfig)(config);
9787
+ const configErrors = (0, import_outbound_api8.validateImagesServerConfig)(config);
9282
9788
  if (configErrors.length > 0) {
9283
9789
  throw new TypeError(`image runtime configuration is invalid: ${configErrors[0]}`);
9284
9790
  }
@@ -12870,7 +13376,7 @@ function safeStringify(value) {
12870
13376
  var import_node_crypto20 = require("crypto");
12871
13377
  var import_node_fs20 = require("fs");
12872
13378
  var import_node_path20 = require("path");
12873
- var import_outbound_api8 = require("@omnicross/core/outbound-api");
13379
+ var import_outbound_api9 = require("@omnicross/core/outbound-api");
12874
13380
  function atomicReplaceDocument(targetPath, contents) {
12875
13381
  const tempPath = (0, import_node_path20.join)(
12876
13382
  (0, import_node_path20.dirname)(targetPath),
@@ -12918,13 +13424,13 @@ var JsonApiServerSettingsStore = class {
12918
13424
  box;
12919
13425
  atomicReplace;
12920
13426
  async get(key) {
12921
- if (key !== import_outbound_api8.OUTBOUND_API_SERVER_CONFIG_KEY) return void 0;
13427
+ if (key !== import_outbound_api9.OUTBOUND_API_SERVER_CONFIG_KEY) return void 0;
12922
13428
  const file = this.readFile();
12923
13429
  if (file.server === void 0) return void 0;
12924
13430
  return this.decryptSecrets(file.server);
12925
13431
  }
12926
13432
  async set(key, value) {
12927
- if (key !== import_outbound_api8.OUTBOUND_API_SERVER_CONFIG_KEY) return;
13433
+ if (key !== import_outbound_api9.OUTBOUND_API_SERVER_CONFIG_KEY) return;
12928
13434
  const file = this.readFile();
12929
13435
  file.server = this.encryptSecrets(value);
12930
13436
  this.atomicReplace(
@@ -17269,7 +17775,7 @@ function buildDaemon(config, paths) {
17269
17775
  );
17270
17776
  (0, import_AccountAllowanceStore4.setSharedAccountAllowanceStore)(accountAllowanceStore);
17271
17777
  (0, import_AccountAllowanceScheduling5.getSharedAccountAllowanceScheduling)().configure(
17272
- (0, import_outbound_api9.normalizeServerConfig)(decryptedConfig.server).allowanceScheduling
17778
+ (0, import_outbound_api10.normalizeServerConfig)(decryptedConfig.server).allowanceScheduling
17273
17779
  );
17274
17780
  const llmConfig = new ConfigFileProviderConfigSource(decryptedConfig);
17275
17781
  const keyDb = new JsonOutboundKeyDb(paths.keysPath, secretBox3);
@@ -17290,7 +17796,7 @@ function buildDaemon(config, paths) {
17290
17796
  logger
17291
17797
  );
17292
17798
  claudeAllowanceRefreshScheduler.configure(
17293
- (0, import_outbound_api9.normalizeServerConfig)(decryptedConfig.server).allowanceScheduling
17799
+ (0, import_outbound_api10.normalizeServerConfig)(decryptedConfig.server).allowanceScheduling
17294
17800
  );
17295
17801
  const subscriptionAccounts = new import_subscriptions6.SubscriptionAccountService(credentialStore);
17296
17802
  (0, import_subscriptions6.setSubscriptionAccountService)(subscriptionAccounts);
@@ -17335,13 +17841,21 @@ function buildDaemon(config, paths) {
17335
17841
  defaultUsageEventsPath(paths.configPath),
17336
17842
  async (providerId, model) => await pricingEngine.getEntry(providerId, model) !== null
17337
17843
  );
17338
- const keySpendTracker = new import_outbound_api10.KeySpendTracker(usageEventStore);
17844
+ const keySpendTracker = new import_outbound_api11.KeySpendTracker(usageEventStore);
17339
17845
  const usageThroughput = (0, import_usage2.getSharedUsageThroughputTracker)();
17340
17846
  const usageRecorder = new import_usage2.UsageRecorder(usageEventStore, pricingEngine, logger, {
17341
17847
  onRecord: (apiKeyId, costUsd, at) => keySpendTracker.add(apiKeyId, costUsd, at),
17342
17848
  onEvent: (row, at) => usageThroughput.record(row, at)
17343
17849
  });
17344
- const initialImagesConfig = (0, import_outbound_api9.normalizeServerConfig)(decryptedConfig.server).images;
17850
+ const initialImagesConfig = (0, import_outbound_api10.normalizeServerConfig)(decryptedConfig.server).images;
17851
+ const initialSearchConfig = (0, import_outbound_api10.normalizeServerConfig)(decryptedConfig.server).search;
17852
+ for (const issue of (0, import_outbound_api10.validateSearchServerConfig)(
17853
+ decryptedConfig.server?.search
17854
+ )) {
17855
+ logger.warn("[search] ignoring invalid config: " + issue);
17856
+ }
17857
+ const searchRuntime = buildSearchRuntime(initialSearchConfig, { logger });
17858
+ const searchFrontendModes = initialSearchConfig.modes;
17345
17859
  const imageObservability = new ImageObservability();
17346
17860
  const imageRuntimeObservability = Object.freeze({
17347
17861
  telemetrySink: imageObservability.telemetrySink,
@@ -17471,7 +17985,9 @@ function buildDaemon(config, paths) {
17471
17985
  apiKeyPool,
17472
17986
  usageRecorder,
17473
17987
  openAIOperationRegistry,
17474
- responsesHostedImageIngress
17988
+ responsesHostedImageIngress,
17989
+ searchRuntime,
17990
+ searchFrontendModes
17475
17991
  });
17476
17992
  if (providerProxy.getDeps().openAIOperationRegistry !== openAIOperationRegistry) {
17477
17993
  throw new Error(
@@ -17505,7 +18021,7 @@ function buildDaemon(config, paths) {
17505
18021
  credentialStore,
17506
18022
  (0, import_SubscriptionAccountHealth4.getSharedAccountHealth)(),
17507
18023
  logger,
17508
- import_outbound_api9.DEFAULT_ACCOUNT_PROBE
18024
+ import_outbound_api10.DEFAULT_ACCOUNT_PROBE
17509
18025
  );
17510
18026
  const getHealthReport = () => buildHealthReport({
17511
18027
  version: DAEMON_VERSION,
@@ -17522,7 +18038,7 @@ function buildDaemon(config, paths) {
17522
18038
  // `/health` body stays byte-identical (zero regression).
17523
18039
  subscriptionAccountsHealthy: () => accountHealthProbeScheduler.enabled ? accountHealthProbeScheduler.probedAccountsHealthy() : void 0
17524
18040
  });
17525
- const outboundApiServer = (0, import_outbound_api9.getOutboundApiServer)({
18041
+ const outboundApiServer = (0, import_outbound_api10.getOutboundApiServer)({
17526
18042
  db: keyDb,
17527
18043
  // voucher-redemption #9: the key-authenticated `POST /redeem` endpoint redeems
17528
18044
  // cards against the presenting key (gated on `voucher.enabled`).
@@ -17536,7 +18052,9 @@ function buildDaemon(config, paths) {
17536
18052
  keySpendTracker,
17537
18053
  // configurable-logging: route the server's OWN lifecycle + relay dispatch-error
17538
18054
  // lines through the injected logger (honors level/format/file sink).
17539
- logger
18055
+ logger,
18056
+ // plan 阶段5: the same instance the managed frontends hold.
18057
+ searchRuntime
17540
18058
  });
17541
18059
  const auditDir = defaultAuditDir(paths.configPath);
17542
18060
  const billingDir = defaultBillingDir(paths.configPath);
@@ -17558,6 +18076,10 @@ function buildDaemon(config, paths) {
17558
18076
  }),
17559
18077
  routeLeaseManager,
17560
18078
  subscriptionAccounts,
18079
+ // search-settings-ui D3: the daemon's ONE search runtime + its
18080
+ // bootstrap-captured modes, for `GET /admin/api/search/diagnostics` and
18081
+ // `POST /admin/api/search/test` (501 when a light embedder omits it).
18082
+ searchStatus: { runtime: searchRuntime, modes: searchFrontendModes },
17561
18083
  accountAllowanceService,
17562
18084
  allowanceRefreshScheduler: claudeAllowanceRefreshScheduler,
17563
18085
  accountProbeService: accountHealthProbeScheduler,
@@ -17607,7 +18129,7 @@ function buildDaemon(config, paths) {
17607
18129
  cliCommandRunner: paths.cliCommandRunner,
17608
18130
  integrationManagerFactory: () => {
17609
18131
  const live = outboundApiServer.getStatus();
17610
- const port = live.port || decryptedConfig.server?.port || import_outbound_api9.DEFAULT_OUTBOUND_PORT;
18132
+ const port = live.port || decryptedConfig.server?.port || import_outbound_api10.DEFAULT_OUTBOUND_PORT;
17611
18133
  return new IntegrationManager({
17612
18134
  configPath: paths.configPath,
17613
18135
  gatewayBaseUrl: live.loopbackUrl ?? `http://127.0.0.1:${port}`,
@@ -17694,6 +18216,8 @@ function buildDaemon(config, paths) {
17694
18216
  keyDb,
17695
18217
  settingsStore,
17696
18218
  openAIOperationRegistry,
18219
+ searchRuntime,
18220
+ searchFrontendModes,
17697
18221
  imageRuntimeManager,
17698
18222
  imageObservability,
17699
18223
  imageCleanupService,
@@ -17830,7 +18354,7 @@ async function runImagesLiveDoctor(config, doctor, signal = new AbortController(
17830
18354
  console.info(
17831
18355
  " [\u26A0] live Images verification may consume subscription quota; one minimal low-quality PNG request will be sent"
17832
18356
  );
17833
- const result = await doctor.verifyLive(config.images ?? import_outbound_api11.DEFAULT_IMAGES_SERVER_CONFIG, signal);
18357
+ const result = await doctor.verifyLive(config.images ?? import_outbound_api12.DEFAULT_IMAGES_SERVER_CONFIG, signal);
17834
18358
  if (!result.ok) {
17835
18359
  console.info(` [\u2717] live Images verification: ${result.code}`);
17836
18360
  return false;
@@ -17840,6 +18364,116 @@ async function runImagesLiveDoctor(config, doctor, signal = new AbortController(
17840
18364
  );
17841
18365
  return true;
17842
18366
  }
18367
+ function readSearchApiConfigFromEnv(env = process.env) {
18368
+ const configs = {};
18369
+ const read = (name) => {
18370
+ const value = env[`OMNICROSS_SEARCH_${name}`]?.trim();
18371
+ return value ? value : void 0;
18372
+ };
18373
+ const tavilyKey = read("TAVILY_API_KEY");
18374
+ if (tavilyKey) {
18375
+ configs.tavily = { apiKey: tavilyKey, ...optionalHost(read("TAVILY_API_HOST")) };
18376
+ }
18377
+ const jinaKey = read("JINA_API_KEY");
18378
+ const jinaHost = read("JINA_API_HOST");
18379
+ if (jinaKey || jinaHost) {
18380
+ configs.jina = { ...jinaKey ? { apiKey: jinaKey } : {}, ...optionalHost(jinaHost) };
18381
+ }
18382
+ const searxngHost = read("SEARXNG_API_HOST");
18383
+ if (searxngHost) {
18384
+ const username = read("SEARXNG_BASIC_AUTH_USERNAME");
18385
+ const password = read("SEARXNG_BASIC_AUTH_PASSWORD");
18386
+ configs.searxng = {
18387
+ apiHost: searxngHost,
18388
+ ...username ? { basicAuthUsername: username } : {},
18389
+ ...password ? { basicAuthPassword: password } : {}
18390
+ };
18391
+ }
18392
+ const zhipuKey = read("ZHIPU_API_KEY");
18393
+ if (zhipuKey) {
18394
+ configs.zhipu = { apiKey: zhipuKey, ...optionalHost(read("ZHIPU_API_HOST")) };
18395
+ }
18396
+ const zaiKey = read("Z_AI_API_KEY");
18397
+ if (zaiKey) {
18398
+ configs["z.ai"] = { apiKey: zaiKey, ...optionalHost(read("Z_AI_API_HOST")) };
18399
+ }
18400
+ return configs;
18401
+ }
18402
+ function optionalHost(apiHost) {
18403
+ return apiHost ? { apiHost } : {};
18404
+ }
18405
+ function resolveSearchApiConfigs(configured, env = process.env) {
18406
+ const fromEnv = readSearchApiConfigFromEnv(env);
18407
+ const resolved = {};
18408
+ const tavily = configured?.tavily ?? fromEnv.tavily;
18409
+ if (tavily) resolved.tavily = tavily;
18410
+ const jina = configured?.jina ?? fromEnv.jina;
18411
+ if (jina) resolved.jina = jina;
18412
+ const searxng = configured?.searxng ?? fromEnv.searxng;
18413
+ if (searxng) resolved.searxng = searxng;
18414
+ const zhipu = configured?.zhipu ?? fromEnv.zhipu;
18415
+ if (zhipu) resolved.zhipu = zhipu;
18416
+ const zai = configured?.["z.ai"] ?? fromEnv["z.ai"];
18417
+ if (zai) resolved["z.ai"] = zai;
18418
+ return resolved;
18419
+ }
18420
+ function formatCapabilities(capabilities) {
18421
+ return [
18422
+ `apiKey=${capabilities.requiresApiKey}`,
18423
+ `cancellation=${capabilities.supportsCancellation}`,
18424
+ `urlRead=${capabilities.supportsUrlRead}`,
18425
+ `region=${capabilities.supportsRegion}`,
18426
+ `language=${capabilities.supportsLanguage}`,
18427
+ `timeRange=${capabilities.supportsTimeRange}`,
18428
+ `maxResults=${capabilities.maxResults ?? "unbounded"}`
18429
+ ].join(", ");
18430
+ }
18431
+ function formatDiagnostic(diagnostic) {
18432
+ const parts = [diagnostic.status];
18433
+ if (diagnostic.reason) parts.push(diagnostic.reason);
18434
+ if (diagnostic.error) {
18435
+ const { code, details } = diagnostic.error;
18436
+ parts.push(
18437
+ `code=${code}, transport=${details?.transport ?? "unknown"}, stage=${details?.stage ?? "unknown"}`
18438
+ );
18439
+ }
18440
+ return parts.join(" \u2014 ");
18441
+ }
18442
+ async function runSearchDoctor(live, env = process.env, source = {}) {
18443
+ const apiConfigs = resolveSearchApiConfigs(source.config?.providers, env);
18444
+ const egressPolicy = source.config && source.config.egress.allowedPrivateHosts.length > 0 ? { allowedPrivateHosts: [...source.config.egress.allowedPrivateHosts] } : void 0;
18445
+ const contributions = [
18446
+ ...(0, import_http4.builtinHttpSearchContributions)(),
18447
+ ...(0, import_api4.apiSearchContributions)(apiConfigs, { ...egressPolicy ? { egressPolicy } : {} })
18448
+ ];
18449
+ const declarations = source.runtime?.listProviders() ?? contributions;
18450
+ console.info("omnicross doctor search \u2014 builtin search contributions (offline, no network)");
18451
+ const modes = source.config?.modes ?? import_search2.DEFAULT_SEARCH_FRONTEND_MODES;
18452
+ console.info(
18453
+ ` [i] frontend modes: ${import_search2.SEARCH_FRONTEND_NAMES.map((name) => `${name}=${modes[name]}`).join(", ")}`
18454
+ );
18455
+ console.info(
18456
+ " [i] codex mode applies immediately; responses/anthropic modes, provider config, egress allowlist and policy apply on daemon restart"
18457
+ );
18458
+ for (const row of buildSearchDoctorSnapshot(declarations, apiConfigs)) {
18459
+ const mark = row.status === "unconfigured" ? "\u2013" : "\u2713";
18460
+ const suffix = row.status ? ` \u2014 ${row.status}: ${row.reason ?? ""}` : "";
18461
+ console.info(
18462
+ ` [${mark}] ${row.providerId}: source=${row.source}, kind=${row.kind}, ${formatCapabilities(row.capabilities)}${suffix}`
18463
+ );
18464
+ }
18465
+ if (!live) return 0;
18466
+ console.info(
18467
+ ` [\u26A0] live search sends ONE fixed public query per provider ("${SEARCH_DOCTOR_QUERY}") to the engine itself`
18468
+ );
18469
+ let hardFailure = false;
18470
+ for (const diagnostic of await runSearchLiveChecks(contributions)) {
18471
+ const mark = diagnostic.status === "healthy" ? "\u2713" : diagnostic.status === "failed" ? "\u2717" : "\u26A0";
18472
+ if (diagnostic.status === "failed") hardFailure = true;
18473
+ console.info(` [${mark}] ${diagnostic.providerId}: ${formatDiagnostic(diagnostic)}`);
18474
+ }
18475
+ return hardFailure ? 1 : 0;
18476
+ }
17843
18477
  async function runLiveProbe(url, key, fetchImpl = fetch) {
17844
18478
  try {
17845
18479
  const res = await fetchImpl(`${url.replace(/\/+$/, "")}/v1/messages/count_tokens`, {
@@ -17876,11 +18510,12 @@ async function runDoctor(argv, fetchImpl = fetch) {
17876
18510
  allowPositionals: true
17877
18511
  });
17878
18512
  const subject = positionals[0] ?? "claude";
17879
- if (subject !== "claude" && subject !== "images") {
17880
- throw new Error(`doctor: unknown subject '${subject}' (supported: 'claude', 'images')`);
18513
+ if (subject !== "claude" && subject !== "images" && subject !== "search") {
18514
+ throw new Error(`doctor: unknown subject '${subject}' (supported: 'claude', 'images', 'search')`);
17881
18515
  }
17882
18516
  const configPath = values.config;
17883
18517
  if (!configPath) {
18518
+ if (subject === "search") return runSearchDoctor(values.live === true);
17884
18519
  throw new Error("doctor: --config <path> is required (the same config `omnicross start` uses)");
17885
18520
  }
17886
18521
  const config = loadConfig(configPath);
@@ -17892,9 +18527,15 @@ async function runDoctor(argv, fetchImpl = fetch) {
17892
18527
  };
17893
18528
  const daemon = await buildDaemon(config, paths);
17894
18529
  try {
17895
- const serverConfig = await (0, import_outbound_api11.loadServerConfig)(daemon.settingsStore);
18530
+ const serverConfig = await (0, import_outbound_api12.loadServerConfig)(daemon.settingsStore);
18531
+ if (subject === "search") {
18532
+ return await runSearchDoctor(values.live === true, process.env, {
18533
+ ...serverConfig.search ? { config: serverConfig.search } : {},
18534
+ runtime: daemon.searchRuntime
18535
+ });
18536
+ }
17896
18537
  const checks = subject === "images" ? buildImagesDoctorChecks(await daemon.imageDoctor.inspectLocal(
17897
- serverConfig.images ?? import_outbound_api11.DEFAULT_IMAGES_SERVER_CONFIG
18538
+ serverConfig.images ?? import_outbound_api12.DEFAULT_IMAGES_SERVER_CONFIG
17898
18539
  )) : buildClaudeDoctorChecks(serverConfig);
17899
18540
  let hardFailure = false;
17900
18541
  console.info(subject === "images" ? "omnicross doctor images \u2014 local metadata only" : `omnicross doctor ${subject} \u2014 config: ${configPath}`);
@@ -18117,7 +18758,7 @@ function isClient(value) {
18117
18758
 
18118
18759
  // src/commands/keys.ts
18119
18760
  var import_node_util5 = require("util");
18120
- var import_outbound_api12 = require("@omnicross/core/outbound-api");
18761
+ var import_outbound_api13 = require("@omnicross/core/outbound-api");
18121
18762
  async function runKeys(argv) {
18122
18763
  const { values, positionals } = (0, import_node_util5.parseArgs)({
18123
18764
  args: argv,
@@ -18143,7 +18784,7 @@ async function runKeys(argv) {
18143
18784
  }
18144
18785
  async function keysAdd(db, name) {
18145
18786
  if (!name) throw new Error("keys add: a <name> is required");
18146
- const created = await (0, import_outbound_api12.createNamedKey)(db, name);
18787
+ const created = await (0, import_outbound_api13.createNamedKey)(db, name);
18147
18788
  console.info(`Created key '${created.name}' (id: ${created.id}).`);
18148
18789
  console.info("");
18149
18790
  console.info(` ${created.plaintextOnce}`);
@@ -18966,7 +19607,7 @@ function tokensSuffix(configPath) {
18966
19607
 
18967
19608
  // src/commands/start.ts
18968
19609
  var import_node_util10 = require("util");
18969
- var import_outbound_api13 = require("@omnicross/core/outbound-api");
19610
+ var import_outbound_api14 = require("@omnicross/core/outbound-api");
18970
19611
  var import_SubscriptionAccountHealth5 = require("@omnicross/core/pipeline/SubscriptionAccountHealth");
18971
19612
  var import_AccountAllowanceScheduling6 = require("@omnicross/core/pipeline/AccountAllowanceScheduling");
18972
19613
 
@@ -19028,7 +19669,7 @@ async function runStart(argv) {
19028
19669
  await daemon.llmConfig.ready();
19029
19670
  await daemon.migrateUsageStore();
19030
19671
  await daemon.providerProxy.start();
19031
- const serverConfig = await (0, import_outbound_api13.loadServerConfig)(daemon.settingsStore);
19672
+ const serverConfig = await (0, import_outbound_api14.loadServerConfig)(daemon.settingsStore);
19032
19673
  await daemon.imageCleanupService.runOnce();
19033
19674
  daemon.imageCleanupService.start();
19034
19675
  (0, import_SubscriptionAccountHealth5.getSharedAccountHealth)().configure({
@@ -19051,7 +19692,9 @@ async function runStart(argv) {
19051
19692
  voucher: serverConfig.voucher,
19052
19693
  // claude-api-protocol-fidelity (§10): count_tokens strategy/budget,
19053
19694
  // /v1/models shape, synthetic-ping heartbeat (hot-applied inside applyConfig).
19054
- anthropic: serverConfig.anthropic
19695
+ anthropic: serverConfig.anthropic,
19696
+ // plan 阶段5: the Codex frontend mode is read live per request from here.
19697
+ search: serverConfig.search
19055
19698
  });
19056
19699
  let dashboardUrl = null;
19057
19700
  if (!values["no-dashboard"]) {
@@ -19166,6 +19809,16 @@ Usage:
19166
19809
  Check local Images config, roots, stores, permissions,
19167
19810
  account and cached evidence. --live warns, then consumes
19168
19811
  at most one minimal subscription image request.
19812
+ omnicross doctor search [--live] List the builtin search providers and their declared
19813
+ capabilities (offline, no config needed). --live sends ONE
19814
+ fixed public query per provider and reports healthy /
19815
+ degraded / blocked / failed.
19816
+ Keyed API providers show as unconfigured unless enabled
19817
+ through the DIAGNOSTIC-ONLY environment variables
19818
+ OMNICROSS_SEARCH_{TAVILY,JINA,SEARXNG,ZHIPU,Z_AI}_API_KEY
19819
+ / _API_HOST (plus SEARXNG_BASIC_AUTH_USERNAME/_PASSWORD).
19820
+ These are read only by this command and are not the
19821
+ configuration system.
19169
19822
  `;
19170
19823
  async function main() {
19171
19824
  const [, , subcommand, ...rest] = process.argv;