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