@omnicross/daemon 0.4.2 → 0.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +1193 -281
- package/dist/cli.js +1141 -221
- package/dist/index.cjs +986 -238
- package/dist/index.d.cts +1588 -1386
- package/dist/index.d.ts +1588 -1386
- package/dist/index.js +954 -199
- package/package.json +6 -6
package/dist/index.d.cts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { Logger, OutboundApiServerConfig, ProviderConfigSource, TransformerService, Transformer, ResolvedTransformerChain, ApiServerSettingsStore, PricingStore, AutomaticPricingSource, OutboundPermission, OutboundKeyDb, OutboundKeyDbRow, OutboundKeyPolicy, PricingEngine as PricingEngine$1, OpenAIOperationRegistry } from '@omnicross/core';
|
|
2
2
|
import { SearchRuntime, SearchFrontendModes } from '@omnicross/core/search';
|
|
3
3
|
import { ApiKeyPoolService } from '@omnicross/core/completion/ApiKeyPoolService';
|
|
4
|
-
import { AllowanceSchedulingConfig, AccountProbeConfig, OutboundKeyDb as OutboundKeyDb$1, VoucherDb, KeySpendReader, OutboundApiServer
|
|
4
|
+
import { AllowanceSchedulingConfig, AccountProbeConfig, ImagesServerConfig, ImageProviderId, OutboundKeyDb as OutboundKeyDb$1, VoucherDb, KeySpendReader, OutboundApiServer } from '@omnicross/core/outbound-api';
|
|
5
5
|
import { RouteLeaseManager, ProviderProxy } from '@omnicross/core/provider-proxy';
|
|
6
6
|
import { UsageRecorder, PricingEngine } from '@omnicross/core/usage';
|
|
7
7
|
import { SubscriptionCredentialStore, FetchLike, CodexImageCapabilityEvidenceSource, CodexImageCapabilityEvidenceRequest, CodexImageCapabilityEvidence, CodexImageCapabilityObservation, ImageExecutionScheduler, ImageExecutionAccountKey, ImageExecutionSchedulerRequest, ImageExecutionSchedulerGrant, SubscriptionAccountService, SubscriptionProviderRegistry } from '@omnicross/subscriptions';
|
|
8
8
|
import { AccountAllowanceSnapshot, AllowanceWindow } from '@omnicross/contracts/account-allowance-types';
|
|
9
|
-
import { ClaudeTokenConfig, CodexTokenConfig, GeminiTokenConfig, KimiTokenConfig, GrokTokenConfig, CopilotTokenConfig, AccountTokensConfig, ProxyConfig, SubscriptionAccountSanitized, AccountClientIdentity, SubscriptionAccountEntry } from '@omnicross/contracts/account-tokens-types';
|
|
9
|
+
import { ClaudeTokenConfig, CodexTokenConfig, GeminiTokenConfig, KimiTokenConfig, GrokTokenConfig, CopilotTokenConfig, AntigravityTokenConfig, AccountTokensConfig, ProxyConfig, SubscriptionAccountSanitized, AccountClientIdentity, SubscriptionAccountEntry } from '@omnicross/contracts/account-tokens-types';
|
|
10
10
|
import * as _omnicross_contracts_subscription_types from '@omnicross/contracts/subscription-types';
|
|
11
11
|
import { OpenCodeGoTokenConfig, SubscriptionProviderId } from '@omnicross/contracts/subscription-types';
|
|
12
12
|
import { AccountAllowanceStore } from '@omnicross/core/pipeline/AccountAllowanceStore';
|
|
@@ -21,8 +21,8 @@ import { AuditRecord, AuditStats, AuditBodyResult, AuditConfig } from '@omnicros
|
|
|
21
21
|
import { BillingDeliveryStatus, BillingConfig, BillingEvent } from '@omnicross/contracts/billing-types';
|
|
22
22
|
import http from 'node:http';
|
|
23
23
|
import * as _omnicross_contracts_image_generation_types from '@omnicross/contracts/image-generation-types';
|
|
24
|
-
import {
|
|
25
|
-
import { ImageApiContributions, ResponsesImageGenerationContribution, ResponsesImageInspectionInput, ResponsesImageAdmission, ResponsesHostedToolSelection, ResponsesImageRequestScope, ImageTelemetrySink, ImageApiAuditRecord, ImageTelemetryRecord,
|
|
24
|
+
import { ImageReferenceMetadata, ImageReferenceId, ImageGenerationErrorCode, ImageCapabilityUnavailableReason, ImageCapabilities } from '@omnicross/contracts/image-generation-types';
|
|
25
|
+
import { ImageReferenceStore, ImageReferenceSaveInput, ImageReferenceResolution, ImageApiContributions, ResponsesImageGenerationContribution, ResponsesImageInspectionInput, ResponsesImageAdmission, ResponsesHostedToolSelection, ResponsesImageRequestScope, ImageTelemetrySink, ImageApiAuditRecord, ImageTelemetryRecord, ImageTemporaryResourceBudget, ImageTemporaryResourceBudgetLease, ImageApiLimits, ImageRequestResourceScope, ImageApiRuntimeResolver, RemoteImageAssetResolver, ImageProvider, ImageProviderRegistry, ImageOrchestrator } from '@omnicross/core/image-generation';
|
|
26
26
|
import { LLMProvider, AgentDefaultModels, GlobalModelParameters } from '@omnicross/contracts/llm-config';
|
|
27
27
|
import { PricingEntry, PricingEntryInput, PricingResolution, PricingSourceRefreshResult } from '@omnicross/contracts/pricing-types';
|
|
28
28
|
import { ResponsesImageStateStore, ResponsesImageStateCommitInput, ResponsesImageCallBinding, ResponsesImageCallId, ResponsesImageCallResolution, ResponsesImageResponseResolution } from '@omnicross/core/image-generation/responses';
|
|
@@ -602,6 +602,32 @@ interface ResolvedAdminConfig {
|
|
|
602
602
|
}
|
|
603
603
|
/** The default admin port (distinct from the 8765 outbound server). */
|
|
604
604
|
declare const DEFAULT_ADMIN_PORT = 8766;
|
|
605
|
+
/**
|
|
606
|
+
* Per-provider behavior flags for the antigravity subscription.
|
|
607
|
+
*/
|
|
608
|
+
interface AntigravityProviderConfig {
|
|
609
|
+
/**
|
|
610
|
+
* Enable the production→sandbox endpoint failover for retryable antigravity
|
|
611
|
+
* upstream failures (design D5). DEFAULT OFF — with the switch off a
|
|
612
|
+
* retryable failure surfaces the original error semantics unchanged.
|
|
613
|
+
*/
|
|
614
|
+
sandboxFailover?: boolean;
|
|
615
|
+
}
|
|
616
|
+
/**
|
|
617
|
+
* Per-provider identity config for the OpenCodeGo subscription
|
|
618
|
+
* (opencodego-egress-identity).
|
|
619
|
+
*/
|
|
620
|
+
interface OpenCodeGoProviderConfig {
|
|
621
|
+
/**
|
|
622
|
+
* The `user-agent` every OpenCodeGo egress presents to opencode.ai (relay
|
|
623
|
+
* traffic AND the background usage poll). An app embedding omnicross sets
|
|
624
|
+
* its own identity here (e.g. `elftia/1.2.0`); absent ⇒ the product default
|
|
625
|
+
* `omnicross/<version>`. opencode.ai's announcement asks proxy tools to
|
|
626
|
+
* identify themselves clearly — a bare relay falls to Node's generic `node`
|
|
627
|
+
* UA, exactly the shape their traffic monitoring flags. NON-SECRET.
|
|
628
|
+
*/
|
|
629
|
+
userAgent?: string;
|
|
630
|
+
}
|
|
605
631
|
/** The full daemon config. */
|
|
606
632
|
interface DaemonConfig {
|
|
607
633
|
providers: DaemonProviderConfig[];
|
|
@@ -621,6 +647,16 @@ interface DaemonConfig {
|
|
|
621
647
|
* lifetime per-key spend the outbound key policy seeds from.
|
|
622
648
|
*/
|
|
623
649
|
usage?: UsageRetentionConfig;
|
|
650
|
+
/**
|
|
651
|
+
* Optional antigravity-subscription behavior flags (sandbox failover et al).
|
|
652
|
+
* Absent ⇒ every default (failover OFF).
|
|
653
|
+
*/
|
|
654
|
+
antigravity?: AntigravityProviderConfig;
|
|
655
|
+
/**
|
|
656
|
+
* Optional OpenCodeGo identity config (outbound user-agent). Absent ⇒ the
|
|
657
|
+
* product default `omnicross/<version>` on every opencode.ai egress.
|
|
658
|
+
*/
|
|
659
|
+
opencodego?: OpenCodeGoProviderConfig;
|
|
624
660
|
}
|
|
625
661
|
/** Apply defaults to a (possibly absent) admin block: enabled, port 8766,
|
|
626
662
|
* loopback, no token. NOTE: an EXPLICIT `port: 0` is honored as "bind an
|
|
@@ -678,7 +714,7 @@ type AtomicFileReplace = (targetPath: string, contents: string) => void;
|
|
|
678
714
|
*/
|
|
679
715
|
|
|
680
716
|
/** Provider id → owned contract field names. */
|
|
681
|
-
type DaemonProvider = 'claude' | 'codex' | 'gemini' | 'opencodego' | 'kimi' | 'grok' | 'copilot';
|
|
717
|
+
type DaemonProvider = 'claude' | 'codex' | 'gemini' | 'opencodego' | 'kimi' | 'grok' | 'copilot' | 'antigravity';
|
|
682
718
|
interface AccountMetadataPatch {
|
|
683
719
|
label?: string;
|
|
684
720
|
enabled?: boolean;
|
|
@@ -807,7 +843,7 @@ type ExternalCliReader = (provider: ExternalCliProvider) => ExternalCliCredentia
|
|
|
807
843
|
* shapes), keyed by `SubscriptionProviderId` the daemon admin layer validates
|
|
808
844
|
* the wire body to one of these before calling the writer.
|
|
809
845
|
*/
|
|
810
|
-
type SubscriptionTokenBlock = ClaudeTokenConfig | CodexTokenConfig | GeminiTokenConfig | OpenCodeGoTokenConfig | KimiTokenConfig | GrokTokenConfig | CopilotTokenConfig;
|
|
846
|
+
type SubscriptionTokenBlock = ClaudeTokenConfig | CodexTokenConfig | GeminiTokenConfig | OpenCodeGoTokenConfig | KimiTokenConfig | GrokTokenConfig | CopilotTokenConfig | AntigravityTokenConfig;
|
|
811
847
|
declare class JsonSubscriptionCredentialStore implements SubscriptionCredentialStore {
|
|
812
848
|
private readonly tokensPath;
|
|
813
849
|
private readonly box;
|
|
@@ -934,13 +970,32 @@ declare class JsonSubscriptionCredentialStore implements SubscriptionCredentialS
|
|
|
934
970
|
* the retry instead of looping on a dead token).
|
|
935
971
|
*/
|
|
936
972
|
refreshCopilotToken(): Promise<boolean>;
|
|
973
|
+
/**
|
|
974
|
+
* Refresh the Antigravity (Google) OAuth access token. Like gemini, the
|
|
975
|
+
* Google token endpoint does NOT return a refresh_token on refresh, so this
|
|
976
|
+
* writes ONLY access+expiresAt (+status/lastRefreshedAt) and DELIBERATELY
|
|
977
|
+
* preserves the stored `refreshToken` — plus the account's `projectId`
|
|
978
|
+
* (a handshake product; the post-refresh re-validation is the refresh
|
|
979
|
+
* scheduler's hook, not this write) and `email`. HONEST `false` when no
|
|
980
|
+
* refresh_token.
|
|
981
|
+
*/
|
|
982
|
+
refreshAntigravityToken(): Promise<boolean>;
|
|
983
|
+
/**
|
|
984
|
+
* Post-refresh project re-validation hook (antigravity design D8): after a
|
|
985
|
+
* successful antigravity token refresh, re-run the Code Assist project
|
|
986
|
+
* handshake and write the (possibly rotated) `projectId` back to the account.
|
|
987
|
+
* A handshake FAILURE keeps the stored projectId untouched (logged) so the
|
|
988
|
+
* account keeps serving with the last-known-good project until a later
|
|
989
|
+
* refresh succeeds. Returns whether the handshake produced a project.
|
|
990
|
+
*/
|
|
991
|
+
revalidateAntigravityProject(accountId: string): Promise<boolean>;
|
|
937
992
|
/**
|
|
938
993
|
* Refresh a SPECIFIC managed account by id (background scheduler sweep and
|
|
939
994
|
* account-pool resolution). It uses only that account's stored refresh
|
|
940
995
|
* token. Coalesced per `provider:id`; on failure flags ONLY that account
|
|
941
996
|
* `expired`.
|
|
942
997
|
*/
|
|
943
|
-
refreshAccountById(provider: 'claude' | 'codex' | 'gemini' | 'kimi' | 'grok' | 'copilot', id: string): Promise<boolean>;
|
|
998
|
+
refreshAccountById(provider: 'claude' | 'codex' | 'gemini' | 'kimi' | 'grok' | 'copilot' | 'antigravity', id: string): Promise<boolean>;
|
|
944
999
|
/**
|
|
945
1000
|
* Resolve a SPECIFIC account's access token by id (design D6). Mirrors each
|
|
946
1001
|
* provider's ACTIVE-getter policy, keyed by id: claude returns the stored token
|
|
@@ -1686,6 +1741,59 @@ declare class GeminiAllowanceCollector {
|
|
|
1686
1741
|
private unsupportedSnapshot;
|
|
1687
1742
|
}
|
|
1688
1743
|
|
|
1744
|
+
/**
|
|
1745
|
+
* AntigravityAllowanceCollector — the antigravity subscription's quota via the
|
|
1746
|
+
* Code Assist internal quotaSummary RPC (antigravity-subscription-provider
|
|
1747
|
+
* design D6).
|
|
1748
|
+
*
|
|
1749
|
+
* Probes `POST ${daily-cloudcode-pa}/v1internal:retrieveUserQuotaSummary` per
|
|
1750
|
+
* antigravity account (Bearer access token + the antigravity/hub UA the
|
|
1751
|
+
* inference path also carries), parsing the rolling 5h + weekly DUAL buckets —
|
|
1752
|
+
* split by counter family (Google / Anthropic / OpenAI) — into per-model-family
|
|
1753
|
+
* allowance windows: `remainingFraction` (0–1) → used percent, `resetTime` →
|
|
1754
|
+
* resetsAt, `disabled` → the window's hard-block flag. The shared
|
|
1755
|
+
* counter-family ←→ model-family map lives in core (`antigravityQuotaFamily`).
|
|
1756
|
+
*
|
|
1757
|
+
* FALLBACK: when the summary RPC fails, `v1internal:fetchAvailableModels`'s
|
|
1758
|
+
* per-model `quotaInfo` is the legacy source — the collect then updates from
|
|
1759
|
+
* it instead. A total failure NEVER clears the existing windows (stale-marking
|
|
1760
|
+
* only), mirroring the sibling collectors.
|
|
1761
|
+
*
|
|
1762
|
+
* Mirrors the sibling collectors' cache contract: 5-minute cache, per-account
|
|
1763
|
+
* in-flight merging, one 401/403→refresh→retry. Unlike gemini (per-model
|
|
1764
|
+
* fractions, display-only), antigravity IS in the allowance scheduling
|
|
1765
|
+
* whitelist — the account-level dual buckets fit the worst-window rule.
|
|
1766
|
+
*
|
|
1767
|
+
* @module @omnicross/daemon/allowance/AntigravityAllowanceCollector
|
|
1768
|
+
*/
|
|
1769
|
+
|
|
1770
|
+
interface AntigravityAllowanceCredentialReader {
|
|
1771
|
+
getAccessTokenForAccount(providerId: 'antigravity', accountId: string): Promise<string | null>;
|
|
1772
|
+
refreshAccountToken(providerId: 'antigravity', accountId: string): Promise<boolean>;
|
|
1773
|
+
}
|
|
1774
|
+
type AntigravityAllowanceFetch = (url: string, init: RequestInit, accountId: string) => Promise<Response>;
|
|
1775
|
+
interface AntigravityAllowanceCollectOptions {
|
|
1776
|
+
force?: boolean;
|
|
1777
|
+
refreshAheadMs?: number;
|
|
1778
|
+
}
|
|
1779
|
+
declare class AntigravityAllowanceCollector {
|
|
1780
|
+
private readonly credentials;
|
|
1781
|
+
private readonly store;
|
|
1782
|
+
private readonly fetchImpl;
|
|
1783
|
+
private readonly now;
|
|
1784
|
+
private readonly inFlight;
|
|
1785
|
+
constructor(credentials: AntigravityAllowanceCredentialReader, store?: AccountAllowanceStore, fetchImpl?: AntigravityAllowanceFetch, now?: () => number);
|
|
1786
|
+
collectMany(accounts: readonly SubscriptionAccountEntry<AntigravityTokenConfig>[], options?: AntigravityAllowanceCollectOptions): Promise<AccountAllowanceSnapshot[]>;
|
|
1787
|
+
collect(account: SubscriptionAccountEntry<AntigravityTokenConfig>, options?: AntigravityAllowanceCollectOptions): Promise<AccountAllowanceSnapshot>;
|
|
1788
|
+
private isCacheValid;
|
|
1789
|
+
private fetchAccount;
|
|
1790
|
+
/** One upstream round-trip with the antigravity/hub masquerade UA. */
|
|
1791
|
+
private request;
|
|
1792
|
+
private snapshot;
|
|
1793
|
+
private failureSnapshot;
|
|
1794
|
+
private unsupportedSnapshot;
|
|
1795
|
+
}
|
|
1796
|
+
|
|
1689
1797
|
/**
|
|
1690
1798
|
* OpenCodeGo usage collector.
|
|
1691
1799
|
*
|
|
@@ -1733,8 +1841,8 @@ declare class OpenCodeGoAllowanceCollector {
|
|
|
1733
1841
|
* literals — extending them all would make the overloads conflict.
|
|
1734
1842
|
*/
|
|
1735
1843
|
interface AccountAllowanceCredentialReader {
|
|
1736
|
-
getAccessTokenForAccount(providerId: 'claude' | 'codex' | 'kimi' | 'opencodego' | 'grok' | 'copilot' | 'gemini', accountId: string): Promise<string | null>;
|
|
1737
|
-
refreshAccountToken(providerId: 'claude' | 'codex' | 'kimi' | 'grok' | 'copilot' | 'gemini', accountId: string): Promise<boolean>;
|
|
1844
|
+
getAccessTokenForAccount(providerId: 'claude' | 'codex' | 'kimi' | 'opencodego' | 'grok' | 'copilot' | 'gemini' | 'antigravity', accountId: string): Promise<string | null>;
|
|
1845
|
+
refreshAccountToken(providerId: 'claude' | 'codex' | 'kimi' | 'grok' | 'copilot' | 'gemini' | 'antigravity', accountId: string): Promise<boolean>;
|
|
1738
1846
|
getFullConfig(): Promise<AccountTokensConfig>;
|
|
1739
1847
|
}
|
|
1740
1848
|
interface AccountAllowanceFilter {
|
|
@@ -1756,7 +1864,8 @@ declare class AccountAllowanceService {
|
|
|
1756
1864
|
readonly copilotCollector: CopilotAllowanceCollector;
|
|
1757
1865
|
readonly opencodegoCollector: OpenCodeGoAllowanceCollector;
|
|
1758
1866
|
readonly geminiCollector: GeminiAllowanceCollector;
|
|
1759
|
-
|
|
1867
|
+
readonly antigravityCollector: AntigravityAllowanceCollector;
|
|
1868
|
+
constructor(credentials: AccountAllowanceCredentialReader, store?: AccountAllowanceStore, collector?: ClaudeAllowanceCollector, codexCollector?: CodexAllowanceCollector, kimiCollector?: KimiAllowanceCollector, opencodegoCollector?: OpenCodeGoAllowanceCollector, grokCollector?: GrokAllowanceCollector, copilotCollector?: CopilotAllowanceCollector, geminiCollector?: GeminiAllowanceCollector, antigravityCollector?: AntigravityAllowanceCollector, now?: () => number);
|
|
1760
1869
|
/**
|
|
1761
1870
|
* Read all/filtered snapshots. Claude's and Codex's five-minute caches are
|
|
1762
1871
|
* refreshed lazily on read (Codex polls `/backend-api/wham/usage`; the
|
|
@@ -1782,6 +1891,8 @@ declare class AccountAllowanceService {
|
|
|
1782
1891
|
refreshGrok(accountId?: string): Promise<AccountAllowanceSnapshot[]>;
|
|
1783
1892
|
/** Force-refresh Gemini usage (Code Assist retrieveUserQuota) for one/all accounts. */
|
|
1784
1893
|
refreshGemini(accountId?: string): Promise<AccountAllowanceSnapshot[]>;
|
|
1894
|
+
/** Force-refresh Antigravity usage (quotaSummary dual buckets) for one/all accounts. */
|
|
1895
|
+
refreshAntigravity(accountId?: string): Promise<AccountAllowanceSnapshot[]>;
|
|
1785
1896
|
/**
|
|
1786
1897
|
* Keep Claude + Codex + Kimi snapshots warm for allowance-aware routing. All
|
|
1787
1898
|
* collectors preserve their cache + per-account in-flight coalescing; a tick
|
|
@@ -2214,1491 +2325,1575 @@ interface ProviderKeyQuota {
|
|
|
2214
2325
|
errorCode?: string;
|
|
2215
2326
|
}
|
|
2216
2327
|
|
|
2217
|
-
type
|
|
2218
|
-
|
|
2219
|
-
readonly
|
|
2220
|
-
readonly
|
|
2221
|
-
readonly
|
|
2222
|
-
readonly
|
|
2223
|
-
readonly
|
|
2224
|
-
readonly
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
readonly
|
|
2228
|
-
readonly enabled: false;
|
|
2229
|
-
dispose(): void | Promise<void>;
|
|
2230
|
-
};
|
|
2231
|
-
interface HostedImageRuntimeGenerationLease {
|
|
2232
|
-
readonly generationId: string;
|
|
2233
|
-
/** Compatibility/debug view; callers should prefer the deep methods below. */
|
|
2234
|
-
readonly contribution: ResponsesImageGenerationContribution;
|
|
2235
|
-
inspectRequest(input: ResponsesImageInspectionInput): ResponsesImageAdmission;
|
|
2236
|
-
validateSelection(admission: ResponsesImageAdmission, selection: ResponsesHostedToolSelection): void;
|
|
2237
|
-
openRequest(input: HostedImageOpenRequestInput): Promise<ResponsesImageRequestScope>;
|
|
2238
|
-
release(): Promise<void>;
|
|
2328
|
+
type DaemonImagePathArea = 'temporary' | 'artifacts' | 'state' | 'evidence' | 'mountManifest';
|
|
2329
|
+
interface DaemonImagePaths {
|
|
2330
|
+
readonly applicationDataRoot: string;
|
|
2331
|
+
readonly imagesRoot: string;
|
|
2332
|
+
readonly temporaryRoot: string;
|
|
2333
|
+
readonly durableRoot: string;
|
|
2334
|
+
readonly artifactsRoot: string;
|
|
2335
|
+
readonly stateRoot: string;
|
|
2336
|
+
readonly evidenceRoot: string;
|
|
2337
|
+
readonly mountManifestRoot: string;
|
|
2338
|
+
readonly mountManifestPath: string;
|
|
2239
2339
|
}
|
|
2240
|
-
interface
|
|
2241
|
-
readonly
|
|
2242
|
-
readonly
|
|
2243
|
-
readonly
|
|
2244
|
-
readonly
|
|
2245
|
-
readonly maxTotalOutputBytes: number;
|
|
2246
|
-
readonly preferredAccountId?: string;
|
|
2247
|
-
readonly preferredAccountGroup?: string;
|
|
2248
|
-
readonly boundAccountFallbackPolicy?: 'strict' | 'pool';
|
|
2340
|
+
interface ImageRootValidationOptions {
|
|
2341
|
+
readonly label?: string;
|
|
2342
|
+
readonly processDirectory?: string;
|
|
2343
|
+
readonly userHome?: string;
|
|
2344
|
+
readonly temporaryDirectory?: string;
|
|
2249
2345
|
}
|
|
2250
|
-
interface
|
|
2251
|
-
readonly
|
|
2252
|
-
readonly
|
|
2253
|
-
readonly requestId: string;
|
|
2254
|
-
readonly sessionKey: string;
|
|
2255
|
-
readonly signal: AbortSignal;
|
|
2256
|
-
readonly authorizedPreviousResponseId?: string;
|
|
2257
|
-
/** Trusted affinity fact forwarded unchanged into the contribution scope. */
|
|
2258
|
-
readonly authorizedPreviousResponseKnownEmpty?: boolean;
|
|
2259
|
-
readonly mainProviderId: string;
|
|
2260
|
-
readonly selectedMainAccountId?: string;
|
|
2346
|
+
interface CreateDaemonImagePathResolverOptions extends ImageRootValidationOptions {
|
|
2347
|
+
readonly configPath: string;
|
|
2348
|
+
readonly storageRoot?: string;
|
|
2261
2349
|
}
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2350
|
+
interface VerifiedDaemonImagePath {
|
|
2351
|
+
readonly area: DaemonImagePathArea;
|
|
2352
|
+
readonly absolutePath: string;
|
|
2353
|
+
readonly kind: 'opaque-file' | 'opaque-directory' | 'mount-manifest';
|
|
2265
2354
|
}
|
|
2266
|
-
/**
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2355
|
+
/**
|
|
2356
|
+
* Owns all daemon Images filesystem names. Callers receive opaque capabilities,
|
|
2357
|
+
* never a filename-accepting delete primitive; destructive methods revalidate
|
|
2358
|
+
* the root identity, descendant relationship, basename, and symlink state.
|
|
2359
|
+
*/
|
|
2360
|
+
declare class DaemonImagePathResolver {
|
|
2361
|
+
#private;
|
|
2362
|
+
readonly paths: DaemonImagePaths;
|
|
2363
|
+
constructor(options: CreateDaemonImagePathResolverOptions);
|
|
2364
|
+
createOpaqueFile(area: Exclude<DaemonImagePathArea, 'temporary' | 'mountManifest'>, format?: 'bin' | 'json' | 'tmp'): VerifiedDaemonImagePath;
|
|
2365
|
+
createOpaqueDirectory(area?: Exclude<DaemonImagePathArea, 'mountManifest'>): VerifiedDaemonImagePath;
|
|
2366
|
+
mountManifest(): VerifiedDaemonImagePath;
|
|
2367
|
+
/** Revalidate and return one internal root for store-local bounded I/O. */
|
|
2368
|
+
verifiedRoot(area: DaemonImagePathArea): string;
|
|
2369
|
+
/** Revalidate immediately before unlinking a resolver-issued file capability. */
|
|
2370
|
+
removeFile(target: VerifiedDaemonImagePath): void;
|
|
2371
|
+
/** Only empty opaque directories may be removed until the owned-marker layer is composed. */
|
|
2372
|
+
removeEmptyDirectory(target: VerifiedDaemonImagePath): void;
|
|
2373
|
+
private issue;
|
|
2374
|
+
private verifyDestructiveTarget;
|
|
2273
2375
|
}
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
readonly
|
|
2277
|
-
readonly
|
|
2376
|
+
|
|
2377
|
+
interface FileCodexImageCapabilityEvidenceManifestOwnerOptions {
|
|
2378
|
+
readonly paths: DaemonImagePathResolver;
|
|
2379
|
+
readonly maxEntries?: number;
|
|
2380
|
+
readonly now?: () => number;
|
|
2381
|
+
readonly random?: (bytes: number) => Buffer;
|
|
2382
|
+
readonly hmacSalt?: Uint8Array;
|
|
2383
|
+
readonly replaceManifest?: (targetPath: string, contents: Uint8Array) => void;
|
|
2278
2384
|
}
|
|
2279
|
-
type
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
readonly
|
|
2283
|
-
readonly
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
readonly
|
|
2287
|
-
readonly
|
|
2385
|
+
type FileCodexImageCapabilityEvidenceSourceOptions = Readonly<(FileCodexImageCapabilityEvidenceManifestOwnerOptions & {
|
|
2386
|
+
readonly ttlMs: number;
|
|
2387
|
+
}) | {
|
|
2388
|
+
readonly owner: FileCodexImageCapabilityEvidenceManifestOwner;
|
|
2389
|
+
readonly ttlMs: number;
|
|
2390
|
+
}>;
|
|
2391
|
+
interface FileCodexImageCapabilityEvidenceStatus {
|
|
2392
|
+
readonly entries: number;
|
|
2393
|
+
readonly freshEntries: number;
|
|
2394
|
+
readonly staleEntries: number;
|
|
2395
|
+
readonly bytes: number;
|
|
2288
2396
|
}
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
shuttingDown: boolean;
|
|
2300
|
-
}>;
|
|
2301
|
-
readonly temporary: Readonly<{
|
|
2302
|
-
activeScopes: number;
|
|
2303
|
-
totalBytes: number;
|
|
2304
|
-
tenantCount: number;
|
|
2305
|
-
maxActiveScopes: number;
|
|
2306
|
-
maxTotalBytes: number;
|
|
2307
|
-
maxTenantBytes: number;
|
|
2308
|
-
}>;
|
|
2309
|
-
readonly storage: Readonly<{
|
|
2310
|
-
mounts: number;
|
|
2311
|
-
retiredMounts: number;
|
|
2312
|
-
referenceEntries: number;
|
|
2313
|
-
referenceBytes: number;
|
|
2314
|
-
referenceTombstones: number;
|
|
2315
|
-
stateCalls: number;
|
|
2316
|
-
stateResponses: number;
|
|
2317
|
-
stateTombstones: number;
|
|
2318
|
-
pendingReferenceDeletes: number;
|
|
2319
|
-
maxReferenceEntries: number;
|
|
2320
|
-
maxReferenceBytes: number;
|
|
2321
|
-
maxTenantReferenceBytes: number;
|
|
2322
|
-
maxStateCalls: number;
|
|
2323
|
-
maxStateResponses: number;
|
|
2397
|
+
/** Revision-aware manifest owner shared by runtime generations, doctor, and cleanup. */
|
|
2398
|
+
declare class FileCodexImageCapabilityEvidenceManifestOwner {
|
|
2399
|
+
#private;
|
|
2400
|
+
constructor(options: FileCodexImageCapabilityEvidenceManifestOwnerOptions);
|
|
2401
|
+
createSource(ttlMs: number): FileCodexImageCapabilityEvidenceSource;
|
|
2402
|
+
resolveWithTtl(request: CodexImageCapabilityEvidenceRequest, ttlMs: number): Promise<CodexImageCapabilityEvidence>;
|
|
2403
|
+
recordSuccessfulVerificationWithTtl(observation: CodexImageCapabilityObservation, ttlMs: number): Promise<void>;
|
|
2404
|
+
cleanup(now: number, limit: number): Promise<{
|
|
2405
|
+
readonly entriesRemoved: number;
|
|
2406
|
+
readonly bytesRemoved: number;
|
|
2324
2407
|
}>;
|
|
2408
|
+
statusWithTtl(ttlMs: number): FileCodexImageCapabilityEvidenceStatus;
|
|
2325
2409
|
}
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
publish(): void;
|
|
2329
|
-
rollback(): void;
|
|
2330
|
-
dispose(): Promise<void>;
|
|
2331
|
-
}
|
|
2332
|
-
/** App-session owner for stable forwarders and generation-pinned work. */
|
|
2333
|
-
declare class ImageRuntimeManager {
|
|
2410
|
+
/** Immutable TTL view over a revision-aware file-backed evidence manifest owner. */
|
|
2411
|
+
declare class FileCodexImageCapabilityEvidenceSource implements CodexImageCapabilityEvidenceSource {
|
|
2334
2412
|
#private;
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2413
|
+
constructor(options: FileCodexImageCapabilityEvidenceSourceOptions);
|
|
2414
|
+
createView(ttlMs: number): FileCodexImageCapabilityEvidenceSource;
|
|
2415
|
+
resolve(request: CodexImageCapabilityEvidenceRequest): Promise<CodexImageCapabilityEvidence>;
|
|
2416
|
+
recordSuccessfulVerification(observation: CodexImageCapabilityObservation): Promise<void>;
|
|
2417
|
+
cleanup(now: number, limit: number): Promise<{
|
|
2418
|
+
readonly entriesRemoved: number;
|
|
2419
|
+
readonly bytesRemoved: number;
|
|
2420
|
+
}>;
|
|
2421
|
+
status(): FileCodexImageCapabilityEvidenceStatus;
|
|
2422
|
+
ttlMs(): number;
|
|
2423
|
+
/** Lifecycle-symmetric no-op; physical safety no longer depends on local leases. */
|
|
2424
|
+
dispose(): void;
|
|
2344
2425
|
}
|
|
2345
2426
|
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
declare const IMAGE_CONFIGURATION_AUDIT_FIELDS: readonly ["enablement", "provider", "model", "account", "queue", "temporary", "limits", "retention", "storage", "remote", "evidence"];
|
|
2355
|
-
type ImageConfigurationAuditField = typeof IMAGE_CONFIGURATION_AUDIT_FIELDS[number];
|
|
2356
|
-
/** Values are deliberately limited to safe categories and internal generation ids. */
|
|
2357
|
-
interface ImageConfigurationAuditRecord {
|
|
2358
|
-
readonly outcome: 'applied';
|
|
2359
|
-
readonly fields: readonly ImageConfigurationAuditField[];
|
|
2360
|
-
readonly previousGenerationId?: string;
|
|
2361
|
-
readonly generationId?: string;
|
|
2362
|
-
}
|
|
2363
|
-
interface ImageHistogramSnapshot {
|
|
2364
|
-
readonly count: number;
|
|
2365
|
-
readonly sum: number;
|
|
2366
|
-
/** Non-cumulative fixed buckets; `upperBound:null` is the overflow bucket. */
|
|
2367
|
-
readonly buckets: readonly {
|
|
2368
|
-
readonly upperBound: number | null;
|
|
2369
|
-
readonly count: number;
|
|
2370
|
-
}[];
|
|
2427
|
+
interface FileImageReferenceStoreLimits {
|
|
2428
|
+
readonly ttlMs: number;
|
|
2429
|
+
readonly maxArtifactBytes: number;
|
|
2430
|
+
readonly maxTotalBytes: number;
|
|
2431
|
+
readonly maxTenantBytes: number;
|
|
2432
|
+
readonly maxEntries: number;
|
|
2433
|
+
readonly maxTombstones: number;
|
|
2434
|
+
readonly tombstoneTtlMs: number;
|
|
2371
2435
|
}
|
|
2372
|
-
interface
|
|
2373
|
-
readonly
|
|
2374
|
-
readonly
|
|
2375
|
-
readonly
|
|
2376
|
-
readonly
|
|
2377
|
-
readonly
|
|
2378
|
-
readonly
|
|
2379
|
-
readonly outputFormat: SafeOutputFormat;
|
|
2380
|
-
readonly streaming: SafeBooleanOption;
|
|
2381
|
-
readonly requestedOutputs: SafeCountOption;
|
|
2382
|
-
readonly partialImages: SafeCountOption;
|
|
2383
|
-
readonly terminal: 'completed' | 'failed' | 'cancelled';
|
|
2384
|
-
readonly errorCode: SafeErrorCode;
|
|
2436
|
+
interface FileImageReferenceStoreOptions {
|
|
2437
|
+
readonly paths: DaemonImagePathResolver;
|
|
2438
|
+
readonly limits: FileImageReferenceStoreLimits;
|
|
2439
|
+
readonly secretBox?: SecretBox;
|
|
2440
|
+
readonly now?: () => number;
|
|
2441
|
+
readonly random?: (bytes: number) => Buffer;
|
|
2442
|
+
readonly replaceManifest?: (targetPath: string, contents: Uint8Array) => void;
|
|
2385
2443
|
}
|
|
2386
|
-
interface
|
|
2387
|
-
readonly
|
|
2388
|
-
readonly
|
|
2389
|
-
readonly
|
|
2390
|
-
readonly
|
|
2391
|
-
readonly
|
|
2392
|
-
readonly outputFormat: SafeOutputFormat;
|
|
2393
|
-
readonly streaming: SafeBooleanOption;
|
|
2394
|
-
readonly requestedOutputs: SafeCountOption;
|
|
2395
|
-
readonly terminal: 'completed' | 'failed' | 'cancelled' | 'other';
|
|
2396
|
-
readonly errorCode: SafeErrorCode;
|
|
2444
|
+
interface FileImageReferenceReconciliationResult {
|
|
2445
|
+
readonly metadataRemoved: number;
|
|
2446
|
+
readonly metadataDegradedToProviderReference: number;
|
|
2447
|
+
readonly orphanFilesRemoved: number;
|
|
2448
|
+
readonly incompleteFilesRemoved: number;
|
|
2449
|
+
readonly invalidDescendants: number;
|
|
2397
2450
|
}
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2451
|
+
declare class FileImageReferenceStore implements ImageReferenceStore {
|
|
2452
|
+
#private;
|
|
2453
|
+
constructor(options: FileImageReferenceStoreOptions);
|
|
2454
|
+
save(input: ImageReferenceSaveInput): Promise<ImageReferenceMetadata>;
|
|
2455
|
+
/** Generation-bound write entry point; reads and maintenance remain shared. */
|
|
2456
|
+
saveWithLimits(input: ImageReferenceSaveInput, limits: FileImageReferenceStoreLimits): Promise<ImageReferenceMetadata>;
|
|
2457
|
+
/** Updates only app-session maintenance policy; pinned writes pass their own limits. */
|
|
2458
|
+
updateMaintenanceLimits(limits: FileImageReferenceStoreLimits): void;
|
|
2459
|
+
resolve(tenantId: string, referenceId: ImageReferenceId): Promise<ImageReferenceResolution>;
|
|
2460
|
+
delete(tenantId: string, referenceId: ImageReferenceId): Promise<boolean>;
|
|
2461
|
+
/** Daemon-internal cleanup path; accepts only the local reference-domain tenant HMAC. */
|
|
2462
|
+
deleteByHashedTenantKey(tenantKey: string, referenceId: ImageReferenceId): Promise<boolean>;
|
|
2463
|
+
cleanup(now?: number): Promise<number>;
|
|
2464
|
+
status(): {
|
|
2465
|
+
readonly entries: number;
|
|
2466
|
+
readonly bytes: number;
|
|
2467
|
+
readonly tombstones: number;
|
|
2468
|
+
};
|
|
2469
|
+
hasLiveReferenceByHashedTenantKey(tenantKey: string, referenceId: ImageReferenceId, now?: number): Promise<boolean>;
|
|
2470
|
+
reconcileOwnedFiles(maxEntries: number): Promise<FileImageReferenceReconciliationResult>;
|
|
2471
|
+
openArtifact(fileName: string, byteLength: number, signal?: AbortSignal): Promise<ReadableStream<Uint8Array>>;
|
|
2472
|
+
private exclusive;
|
|
2473
|
+
private tenantKey;
|
|
2474
|
+
private newReferenceId;
|
|
2475
|
+
private validateSaveInput;
|
|
2476
|
+
private selectVictims;
|
|
2477
|
+
private nextTombstones;
|
|
2478
|
+
private writeArtifact;
|
|
2479
|
+
private artifactPath;
|
|
2480
|
+
private validArtifact;
|
|
2481
|
+
private removeArtifact;
|
|
2482
|
+
private safeUnlinkArtifactPath;
|
|
2483
|
+
private releaseLease;
|
|
2484
|
+
private manifestPath;
|
|
2485
|
+
private persist;
|
|
2486
|
+
private atomicReplace;
|
|
2487
|
+
private loadManifest;
|
|
2413
2488
|
}
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
readonly
|
|
2417
|
-
readonly
|
|
2418
|
-
readonly
|
|
2419
|
-
readonly
|
|
2420
|
-
readonly firstPartialLatencyMs?: ImageHistogramSnapshot;
|
|
2421
|
-
readonly inputCount: ImageHistogramSnapshot;
|
|
2422
|
-
readonly inputBytes: ImageHistogramSnapshot;
|
|
2423
|
-
readonly outputCount: ImageHistogramSnapshot;
|
|
2424
|
-
readonly outputBytes: ImageHistogramSnapshot;
|
|
2425
|
-
readonly retryCount?: ImageHistogramSnapshot;
|
|
2426
|
-
readonly authRefreshCount?: ImageHistogramSnapshot;
|
|
2427
|
-
readonly referenceSaveCount?: ImageHistogramSnapshot;
|
|
2428
|
-
readonly retentionRollbackFailures?: ImageHistogramSnapshot;
|
|
2489
|
+
|
|
2490
|
+
interface FileResponsesImageStateStoreLimits {
|
|
2491
|
+
readonly maxCalls: number;
|
|
2492
|
+
readonly maxResponses: number;
|
|
2493
|
+
readonly maxTombstones: number;
|
|
2494
|
+
readonly tombstoneTtlMs: number;
|
|
2429
2495
|
}
|
|
2430
|
-
interface
|
|
2431
|
-
readonly
|
|
2432
|
-
readonly
|
|
2433
|
-
readonly
|
|
2434
|
-
readonly
|
|
2435
|
-
|
|
2436
|
-
telemetryRecords: number;
|
|
2437
|
-
}>;
|
|
2496
|
+
interface FileResponsesImageStateStoreOptions {
|
|
2497
|
+
readonly paths: DaemonImagePathResolver;
|
|
2498
|
+
readonly limits: FileResponsesImageStateStoreLimits;
|
|
2499
|
+
readonly now?: () => number;
|
|
2500
|
+
readonly random?: (bytes: number) => Buffer;
|
|
2501
|
+
readonly replaceManifest?: (targetPath: string, contents: Uint8Array) => void;
|
|
2438
2502
|
}
|
|
2439
|
-
interface
|
|
2440
|
-
|
|
2441
|
-
readonly
|
|
2503
|
+
interface PendingResponsesImageReferenceDelete {
|
|
2504
|
+
readonly referenceTenantKey: string;
|
|
2505
|
+
readonly binding: ResponsesImageCallBinding;
|
|
2442
2506
|
}
|
|
2443
|
-
/**
|
|
2444
|
-
declare class
|
|
2507
|
+
/** Durable production implementation of the existing Responses image-state contract. */
|
|
2508
|
+
declare class FileResponsesImageStateStore implements ResponsesImageStateStore {
|
|
2445
2509
|
#private;
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2510
|
+
constructor(options: FileResponsesImageStateStoreOptions);
|
|
2511
|
+
commit(input: ResponsesImageStateCommitInput): Promise<readonly ResponsesImageCallBinding[]>;
|
|
2512
|
+
/** Generation-bound write entry point; reads and maintenance remain shared. */
|
|
2513
|
+
commitWithLimits(input: ResponsesImageStateCommitInput, limits: FileResponsesImageStateStoreLimits): Promise<readonly ResponsesImageCallBinding[]>;
|
|
2514
|
+
/** Updates only app-session maintenance policy; pinned commits pass their own limits. */
|
|
2515
|
+
updateMaintenanceLimits(limits: FileResponsesImageStateStoreLimits): void;
|
|
2516
|
+
resolveCall(tenantId: string, callId: ResponsesImageCallId): Promise<ResponsesImageCallResolution>;
|
|
2517
|
+
resolveResponse(tenantId: string, responseId: string): Promise<ResponsesImageResponseResolution>;
|
|
2518
|
+
deleteCall(tenantId: string, callId: ResponsesImageCallId): Promise<ResponsesImageCallBinding | undefined>;
|
|
2519
|
+
deleteResponse(tenantId: string, responseId: string): Promise<boolean>;
|
|
2520
|
+
cleanup(now?: number): Promise<readonly ResponsesImageCallBinding[]>;
|
|
2521
|
+
pendingReferenceDeletes(limit?: number): readonly PendingResponsesImageReferenceDelete[];
|
|
2522
|
+
acknowledgeReferenceDeletes(completed: readonly PendingResponsesImageReferenceDelete[]): Promise<number>;
|
|
2523
|
+
reconcileBrokenReferenceLinks(hasLiveReference: (referenceTenantKey: string, referenceId: ResponsesImageCallBinding['referenceId']) => Promise<boolean>, maxEntries: number): Promise<readonly ResponsesImageCallBinding[]>;
|
|
2524
|
+
status(): {
|
|
2525
|
+
readonly calls: number;
|
|
2526
|
+
readonly responses: number;
|
|
2527
|
+
readonly tombstones: number;
|
|
2528
|
+
readonly pendingReferenceDeletes: number;
|
|
2529
|
+
};
|
|
2530
|
+
private exclusive;
|
|
2531
|
+
private failure;
|
|
2532
|
+
private assertCommit;
|
|
2533
|
+
private tenantKey;
|
|
2534
|
+
private rememberTombstone;
|
|
2535
|
+
private enqueuePendingReferenceDelete;
|
|
2536
|
+
private hasTombstone;
|
|
2537
|
+
private prunedTombstones;
|
|
2538
|
+
private pruneTombstonesInPlace;
|
|
2539
|
+
private sameTombstones;
|
|
2540
|
+
private touch;
|
|
2541
|
+
private releaseCall;
|
|
2542
|
+
private releaseResponse;
|
|
2543
|
+
private manifestPath;
|
|
2544
|
+
private persist;
|
|
2545
|
+
private atomicReplace;
|
|
2546
|
+
private loadManifest;
|
|
2454
2547
|
}
|
|
2455
2548
|
|
|
2456
|
-
interface
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
dispose(): void | Promise<void>;
|
|
2549
|
+
interface ImageStorageMountBackend {
|
|
2550
|
+
readonly id: string;
|
|
2551
|
+
readonly createdAt: number;
|
|
2552
|
+
readonly resolver: DaemonImagePathResolver;
|
|
2553
|
+
readonly references: FileImageReferenceStore;
|
|
2554
|
+
readonly responsesState: FileResponsesImageStateStore;
|
|
2463
2555
|
}
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
* - `markAutoDisabled(keyId, status, at)` records `{ status, at, reason }`,
|
|
2475
|
-
* - `isDisabled(keyId)` / `get(keyId)` read it back,
|
|
2476
|
-
* - `loadPoolKeys` reads this store and flips a flagged key's `enabled` to
|
|
2477
|
-
* `false`, so `getAvailableKeys` skips it within this process lifetime.
|
|
2478
|
-
*
|
|
2479
|
-
* Restart resets the store (the honest v1 boundary — see spec). Persistence
|
|
2480
|
-
* (encrypted write-back) is a child-3 follow-up.
|
|
2481
|
-
*
|
|
2482
|
-
* @module @omnicross/daemon/pool/autoDisableStore
|
|
2483
|
-
*/
|
|
2484
|
-
/** One in-memory auto-disable record for a pool key. */
|
|
2485
|
-
interface AutoDisableRecord {
|
|
2486
|
-
/** The HTTP status that triggered the disable (401/403). */
|
|
2487
|
-
status: number;
|
|
2488
|
-
/** Epoch-ms when the disable was recorded. */
|
|
2489
|
-
at: number;
|
|
2490
|
-
/** Always `'auth_failure'` in v1 (the only auto-disable trigger). */
|
|
2491
|
-
reason: 'auth_failure';
|
|
2556
|
+
interface ImageStorageMountCatalogOptions {
|
|
2557
|
+
readonly pathOptions: Omit<CreateDaemonImagePathResolverOptions, 'storageRoot'>;
|
|
2558
|
+
readonly activeStorageRoot?: string;
|
|
2559
|
+
readonly referenceLimits: FileImageReferenceStoreLimits;
|
|
2560
|
+
readonly responsesStateLimits: FileResponsesImageStateStoreLimits;
|
|
2561
|
+
readonly secretBox?: SecretBox;
|
|
2562
|
+
readonly now?: () => number;
|
|
2563
|
+
readonly random?: (bytes: number) => Buffer;
|
|
2564
|
+
readonly replaceCatalog?: (targetPath: string, contents: Uint8Array) => void;
|
|
2565
|
+
readonly reconcileCorruptManifests?: boolean;
|
|
2492
2566
|
}
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
* `disableKey` / `markAutoDisabled` sinks AND read by `loadPoolKeys`.
|
|
2497
|
-
*/
|
|
2498
|
-
declare class AutoDisableStore {
|
|
2499
|
-
private readonly records;
|
|
2500
|
-
/** Record (or overwrite) an auth-failure auto-disable for `keyId`. */
|
|
2501
|
-
markAutoDisabled(keyId: string, status: number, at: number): void;
|
|
2502
|
-
/** Whether `keyId` is currently auto-disabled in this process. */
|
|
2503
|
-
isDisabled(keyId: string): boolean;
|
|
2504
|
-
/** Read the auto-disable record for `keyId`, or `undefined` when healthy. */
|
|
2505
|
-
get(keyId: string): AutoDisableRecord | undefined;
|
|
2506
|
-
/** Clear all records (tests / teardown). */
|
|
2507
|
-
clear(): void;
|
|
2567
|
+
interface ImageStorageMountPolicy {
|
|
2568
|
+
readonly referenceLimits: FileImageReferenceStoreLimits;
|
|
2569
|
+
readonly responsesStateLimits: FileResponsesImageStateStoreLimits;
|
|
2508
2570
|
}
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
* The daemon's `config.json` provider rows ARE the catalog. Of the port's ten
|
|
2515
|
-
* methods, FOUR are real (the ones the BYO proxy/outbound path actually hits):
|
|
2516
|
-
* - `getProvider(id)` — map a `DaemonProviderConfig` row to an `LLMProvider`.
|
|
2517
|
-
* - `getTransformerService()` — a single `TransformerService` seeded by
|
|
2518
|
-
* `registerBuiltinTransformers` in the ctor.
|
|
2519
|
-
* - `getMainTransformer(id)` — the transformer for the provider's target wire
|
|
2520
|
-
* format (anthropic → AnthropicTransformer, gemini → GeminiTransformer,
|
|
2521
|
-
* openai → null/identity), mirroring the host `AgentModelsManager` switch.
|
|
2522
|
-
* `resolveProviderChain` unshifts this FORMAT-FIRST into the provider chain.
|
|
2523
|
-
* - `resolveTransformerChain(id, model)` — the provider's CUSTOM
|
|
2524
|
-
* `transformer.use[]` chain (app-parity-2 child 2: ENFORCED). The format
|
|
2525
|
-
* transformer is NOT included here (getMainTransformer supplies it,
|
|
2526
|
-
* format-first); no `transformer.use[]` → empty chain.
|
|
2527
|
-
*
|
|
2528
|
-
* The remaining SIX are minimal sensible stubs (never hit on the BYO single-key
|
|
2529
|
-
* path — the boot smoke test is the proof).
|
|
2530
|
-
*
|
|
2531
|
-
* @module @omnicross/daemon/ports/ConfigFileProviderConfigSource
|
|
2532
|
-
*/
|
|
2533
|
-
|
|
2534
|
-
declare class ConfigFileProviderConfigSource implements ProviderConfigSource {
|
|
2535
|
-
private readonly providers;
|
|
2536
|
-
private readonly transformerService;
|
|
2537
|
-
/**
|
|
2538
|
-
* Optional reload-hook (key-pool change, design D4). A no-type-coupling
|
|
2539
|
-
* callback invoked at the END of `reload(...)`. `buildDaemon` injects
|
|
2540
|
-
* `() => pool.invalidateCache()` so the `ApiKeyPoolService.keyCache` is
|
|
2541
|
-
* flushed after a hot-reload swaps the catalog — WITHOUT this port ever
|
|
2542
|
-
* importing/depending on `ApiKeyPoolService`. Absent = no-op (single-key
|
|
2543
|
-
* boots that never construct a pool stay byte-identical).
|
|
2544
|
-
*/
|
|
2545
|
-
private reloadHook;
|
|
2546
|
-
constructor(config: DaemonConfig);
|
|
2547
|
-
/**
|
|
2548
|
-
* Register a callback fired after every `reload(...)`. Used by `buildDaemon`
|
|
2549
|
-
* to invalidate the pool's keyCache on a hot-reload. The port stays ignorant
|
|
2550
|
-
* of what the callback does (no pool type dependency).
|
|
2551
|
-
*/
|
|
2552
|
-
setReloadHook(fn: () => void): void;
|
|
2553
|
-
/**
|
|
2554
|
-
* Read the live (post-reload) provider row for `providerId`, or `undefined`.
|
|
2555
|
-
* Exposed so the pool's `loadKeys` reads the SAME live catalog Map this port
|
|
2556
|
-
* serves (so a hot-reload is observed on the next load after `invalidateCache`).
|
|
2557
|
-
*/
|
|
2558
|
-
getProviderRow(providerId: string): DaemonProviderConfig | undefined;
|
|
2559
|
-
/** Await the built-in transformer registration (tests await this before dispatch). */
|
|
2560
|
-
ready(): Promise<void>;
|
|
2561
|
-
/**
|
|
2562
|
-
* Replace the live provider catalog in place (additive — does NOT touch the
|
|
2563
|
-
* ten port methods, the seeded `TransformerService`, or `ready()`). Called by
|
|
2564
|
-
* the admin API after a provider POST/PUT/DELETE persists `config.json`, so the
|
|
2565
|
-
* next outbound request sees the new catalog WITHOUT a daemon restart. The Map
|
|
2566
|
-
* swap is synchronous; an in-flight request keeps its already-resolved
|
|
2567
|
-
* provider (no locking needed for a single-operator local daemon).
|
|
2568
|
-
*/
|
|
2569
|
-
reload(config: DaemonConfig): void;
|
|
2570
|
-
/** Clear + repopulate the private providers Map from a fresh provider list. */
|
|
2571
|
-
setProviders(providers: readonly DaemonProviderConfig[]): void;
|
|
2572
|
-
getProvider(id: string): Promise<LLMProvider | null>;
|
|
2573
|
-
getTransformerService(): TransformerService | undefined;
|
|
2574
|
-
getMainTransformer(providerId: string): Promise<Transformer | null>;
|
|
2575
|
-
resolveTransformerChain(providerId: string, _model?: string): Promise<ResolvedTransformerChain>;
|
|
2576
|
-
resolveRoutedModel(): Promise<null>;
|
|
2577
|
-
resolveEffectiveModels(): Promise<{
|
|
2578
|
-
background?: string;
|
|
2579
|
-
vision?: string;
|
|
2580
|
-
}>;
|
|
2581
|
-
getAgentDefaultModels(): Promise<AgentDefaultModels>;
|
|
2582
|
-
hasVisionCapability(): Promise<boolean>;
|
|
2583
|
-
getGlobalModelParameters(): Promise<GlobalModelParameters>;
|
|
2584
|
-
getDiscoveredModelMaxTokens(): Promise<number | undefined>;
|
|
2571
|
+
interface PreparedImageStorageMountActivation {
|
|
2572
|
+
readonly backend: ImageStorageMountBackend;
|
|
2573
|
+
publish(): ImageStorageMountBackend;
|
|
2574
|
+
rollback(): void;
|
|
2575
|
+
dispose(): void;
|
|
2585
2576
|
}
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2577
|
+
/** Owns the durable-root set independently from any one runtime generation. */
|
|
2578
|
+
declare class ImageStorageMountCatalog {
|
|
2579
|
+
#private;
|
|
2580
|
+
constructor(options: ImageStorageMountCatalogOptions);
|
|
2581
|
+
active(): ImageStorageMountBackend;
|
|
2582
|
+
mountsForRead(): readonly ImageStorageMountBackend[];
|
|
2583
|
+
status(): {
|
|
2584
|
+
readonly mounts: number;
|
|
2585
|
+
readonly retiredMounts: number;
|
|
2586
|
+
};
|
|
2587
|
+
startupReconciliationStatus(): {
|
|
2588
|
+
readonly corruptManifestsQuarantined: number;
|
|
2589
|
+
};
|
|
2590
|
+
utilization(): {
|
|
2591
|
+
readonly referenceEntries: number;
|
|
2592
|
+
readonly referenceBytes: number;
|
|
2593
|
+
readonly referenceTombstones: number;
|
|
2594
|
+
readonly stateCalls: number;
|
|
2595
|
+
readonly stateResponses: number;
|
|
2596
|
+
readonly stateTombstones: number;
|
|
2597
|
+
readonly pendingReferenceDeletes: number;
|
|
2598
|
+
};
|
|
2599
|
+
/** Pins a backend object until its owning runtime generation drains. */
|
|
2600
|
+
retainBackend(backend: ImageStorageMountBackend): () => void;
|
|
2601
|
+
activate(storageRoot?: string): ImageStorageMountBackend;
|
|
2602
|
+
/** Prepare a validated backend without changing the catalog's active mount. */
|
|
2603
|
+
prepareActivation(storageRoot?: string, policy?: ImageStorageMountPolicy): PreparedImageStorageMountActivation;
|
|
2604
|
+
retireEmptyMount(mountId: string): boolean;
|
|
2605
|
+
private createResolver;
|
|
2606
|
+
private createBackend;
|
|
2607
|
+
private applyMaintenancePolicy;
|
|
2608
|
+
private newMountId;
|
|
2609
|
+
private isManifestError;
|
|
2610
|
+
private quarantineManifest;
|
|
2611
|
+
private isVerifiedEmpty;
|
|
2612
|
+
private catalogPath;
|
|
2613
|
+
private persist;
|
|
2614
|
+
private atomicReplace;
|
|
2615
|
+
private loadCatalog;
|
|
2608
2616
|
}
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
private readonly
|
|
2612
|
-
private readonly
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
private
|
|
2617
|
+
declare class MountedImageReferenceStore implements ImageReferenceStore {
|
|
2618
|
+
private readonly catalog;
|
|
2619
|
+
private readonly writeBackend?;
|
|
2620
|
+
private readonly writeLimits?;
|
|
2621
|
+
constructor(catalog: ImageStorageMountCatalog, writeBackend?: ImageStorageMountBackend | undefined, writeLimits?: FileImageReferenceStoreLimits | undefined);
|
|
2622
|
+
bindWriteBackend(backend: ImageStorageMountBackend, limits: FileImageReferenceStoreLimits): MountedImageReferenceStore;
|
|
2623
|
+
status(): Readonly<{
|
|
2624
|
+
referenceEntries: number;
|
|
2625
|
+
referenceBytes: number;
|
|
2626
|
+
referenceTombstones: number;
|
|
2627
|
+
stateCalls: number;
|
|
2628
|
+
stateResponses: number;
|
|
2629
|
+
stateTombstones: number;
|
|
2630
|
+
pendingReferenceDeletes: number;
|
|
2631
|
+
mounts: number;
|
|
2632
|
+
retiredMounts: number;
|
|
2633
|
+
}>;
|
|
2634
|
+
save(input: ImageReferenceSaveInput): Promise<_omnicross_contracts_image_generation_types.ImageReferenceMetadata>;
|
|
2635
|
+
resolve(tenantId: string, referenceId: ImageReferenceId): Promise<ImageReferenceResolution>;
|
|
2636
|
+
delete(tenantId: string, referenceId: ImageReferenceId): Promise<boolean>;
|
|
2637
|
+
deleteByHashedTenantKey(tenantKey: string, referenceId: ImageReferenceId): Promise<boolean>;
|
|
2638
|
+
cleanup(now?: number): Promise<number>;
|
|
2639
|
+
}
|
|
2640
|
+
declare class MountedResponsesImageStateStore implements ResponsesImageStateStore {
|
|
2641
|
+
private readonly catalog;
|
|
2642
|
+
private readonly writeBackend?;
|
|
2643
|
+
private readonly writeLimits?;
|
|
2644
|
+
constructor(catalog: ImageStorageMountCatalog, writeBackend?: ImageStorageMountBackend | undefined, writeLimits?: FileResponsesImageStateStoreLimits | undefined);
|
|
2645
|
+
bindWriteBackend(backend: ImageStorageMountBackend, limits: FileResponsesImageStateStoreLimits): MountedResponsesImageStateStore;
|
|
2646
|
+
commit(input: ResponsesImageStateCommitInput): Promise<readonly ResponsesImageCallBinding[]>;
|
|
2647
|
+
resolveCall(tenantId: string, callId: ResponsesImageCallId): Promise<ResponsesImageCallResolution>;
|
|
2648
|
+
resolveResponse(tenantId: string, responseId: string): Promise<ResponsesImageResponseResolution>;
|
|
2649
|
+
deleteCall(tenantId: string, callId: ResponsesImageCallId): Promise<ResponsesImageCallBinding | undefined>;
|
|
2650
|
+
deleteResponse(tenantId: string, responseId: string): Promise<boolean>;
|
|
2651
|
+
cleanup(now?: number): Promise<readonly ResponsesImageCallBinding[]>;
|
|
2635
2652
|
}
|
|
2636
2653
|
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
current: PricingEntry;
|
|
2684
|
-
incoming: PricingEntryInput;
|
|
2685
|
-
}>;
|
|
2654
|
+
interface ImageDoctorLocalSnapshot {
|
|
2655
|
+
readonly config: Readonly<{
|
|
2656
|
+
enabled: boolean;
|
|
2657
|
+
provider: ImageProviderId;
|
|
2658
|
+
model: string;
|
|
2659
|
+
valid: boolean;
|
|
2660
|
+
errorCount: number;
|
|
2661
|
+
/** Distinct providers the routing table names (doctor per-provider rows). */
|
|
2662
|
+
routedProviders: readonly ImageProviderId[];
|
|
2663
|
+
}>;
|
|
2664
|
+
readonly roots: Readonly<{
|
|
2665
|
+
valid: boolean;
|
|
2666
|
+
verifiedAreas: number;
|
|
2667
|
+
expectedAreas: number;
|
|
2668
|
+
}>;
|
|
2669
|
+
readonly stores: Readonly<{
|
|
2670
|
+
valid: boolean;
|
|
2671
|
+
mounts: number;
|
|
2672
|
+
retiredMounts: number;
|
|
2673
|
+
referenceEntries: number;
|
|
2674
|
+
referenceBytes: number;
|
|
2675
|
+
stateCalls: number;
|
|
2676
|
+
stateResponses: number;
|
|
2677
|
+
corruptManifestsQuarantined: number;
|
|
2678
|
+
}>;
|
|
2679
|
+
readonly permissions: Readonly<{
|
|
2680
|
+
valid: boolean;
|
|
2681
|
+
rows: number;
|
|
2682
|
+
legacyRows: number;
|
|
2683
|
+
invalidRows: number;
|
|
2684
|
+
imagesAuthorizedRows: number;
|
|
2685
|
+
}>;
|
|
2686
|
+
/** The Codex-subscription account backing codex-routed image models. */
|
|
2687
|
+
readonly account: Readonly<{
|
|
2688
|
+
present: boolean;
|
|
2689
|
+
usable: boolean;
|
|
2690
|
+
reason: 'ready' | 'missing' | 'unavailable';
|
|
2691
|
+
}>;
|
|
2692
|
+
/** The Antigravity-subscription account backing NanoBanana image models. */
|
|
2693
|
+
readonly antigravityAccount: Readonly<{
|
|
2694
|
+
present: boolean;
|
|
2695
|
+
usable: boolean;
|
|
2696
|
+
reason: 'ready' | 'missing' | 'unavailable';
|
|
2697
|
+
}>;
|
|
2698
|
+
readonly evidence: Readonly<FileCodexImageCapabilityEvidenceStatus & {
|
|
2699
|
+
valid: boolean;
|
|
2686
2700
|
}>;
|
|
2687
|
-
/**
|
|
2688
|
-
* Apply per-row conflict decisions: 'overwrite' replaces the local row with
|
|
2689
|
-
* the incoming values (clearing the user-edited mark), 'skip' counts only.
|
|
2690
|
-
*/
|
|
2691
|
-
applyResolutions(resolutions: Array<{
|
|
2692
|
-
incoming: PricingEntryInput;
|
|
2693
|
-
action: 'overwrite' | 'skip';
|
|
2694
|
-
}>): Promise<PricingResolution>;
|
|
2695
|
-
/**
|
|
2696
|
-
* STORE-LOCAL (not on the core port): remove one row. Returns whether a row
|
|
2697
|
-
* was actually removed. The admin DELETE handler calls this then invalidates
|
|
2698
|
-
* the engine cache.
|
|
2699
|
-
*/
|
|
2700
|
-
delete(providerId: string, modelId: string): Promise<boolean>;
|
|
2701
|
-
/** Upsert into `rows` IN PLACE (no write) and return the resulting entry. */
|
|
2702
|
-
private applyUpsert;
|
|
2703
|
-
/** Read the pricing rows, tolerating a missing/corrupt file (→ empty list). */
|
|
2704
|
-
private readRows;
|
|
2705
|
-
private writeRows;
|
|
2706
|
-
/** Isolated for deterministic failure testing; never removes the target. */
|
|
2707
|
-
private replaceFile;
|
|
2708
2701
|
}
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2702
|
+
type ImageDoctorLiveFailureCode = 'images_disabled' | 'codex_account_unavailable' | 'evidence_store_unavailable' | 'evidence_persist_failed' | ImageGenerationErrorCode;
|
|
2703
|
+
type ImageDoctorLiveResult = Readonly<{
|
|
2704
|
+
ok: true;
|
|
2705
|
+
code: 'verified';
|
|
2706
|
+
model: 'gpt-image-2';
|
|
2707
|
+
quality: 'low';
|
|
2708
|
+
outputFormat: 'png';
|
|
2709
|
+
freshEvidenceEntries: number;
|
|
2710
|
+
}> | Readonly<{
|
|
2711
|
+
ok: false;
|
|
2712
|
+
code: ImageDoctorLiveFailureCode;
|
|
2713
|
+
}>;
|
|
2714
|
+
interface ImageDoctorService {
|
|
2715
|
+
inspectLocal(config: ImagesServerConfig): Promise<ImageDoctorLocalSnapshot>;
|
|
2716
|
+
verifyLive(config: ImagesServerConfig, signal: AbortSignal): Promise<ImageDoctorLiveResult>;
|
|
2713
2717
|
}
|
|
2714
2718
|
|
|
2715
|
-
type
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2719
|
+
type PreparedImageRuntimeGeneration = {
|
|
2720
|
+
readonly id: string;
|
|
2721
|
+
readonly enabled: true;
|
|
2722
|
+
readonly imageApi: ImageApiContributions;
|
|
2723
|
+
readonly hosted: ResponsesImageGenerationContribution;
|
|
2724
|
+
readonly hostedRuntime: HostedImageRuntimePolicy;
|
|
2725
|
+
readonly inspectCapability?: (apiKeyId: string) => Promise<Omit<ImageRuntimeCapabilityInspection, 'generationId'>>;
|
|
2726
|
+
readonly readRuntimeStatus?: () => ImageRuntimeResourceStatus;
|
|
2727
|
+
dispose(): void | Promise<void>;
|
|
2728
|
+
} | {
|
|
2729
|
+
readonly id: string;
|
|
2730
|
+
readonly enabled: false;
|
|
2731
|
+
dispose(): void | Promise<void>;
|
|
2732
|
+
};
|
|
2733
|
+
interface HostedImageRuntimeGenerationLease {
|
|
2734
|
+
readonly generationId: string;
|
|
2735
|
+
/** Compatibility/debug view; callers should prefer the deep methods below. */
|
|
2736
|
+
readonly contribution: ResponsesImageGenerationContribution;
|
|
2737
|
+
inspectRequest(input: ResponsesImageInspectionInput): ResponsesImageAdmission;
|
|
2738
|
+
validateSelection(admission: ResponsesImageAdmission, selection: ResponsesHostedToolSelection): void;
|
|
2739
|
+
openRequest(input: HostedImageOpenRequestInput): Promise<ResponsesImageRequestScope>;
|
|
2740
|
+
release(): Promise<void>;
|
|
2721
2741
|
}
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
allowedEndpoints: OutboundPermission[];
|
|
2732
|
-
requiredEndpoints: OutboundPermission[];
|
|
2733
|
-
loopbackOnly: boolean;
|
|
2742
|
+
interface HostedImageRuntimePolicy {
|
|
2743
|
+
readonly providerId: string;
|
|
2744
|
+
readonly imageModel: string;
|
|
2745
|
+
readonly referenceTtlMs: number;
|
|
2746
|
+
readonly maxOutputBytes: number;
|
|
2747
|
+
readonly maxTotalOutputBytes: number;
|
|
2748
|
+
readonly preferredAccountId?: string;
|
|
2749
|
+
readonly preferredAccountGroup?: string;
|
|
2750
|
+
readonly boundAccountFallbackPolicy?: 'strict' | 'pool';
|
|
2734
2751
|
}
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2752
|
+
interface HostedImageOpenRequestInput {
|
|
2753
|
+
readonly admission: ResponsesImageAdmission;
|
|
2754
|
+
readonly tenantId: string;
|
|
2755
|
+
readonly requestId: string;
|
|
2756
|
+
readonly sessionKey: string;
|
|
2757
|
+
readonly signal: AbortSignal;
|
|
2758
|
+
readonly authorizedPreviousResponseId?: string;
|
|
2759
|
+
/** Trusted affinity fact forwarded unchanged into the contribution scope. */
|
|
2760
|
+
readonly authorizedPreviousResponseKnownEmpty?: boolean;
|
|
2761
|
+
readonly mainProviderId: string;
|
|
2762
|
+
readonly selectedMainAccountId?: string;
|
|
2745
2763
|
}
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
action: 'install' | 'none' | 'repair';
|
|
2750
|
-
canApply: boolean;
|
|
2751
|
-
/** Redacted logical fields only; never file contents or credential values. */
|
|
2752
|
-
changes: string[];
|
|
2753
|
-
warnings: string[];
|
|
2764
|
+
/** Dormant integration seam for a later Native Responses owner. */
|
|
2765
|
+
interface HostedImageContributionFactory {
|
|
2766
|
+
acquire(): Promise<HostedImageRuntimeGenerationLease>;
|
|
2754
2767
|
}
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
installedHash: string;
|
|
2763
|
-
installedAt: number;
|
|
2764
|
-
gatewayBaseUrl: string;
|
|
2765
|
-
/** Codex auth.json snapshot and installed hash; absent on legacy records. */
|
|
2766
|
-
credentialFile?: IntegrationManagedFileRecord;
|
|
2768
|
+
/** Bind a stable factory to one app-session runtime manager without acquiring. */
|
|
2769
|
+
declare function createHostedImageContributionFactory(manager: ImageRuntimeManager): HostedImageContributionFactory;
|
|
2770
|
+
interface ImageRuntimeGenerationStatus {
|
|
2771
|
+
readonly generationId: string;
|
|
2772
|
+
readonly enabled: boolean;
|
|
2773
|
+
readonly httpLeases: number;
|
|
2774
|
+
readonly hostedLeases: number;
|
|
2767
2775
|
}
|
|
2768
|
-
interface
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
originalContent: string;
|
|
2773
|
-
originalHash: string;
|
|
2774
|
-
installedHash: string;
|
|
2776
|
+
interface ImageRuntimeManagerStatus {
|
|
2777
|
+
readonly disposed: boolean;
|
|
2778
|
+
readonly current: ImageRuntimeGenerationStatus;
|
|
2779
|
+
readonly draining: readonly ImageRuntimeGenerationStatus[];
|
|
2775
2780
|
}
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
+
type ImageRuntimeSafeUnavailableReason = ImageCapabilityUnavailableReason | 'disabled' | 'runtime_unavailable';
|
|
2782
|
+
|
|
2783
|
+
interface ImageRuntimeCapabilityInspection {
|
|
2784
|
+
readonly generationId: string;
|
|
2785
|
+
readonly enabled: boolean;
|
|
2786
|
+
readonly available: boolean;
|
|
2787
|
+
readonly providerId?: ImageProviderId;
|
|
2788
|
+
readonly model?: string;
|
|
2789
|
+
readonly reason?: ImageRuntimeSafeUnavailableReason;
|
|
2790
|
+
readonly capabilities?: ImageCapabilities;
|
|
2791
|
+
/**
|
|
2792
|
+
* Route-table keys whose ROUTED provider's fresh capability intersection
|
|
2793
|
+
* affirms them (multi-provider-image-generation 6.1: /v1/models lists
|
|
2794
|
+
* routed-model × fresh-evidence). Absent on synthetic/legacy generations —
|
|
2795
|
+
* `listAvailableModels` then falls back to the single-model shape.
|
|
2796
|
+
*/
|
|
2797
|
+
readonly routedModels?: readonly string[];
|
|
2798
|
+
/**
|
|
2799
|
+
* One row per provider named by the routing table (images-settings-tab D2):
|
|
2800
|
+
* availability with a safe reason, the models its own fresh evidence
|
|
2801
|
+
* affirms, and the capability values for evidence-age projection. Absent on
|
|
2802
|
+
* synthetic/legacy generations.
|
|
2803
|
+
*/
|
|
2804
|
+
readonly providers?: readonly ImageRuntimeProviderInspection[];
|
|
2781
2805
|
}
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2806
|
+
/** Per-provider capability inspection row (images-settings-tab D2). */
|
|
2807
|
+
interface ImageRuntimeProviderInspection {
|
|
2808
|
+
readonly providerId: ImageProviderId;
|
|
2809
|
+
readonly available: boolean;
|
|
2810
|
+
readonly reason?: ImageRuntimeSafeUnavailableReason;
|
|
2811
|
+
/** This provider's route keys affirmed by its fresh evidence intersection. */
|
|
2812
|
+
readonly models: readonly string[];
|
|
2813
|
+
readonly capabilities?: ImageCapabilities;
|
|
2788
2814
|
}
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2815
|
+
interface ImageRuntimeResourceStatus {
|
|
2816
|
+
readonly queue: Readonly<{
|
|
2817
|
+
activeJobs: number;
|
|
2818
|
+
waitingJobs: number;
|
|
2819
|
+
activeAccounts: number;
|
|
2820
|
+
waitingAccounts: number;
|
|
2821
|
+
waitingTenants: number;
|
|
2822
|
+
maxConcurrentJobsPerAccount: number;
|
|
2823
|
+
maxQueuedJobs: number;
|
|
2824
|
+
accepting: boolean;
|
|
2825
|
+
shuttingDown: boolean;
|
|
2826
|
+
}>;
|
|
2827
|
+
readonly temporary: Readonly<{
|
|
2828
|
+
activeScopes: number;
|
|
2829
|
+
totalBytes: number;
|
|
2830
|
+
tenantCount: number;
|
|
2831
|
+
maxActiveScopes: number;
|
|
2832
|
+
maxTotalBytes: number;
|
|
2833
|
+
maxTenantBytes: number;
|
|
2834
|
+
}>;
|
|
2835
|
+
readonly storage: Readonly<{
|
|
2836
|
+
mounts: number;
|
|
2837
|
+
retiredMounts: number;
|
|
2838
|
+
referenceEntries: number;
|
|
2839
|
+
referenceBytes: number;
|
|
2840
|
+
referenceTombstones: number;
|
|
2841
|
+
stateCalls: number;
|
|
2842
|
+
stateResponses: number;
|
|
2843
|
+
stateTombstones: number;
|
|
2844
|
+
pendingReferenceDeletes: number;
|
|
2845
|
+
maxReferenceEntries: number;
|
|
2846
|
+
maxReferenceBytes: number;
|
|
2847
|
+
maxTenantReferenceBytes: number;
|
|
2848
|
+
maxStateCalls: number;
|
|
2849
|
+
maxStateResponses: number;
|
|
2850
|
+
}>;
|
|
2797
2851
|
}
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
stateStore: IntegrationStateStore;
|
|
2804
|
-
codexAuthHelper?: CodexAuthHelperConfig;
|
|
2805
|
-
homeDir?: string;
|
|
2852
|
+
interface PreparedImageRuntimeChange {
|
|
2853
|
+
readonly generationId: string;
|
|
2854
|
+
publish(): void;
|
|
2855
|
+
rollback(): void;
|
|
2856
|
+
dispose(): Promise<void>;
|
|
2806
2857
|
}
|
|
2807
|
-
/**
|
|
2808
|
-
declare class
|
|
2809
|
-
private
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
bindIntegrationKey(client: IntegrationClientId, keyId: string): Promise<IntegrationClientStatus>;
|
|
2820
|
-
/** Rotate every Omnicross-managed client binding; user-selected keys remain untouched. */
|
|
2821
|
-
rotateGatewayKey(): Promise<{
|
|
2822
|
-
keyIds: Partial<Record<IntegrationClientId, string>>;
|
|
2823
|
-
}>;
|
|
2824
|
-
/** Resolve the plaintext only for the command-auth helper; callers must not log it. */
|
|
2825
|
-
getIntegrationToken(client: IntegrationClientId): Promise<string>;
|
|
2826
|
-
/** Compatibility alias for callers predating per-client bindings. */
|
|
2827
|
-
getGatewayToken(client?: IntegrationClientId): Promise<string>;
|
|
2828
|
-
private ensureClientKey;
|
|
2829
|
-
private createManagedClientKey;
|
|
2830
|
-
private installedSecret;
|
|
2831
|
-
private rebindInstalledClient;
|
|
2832
|
-
private statusFor;
|
|
2833
|
-
private boundKeyDetails;
|
|
2834
|
-
private retireManagedKeys;
|
|
2835
|
-
private defaultConfigPath;
|
|
2836
|
-
private renderInstalled;
|
|
2858
|
+
/** App-session owner for stable forwarders and generation-pinned work. */
|
|
2859
|
+
declare class ImageRuntimeManager {
|
|
2860
|
+
#private;
|
|
2861
|
+
readonly contributions: ImageApiContributions;
|
|
2862
|
+
constructor(initial?: PreparedImageRuntimeGeneration);
|
|
2863
|
+
prepare(generation: PreparedImageRuntimeGeneration): PreparedImageRuntimeChange;
|
|
2864
|
+
acquireHosted(): Promise<HostedImageRuntimeGenerationLease>;
|
|
2865
|
+
inspectCapability(apiKeyId: string): Promise<ImageRuntimeCapabilityInspection>;
|
|
2866
|
+
listAvailableModels(apiKeyId: string): Promise<readonly string[]>;
|
|
2867
|
+
resourceStatus(): ImageRuntimeResourceStatus | undefined;
|
|
2868
|
+
status(): ImageRuntimeManagerStatus;
|
|
2869
|
+
dispose(): Promise<void>;
|
|
2837
2870
|
}
|
|
2838
2871
|
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
* @module @omnicross/daemon/admin/cliLaunch
|
|
2856
|
-
*/
|
|
2857
|
-
|
|
2858
|
-
/** Injectable PATH probe (tests stub this; default scans `process.env.PATH`). */
|
|
2859
|
-
type PathProbe = (candidate: string) => string | null;
|
|
2860
|
-
/** Open a NEW terminal window running `command [extraArgs…]` with `env` injected. */
|
|
2861
|
-
type TerminalCleanup = () => void;
|
|
2862
|
-
type TerminalOpener = (input: {
|
|
2863
|
-
cli: string;
|
|
2864
|
-
command: string;
|
|
2865
|
-
extraArgs: string[];
|
|
2866
|
-
env: Record<string, string>;
|
|
2867
|
-
cwd?: string;
|
|
2868
|
-
platform: NodeJS.Platform;
|
|
2869
|
-
onFailure?: () => void;
|
|
2870
|
-
}) => void | TerminalCleanup;
|
|
2871
|
-
/**
|
|
2872
|
-
* Injectable shell runner for `POST /cli/:cli/install` (tests stub this; the
|
|
2873
|
-
* default execs the install command with a bounded timeout). Returns the host's
|
|
2874
|
-
* honest install outcome — `error` carries stderr/the failure reason.
|
|
2875
|
-
*/
|
|
2876
|
-
type CommandRunner = (command: string) => Promise<{
|
|
2877
|
-
ok: boolean;
|
|
2878
|
-
error?: string;
|
|
2879
|
-
}>;
|
|
2880
|
-
|
|
2881
|
-
/**
|
|
2882
|
-
* searchAdminApi — the admin API's search surface (search-settings-ui D3 +
|
|
2883
|
-
* search-settings-tab D4).
|
|
2884
|
-
*
|
|
2885
|
-
* Three routes over the daemon's search state, dispatched from `adminApi.ts`'s
|
|
2886
|
-
* `case 'search'`:
|
|
2887
|
-
*
|
|
2888
|
-
* - `GET /admin/api/search/diagnostics` — a READ-ONLY, secret-free, network-free
|
|
2889
|
-
* snapshot: one row per provider the daemon can run (the ONE runtime's
|
|
2890
|
-
* descriptors, plus `unconfigured` rows for known API providers the persisted
|
|
2891
|
-
* config does not name — the doctor's classification), the effective frontend
|
|
2892
|
-
* modes, and the explicit apply semantics (codex immediate, rest restart).
|
|
2893
|
-
* - `POST /admin/api/search/test { providerId }` — ONE live fixed-query check on
|
|
2894
|
-
* a configured provider, classified by the doctor's pure functions. The
|
|
2895
|
-
* machine-facing health probe: it sends exactly `SEARCH_DOCTOR_QUERY`, never a
|
|
2896
|
-
* caller-supplied query, and never returns result content (plan §11.3 — its
|
|
2897
|
-
* contract is the automated doctor's fixed-query discipline).
|
|
2898
|
-
* - `POST /admin/api/search/query { providerId, query }` — the INTERACTIVE
|
|
2899
|
-
* channel for the settings page's per-provider test panel (owner feedback
|
|
2900
|
-
* 2026-09-02): ONE operator-typed query through ONE provider's contribution
|
|
2901
|
-
* built from the PERSISTED config, returning the doctor-classified diagnostic
|
|
2902
|
-
* PLUS the sanitized results. The two disciplines stay separate routes on
|
|
2903
|
-
* purpose: bending `/test` to accept a query would erase the boundary its
|
|
2904
|
-
* pinned tests and consumers depend on.
|
|
2905
|
-
*
|
|
2906
|
-
* SECRET SPINE (all three routes): no response ever carries a configured VALUE,
|
|
2907
|
-
* and a failure response carries only the doctor's SANITIZED error shape — raw
|
|
2908
|
-
* upstream error bodies (which may quote the stored key) never serialize. The
|
|
2909
|
-
* query endpoint additionally sanitizes every returned result field BEFORE
|
|
2910
|
-
* serialization (plan §11.1: search results are untrusted input), and the
|
|
2911
|
-
* operator's query is never logged anywhere.
|
|
2912
|
-
*
|
|
2913
|
-
* The diagnostics dep is OPTIONAL (`AdminApiDeps.searchStatus`): light embedders
|
|
2914
|
-
* that wire no search runtime get 501 for all routes (the voucher/allowance
|
|
2915
|
-
* optionality precedent) rather than a fabricated snapshot.
|
|
2916
|
-
*
|
|
2917
|
-
* @module @omnicross/daemon/admin/searchAdminApi
|
|
2918
|
-
*/
|
|
2919
|
-
|
|
2920
|
-
/**
|
|
2921
|
-
* The daemon search state the admin surface needs. Structurally satisfied by
|
|
2922
|
-
* what `bootstrap.ts` already holds (the ONE runtime + its captured modes);
|
|
2923
|
-
* `testFetch` is a TEST SEAM so route tests can intercept the one live probe
|
|
2924
|
-
* without any network.
|
|
2925
|
-
*/
|
|
2926
|
-
interface SearchAdminRuntimeStatus {
|
|
2927
|
-
/** The daemon's ONE assembled search runtime (provider descriptors). */
|
|
2928
|
-
readonly runtime: SearchRuntime;
|
|
2929
|
-
/** Modes as captured at bootstrap (responses/anthropic are these, live). */
|
|
2930
|
-
readonly modes: SearchFrontendModes;
|
|
2931
|
-
/** TEST SEAM: fetch primitive for the live-test probe. Absent ⇒ real transport. */
|
|
2932
|
-
readonly testFetch?: (url: string, init: RequestInit) => Promise<Response>;
|
|
2872
|
+
type SafeProvider = 'codex-subscription' | 'unknown' | 'other';
|
|
2873
|
+
type SafeModel = 'gpt-image-2' | 'unknown' | 'other';
|
|
2874
|
+
type SafeErrorCode = ImageGenerationErrorCode | 'none' | 'other';
|
|
2875
|
+
type SafeCountOption = 'unknown' | '0' | '1' | '2-4' | '5+';
|
|
2876
|
+
type SafeBooleanOption = boolean | 'unknown';
|
|
2877
|
+
type SafeQuality = 'auto' | 'low' | 'medium' | 'high' | 'unknown' | 'other';
|
|
2878
|
+
type SafeBackground = 'auto' | 'opaque' | 'transparent' | 'unknown' | 'other';
|
|
2879
|
+
type SafeOutputFormat = 'png' | 'jpeg' | 'webp' | 'unknown' | 'other';
|
|
2880
|
+
declare const IMAGE_CONFIGURATION_AUDIT_FIELDS: readonly ["enablement", "provider", "model", "account", "queue", "temporary", "limits", "retention", "storage", "remote", "evidence"];
|
|
2881
|
+
type ImageConfigurationAuditField = typeof IMAGE_CONFIGURATION_AUDIT_FIELDS[number];
|
|
2882
|
+
/** Values are deliberately limited to safe categories and internal generation ids. */
|
|
2883
|
+
interface ImageConfigurationAuditRecord {
|
|
2884
|
+
readonly outcome: 'applied';
|
|
2885
|
+
readonly fields: readonly ImageConfigurationAuditField[];
|
|
2886
|
+
readonly previousGenerationId?: string;
|
|
2887
|
+
readonly generationId?: string;
|
|
2933
2888
|
}
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
* subscription tokens via `credentialStore.getFullConfig()` (also decrypted) —
|
|
2943
|
-
* serialize to ONE bundle JSON, and `sealPack` it under the passphrase-derived
|
|
2944
|
-
* key. The caller returns ONLY the opaque pack; the decrypted bundle + the
|
|
2945
|
-
* passphrase live only in local variables and are never logged.
|
|
2946
|
-
*
|
|
2947
|
-
* IMPORT (`applyImport`): `openPack` decrypts + authenticates (a wrong passphrase
|
|
2948
|
-
* or a tampered pack fails the GCM auth-tag BEFORE any write — atomic). Every
|
|
2949
|
-
* provider is re-validated through `parseProviderInput` and every token block
|
|
2950
|
-
* through `validateTokenBody` (deny-by-default — a malicious blob cannot inject
|
|
2951
|
-
* unknown fields or escape the allowlist). Validation collects ALL rows BEFORE
|
|
2952
|
-
* applying, so a structurally invalid pack does not leave a half-applied state.
|
|
2953
|
-
* Apply merges by provider id (additive default): a new id is added; a colliding
|
|
2954
|
-
* id is skipped (or overwritten with `mode:'overwrite'`). Writes go through the
|
|
2955
|
-
* EXISTING paths (`saveConfig` re-encrypts at-rest under the LOCAL box +
|
|
2956
|
-
* `writeProviderTokens`/`appendProviderAccount`), so imported secrets land
|
|
2957
|
-
* `enc:`-encrypted under the TARGET machine's key — the passphrase key is used
|
|
2958
|
-
* ONLY for transport.
|
|
2959
|
-
*
|
|
2960
|
-
* SECRET SPINE: the export RESPONSE is the opaque pack ONLY; the import RESPONSE
|
|
2961
|
-
* is status-only counts. No decrypted secret + no passphrase ever reaches a
|
|
2962
|
-
* response body or a log here.
|
|
2963
|
-
*
|
|
2964
|
-
* @module @omnicross/daemon/migration/migration
|
|
2965
|
-
*/
|
|
2966
|
-
|
|
2967
|
-
/**
|
|
2968
|
-
* The credential-store surface the migration paths need: the full DECRYPTED read
|
|
2969
|
-
* (export) + the multi-account append (import re-encrypts at-rest). One shape so
|
|
2970
|
-
* `ExportDeps` + `ImportDeps` can be unified into `MigrationDeps` without a
|
|
2971
|
-
* `credentialStore` type conflict.
|
|
2972
|
-
*/
|
|
2973
|
-
interface MigrationCredentialStore extends SubscriptionAccountAppender {
|
|
2974
|
-
getFullConfig(): Promise<AccountTokensConfig>;
|
|
2889
|
+
interface ImageHistogramSnapshot {
|
|
2890
|
+
readonly count: number;
|
|
2891
|
+
readonly sum: number;
|
|
2892
|
+
/** Non-cumulative fixed buckets; `upperBound:null` is the overflow bucket. */
|
|
2893
|
+
readonly buckets: readonly {
|
|
2894
|
+
readonly upperBound: number | null;
|
|
2895
|
+
readonly count: number;
|
|
2896
|
+
}[];
|
|
2975
2897
|
}
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
/** Optional: OpenCodeGo `/v1/usage` refresh (absent on older daemons). */
|
|
2990
|
-
refreshOpenCodeGo?(accountId?: string): Promise<AccountAllowanceSnapshot[]>;
|
|
2991
|
-
/** Optional: Grok CLI-billing refresh (absent on older daemons). */
|
|
2992
|
-
refreshGrok?(accountId?: string): Promise<AccountAllowanceSnapshot[]>;
|
|
2993
|
-
/** Optional: Copilot user-quota refresh (absent on older daemons). */
|
|
2994
|
-
refreshCopilot?(accountId?: string): Promise<AccountAllowanceSnapshot[]>;
|
|
2995
|
-
/** Optional: Gemini Code-Assist quota refresh (absent on older daemons). */
|
|
2996
|
-
refreshGemini?(accountId?: string): Promise<AccountAllowanceSnapshot[]>;
|
|
2997
|
-
removeAccountSnapshot?(providerId: SubscriptionProviderId, accountId: string): void;
|
|
2998
|
-
removeProviderSnapshots?(providerId: SubscriptionProviderId): void;
|
|
2999
|
-
getSchedulingStatus?(): AccountAllowanceSchedulingStatus;
|
|
2898
|
+
interface ImageApiMetricDimensions {
|
|
2899
|
+
readonly endpoint: 'images.generate' | 'images.edit';
|
|
2900
|
+
readonly provider: SafeProvider;
|
|
2901
|
+
readonly model: SafeModel;
|
|
2902
|
+
readonly action: 'generate' | 'edit';
|
|
2903
|
+
readonly quality: SafeQuality;
|
|
2904
|
+
readonly background: SafeBackground;
|
|
2905
|
+
readonly outputFormat: SafeOutputFormat;
|
|
2906
|
+
readonly streaming: SafeBooleanOption;
|
|
2907
|
+
readonly requestedOutputs: SafeCountOption;
|
|
2908
|
+
readonly partialImages: SafeCountOption;
|
|
2909
|
+
readonly terminal: 'completed' | 'failed' | 'cancelled';
|
|
2910
|
+
readonly errorCode: SafeErrorCode;
|
|
3000
2911
|
}
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
2912
|
+
interface ImageExecutionMetricDimensions {
|
|
2913
|
+
readonly provider: SafeProvider;
|
|
2914
|
+
readonly model: SafeModel;
|
|
2915
|
+
readonly action: 'generate' | 'edit' | 'other';
|
|
2916
|
+
readonly quality: SafeQuality;
|
|
2917
|
+
readonly background: SafeBackground;
|
|
2918
|
+
readonly outputFormat: SafeOutputFormat;
|
|
2919
|
+
readonly streaming: SafeBooleanOption;
|
|
2920
|
+
readonly requestedOutputs: SafeCountOption;
|
|
2921
|
+
readonly terminal: 'completed' | 'failed' | 'cancelled' | 'other';
|
|
2922
|
+
readonly errorCode: SafeErrorCode;
|
|
3005
2923
|
}
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
2924
|
+
interface ImageApiMetricSnapshot {
|
|
2925
|
+
readonly dimensions: ImageApiMetricDimensions;
|
|
2926
|
+
readonly requests: number;
|
|
2927
|
+
readonly inputCount?: ImageHistogramSnapshot;
|
|
2928
|
+
readonly inputBytes?: ImageHistogramSnapshot;
|
|
2929
|
+
readonly referenceOutcomes: Readonly<{
|
|
2930
|
+
hits: number;
|
|
2931
|
+
notFound: number;
|
|
2932
|
+
expired: number;
|
|
2933
|
+
failed: number;
|
|
2934
|
+
}>;
|
|
2935
|
+
readonly cleanupOutcomes: Readonly<{
|
|
2936
|
+
completed: number;
|
|
2937
|
+
failed: number;
|
|
2938
|
+
}>;
|
|
3011
2939
|
}
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
2940
|
+
interface ImageExecutionMetricSnapshot {
|
|
2941
|
+
readonly dimensions: ImageExecutionMetricDimensions;
|
|
2942
|
+
readonly executions: number;
|
|
2943
|
+
readonly finalLatencyMs: ImageHistogramSnapshot;
|
|
2944
|
+
readonly queueWaitMs?: ImageHistogramSnapshot;
|
|
2945
|
+
readonly generationDurationMs?: ImageHistogramSnapshot;
|
|
2946
|
+
readonly firstPartialLatencyMs?: ImageHistogramSnapshot;
|
|
2947
|
+
readonly inputCount: ImageHistogramSnapshot;
|
|
2948
|
+
readonly inputBytes: ImageHistogramSnapshot;
|
|
2949
|
+
readonly outputCount: ImageHistogramSnapshot;
|
|
2950
|
+
readonly outputBytes: ImageHistogramSnapshot;
|
|
2951
|
+
readonly retryCount?: ImageHistogramSnapshot;
|
|
2952
|
+
readonly authRefreshCount?: ImageHistogramSnapshot;
|
|
2953
|
+
readonly referenceSaveCount?: ImageHistogramSnapshot;
|
|
2954
|
+
readonly retentionRollbackFailures?: ImageHistogramSnapshot;
|
|
3020
2955
|
}
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
}): Promise<ProviderKeyQuota | null>;
|
|
2956
|
+
interface ImageObservabilitySnapshot {
|
|
2957
|
+
readonly apiRequests: readonly ImageApiMetricSnapshot[];
|
|
2958
|
+
readonly executions: readonly ImageExecutionMetricSnapshot[];
|
|
2959
|
+
readonly configurationChanges: readonly ImageConfigurationAuditRecord[];
|
|
2960
|
+
readonly overflow: Readonly<{
|
|
2961
|
+
apiRecords: number;
|
|
2962
|
+
telemetryRecords: number;
|
|
2963
|
+
}>;
|
|
3030
2964
|
}
|
|
3031
|
-
interface
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
resourceStatus(): ImageRuntimeResourceStatus | undefined;
|
|
2965
|
+
interface ImageObservabilityOptions {
|
|
2966
|
+
/** Hard cap applied independently to API and execution dimension maps. */
|
|
2967
|
+
readonly maxDimensionSets?: number;
|
|
3035
2968
|
}
|
|
3036
|
-
/**
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
readonly
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
/**
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
/**
|
|
3080
|
-
|
|
3081
|
-
/**
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
* `appendProviderAccount` (NOT on the least-authority `SubscriptionTokenWriter`).
|
|
3131
|
-
* A minimal interface, NOT the full read-capable store, so no token-returning
|
|
3132
|
-
* read is reachable. Wired from the concrete `credentialStore` in `bootstrap.ts`.
|
|
3133
|
-
*/
|
|
3134
|
-
readonly subscriptionAccountAppender: SubscriptionAccountAppender;
|
|
3135
|
-
/**
|
|
3136
|
-
* Codex interactive-OAuth flow store (app-parity-2 child 5). Tracks the async
|
|
3137
|
-
* loopback sign-in's polled status (token-free); only ONE codex login may be in
|
|
3138
|
-
* flight (port 1455 is one resource). Wired in `bootstrap.ts`.
|
|
3139
|
-
*/
|
|
3140
|
-
readonly codexSessions: CodexOAuthSessionStore;
|
|
3141
|
-
/**
|
|
3142
|
-
* Kimi interactive-OAuth flow store (device code). Same token-free polled
|
|
3143
|
-
* shape as codex; one sign-in at a time. Wired in `bootstrap.ts`.
|
|
3144
|
-
*/
|
|
3145
|
-
readonly kimiSessions: CodexOAuthSessionStore;
|
|
3146
|
-
/**
|
|
3147
|
-
* Grok interactive-OAuth flow store (device code). Same token-free polled
|
|
3148
|
-
* shape as codex; one sign-in at a time. Wired in `bootstrap.ts`.
|
|
3149
|
-
*/
|
|
3150
|
-
readonly grokSessions: CodexOAuthSessionStore;
|
|
3151
|
-
/**
|
|
3152
|
-
* Copilot interactive-OAuth flow store (device code). Same token-free
|
|
3153
|
-
* polled shape; one sign-in at a time. Wired in `bootstrap.ts`.
|
|
3154
|
-
*/
|
|
3155
|
-
readonly copilotSessions: CodexOAuthSessionStore;
|
|
3156
|
-
/**
|
|
3157
|
-
* Codex loopback listener (app-parity-2 child 5) — defaults to `awaitLoopbackCode`
|
|
3158
|
-
* (binds 127.0.0.1:1455) in `bootstrap.ts`; tests inject a mock so no real port
|
|
3159
|
-
* is bound. The captured code crosses to the daemon ONLY (never the client).
|
|
3160
|
-
*/
|
|
3161
|
-
readonly codexAwaitLoopback: CodexLoopbackFn;
|
|
3162
|
-
/**
|
|
3163
|
-
* Migration credential-store handle (app-parity child 6, design D2/D3). The
|
|
3164
|
-
* export gather needs `getFullConfig()` (full DECRYPTED tokens, in-memory only —
|
|
3165
|
-
* the pack is the only thing that leaves) and import needs `appendProviderAccount`
|
|
3166
|
-
* (multi-account append + re-encrypt at-rest). Confined to the migration
|
|
3167
|
-
* handlers (which seal/validate everything); never reached by a GET handler.
|
|
3168
|
-
* Wired from the concrete `credentialStore` in `bootstrap.ts`.
|
|
3169
|
-
*/
|
|
3170
|
-
readonly migrationCredentialStore: MigrationCredentialStore;
|
|
3171
|
-
/**
|
|
3172
|
-
* Usage-stats query facade (usage-pricing child) — delegates to the JSONL
|
|
3173
|
-
* usage-event store. Aggregates only; carries no key material.
|
|
3174
|
-
*/
|
|
3175
|
-
readonly usageRecorder: UsageRecorder;
|
|
3176
|
-
/** Pricing engine (upsert / source refresh / conflict resolution). */
|
|
3177
|
-
readonly pricingEngine: PricingEngine;
|
|
2969
|
+
/** Process-local, metadata-only aggregation for HTTP and hosted Images work. */
|
|
2970
|
+
declare class ImageObservability {
|
|
2971
|
+
#private;
|
|
2972
|
+
readonly telemetrySink: ImageTelemetrySink;
|
|
2973
|
+
readonly audit: (record: ImageApiAuditRecord) => void;
|
|
2974
|
+
constructor(options?: ImageObservabilityOptions);
|
|
2975
|
+
recordApiAudit(record: ImageApiAuditRecord): void;
|
|
2976
|
+
recordTelemetry(record: ImageTelemetryRecord): void;
|
|
2977
|
+
recordConfigurationAudit(record: ImageConfigurationAuditRecord): void;
|
|
2978
|
+
snapshot(): ImageObservabilitySnapshot;
|
|
2979
|
+
reset(): void;
|
|
2980
|
+
}
|
|
2981
|
+
|
|
2982
|
+
interface PreparedServerConfigChange {
|
|
2983
|
+
/** Publish the prepared snapshot. Implementations should make this an infallible swap. */
|
|
2984
|
+
publish(): void | Promise<void>;
|
|
2985
|
+
/** Restore the exact runtime snapshot that preceded publish. */
|
|
2986
|
+
rollback(): void | Promise<void>;
|
|
2987
|
+
/** Release an unpublished or rolled-back replacement. */
|
|
2988
|
+
dispose(): void | Promise<void>;
|
|
2989
|
+
}
|
|
2990
|
+
|
|
2991
|
+
/**
|
|
2992
|
+
* autoDisableStore.ts — the daemon's PROCESS-IN-MEMORY auto-disable store.
|
|
2993
|
+
*
|
|
2994
|
+
* A DB-backed embedder can persist 401/403 auto-disable durably so a UI can
|
|
2995
|
+
* render per-key health. The daemon has no DB, and its only
|
|
2996
|
+
* durable layer is `config.json` — but writing auto-disable back there in v1
|
|
2997
|
+
* would (1) cause write amplification under a 401 storm and (2) collide with
|
|
2998
|
+
* the at-rest encryption schema that owns the `apiKeys[]` on-disk
|
|
2999
|
+
* format. So v1 records auto-disable IN MEMORY only:
|
|
3000
|
+
* - `markAutoDisabled(keyId, status, at)` records `{ status, at, reason }`,
|
|
3001
|
+
* - `isDisabled(keyId)` / `get(keyId)` read it back,
|
|
3002
|
+
* - `loadPoolKeys` reads this store and flips a flagged key's `enabled` to
|
|
3003
|
+
* `false`, so `getAvailableKeys` skips it within this process lifetime.
|
|
3004
|
+
*
|
|
3005
|
+
* Restart resets the store (the honest v1 boundary — see spec). Persistence
|
|
3006
|
+
* (encrypted write-back) is a child-3 follow-up.
|
|
3007
|
+
*
|
|
3008
|
+
* @module @omnicross/daemon/pool/autoDisableStore
|
|
3009
|
+
*/
|
|
3010
|
+
/** One in-memory auto-disable record for a pool key. */
|
|
3011
|
+
interface AutoDisableRecord {
|
|
3012
|
+
/** The HTTP status that triggered the disable (401/403). */
|
|
3013
|
+
status: number;
|
|
3014
|
+
/** Epoch-ms when the disable was recorded. */
|
|
3015
|
+
at: number;
|
|
3016
|
+
/** Always `'auth_failure'` in v1 (the only auto-disable trigger). */
|
|
3017
|
+
reason: 'auth_failure';
|
|
3018
|
+
}
|
|
3019
|
+
/**
|
|
3020
|
+
* A process-lifetime in-memory store of auto-disabled pool keys, keyed by the
|
|
3021
|
+
* pool key id. Constructed once in `buildDaemon` and injected as the pool's
|
|
3022
|
+
* `disableKey` / `markAutoDisabled` sinks AND read by `loadPoolKeys`.
|
|
3023
|
+
*/
|
|
3024
|
+
declare class AutoDisableStore {
|
|
3025
|
+
private readonly records;
|
|
3026
|
+
/** Record (or overwrite) an auth-failure auto-disable for `keyId`. */
|
|
3027
|
+
markAutoDisabled(keyId: string, status: number, at: number): void;
|
|
3028
|
+
/** Whether `keyId` is currently auto-disabled in this process. */
|
|
3029
|
+
isDisabled(keyId: string): boolean;
|
|
3030
|
+
/** Read the auto-disable record for `keyId`, or `undefined` when healthy. */
|
|
3031
|
+
get(keyId: string): AutoDisableRecord | undefined;
|
|
3032
|
+
/** Clear all records (tests / teardown). */
|
|
3033
|
+
clear(): void;
|
|
3034
|
+
}
|
|
3035
|
+
|
|
3036
|
+
/**
|
|
3037
|
+
* ConfigFileProviderConfigSource — the daemon's file-backed `ProviderConfigSource`
|
|
3038
|
+
* port impl: an embedder of `@omnicross/core`'s provider catalog port.
|
|
3039
|
+
*
|
|
3040
|
+
* The daemon's `config.json` provider rows ARE the catalog. Of the port's ten
|
|
3041
|
+
* methods, FOUR are real (the ones the BYO proxy/outbound path actually hits):
|
|
3042
|
+
* - `getProvider(id)` — map a `DaemonProviderConfig` row to an `LLMProvider`.
|
|
3043
|
+
* - `getTransformerService()` — a single `TransformerService` seeded by
|
|
3044
|
+
* `registerBuiltinTransformers` in the ctor.
|
|
3045
|
+
* - `getMainTransformer(id)` — the transformer for the provider's target wire
|
|
3046
|
+
* format (anthropic → AnthropicTransformer, gemini → GeminiTransformer,
|
|
3047
|
+
* openai → null/identity), mirroring the host `AgentModelsManager` switch.
|
|
3048
|
+
* `resolveProviderChain` unshifts this FORMAT-FIRST into the provider chain.
|
|
3049
|
+
* - `resolveTransformerChain(id, model)` — the provider's CUSTOM
|
|
3050
|
+
* `transformer.use[]` chain (app-parity-2 child 2: ENFORCED). The format
|
|
3051
|
+
* transformer is NOT included here (getMainTransformer supplies it,
|
|
3052
|
+
* format-first); no `transformer.use[]` → empty chain.
|
|
3053
|
+
*
|
|
3054
|
+
* The remaining SIX are minimal sensible stubs (never hit on the BYO single-key
|
|
3055
|
+
* path — the boot smoke test is the proof).
|
|
3056
|
+
*
|
|
3057
|
+
* @module @omnicross/daemon/ports/ConfigFileProviderConfigSource
|
|
3058
|
+
*/
|
|
3059
|
+
|
|
3060
|
+
declare class ConfigFileProviderConfigSource implements ProviderConfigSource {
|
|
3061
|
+
private readonly providers;
|
|
3062
|
+
private readonly transformerService;
|
|
3178
3063
|
/**
|
|
3179
|
-
*
|
|
3180
|
-
*
|
|
3064
|
+
* Optional reload-hook (key-pool change, design D4). A no-type-coupling
|
|
3065
|
+
* callback invoked at the END of `reload(...)`. `buildDaemon` injects
|
|
3066
|
+
* `() => pool.invalidateCache()` so the `ApiKeyPoolService.keyCache` is
|
|
3067
|
+
* flushed after a hot-reload swaps the catalog — WITHOUT this port ever
|
|
3068
|
+
* importing/depending on `ApiKeyPoolService`. Absent = no-op (single-key
|
|
3069
|
+
* boots that never construct a pool stay byte-identical).
|
|
3181
3070
|
*/
|
|
3182
|
-
|
|
3071
|
+
private reloadHook;
|
|
3072
|
+
constructor(config: DaemonConfig);
|
|
3183
3073
|
/**
|
|
3184
|
-
*
|
|
3185
|
-
*
|
|
3186
|
-
*
|
|
3074
|
+
* Register a callback fired after every `reload(...)`. Used by `buildDaemon`
|
|
3075
|
+
* to invalidate the pool's keyCache on a hot-reload. The port stays ignorant
|
|
3076
|
+
* of what the callback does (no pool type dependency).
|
|
3187
3077
|
*/
|
|
3188
|
-
|
|
3189
|
-
/** Injectable PATH probe for CLI detection (tests fake "installed"). */
|
|
3190
|
-
readonly cliPathProbe?: PathProbe;
|
|
3078
|
+
setReloadHook(fn: () => void): void;
|
|
3191
3079
|
/**
|
|
3192
|
-
*
|
|
3193
|
-
*
|
|
3194
|
-
*
|
|
3080
|
+
* Read the live (post-reload) provider row for `providerId`, or `undefined`.
|
|
3081
|
+
* Exposed so the pool's `loadKeys` reads the SAME live catalog Map this port
|
|
3082
|
+
* serves (so a hot-reload is observed on the next load after `invalidateCache`).
|
|
3195
3083
|
*/
|
|
3196
|
-
|
|
3197
|
-
/**
|
|
3198
|
-
|
|
3084
|
+
getProviderRow(providerId: string): DaemonProviderConfig | undefined;
|
|
3085
|
+
/** Await the built-in transformer registration (tests await this before dispatch). */
|
|
3086
|
+
ready(): Promise<void>;
|
|
3199
3087
|
/**
|
|
3200
|
-
*
|
|
3201
|
-
*
|
|
3202
|
-
*
|
|
3203
|
-
*
|
|
3088
|
+
* Replace the live provider catalog in place (additive — does NOT touch the
|
|
3089
|
+
* ten port methods, the seeded `TransformerService`, or `ready()`). Called by
|
|
3090
|
+
* the admin API after a provider POST/PUT/DELETE persists `config.json`, so the
|
|
3091
|
+
* next outbound request sees the new catalog WITHOUT a daemon restart. The Map
|
|
3092
|
+
* swap is synchronous; an in-flight request keeps its already-resolved
|
|
3093
|
+
* provider (no locking needed for a single-operator local daemon).
|
|
3204
3094
|
*/
|
|
3205
|
-
|
|
3095
|
+
reload(config: DaemonConfig): void;
|
|
3096
|
+
/** Clear + repopulate the private providers Map from a fresh provider list. */
|
|
3097
|
+
setProviders(providers: readonly DaemonProviderConfig[]): void;
|
|
3098
|
+
getProvider(id: string): Promise<LLMProvider | null>;
|
|
3099
|
+
getTransformerService(): TransformerService | undefined;
|
|
3100
|
+
getMainTransformer(providerId: string): Promise<Transformer | null>;
|
|
3101
|
+
resolveTransformerChain(providerId: string, _model?: string): Promise<ResolvedTransformerChain>;
|
|
3102
|
+
resolveRoutedModel(): Promise<null>;
|
|
3103
|
+
resolveEffectiveModels(): Promise<{
|
|
3104
|
+
background?: string;
|
|
3105
|
+
vision?: string;
|
|
3106
|
+
}>;
|
|
3107
|
+
getAgentDefaultModels(): Promise<AgentDefaultModels>;
|
|
3108
|
+
hasVisionCapability(): Promise<boolean>;
|
|
3109
|
+
getGlobalModelParameters(): Promise<GlobalModelParameters>;
|
|
3110
|
+
getDiscoveredModelMaxTokens(): Promise<number | undefined>;
|
|
3206
3111
|
}
|
|
3207
|
-
/**
|
|
3208
|
-
* Dispatch one `/admin/api/*` request. `path` is the already-extracted pathname
|
|
3209
|
-
* (no query). The auth gate has already run in `AdminServer`.
|
|
3210
|
-
*/
|
|
3211
|
-
declare function handleAdminApi(req: http.IncomingMessage, res: http.ServerResponse, path: string, deps: AdminApiDeps): Promise<void>;
|
|
3212
3112
|
|
|
3213
3113
|
/**
|
|
3214
|
-
*
|
|
3215
|
-
*
|
|
3216
|
-
*
|
|
3217
|
-
* A SEPARATE `node:http` listener distinct from core's outbound `/v1/*` server
|
|
3218
|
-
* (port 8765, untouched). Mirrors `OutboundApiServer`'s proven shape:
|
|
3219
|
-
* - `http.createServer`, default bind `127.0.0.1` (or `0.0.0.0` when
|
|
3220
|
-
* `networkBinding`), `listen` with `EADDRINUSE`→ephemeral(port 0) fallback,
|
|
3221
|
-
* - `getStatus()` (running / bound port / dashboard URL), `start` / `stop`.
|
|
3222
|
-
* Default admin port 8766.
|
|
3114
|
+
* JsonApiServerSettingsStore — the daemon's file-backed `ApiServerSettingsStore`
|
|
3115
|
+
* port impl.
|
|
3223
3116
|
*
|
|
3224
|
-
*
|
|
3225
|
-
*
|
|
3226
|
-
*
|
|
3227
|
-
*
|
|
3228
|
-
*
|
|
3229
|
-
*
|
|
3230
|
-
* `admin.token` → `start` REFUSES to bind (logs + stays down, fail closed).
|
|
3117
|
+
* The serving core persists the outbound-API server config (`{ enabled,
|
|
3118
|
+
* networkBinding, endpoints, port }`) under a SINGLE settings key
|
|
3119
|
+
* (`OUTBOUND_API_SERVER_CONFIG_KEY === 'outboundApiServer.config'`). Here that
|
|
3120
|
+
* store is the daemon's `config.json` `server`
|
|
3121
|
+
* field. `loadServerConfig(store)` / `saveServerConfig(store, cfg)` (core)
|
|
3122
|
+
* normalize + persist through this 2-method surface.
|
|
3231
3123
|
*
|
|
3232
|
-
*
|
|
3233
|
-
*
|
|
3234
|
-
* UI (`handleUiStatic`, from `@omnicross/ui`); everything else → `404`.
|
|
3124
|
+
* Only the one outbound-API key is ever read/written — any other key is a no-op
|
|
3125
|
+
* miss (returns `undefined`) so the surface stays honest about what it backs.
|
|
3235
3126
|
*
|
|
3236
|
-
* @module @omnicross/daemon/
|
|
3127
|
+
* @module @omnicross/daemon/ports/JsonApiServerSettingsStore
|
|
3237
3128
|
*/
|
|
3238
3129
|
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
/**
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3130
|
+
interface JsonSettingsDocumentSnapshot {
|
|
3131
|
+
readonly existed: boolean;
|
|
3132
|
+
/** Raw persisted bytes; may contain encrypted secrets and must never enter a DTO or log. */
|
|
3133
|
+
readonly bytes?: Uint8Array;
|
|
3134
|
+
}
|
|
3135
|
+
type AtomicDocumentReplace = (targetPath: string, contents: Uint8Array) => void;
|
|
3136
|
+
declare class JsonApiServerSettingsStore implements ApiServerSettingsStore {
|
|
3137
|
+
private readonly configPath;
|
|
3138
|
+
private readonly box;
|
|
3139
|
+
private readonly atomicReplace;
|
|
3249
3140
|
/**
|
|
3250
|
-
*
|
|
3251
|
-
*
|
|
3252
|
-
*
|
|
3141
|
+
* @param configPath the daemon config.json whose `server` field is backed.
|
|
3142
|
+
* @param box OPTIONAL at-rest `SecretBox` (upstream-proxy). When set, the
|
|
3143
|
+
* `server.proxy.*` passwords are encrypted-on-`set` /
|
|
3144
|
+
* decrypted-on-`get` (the settings-store path is otherwise not
|
|
3145
|
+
* secret-aware — every OTHER server field is non-secret). Null
|
|
3146
|
+
* ⇒ passthrough (legacy/pure tests unchanged).
|
|
3253
3147
|
*/
|
|
3254
|
-
|
|
3148
|
+
constructor(configPath: string, box?: SecretBox | null, atomicReplace?: AtomicDocumentReplace);
|
|
3149
|
+
get<T = unknown>(key: string): Promise<T | undefined>;
|
|
3150
|
+
set<T = unknown>(key: string, value: T): Promise<void>;
|
|
3151
|
+
/** Capture the exact prior document for an admin transaction rollback. */
|
|
3152
|
+
captureDocumentSnapshot(): JsonSettingsDocumentSnapshot;
|
|
3153
|
+
/** Restore exact prior bytes (including unrelated fields and encrypted secrets). */
|
|
3154
|
+
restoreDocumentSnapshot(snapshot: JsonSettingsDocumentSnapshot): void;
|
|
3155
|
+
/** Encrypt the proxy passwords + webhook + billing secrets before persisting (no-op without a box). */
|
|
3156
|
+
private encryptSecrets;
|
|
3157
|
+
/** Decrypt the proxy passwords + webhook + billing secrets on read (no-op without a box). */
|
|
3158
|
+
private decryptSecrets;
|
|
3159
|
+
/** Read the config.json, tolerating a missing/corrupt file (→ empty shape). */
|
|
3160
|
+
private readFile;
|
|
3161
|
+
}
|
|
3162
|
+
|
|
3163
|
+
/**
|
|
3164
|
+
* JsonPricingStore — the daemon's file-backed `PricingStore` port impl.
|
|
3165
|
+
*
|
|
3166
|
+
* Durable storage for the model pricing table, backed by a pretty-printed json
|
|
3167
|
+
* file (a sibling of `config.json`, `pricing.json` by convention) holding a
|
|
3168
|
+
* `PricingEntry[]`. Reads tolerate a missing/corrupt file (→ empty table);
|
|
3169
|
+
* every mutation rewrites the full array (the table is at most a few thousand
|
|
3170
|
+
* rows — same trade-off as `JsonOutboundKeyDb`). The table starts EMPTY: no
|
|
3171
|
+
* seeding — the first pricing-source refresh (or a manual upsert) populates it.
|
|
3172
|
+
*
|
|
3173
|
+
* Beyond the core port, the store exposes a STORE-LOCAL `delete` (the port is
|
|
3174
|
+
* frozen; the admin DELETE route calls the concrete class and then invalidates
|
|
3175
|
+
* the engine cache).
|
|
3176
|
+
*
|
|
3177
|
+
* @module @omnicross/daemon/ports/JsonPricingStore
|
|
3178
|
+
*/
|
|
3179
|
+
|
|
3180
|
+
declare class JsonPricingStore implements PricingStore {
|
|
3181
|
+
private readonly pricingPath;
|
|
3182
|
+
constructor(pricingPath: string);
|
|
3255
3183
|
/**
|
|
3256
|
-
*
|
|
3257
|
-
*
|
|
3258
|
-
*
|
|
3259
|
-
*
|
|
3260
|
-
*
|
|
3184
|
+
* Return whether the durable snapshot can actually serve at least one price.
|
|
3185
|
+
*
|
|
3186
|
+
* This intentionally checks the file itself instead of relying on refresh
|
|
3187
|
+
* metadata: a recent `lastSuccessAt` must not hide a deleted, truncated, or
|
|
3188
|
+
* otherwise unusable pricing table after a crash or manual file edit.
|
|
3261
3189
|
*/
|
|
3262
|
-
|
|
3190
|
+
hasUsableSnapshot(): boolean;
|
|
3191
|
+
getAll(): Promise<PricingEntry[]>;
|
|
3263
3192
|
/**
|
|
3264
|
-
*
|
|
3265
|
-
* (
|
|
3266
|
-
*
|
|
3267
|
-
*
|
|
3268
|
-
*
|
|
3193
|
+
* Insert or update one row keyed (providerId, modelId). `asUserEdit` stamps
|
|
3194
|
+
* user provenance (source 'user', userEdited, editedAt now) so the row is
|
|
3195
|
+
* protected from auto-overwrite during source refreshes; a non-user upsert
|
|
3196
|
+
* stamps source 'litellm' and clears nothing it should not (a plain source
|
|
3197
|
+
* upsert through this method overwrites the row wholesale).
|
|
3269
3198
|
*/
|
|
3270
|
-
|
|
3271
|
-
/** Metadata-only audit aggregate used by the overview error-rate metric. */
|
|
3272
|
-
auditStatsReader?: AuditStatsReader;
|
|
3199
|
+
upsert(input: PricingEntryInput, asUserEdit: boolean): Promise<PricingEntry>;
|
|
3273
3200
|
/**
|
|
3274
|
-
*
|
|
3275
|
-
*
|
|
3201
|
+
* Apply a batch fetched from a pricing source. Rows whose local copy is
|
|
3202
|
+
* user-edited are NOT applied — they come back as `{ current, incoming }`
|
|
3203
|
+
* conflicts; everything else is upserted with the supplied automatic source.
|
|
3204
|
+
* ONE file write for the whole batch.
|
|
3276
3205
|
*/
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
3206
|
+
bulkApplyFromSource(entries: PricingEntryInput[], source?: AutomaticPricingSource): Promise<{
|
|
3207
|
+
applied: PricingEntry[];
|
|
3208
|
+
conflicts: Array<{
|
|
3209
|
+
current: PricingEntry;
|
|
3210
|
+
incoming: PricingEntryInput;
|
|
3211
|
+
}>;
|
|
3212
|
+
}>;
|
|
3280
3213
|
/**
|
|
3281
|
-
*
|
|
3282
|
-
*
|
|
3283
|
-
* returns secret-free total/delivered/pending counts. Absent ⇒ zeroed counts.
|
|
3214
|
+
* Apply per-row conflict decisions: 'overwrite' replaces the local row with
|
|
3215
|
+
* the incoming values (clearing the user-edited mark), 'skip' counts only.
|
|
3284
3216
|
*/
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
running: boolean;
|
|
3290
|
-
/** Actual bound port (0 when not running). */
|
|
3291
|
-
port: number;
|
|
3292
|
-
/** The dashboard URL (loopback or LAN base), or null when not running. */
|
|
3293
|
-
url: string | null;
|
|
3294
|
-
}
|
|
3295
|
-
declare class AdminServer {
|
|
3296
|
-
private readonly deps;
|
|
3297
|
-
private server;
|
|
3298
|
-
private boundPort;
|
|
3299
|
-
private boundAddr;
|
|
3300
|
-
/** Control Panel dist dir (resolved once at first request; null = no UI). */
|
|
3301
|
-
private uiDist;
|
|
3302
|
-
constructor(deps: AdminServerDeps);
|
|
3217
|
+
applyResolutions(resolutions: Array<{
|
|
3218
|
+
incoming: PricingEntryInput;
|
|
3219
|
+
action: 'overwrite' | 'skip';
|
|
3220
|
+
}>): Promise<PricingResolution>;
|
|
3303
3221
|
/**
|
|
3304
|
-
*
|
|
3305
|
-
*
|
|
3306
|
-
*
|
|
3222
|
+
* STORE-LOCAL (not on the core port): remove one row. Returns whether a row
|
|
3223
|
+
* was actually removed. The admin DELETE handler calls this then invalidates
|
|
3224
|
+
* the engine cache.
|
|
3307
3225
|
*/
|
|
3308
|
-
|
|
3309
|
-
/**
|
|
3310
|
-
private
|
|
3311
|
-
/**
|
|
3312
|
-
private
|
|
3313
|
-
private
|
|
3314
|
-
/**
|
|
3315
|
-
private
|
|
3316
|
-
/** Stop the listener and release the port. */
|
|
3317
|
-
stop(): Promise<void>;
|
|
3318
|
-
/** A live status snapshot. */
|
|
3319
|
-
getStatus(): AdminServerStatus;
|
|
3226
|
+
delete(providerId: string, modelId: string): Promise<boolean>;
|
|
3227
|
+
/** Upsert into `rows` IN PLACE (no write) and return the resulting entry. */
|
|
3228
|
+
private applyUpsert;
|
|
3229
|
+
/** Read the pricing rows, tolerating a missing/corrupt file (→ empty list). */
|
|
3230
|
+
private readRows;
|
|
3231
|
+
private writeRows;
|
|
3232
|
+
/** Isolated for deterministic failure testing; never removes the target. */
|
|
3233
|
+
private replaceFile;
|
|
3320
3234
|
}
|
|
3321
3235
|
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
readonly imagesRoot: string;
|
|
3326
|
-
readonly temporaryRoot: string;
|
|
3327
|
-
readonly durableRoot: string;
|
|
3328
|
-
readonly artifactsRoot: string;
|
|
3329
|
-
readonly stateRoot: string;
|
|
3330
|
-
readonly evidenceRoot: string;
|
|
3331
|
-
readonly mountManifestRoot: string;
|
|
3332
|
-
readonly mountManifestPath: string;
|
|
3333
|
-
}
|
|
3334
|
-
interface ImageRootValidationOptions {
|
|
3335
|
-
readonly label?: string;
|
|
3336
|
-
readonly processDirectory?: string;
|
|
3337
|
-
readonly userHome?: string;
|
|
3338
|
-
readonly temporaryDirectory?: string;
|
|
3339
|
-
}
|
|
3340
|
-
interface CreateDaemonImagePathResolverOptions extends ImageRootValidationOptions {
|
|
3341
|
-
readonly configPath: string;
|
|
3342
|
-
readonly storageRoot?: string;
|
|
3343
|
-
}
|
|
3344
|
-
interface VerifiedDaemonImagePath {
|
|
3345
|
-
readonly area: DaemonImagePathArea;
|
|
3346
|
-
readonly absolutePath: string;
|
|
3347
|
-
readonly kind: 'opaque-file' | 'opaque-directory' | 'mount-manifest';
|
|
3348
|
-
}
|
|
3349
|
-
/**
|
|
3350
|
-
* Owns all daemon Images filesystem names. Callers receive opaque capabilities,
|
|
3351
|
-
* never a filename-accepting delete primitive; destructive methods revalidate
|
|
3352
|
-
* the root identity, descendant relationship, basename, and symlink state.
|
|
3353
|
-
*/
|
|
3354
|
-
declare class DaemonImagePathResolver {
|
|
3355
|
-
#private;
|
|
3356
|
-
readonly paths: DaemonImagePaths;
|
|
3357
|
-
constructor(options: CreateDaemonImagePathResolverOptions);
|
|
3358
|
-
createOpaqueFile(area: Exclude<DaemonImagePathArea, 'temporary' | 'mountManifest'>, format?: 'bin' | 'json' | 'tmp'): VerifiedDaemonImagePath;
|
|
3359
|
-
createOpaqueDirectory(area?: Exclude<DaemonImagePathArea, 'mountManifest'>): VerifiedDaemonImagePath;
|
|
3360
|
-
mountManifest(): VerifiedDaemonImagePath;
|
|
3361
|
-
/** Revalidate and return one internal root for store-local bounded I/O. */
|
|
3362
|
-
verifiedRoot(area: DaemonImagePathArea): string;
|
|
3363
|
-
/** Revalidate immediately before unlinking a resolver-issued file capability. */
|
|
3364
|
-
removeFile(target: VerifiedDaemonImagePath): void;
|
|
3365
|
-
/** Only empty opaque directories may be removed until the owned-marker layer is composed. */
|
|
3366
|
-
removeEmptyDirectory(target: VerifiedDaemonImagePath): void;
|
|
3367
|
-
private issue;
|
|
3368
|
-
private verifyDestructiveTarget;
|
|
3236
|
+
interface CodexAuthHelperConfig {
|
|
3237
|
+
command: string;
|
|
3238
|
+
args: string[];
|
|
3369
3239
|
}
|
|
3370
3240
|
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
readonly replaceManifest?: (targetPath: string, contents: Uint8Array) => void;
|
|
3241
|
+
type IntegrationClientId = 'codex' | 'claude';
|
|
3242
|
+
type IntegrationKeyOwnership = 'managed' | 'selected';
|
|
3243
|
+
/** Secret-free pointer to an access key. The plaintext remains in the encrypted key store. */
|
|
3244
|
+
interface IntegrationKeyBinding {
|
|
3245
|
+
keyId: string;
|
|
3246
|
+
ownership: IntegrationKeyOwnership;
|
|
3378
3247
|
}
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3248
|
+
/** Redacted access-key state exposed by the integrations admin API. */
|
|
3249
|
+
interface IntegrationKeyBindingStatus {
|
|
3250
|
+
id: string;
|
|
3251
|
+
name: string;
|
|
3252
|
+
keyPrefix: string;
|
|
3253
|
+
ownership: IntegrationKeyOwnership;
|
|
3254
|
+
revealable: boolean;
|
|
3255
|
+
enabled: boolean;
|
|
3256
|
+
revoked: boolean;
|
|
3257
|
+
allowedEndpoints: OutboundPermission[];
|
|
3258
|
+
requiredEndpoints: OutboundPermission[];
|
|
3259
|
+
loopbackOnly: boolean;
|
|
3390
3260
|
}
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
}>;
|
|
3402
|
-
statusWithTtl(ttlMs: number): FileCodexImageCapabilityEvidenceStatus;
|
|
3261
|
+
type IntegrationStatusKind = 'not-installed' | 'enabled' | 'configuration-drift' | 'configuration-missing' | 'key-missing';
|
|
3262
|
+
interface IntegrationClientStatus {
|
|
3263
|
+
client: IntegrationClientId;
|
|
3264
|
+
status: IntegrationStatusKind;
|
|
3265
|
+
configPath: string;
|
|
3266
|
+
installedAt?: number;
|
|
3267
|
+
gatewayBaseUrl?: string;
|
|
3268
|
+
message?: string;
|
|
3269
|
+
/** Selected key metadata only; never contains plaintext or the encrypted envelope. */
|
|
3270
|
+
key?: IntegrationKeyBindingStatus;
|
|
3403
3271
|
}
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
readonly entriesRemoved: number;
|
|
3413
|
-
readonly bytesRemoved: number;
|
|
3414
|
-
}>;
|
|
3415
|
-
status(): FileCodexImageCapabilityEvidenceStatus;
|
|
3416
|
-
ttlMs(): number;
|
|
3417
|
-
/** Lifecycle-symmetric no-op; physical safety no longer depends on local leases. */
|
|
3418
|
-
dispose(): void;
|
|
3272
|
+
interface IntegrationChangePlan {
|
|
3273
|
+
client: IntegrationClientId;
|
|
3274
|
+
configPath: string;
|
|
3275
|
+
action: 'install' | 'none' | 'repair';
|
|
3276
|
+
canApply: boolean;
|
|
3277
|
+
/** Redacted logical fields only; never file contents or credential values. */
|
|
3278
|
+
changes: string[];
|
|
3279
|
+
warnings: string[];
|
|
3419
3280
|
}
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3281
|
+
interface IntegrationInstallRecord {
|
|
3282
|
+
client: IntegrationClientId;
|
|
3283
|
+
configPath: string;
|
|
3284
|
+
originalExisted: boolean;
|
|
3285
|
+
/** Encrypted by IntegrationStateStore before it reaches disk. */
|
|
3286
|
+
originalContent: string;
|
|
3287
|
+
originalHash: string;
|
|
3288
|
+
installedHash: string;
|
|
3289
|
+
installedAt: number;
|
|
3290
|
+
gatewayBaseUrl: string;
|
|
3291
|
+
/** Codex auth.json snapshot and installed hash; absent on legacy records. */
|
|
3292
|
+
credentialFile?: IntegrationManagedFileRecord;
|
|
3429
3293
|
}
|
|
3430
|
-
interface
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3294
|
+
interface IntegrationManagedFileRecord {
|
|
3295
|
+
path: string;
|
|
3296
|
+
originalExisted: boolean;
|
|
3297
|
+
/** Encrypted by IntegrationStateStore before it reaches disk. */
|
|
3298
|
+
originalContent: string;
|
|
3299
|
+
originalHash: string;
|
|
3300
|
+
installedHash: string;
|
|
3437
3301
|
}
|
|
3438
|
-
interface
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3302
|
+
interface IntegrationGatewayKeyRecord {
|
|
3303
|
+
id: string;
|
|
3304
|
+
/** Encrypted by IntegrationStateStore before it reaches disk. */
|
|
3305
|
+
secret: string;
|
|
3306
|
+
createdAt: number;
|
|
3307
|
+
}
|
|
3308
|
+
interface IntegrationState {
|
|
3309
|
+
version: 1;
|
|
3310
|
+
/** Legacy shared-key layout. New installs use `keyBindings`; retained for safe migration. */
|
|
3311
|
+
gatewayKey?: IntegrationGatewayKeyRecord;
|
|
3312
|
+
keyBindings?: Partial<Record<IntegrationClientId, IntegrationKeyBinding>>;
|
|
3313
|
+
clients: Partial<Record<IntegrationClientId, IntegrationInstallRecord>>;
|
|
3444
3314
|
}
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
resolve(tenantId: string, referenceId: ImageReferenceId): Promise<ImageReferenceResolution>;
|
|
3454
|
-
delete(tenantId: string, referenceId: ImageReferenceId): Promise<boolean>;
|
|
3455
|
-
/** Daemon-internal cleanup path; accepts only the local reference-domain tenant HMAC. */
|
|
3456
|
-
deleteByHashedTenantKey(tenantKey: string, referenceId: ImageReferenceId): Promise<boolean>;
|
|
3457
|
-
cleanup(now?: number): Promise<number>;
|
|
3458
|
-
status(): {
|
|
3459
|
-
readonly entries: number;
|
|
3460
|
-
readonly bytes: number;
|
|
3461
|
-
readonly tombstones: number;
|
|
3462
|
-
};
|
|
3463
|
-
hasLiveReferenceByHashedTenantKey(tenantKey: string, referenceId: ImageReferenceId, now?: number): Promise<boolean>;
|
|
3464
|
-
reconcileOwnedFiles(maxEntries: number): Promise<FileImageReferenceReconciliationResult>;
|
|
3465
|
-
openArtifact(fileName: string, byteLength: number, signal?: AbortSignal): Promise<ReadableStream<Uint8Array>>;
|
|
3466
|
-
private exclusive;
|
|
3467
|
-
private tenantKey;
|
|
3468
|
-
private newReferenceId;
|
|
3469
|
-
private validateSaveInput;
|
|
3470
|
-
private selectVictims;
|
|
3471
|
-
private nextTombstones;
|
|
3472
|
-
private writeArtifact;
|
|
3473
|
-
private artifactPath;
|
|
3474
|
-
private validArtifact;
|
|
3475
|
-
private removeArtifact;
|
|
3476
|
-
private safeUnlinkArtifactPath;
|
|
3477
|
-
private releaseLease;
|
|
3478
|
-
private manifestPath;
|
|
3479
|
-
private persist;
|
|
3480
|
-
private atomicReplace;
|
|
3481
|
-
private loadManifest;
|
|
3315
|
+
|
|
3316
|
+
/** Encrypted, Omnicross-owned state for reversible native CLI configuration. */
|
|
3317
|
+
declare class IntegrationStateStore {
|
|
3318
|
+
readonly path: string;
|
|
3319
|
+
private readonly box;
|
|
3320
|
+
constructor(path: string, box: SecretBox);
|
|
3321
|
+
load(): IntegrationState;
|
|
3322
|
+
save(state: IntegrationState): void;
|
|
3482
3323
|
}
|
|
3483
3324
|
|
|
3484
|
-
interface
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3325
|
+
interface IntegrationManagerOptions {
|
|
3326
|
+
configPath: string;
|
|
3327
|
+
gatewayBaseUrl: string;
|
|
3328
|
+
keyDb: OutboundKeyDb;
|
|
3329
|
+
stateStore: IntegrationStateStore;
|
|
3330
|
+
codexAuthHelper?: CodexAuthHelperConfig;
|
|
3331
|
+
homeDir?: string;
|
|
3489
3332
|
}
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
readonly
|
|
3493
|
-
readonly
|
|
3494
|
-
readonly
|
|
3495
|
-
|
|
3333
|
+
/** Coordinates per-client least-privilege keys with reversible native CLI config edits. */
|
|
3334
|
+
declare class IntegrationManager {
|
|
3335
|
+
private readonly options;
|
|
3336
|
+
private readonly homeDir;
|
|
3337
|
+
private readonly codexAuthHelper;
|
|
3338
|
+
constructor(options: IntegrationManagerOptions);
|
|
3339
|
+
listStatus(): Promise<IntegrationClientStatus[]>;
|
|
3340
|
+
plan(client: IntegrationClientId, configPath?: string): Promise<IntegrationChangePlan>;
|
|
3341
|
+
install(client: IntegrationClientId, configPath?: string): Promise<IntegrationClientStatus>;
|
|
3342
|
+
repair(client: IntegrationClientId): Promise<IntegrationClientStatus>;
|
|
3343
|
+
remove(client: IntegrationClientId): Promise<IntegrationClientStatus>;
|
|
3344
|
+
/** Bind a user-confirmed access key and grant only this client's required endpoints. */
|
|
3345
|
+
bindIntegrationKey(client: IntegrationClientId, keyId: string): Promise<IntegrationClientStatus>;
|
|
3346
|
+
/** Rotate every Omnicross-managed client binding; user-selected keys remain untouched. */
|
|
3347
|
+
rotateGatewayKey(): Promise<{
|
|
3348
|
+
keyIds: Partial<Record<IntegrationClientId, string>>;
|
|
3349
|
+
}>;
|
|
3350
|
+
/** Resolve the plaintext only for the command-auth helper; callers must not log it. */
|
|
3351
|
+
getIntegrationToken(client: IntegrationClientId): Promise<string>;
|
|
3352
|
+
/** Compatibility alias for callers predating per-client bindings. */
|
|
3353
|
+
getGatewayToken(client?: IntegrationClientId): Promise<string>;
|
|
3354
|
+
private ensureClientKey;
|
|
3355
|
+
private createManagedClientKey;
|
|
3356
|
+
private installedSecret;
|
|
3357
|
+
private rebindInstalledClient;
|
|
3358
|
+
private statusFor;
|
|
3359
|
+
private boundKeyDetails;
|
|
3360
|
+
private retireManagedKeys;
|
|
3361
|
+
private defaultConfigPath;
|
|
3362
|
+
private renderInstalled;
|
|
3496
3363
|
}
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3364
|
+
|
|
3365
|
+
/**
|
|
3366
|
+
* accountsAntigravityOAuth — the daemon admin API's ANTIGRAVITY interactive
|
|
3367
|
+
* OAuth path (`POST /accounts/antigravity/oauth/start` +
|
|
3368
|
+
* `GET /accounts/antigravity/oauth/:sessionId/status`).
|
|
3369
|
+
*
|
|
3370
|
+
* Modeled on the codex loopback flow (`accountsCodexOAuth`): antigravity's
|
|
3371
|
+
* redirect is a FIXED loopback `http://127.0.0.1:51121/oauth-callback`, so the
|
|
3372
|
+
* flow is ASYNC + POLLED — `start` arms the one-shot loopback listener, kicks
|
|
3373
|
+
* the capture→exchange→email→project-handshake→persist chain off ASYNC, and
|
|
3374
|
+
* returns ONLY `{ authUrl, sessionId }` (public — client_id + state). The app
|
|
3375
|
+
* opens `authUrl`; the browser redirects to the loopback; the daemon captures
|
|
3376
|
+
* the `code`, validates `state`, exchanges it, resolves the userinfo email and
|
|
3377
|
+
* the Code Assist project (the antigravity dialect of the shared resolver),
|
|
3378
|
+
* and persists the minted token through the encrypted credential store. The
|
|
3379
|
+
* app POLLS `status` until `done`/`error`.
|
|
3380
|
+
*
|
|
3381
|
+
* SECRET SPINE (same invariant as codex/grok): the minted access/refresh token
|
|
3382
|
+
* NEVER crosses to the client — it lands ONLY in the encrypted store. The
|
|
3383
|
+
* poll `status` body is TOKEN-FREE (`{ state, message? }`). Port 51121 is a
|
|
3384
|
+
* single resource → only ONE antigravity sign-in may be in flight (409).
|
|
3385
|
+
*
|
|
3386
|
+
* REUSES the `@omnicross/subscriptions` antigravity flow + the CLI's
|
|
3387
|
+
* `awaitLoopbackCode` listener (parameterized binding) — it does NOT rebuild
|
|
3388
|
+
* the authorize/exchange/handshake logic.
|
|
3389
|
+
*
|
|
3390
|
+
* @module @omnicross/daemon/admin/accountsAntigravityOAuth
|
|
3391
|
+
*/
|
|
3392
|
+
|
|
3393
|
+
/** The loopback-listener fn (injected so tests need not bind a real port). */
|
|
3394
|
+
type AntigravityLoopbackFn = (state: string, timeoutMs?: number, signal?: AbortSignal) => Promise<string>;
|
|
3395
|
+
|
|
3396
|
+
/**
|
|
3397
|
+
* cliLaunch — the admin API's "launch a coding CLI in a terminal, pointed at the
|
|
3398
|
+
* daemon" surface (dashboard parity with the desktop app's Code CLI tab).
|
|
3399
|
+
*
|
|
3400
|
+
* This is the EXTERNAL-terminal analogue of `commands/launch.ts`: it reuses the
|
|
3401
|
+
* same `@omnicross/cli-launcher` builders (which register one route on the
|
|
3402
|
+
* RESIDENT `ProviderProxy` and return the redirect env — `ANTHROPIC_BASE_URL` +
|
|
3403
|
+
* a one-shot ROUTE token, codex's `-c base_url=…` overrides, etc.), then opens a
|
|
3404
|
+
* NEW terminal window running the CLI with that env injected. The route token —
|
|
3405
|
+
* NOT an upstream credential — is the only secret in the env; it is removed when
|
|
3406
|
+
* the session is stopped (`onSessionEnd`).
|
|
3407
|
+
*
|
|
3408
|
+
* SECRET DISCIPLINE: the env carries a route token (proxy-scoped, revocable),
|
|
3409
|
+
* never a provider key. On win32 the token rides the spawned process environment
|
|
3410
|
+
* (inherited by the terminal), never the command line / a file on disk.
|
|
3411
|
+
*
|
|
3412
|
+
* @module @omnicross/daemon/admin/cliLaunch
|
|
3413
|
+
*/
|
|
3414
|
+
|
|
3415
|
+
/** Injectable PATH probe (tests stub this; default scans `process.env.PATH`). */
|
|
3416
|
+
type PathProbe = (candidate: string) => string | null;
|
|
3417
|
+
/** Open a NEW terminal window running `command [extraArgs…]` with `env` injected. */
|
|
3418
|
+
type TerminalCleanup = () => void;
|
|
3419
|
+
type TerminalOpener = (input: {
|
|
3420
|
+
cli: string;
|
|
3421
|
+
command: string;
|
|
3422
|
+
extraArgs: string[];
|
|
3423
|
+
env: Record<string, string>;
|
|
3424
|
+
cwd?: string;
|
|
3425
|
+
platform: NodeJS.Platform;
|
|
3426
|
+
onFailure?: () => void;
|
|
3427
|
+
}) => void | TerminalCleanup;
|
|
3428
|
+
/**
|
|
3429
|
+
* Injectable shell runner for `POST /cli/:cli/install` (tests stub this; the
|
|
3430
|
+
* default execs the install command with a bounded timeout). Returns the host's
|
|
3431
|
+
* honest install outcome — `error` carries stderr/the failure reason.
|
|
3432
|
+
*/
|
|
3433
|
+
type CommandRunner = (command: string) => Promise<{
|
|
3434
|
+
ok: boolean;
|
|
3435
|
+
error?: string;
|
|
3436
|
+
}>;
|
|
3437
|
+
|
|
3438
|
+
/**
|
|
3439
|
+
* searchAdminApi — the admin API's search surface (search-settings-ui D3 +
|
|
3440
|
+
* search-settings-tab D4).
|
|
3441
|
+
*
|
|
3442
|
+
* Three routes over the daemon's search state, dispatched from `adminApi.ts`'s
|
|
3443
|
+
* `case 'search'`:
|
|
3444
|
+
*
|
|
3445
|
+
* - `GET /admin/api/search/diagnostics` — a READ-ONLY, secret-free, network-free
|
|
3446
|
+
* snapshot: one row per provider the daemon can run (the ONE runtime's
|
|
3447
|
+
* descriptors, plus `unconfigured` rows for known API providers the persisted
|
|
3448
|
+
* config does not name — the doctor's classification), the effective frontend
|
|
3449
|
+
* modes, and the explicit apply semantics (codex immediate, rest restart).
|
|
3450
|
+
* - `POST /admin/api/search/test { providerId }` — ONE live fixed-query check on
|
|
3451
|
+
* a configured provider, classified by the doctor's pure functions. The
|
|
3452
|
+
* machine-facing health probe: it sends exactly `SEARCH_DOCTOR_QUERY`, never a
|
|
3453
|
+
* caller-supplied query, and never returns result content (plan §11.3 — its
|
|
3454
|
+
* contract is the automated doctor's fixed-query discipline).
|
|
3455
|
+
* - `POST /admin/api/search/query { providerId, query }` — the INTERACTIVE
|
|
3456
|
+
* channel for the settings page's per-provider test panel (owner feedback
|
|
3457
|
+
* 2026-09-02): ONE operator-typed query through ONE provider's contribution
|
|
3458
|
+
* built from the PERSISTED config, returning the doctor-classified diagnostic
|
|
3459
|
+
* PLUS the sanitized results. The two disciplines stay separate routes on
|
|
3460
|
+
* purpose: bending `/test` to accept a query would erase the boundary its
|
|
3461
|
+
* pinned tests and consumers depend on.
|
|
3462
|
+
*
|
|
3463
|
+
* SECRET SPINE (all three routes): no response ever carries a configured VALUE,
|
|
3464
|
+
* and a failure response carries only the doctor's SANITIZED error shape — raw
|
|
3465
|
+
* upstream error bodies (which may quote the stored key) never serialize. The
|
|
3466
|
+
* query endpoint additionally sanitizes every returned result field BEFORE
|
|
3467
|
+
* serialization (plan §11.1: search results are untrusted input), and the
|
|
3468
|
+
* operator's query is never logged anywhere.
|
|
3469
|
+
*
|
|
3470
|
+
* The diagnostics dep is OPTIONAL (`AdminApiDeps.searchStatus`): light embedders
|
|
3471
|
+
* that wire no search runtime get 501 for all routes (the voucher/allowance
|
|
3472
|
+
* optionality precedent) rather than a fabricated snapshot.
|
|
3473
|
+
*
|
|
3474
|
+
* @module @omnicross/daemon/admin/searchAdminApi
|
|
3475
|
+
*/
|
|
3476
|
+
|
|
3477
|
+
/**
|
|
3478
|
+
* The daemon search state the admin surface needs. Structurally satisfied by
|
|
3479
|
+
* what `bootstrap.ts` already holds (the ONE runtime + its captured modes);
|
|
3480
|
+
* `testFetch` is a TEST SEAM so route tests can intercept the one live probe
|
|
3481
|
+
* without any network.
|
|
3482
|
+
*/
|
|
3483
|
+
interface SearchAdminRuntimeStatus {
|
|
3484
|
+
/** The daemon's ONE assembled search runtime (provider descriptors). */
|
|
3485
|
+
readonly runtime: SearchRuntime;
|
|
3486
|
+
/** Modes as captured at bootstrap (responses/anthropic are these, live). */
|
|
3487
|
+
readonly modes: SearchFrontendModes;
|
|
3488
|
+
/** TEST SEAM: fetch primitive for the live-test probe. Absent ⇒ real transport. */
|
|
3489
|
+
readonly testFetch?: (url: string, init: RequestInit) => Promise<Response>;
|
|
3500
3490
|
}
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3491
|
+
|
|
3492
|
+
/**
|
|
3493
|
+
* migration.ts — the export gather + import apply logic for the passphrase pack
|
|
3494
|
+
* (app-parity child 6, design D2/D3/D5).
|
|
3495
|
+
*
|
|
3496
|
+
* EXPORT (`gatherExport`): read the FULL local state DECRYPTED in-memory — every
|
|
3497
|
+
* provider row (scalars / modelConfigs / single apiKey / pool apiKeys /
|
|
3498
|
+
* transformer) via `loadConfig` (the at-rest box decrypts on read) AND the
|
|
3499
|
+
* subscription tokens via `credentialStore.getFullConfig()` (also decrypted) —
|
|
3500
|
+
* serialize to ONE bundle JSON, and `sealPack` it under the passphrase-derived
|
|
3501
|
+
* key. The caller returns ONLY the opaque pack; the decrypted bundle + the
|
|
3502
|
+
* passphrase live only in local variables and are never logged.
|
|
3503
|
+
*
|
|
3504
|
+
* IMPORT (`applyImport`): `openPack` decrypts + authenticates (a wrong passphrase
|
|
3505
|
+
* or a tampered pack fails the GCM auth-tag BEFORE any write — atomic). Every
|
|
3506
|
+
* provider is re-validated through `parseProviderInput` and every token block
|
|
3507
|
+
* through `validateTokenBody` (deny-by-default — a malicious blob cannot inject
|
|
3508
|
+
* unknown fields or escape the allowlist). Validation collects ALL rows BEFORE
|
|
3509
|
+
* applying, so a structurally invalid pack does not leave a half-applied state.
|
|
3510
|
+
* Apply merges by provider id (additive default): a new id is added; a colliding
|
|
3511
|
+
* id is skipped (or overwritten with `mode:'overwrite'`). Writes go through the
|
|
3512
|
+
* EXISTING paths (`saveConfig` re-encrypts at-rest under the LOCAL box +
|
|
3513
|
+
* `writeProviderTokens`/`appendProviderAccount`), so imported secrets land
|
|
3514
|
+
* `enc:`-encrypted under the TARGET machine's key — the passphrase key is used
|
|
3515
|
+
* ONLY for transport.
|
|
3516
|
+
*
|
|
3517
|
+
* SECRET SPINE: the export RESPONSE is the opaque pack ONLY; the import RESPONSE
|
|
3518
|
+
* is status-only counts. No decrypted secret + no passphrase ever reaches a
|
|
3519
|
+
* response body or a log here.
|
|
3520
|
+
*
|
|
3521
|
+
* @module @omnicross/daemon/migration/migration
|
|
3522
|
+
*/
|
|
3523
|
+
|
|
3524
|
+
/**
|
|
3525
|
+
* The credential-store surface the migration paths need: the full DECRYPTED read
|
|
3526
|
+
* (export) + the multi-account append (import re-encrypts at-rest). One shape so
|
|
3527
|
+
* `ExportDeps` + `ImportDeps` can be unified into `MigrationDeps` without a
|
|
3528
|
+
* `credentialStore` type conflict.
|
|
3529
|
+
*/
|
|
3530
|
+
interface MigrationCredentialStore extends SubscriptionAccountAppender {
|
|
3531
|
+
getFullConfig(): Promise<AccountTokensConfig>;
|
|
3541
3532
|
}
|
|
3542
3533
|
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
|
|
3534
|
+
/** Minimal, auth-gated admin API for secret-free account allowance snapshots. */
|
|
3535
|
+
|
|
3536
|
+
interface AccountAllowanceAdminReader {
|
|
3537
|
+
list(filter?: {
|
|
3538
|
+
providerId?: SubscriptionProviderId;
|
|
3539
|
+
accountId?: string;
|
|
3540
|
+
}): Promise<AccountAllowanceSnapshot[]>;
|
|
3541
|
+
refreshClaude(accountId?: string): Promise<AccountAllowanceSnapshot[]>;
|
|
3542
|
+
/** Optional: Codex active `/wham/usage` refresh (absent on older daemons). */
|
|
3543
|
+
refreshCodex?(accountId?: string): Promise<AccountAllowanceSnapshot[]>;
|
|
3544
|
+
/** Optional: Kimi `/coding/v1/usages` refresh (absent on older daemons). */
|
|
3545
|
+
refreshKimi?(accountId?: string): Promise<AccountAllowanceSnapshot[]>;
|
|
3546
|
+
/** Optional: OpenCodeGo `/v1/usage` refresh (absent on older daemons). */
|
|
3547
|
+
refreshOpenCodeGo?(accountId?: string): Promise<AccountAllowanceSnapshot[]>;
|
|
3548
|
+
/** Optional: Grok CLI-billing refresh (absent on older daemons). */
|
|
3549
|
+
refreshGrok?(accountId?: string): Promise<AccountAllowanceSnapshot[]>;
|
|
3550
|
+
/** Optional: Copilot user-quota refresh (absent on older daemons). */
|
|
3551
|
+
refreshCopilot?(accountId?: string): Promise<AccountAllowanceSnapshot[]>;
|
|
3552
|
+
/** Optional: Gemini Code-Assist quota refresh (absent on older daemons). */
|
|
3553
|
+
refreshGemini?(accountId?: string): Promise<AccountAllowanceSnapshot[]>;
|
|
3554
|
+
/** Optional: Antigravity quotaSummary refresh (absent on older daemons). */
|
|
3555
|
+
refreshAntigravity?(accountId?: string): Promise<AccountAllowanceSnapshot[]>;
|
|
3556
|
+
removeAccountSnapshot?(providerId: SubscriptionProviderId, accountId: string): void;
|
|
3557
|
+
removeProviderSnapshots?(providerId: SubscriptionProviderId): void;
|
|
3558
|
+
getSchedulingStatus?(): AccountAllowanceSchedulingStatus;
|
|
3549
3559
|
}
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
readonly responsesStateLimits: FileResponsesImageStateStoreLimits;
|
|
3555
|
-
readonly secretBox?: SecretBox;
|
|
3556
|
-
readonly now?: () => number;
|
|
3557
|
-
readonly random?: (bytes: number) => Buffer;
|
|
3558
|
-
readonly replaceCatalog?: (targetPath: string, contents: Uint8Array) => void;
|
|
3559
|
-
readonly reconcileCorruptManifests?: boolean;
|
|
3560
|
+
|
|
3561
|
+
/** Token-free subscription account list entry (passthrough from core's service). */
|
|
3562
|
+
interface AdminAccountsLister {
|
|
3563
|
+
listAll(): Promise<unknown[]>;
|
|
3560
3564
|
}
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3565
|
+
/** One key's live cooldown health (mirrors core `KeyHealthEntry`; read-only). */
|
|
3566
|
+
interface PoolKeyHealth {
|
|
3567
|
+
until: number;
|
|
3568
|
+
errors: number;
|
|
3569
|
+
lastStatus: number | null;
|
|
3564
3570
|
}
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3571
|
+
/**
|
|
3572
|
+
* The READ-ONLY pool-health surface the admin view needs (key-pool design D7).
|
|
3573
|
+
* Structurally satisfied by core's `ApiKeyPoolService.getKeyHealth`; typed as a
|
|
3574
|
+
* minimal reader so `adminApi` carries no class coupling and can never reach a
|
|
3575
|
+
* key value through it (cooldown health only).
|
|
3576
|
+
*/
|
|
3577
|
+
interface PoolHealthReader {
|
|
3578
|
+
getKeyHealth(providerId: string): Promise<Record<string, PoolKeyHealth>>;
|
|
3570
3579
|
}
|
|
3571
|
-
/**
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
+
/**
|
|
3581
|
+
* The BYO provider-key quota surface the keys view needs — structurally
|
|
3582
|
+
* satisfied by `ProviderKeyQuotaService`. Read-only; the DTO is secret-free by
|
|
3583
|
+
* construction (normalized windows + diagnostic codes only).
|
|
3584
|
+
*/
|
|
3585
|
+
interface ProviderKeyQuotaReader {
|
|
3586
|
+
quotaFor(row: DaemonProviderConfig, keyId: string, options?: {
|
|
3587
|
+
force?: boolean;
|
|
3588
|
+
}): Promise<ProviderKeyQuota | null>;
|
|
3589
|
+
}
|
|
3590
|
+
interface AdminImagesStatusReader {
|
|
3591
|
+
inspectCapability(apiKeyId: string): Promise<ImageRuntimeCapabilityInspection>;
|
|
3592
|
+
status(): ImageRuntimeManagerStatus;
|
|
3593
|
+
resourceStatus(): ImageRuntimeResourceStatus | undefined;
|
|
3594
|
+
}
|
|
3595
|
+
/** The live daemon handles the management API operates over. */
|
|
3596
|
+
interface AdminApiDeps {
|
|
3597
|
+
/** Path to the daemon's `config.json` (provider catalog + `server` field). */
|
|
3598
|
+
readonly configPath: string;
|
|
3599
|
+
/** Live provider catalog (hot-reload target). */
|
|
3600
|
+
readonly llmConfig: ConfigFileProviderConfigSource;
|
|
3601
|
+
/** Named outbound-key store. */
|
|
3602
|
+
readonly keyDb: OutboundKeyDb$1;
|
|
3603
|
+
/**
|
|
3604
|
+
* OPTIONAL voucher (redemption-card) store (voucher-redemption #9). When wired,
|
|
3605
|
+
* the `/admin/api/voucher` surface can generate/list/revoke cards. Absent ⇒ the
|
|
3606
|
+
* surface returns 501 (feature not available in this build).
|
|
3607
|
+
*/
|
|
3608
|
+
readonly voucherDb?: VoucherDb;
|
|
3609
|
+
/**
|
|
3610
|
+
* OPTIONAL per-key spend reader (outbound-key-policy). When wired, the key list
|
|
3611
|
+
* surfaces each key's OWN accumulated spend (daily/weekly/total) so the admin
|
|
3612
|
+
* can see spend-vs-limit. Leak-safe: only the key's own numbers are exposed.
|
|
3613
|
+
*/
|
|
3614
|
+
readonly keySpendReader?: KeySpendReader;
|
|
3615
|
+
/** Outbound server settings store (server config persistence). */
|
|
3616
|
+
readonly settingsStore: JsonApiServerSettingsStore;
|
|
3617
|
+
/** The running outbound server (status + live applyConfig). */
|
|
3618
|
+
readonly outboundApiServer: OutboundApiServer;
|
|
3619
|
+
/** True only when production composed the hardened per-hop remote resolver. */
|
|
3620
|
+
readonly imageRemoteResolverAvailable?: boolean;
|
|
3621
|
+
/** Optional production Images runtime generation participant. */
|
|
3622
|
+
readonly imageRuntimeConfig?: {
|
|
3623
|
+
prepareConfig(config: ImagesServerConfig): Promise<PreparedServerConfigChange>;
|
|
3580
3624
|
};
|
|
3581
|
-
|
|
3582
|
-
|
|
3625
|
+
/** Narrow metadata-only reader for authenticated Images capability/status. */
|
|
3626
|
+
readonly imageRuntimeStatus?: AdminImagesStatusReader;
|
|
3627
|
+
/**
|
|
3628
|
+
* Explicitly-consuming live image verification (images-settings-tab D3) —
|
|
3629
|
+
* delegates to the doctor service's `verifyLive` (Codex wire only). Absent
|
|
3630
|
+
* on light embedders; the endpoint then responds 501 and consumes nothing.
|
|
3631
|
+
*/
|
|
3632
|
+
readonly imageLiveVerifier?: {
|
|
3633
|
+
verifyLive(config: ImagesServerConfig, signal: AbortSignal): Promise<ImageDoctorLiveResult>;
|
|
3583
3634
|
};
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3635
|
+
/** Metadata-only successful Images configuration audit sink. */
|
|
3636
|
+
readonly imageConfigAudit?: (record: ImageConfigurationAuditRecord) => void;
|
|
3637
|
+
/** Process-local machine-managed routing leases (optional for light embedders). */
|
|
3638
|
+
readonly routeLeaseManager?: RouteLeaseManager;
|
|
3639
|
+
/** Subscription accounts (token-free `listAll`). */
|
|
3640
|
+
readonly subscriptionAccounts: AdminAccountsLister;
|
|
3641
|
+
/**
|
|
3642
|
+
* Secret-free upstream allowance facade. Optional for lightweight embedders;
|
|
3643
|
+
* the standalone daemon wires it and the route returns 501 when absent.
|
|
3644
|
+
*/
|
|
3645
|
+
readonly accountAllowanceService?: AccountAllowanceAdminReader;
|
|
3646
|
+
/** Live Claude cache worker; hot-reconfigured with allowance scheduling. */
|
|
3647
|
+
readonly allowanceRefreshScheduler?: Pick<ClaudeAllowanceRefreshScheduler, 'configure'>;
|
|
3648
|
+
/** Optional secret-free account connection probe + rolling history surface. */
|
|
3649
|
+
readonly accountProbeService?: AccountProbeHistoryReader & {
|
|
3650
|
+
probeAccount(providerId: _omnicross_contracts_subscription_types.SubscriptionProviderId, accountId: string): Promise<{
|
|
3651
|
+
ok: boolean;
|
|
3652
|
+
marked: boolean;
|
|
3653
|
+
}>;
|
|
3654
|
+
testAccountConnection(providerId: _omnicross_contracts_subscription_types.SubscriptionProviderId, accountId: string): Promise<{
|
|
3655
|
+
ok: boolean;
|
|
3656
|
+
marked: boolean;
|
|
3657
|
+
tier: 'local' | 'upstream' | 'generation';
|
|
3658
|
+
model?: string;
|
|
3659
|
+
}>;
|
|
3592
3660
|
};
|
|
3593
|
-
/**
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
|
|
3661
|
+
/**
|
|
3662
|
+
* Least-authority subscription-token WRITER (design D4) — ONLY the mutation
|
|
3663
|
+
* methods (`writeProviderTokens` / `clearProvider`), never a token-returning
|
|
3664
|
+
* read. The token-free `subscriptionAccounts` lister stays separate so a GET
|
|
3665
|
+
* handler can never reach a token through this dep.
|
|
3666
|
+
*/
|
|
3667
|
+
readonly subscriptionTokenWriter: SubscriptionTokenWriter;
|
|
3668
|
+
/**
|
|
3669
|
+
* Read-only pool-health reader (key-pool design D7) — cooldown health only,
|
|
3670
|
+
* never a key value. Drives `GET /admin/api/providers/:id/keys`.
|
|
3671
|
+
*/
|
|
3672
|
+
readonly apiKeyPool: PoolHealthReader;
|
|
3673
|
+
/** In-memory auto-disable store (design D5) — read-only for the health view. */
|
|
3674
|
+
readonly autoDisableStore: AutoDisableStore;
|
|
3675
|
+
/**
|
|
3676
|
+
* OPTIONAL BYO provider-key quota service — same-key usage/quota probes for
|
|
3677
|
+
* provider rows with a known adapter (Z.AI coding plan, MiniMax Token Plan).
|
|
3678
|
+
* Absent ⇒ the keys view carries no `quota` field (light embedders).
|
|
3679
|
+
*/
|
|
3680
|
+
readonly providerKeyQuota?: ProviderKeyQuotaReader;
|
|
3681
|
+
/**
|
|
3682
|
+
* Pending interactive-OAuth sessions (app-parity child 4, design D1) — the
|
|
3683
|
+
* in-memory `{ codeVerifier, state }` map keyed by a minted `sessionId`,
|
|
3684
|
+
* NEVER serialized to the client.
|
|
3685
|
+
*/
|
|
3686
|
+
readonly oauthSessions: OAuthSessionStore;
|
|
3687
|
+
/**
|
|
3688
|
+
* Injected token-exchange `FetchLike` FACTORY (oauth design D2-a) — built per
|
|
3689
|
+
* provider in `bootstrap.ts` so the exchange carries a `{ providerId }` egress
|
|
3690
|
+
* ctx (per-provider proxy layer + upstream trace, bodies redacted); tests
|
|
3691
|
+
* inject a mock so no real token endpoint is hit. Mirrors how `login.ts`
|
|
3692
|
+
* injects its exchange fetch.
|
|
3693
|
+
*/
|
|
3694
|
+
readonly oauthExchangeFetch: (providerId: SubscriptionProviderId) => FetchLike;
|
|
3695
|
+
/**
|
|
3696
|
+
* NARROW append handle (oauth design D2-a) — the OAuth complete handler needs
|
|
3697
|
+
* `appendProviderAccount` (NOT on the least-authority `SubscriptionTokenWriter`).
|
|
3698
|
+
* A minimal interface, NOT the full read-capable store, so no token-returning
|
|
3699
|
+
* read is reachable. Wired from the concrete `credentialStore` in `bootstrap.ts`.
|
|
3700
|
+
*/
|
|
3701
|
+
readonly subscriptionAccountAppender: SubscriptionAccountAppender;
|
|
3702
|
+
/**
|
|
3703
|
+
* Codex interactive-OAuth flow store (app-parity-2 child 5). Tracks the async
|
|
3704
|
+
* loopback sign-in's polled status (token-free); only ONE codex login may be in
|
|
3705
|
+
* flight (port 1455 is one resource). Wired in `bootstrap.ts`.
|
|
3706
|
+
*/
|
|
3707
|
+
readonly codexSessions: CodexOAuthSessionStore;
|
|
3708
|
+
/**
|
|
3709
|
+
* Kimi interactive-OAuth flow store (device code). Same token-free polled
|
|
3710
|
+
* shape as codex; one sign-in at a time. Wired in `bootstrap.ts`.
|
|
3711
|
+
*/
|
|
3712
|
+
readonly kimiSessions: CodexOAuthSessionStore;
|
|
3713
|
+
/**
|
|
3714
|
+
* Grok interactive-OAuth flow store (device code). Same token-free polled
|
|
3715
|
+
* shape as codex; one sign-in at a time. Wired in `bootstrap.ts`.
|
|
3716
|
+
*/
|
|
3717
|
+
readonly grokSessions: CodexOAuthSessionStore;
|
|
3718
|
+
/**
|
|
3719
|
+
* Copilot interactive-OAuth flow store (device code). Same token-free
|
|
3720
|
+
* polled shape; one sign-in at a time. Wired in `bootstrap.ts`.
|
|
3721
|
+
*/
|
|
3722
|
+
readonly copilotSessions: CodexOAuthSessionStore;
|
|
3723
|
+
/** Antigravity interactive OAuth (loopback 51121) — the async flow store. */
|
|
3724
|
+
readonly antigravitySessions: CodexOAuthSessionStore;
|
|
3725
|
+
/** The one-shot 127.0.0.1:51121 listener (test seam like `codexAwaitLoopback`). */
|
|
3726
|
+
readonly antigravityAwaitLoopback: AntigravityLoopbackFn;
|
|
3727
|
+
/**
|
|
3728
|
+
* Resolve the ACTIVE antigravity account's access token for the dynamic
|
|
3729
|
+
* model-catalog probe (secret used daemon-side only; the route is
|
|
3730
|
+
* secret-free). Optional for older tests; absent → static-only catalog.
|
|
3731
|
+
*/
|
|
3732
|
+
readonly resolveAntigravityAccessToken?: () => Promise<string | null>;
|
|
3733
|
+
/**
|
|
3734
|
+
* Codex loopback listener (app-parity-2 child 5) — defaults to `awaitLoopbackCode`
|
|
3735
|
+
* (binds 127.0.0.1:1455) in `bootstrap.ts`; tests inject a mock so no real port
|
|
3736
|
+
* is bound. The captured code crosses to the daemon ONLY (never the client).
|
|
3737
|
+
*/
|
|
3738
|
+
readonly codexAwaitLoopback: CodexLoopbackFn;
|
|
3739
|
+
/**
|
|
3740
|
+
* Migration credential-store handle (app-parity child 6, design D2/D3). The
|
|
3741
|
+
* export gather needs `getFullConfig()` (full DECRYPTED tokens, in-memory only —
|
|
3742
|
+
* the pack is the only thing that leaves) and import needs `appendProviderAccount`
|
|
3743
|
+
* (multi-account append + re-encrypt at-rest). Confined to the migration
|
|
3744
|
+
* handlers (which seal/validate everything); never reached by a GET handler.
|
|
3745
|
+
* Wired from the concrete `credentialStore` in `bootstrap.ts`.
|
|
3746
|
+
*/
|
|
3747
|
+
readonly migrationCredentialStore: MigrationCredentialStore;
|
|
3748
|
+
/**
|
|
3749
|
+
* Usage-stats query facade (usage-pricing child) — delegates to the JSONL
|
|
3750
|
+
* usage-event store. Aggregates only; carries no key material.
|
|
3751
|
+
*/
|
|
3752
|
+
readonly usageRecorder: UsageRecorder;
|
|
3753
|
+
/** Pricing engine (upsert / source refresh / conflict resolution). */
|
|
3754
|
+
readonly pricingEngine: PricingEngine;
|
|
3755
|
+
/**
|
|
3756
|
+
* CONCRETE pricing store — ONLY for the store-local row `delete` (the core
|
|
3757
|
+
* `PricingStore` port is frozen; delete is a daemon-local extra).
|
|
3758
|
+
*/
|
|
3759
|
+
readonly pricingStore: JsonPricingStore;
|
|
3760
|
+
/**
|
|
3761
|
+
* External-terminal opener for the Code CLI launch route (dashboard parity).
|
|
3762
|
+
* Optional — defaults to the real `defaultTerminalOpener`; tests inject a spy so
|
|
3763
|
+
* no terminal window is actually spawned.
|
|
3764
|
+
*/
|
|
3765
|
+
readonly cliTerminalOpener?: TerminalOpener;
|
|
3766
|
+
/** Injectable PATH probe for CLI detection (tests fake "installed"). */
|
|
3767
|
+
readonly cliPathProbe?: PathProbe;
|
|
3768
|
+
/**
|
|
3769
|
+
* Injectable shell runner for the Code CLI install route. Optional — defaults
|
|
3770
|
+
* to the real `exec`-based runner; tests inject a stub so no package manager
|
|
3771
|
+
* actually runs.
|
|
3772
|
+
*/
|
|
3773
|
+
readonly cliCommandRunner?: CommandRunner;
|
|
3774
|
+
/** Factory so each request observes the outbound server's current loopback port. */
|
|
3775
|
+
readonly integrationManagerFactory?: () => IntegrationManager;
|
|
3776
|
+
/**
|
|
3777
|
+
* search-settings-ui D3: the daemon's ONE search runtime plus its
|
|
3778
|
+
* bootstrap-captured frontend modes. Optional for lightweight embedders; the
|
|
3779
|
+
* standalone daemon wires it and the `/admin/api/search` diagnostics/test
|
|
3780
|
+
* routes return 501 when absent (the voucher/allowance optionality precedent).
|
|
3781
|
+
*/
|
|
3782
|
+
readonly searchStatus?: SearchAdminRuntimeStatus;
|
|
3646
3783
|
}
|
|
3784
|
+
/**
|
|
3785
|
+
* Dispatch one `/admin/api/*` request. `path` is the already-extracted pathname
|
|
3786
|
+
* (no query). The auth gate has already run in `AdminServer`.
|
|
3787
|
+
*/
|
|
3788
|
+
declare function handleAdminApi(req: http.IncomingMessage, res: http.ServerResponse, path: string, deps: AdminApiDeps): Promise<void>;
|
|
3647
3789
|
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3790
|
+
/**
|
|
3791
|
+
* AdminServer — the daemon's localhost admin/dashboard HTTP listener (RT3,
|
|
3792
|
+
* design D1/D2).
|
|
3793
|
+
*
|
|
3794
|
+
* A SEPARATE `node:http` listener distinct from core's outbound `/v1/*` server
|
|
3795
|
+
* (port 8765, untouched). Mirrors `OutboundApiServer`'s proven shape:
|
|
3796
|
+
* - `http.createServer`, default bind `127.0.0.1` (or `0.0.0.0` when
|
|
3797
|
+
* `networkBinding`), `listen` with `EADDRINUSE`→ephemeral(port 0) fallback,
|
|
3798
|
+
* - `getStatus()` (running / bound port / dashboard URL), `start` / `stop`.
|
|
3799
|
+
* Default admin port 8766.
|
|
3800
|
+
*
|
|
3801
|
+
* Auth (design D2):
|
|
3802
|
+
* - Baseline = localhost bind, no token → reachable only from the machine.
|
|
3803
|
+
* - Optional `admin.token` → every `/admin/*` request (incl. `GET /`) must
|
|
3804
|
+
* carry `Authorization: Bearer <token>` or `X-Admin-Token: <token>`; compared
|
|
3805
|
+
* server-side with a constant-time equality → `401` on a miss.
|
|
3806
|
+
* - HARD SAFETY GATE: `networkBinding` (LAN/`0.0.0.0`) without a non-empty
|
|
3807
|
+
* `admin.token` → `start` REFUSES to bind (logs + stays down, fail closed).
|
|
3808
|
+
*
|
|
3809
|
+
* Routing: `GET /` (and `GET /admin`) → `302 /ui/`; `* /admin/api/*` → the
|
|
3810
|
+
* management API (`handleAdminApi`); `GET /ui[/...]` → the Control Panel static
|
|
3811
|
+
* UI (`handleUiStatic`, from `@omnicross/ui`); everything else → `404`.
|
|
3812
|
+
*
|
|
3813
|
+
* @module @omnicross/daemon/admin/AdminServer
|
|
3814
|
+
*/
|
|
3815
|
+
|
|
3816
|
+
/** The dependencies the admin server + its API need (live daemon handles). */
|
|
3817
|
+
interface AdminServerDeps extends AdminApiDeps {
|
|
3818
|
+
/** Read the resolved admin config (enabled/port/networkBinding/token). */
|
|
3819
|
+
getAdminConfig: () => ResolvedAdminConfig;
|
|
3820
|
+
/**
|
|
3821
|
+
* Build the coarse, secret-free `/health` report (daemon-health-endpoint). A
|
|
3822
|
+
* shared closure over live handles (bootstrap wires the SAME builder into the
|
|
3823
|
+
* outbound server), served UNAUTHENTICATED — before the admin auth gate.
|
|
3824
|
+
*/
|
|
3825
|
+
getHealthReport: () => HealthReport;
|
|
3826
|
+
/**
|
|
3827
|
+
* Injected logger (configurable-logging) — the admin listener's OWN lifecycle
|
|
3828
|
+
* lines (bind/refuse/error) route through it so they honor the configured
|
|
3829
|
+
* level / format / file sink.
|
|
3830
|
+
*/
|
|
3831
|
+
logger: Logger;
|
|
3832
|
+
/**
|
|
3833
|
+
* OPTIONAL per-account probe-history reader (subscription-account-probe #8,
|
|
3834
|
+
* design D5). When wired (bootstrap → the `AccountHealthProbeScheduler`), the
|
|
3835
|
+
* AUTHED `GET /admin/api/account-probes` returns per-account probe history.
|
|
3836
|
+
* Absent ⇒ the route serves an empty list (byte-safe for embedders/tests that
|
|
3837
|
+
* do not wire it). Read-only + secret-free (ids + status labels, no tokens).
|
|
3838
|
+
*/
|
|
3839
|
+
probeHistoryReader?: AccountProbeHistoryReader;
|
|
3840
|
+
/**
|
|
3841
|
+
* OPTIONAL audit query reader (request-audit-log, design D6). When wired
|
|
3842
|
+
* (bootstrap → the date-rotated store), the AUTHED `GET /admin/api/audit`
|
|
3843
|
+
* returns filtered records. Absent ⇒ the route serves an empty list. The
|
|
3844
|
+
* records carry IP/UA/bodies → this route is behind the auth gate ONLY, NEVER
|
|
3845
|
+
* unauthenticated, NEVER on `/health`.
|
|
3846
|
+
*/
|
|
3847
|
+
auditReader?: AuditQueryReader;
|
|
3848
|
+
/** Metadata-only audit aggregate used by the overview error-rate metric. */
|
|
3849
|
+
auditStatsReader?: AuditStatsReader;
|
|
3850
|
+
/**
|
|
3851
|
+
* Reconstructs one record's bodies from the per-session shard store
|
|
3852
|
+
* (audit-store-sharding). Absent when audit was never enabled.
|
|
3853
|
+
*/
|
|
3854
|
+
auditBodyReader?: AuditBodyReader;
|
|
3855
|
+
/** Runs cross-session body compaction on demand (audit-store-sharding D8). */
|
|
3856
|
+
auditCompactor?: AuditCompactor;
|
|
3857
|
+
/**
|
|
3858
|
+
* OPTIONAL billing delivery-status reader (billing-event-stream, design D5).
|
|
3859
|
+
* When wired (bootstrap → the ledger dir), the AUTHED `GET /admin/api/billing-status`
|
|
3860
|
+
* returns secret-free total/delivered/pending counts. Absent ⇒ zeroed counts.
|
|
3861
|
+
*/
|
|
3862
|
+
billingStatusReader?: BillingStatusReader;
|
|
3686
3863
|
}
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3864
|
+
/** A live status snapshot for the admin listener. */
|
|
3865
|
+
interface AdminServerStatus {
|
|
3866
|
+
running: boolean;
|
|
3867
|
+
/** Actual bound port (0 when not running). */
|
|
3868
|
+
port: number;
|
|
3869
|
+
/** The dashboard URL (loopback or LAN base), or null when not running. */
|
|
3870
|
+
url: string | null;
|
|
3871
|
+
}
|
|
3872
|
+
declare class AdminServer {
|
|
3873
|
+
private readonly deps;
|
|
3874
|
+
private server;
|
|
3875
|
+
private boundPort;
|
|
3876
|
+
private boundAddr;
|
|
3877
|
+
/** Control Panel dist dir (resolved once at first request; null = no UI). */
|
|
3878
|
+
private uiDist;
|
|
3879
|
+
constructor(deps: AdminServerDeps);
|
|
3880
|
+
/**
|
|
3881
|
+
* Start the admin listener honoring the resolved admin config. Returns the
|
|
3882
|
+
* actual bound port, or `0` when it refuses/declines to bind (disabled or the
|
|
3883
|
+
* LAN fail-closed gate). Idempotent: a second call returns the bound port.
|
|
3884
|
+
*/
|
|
3885
|
+
start(): Promise<number>;
|
|
3886
|
+
/** Bind once; on EADDRINUSE retry with an ephemeral port (port 0). */
|
|
3887
|
+
private listen;
|
|
3888
|
+
/** Per-request handler: auth gate (when a token is set) → routing. */
|
|
3889
|
+
private onRequest;
|
|
3890
|
+
private dispatch;
|
|
3891
|
+
/** Constant-time bearer/header check against the configured token. */
|
|
3892
|
+
private isAuthorized;
|
|
3893
|
+
/** Stop the listener and release the port. */
|
|
3894
|
+
stop(): Promise<void>;
|
|
3895
|
+
/** A live status snapshot. */
|
|
3896
|
+
getStatus(): AdminServerStatus;
|
|
3702
3897
|
}
|
|
3703
3898
|
|
|
3704
3899
|
interface ImageTemporaryBudgetStatus {
|
|
@@ -4600,6 +4795,11 @@ interface DaemonPaths {
|
|
|
4600
4795
|
* so tests need not bind `127.0.0.1:1455`. Absent → the real `awaitLoopbackCode`.
|
|
4601
4796
|
*/
|
|
4602
4797
|
codexAwaitLoopback?: CodexLoopbackFn;
|
|
4798
|
+
/**
|
|
4799
|
+
* TEST SEAM (optional): override the antigravity loopback listener so tests
|
|
4800
|
+
* need not bind 127.0.0.1:51121. Absent → the real awaitLoopbackCode.
|
|
4801
|
+
*/
|
|
4802
|
+
antigravityAwaitLoopback?: AntigravityLoopbackFn;
|
|
4603
4803
|
/**
|
|
4604
4804
|
* TEST SEAM (optional): override the OAuth token-exchange fetch so tests need not
|
|
4605
4805
|
* hit a real token endpoint. Absent → the global `fetch`.
|
|
@@ -4624,6 +4824,8 @@ interface DaemonPaths {
|
|
|
4624
4824
|
imageRuntimeConfig?: AdminApiDeps['imageRuntimeConfig'];
|
|
4625
4825
|
/** TEST/COMPOSITION SEAM: metadata-only Images status reader. */
|
|
4626
4826
|
imageRuntimeStatus?: AdminApiDeps['imageRuntimeStatus'];
|
|
4827
|
+
/** TEST/COMPOSITION SEAM: explicitly-consuming Images live verifier. */
|
|
4828
|
+
imageLiveVerifier?: AdminApiDeps['imageLiveVerifier'];
|
|
4627
4829
|
/** TEST/COMPOSITION SEAM: metadata-only successful Images config audit sink. */
|
|
4628
4830
|
imageConfigAudit?: AdminApiDeps['imageConfigAudit'];
|
|
4629
4831
|
/** TEST ONLY: deterministic Tier-A provider inside the production Images composition. */
|