@omnicross/daemon 0.1.9 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +11392 -3527
- package/dist/cli.js +11730 -3705
- package/dist/index.cjs +8551 -1104
- package/dist/index.d.cts +1452 -58
- package/dist/index.d.ts +1452 -58
- package/dist/index.js +8525 -926
- package/package.json +63 -63
package/dist/index.d.cts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { OutboundApiServerConfig, Logger, ProviderConfigSource, TransformerService, Transformer, ResolvedTransformerChain, ApiServerSettingsStore, PricingStore, AutomaticPricingSource, OutboundKeyDb, OutboundKeyDbRow, OutboundKeyPolicy, PricingEngine as PricingEngine$1 } from '@omnicross/core';
|
|
1
|
+
import { Logger, OutboundApiServerConfig, ProviderConfigSource, TransformerService, Transformer, ResolvedTransformerChain, ApiServerSettingsStore, PricingStore, AutomaticPricingSource, OutboundPermission, OutboundKeyDb, OutboundKeyDbRow, OutboundKeyPolicy, PricingEngine as PricingEngine$1, OpenAIOperationRegistry } from '@omnicross/core';
|
|
3
2
|
import { ApiKeyPoolService } from '@omnicross/core/completion/ApiKeyPoolService';
|
|
4
|
-
import { AllowanceSchedulingConfig, AccountProbeConfig, OutboundKeyDb as OutboundKeyDb$1, VoucherDb, KeySpendReader, OutboundApiServer } from '@omnicross/core/outbound-api';
|
|
3
|
+
import { AllowanceSchedulingConfig, AccountProbeConfig, OutboundKeyDb as OutboundKeyDb$1, VoucherDb, KeySpendReader, OutboundApiServer, ImagesServerConfig } from '@omnicross/core/outbound-api';
|
|
5
4
|
import { RouteLeaseManager, ProviderProxy } from '@omnicross/core/provider-proxy';
|
|
6
5
|
import { UsageRecorder, PricingEngine } from '@omnicross/core/usage';
|
|
7
|
-
import { SubscriptionCredentialStore, FetchLike,
|
|
6
|
+
import { SubscriptionCredentialStore, FetchLike, CodexImageCapabilityEvidenceSource, CodexImageCapabilityEvidenceRequest, CodexImageCapabilityEvidence, CodexImageCapabilityObservation, ImageExecutionScheduler, ImageExecutionAccountKey, ImageExecutionSchedulerRequest, ImageExecutionSchedulerGrant, SubscriptionAccountService, SubscriptionProviderRegistry } from '@omnicross/subscriptions';
|
|
8
7
|
import { AccountAllowanceSnapshot } from '@omnicross/contracts/account-allowance-types';
|
|
9
8
|
import { ClaudeTokenConfig, CodexTokenConfig, GeminiTokenConfig, AccountTokensConfig, ProxyConfig, SubscriptionAccountSanitized, AccountClientIdentity, SubscriptionAccountEntry } from '@omnicross/contracts/account-tokens-types';
|
|
10
9
|
import * as _omnicross_contracts_subscription_types from '@omnicross/contracts/subscription-types';
|
|
@@ -15,12 +14,17 @@ import { SubscriptionIdentityStore } from '@omnicross/core/provider-proxy/identi
|
|
|
15
14
|
import { LoggingConfig, HealthReport } from '@omnicross/contracts/health-logging-types';
|
|
16
15
|
import { SubscriptionAccountHealth } from '@omnicross/core/pipeline/SubscriptionAccountHealth';
|
|
17
16
|
import { fetchUpstream } from '@omnicross/core/pipeline/upstreamFetch';
|
|
17
|
+
import { UsageTotals, ModelUsageRow, ApiKeyUsageRow } from '@omnicross/contracts/usage-stats-types';
|
|
18
18
|
import { ThinkLevel } from '@omnicross/contracts/completion-types';
|
|
19
|
-
import { AuditRecord, AuditStats, AuditConfig } from '@omnicross/contracts/audit-types';
|
|
19
|
+
import { AuditRecord, AuditStats, AuditBodyResult, AuditConfig } from '@omnicross/contracts/audit-types';
|
|
20
20
|
import { BillingDeliveryStatus, BillingConfig, BillingEvent } from '@omnicross/contracts/billing-types';
|
|
21
21
|
import http from 'node:http';
|
|
22
|
+
import * as _omnicross_contracts_image_generation_types from '@omnicross/contracts/image-generation-types';
|
|
23
|
+
import { ImageCapabilityUnavailableReason, ImageCapabilities, ImageGenerationErrorCode, ImageReferenceMetadata, ImageReferenceId } from '@omnicross/contracts/image-generation-types';
|
|
24
|
+
import { ImageApiContributions, ResponsesImageGenerationContribution, ResponsesImageInspectionInput, ResponsesImageAdmission, ResponsesHostedToolSelection, ResponsesImageRequestScope, ImageTelemetrySink, ImageApiAuditRecord, ImageTelemetryRecord, ImageReferenceStore, ImageReferenceSaveInput, ImageReferenceResolution, ImageTemporaryResourceBudget, ImageTemporaryResourceBudgetLease, ImageApiLimits, ImageRequestResourceScope, ImageApiRuntimeResolver, RemoteImageAssetResolver, ImageProvider, ImageProviderRegistry, ImageOrchestrator } from '@omnicross/core/image-generation';
|
|
22
25
|
import { LLMProvider, AgentDefaultModels, GlobalModelParameters } from '@omnicross/contracts/llm-config';
|
|
23
26
|
import { PricingEntry, PricingEntryInput, PricingResolution, PricingSourceRefreshResult } from '@omnicross/contracts/pricing-types';
|
|
27
|
+
import { ResponsesImageStateStore, ResponsesImageStateCommitInput, ResponsesImageCallBinding, ResponsesImageCallId, ResponsesImageCallResolution, ResponsesImageResponseResolution } from '@omnicross/core/image-generation/responses';
|
|
24
28
|
import { WebhookConfig, WebhookEvent } from '@omnicross/contracts/webhook-types';
|
|
25
29
|
|
|
26
30
|
/**
|
|
@@ -86,6 +90,193 @@ declare class SecretBox {
|
|
|
86
90
|
encryptMaybe(value: string): string;
|
|
87
91
|
}
|
|
88
92
|
|
|
93
|
+
/**
|
|
94
|
+
* usageRollup — the immutable per-day aggregate that lets a range query skip a
|
|
95
|
+
* day's rows entirely.
|
|
96
|
+
*
|
|
97
|
+
* A rollup is computed ONCE, when a day is closed (strictly before today's local
|
|
98
|
+
* date), and is then never rewritten. It is deliberately kept FOREVER, outliving
|
|
99
|
+
* the raw shard it was built from, for one load-bearing reason:
|
|
100
|
+
* `getSpendByKey().totalUsd` is a LIFETIME number that seeds the outbound key
|
|
101
|
+
* spend policy (`core/outbound-api/keySpendTracker`). Pruning raw rows without
|
|
102
|
+
* keeping a per-key aggregate behind would silently reset every key's lifetime
|
|
103
|
+
* spend and quietly re-open budgets the operator had already spent.
|
|
104
|
+
*
|
|
105
|
+
* WHAT IS EXACT AND WHAT IS NOT
|
|
106
|
+
*
|
|
107
|
+
* Every count, token sum and cost in a rollup is exact — it is a plain sum of
|
|
108
|
+
* the same rows a raw scan would have visited. The ONE approximation is
|
|
109
|
+
* `medianCacheHitRate`: a median cannot be composed from per-day medians, so
|
|
110
|
+
* each day stores a 1000-bin histogram of its per-event hit rates instead. The
|
|
111
|
+
* median recovered from bins is off by at most half a bin — 0.0005, i.e. well
|
|
112
|
+
* under a twentieth of a percentage point. Storing the raw rates instead would
|
|
113
|
+
* make a "permanent" sidecar grow with traffic, which is the property this whole
|
|
114
|
+
* layout exists to avoid.
|
|
115
|
+
*
|
|
116
|
+
* STALENESS: a rollup records the byte size of the shard it was built from.
|
|
117
|
+
* Because a shard is append-only, a size change means rows were added after the
|
|
118
|
+
* rollup was computed (only reachable by inserting into a past day — the tests
|
|
119
|
+
* do it, production does not) and the rollup is recomputed. A shard that is GONE
|
|
120
|
+
* is the pruned case, where the rollup is the sole surviving authority.
|
|
121
|
+
*
|
|
122
|
+
* @module @omnicross/daemon/usage/usageRollup
|
|
123
|
+
*/
|
|
124
|
+
|
|
125
|
+
/** Everything in `UsageTotals` except the one field that cannot be summed. */
|
|
126
|
+
type SummableTotals = Omit<UsageTotals, 'medianCacheHitRate'>;
|
|
127
|
+
/** Per-model group inside a rollup. `unpriced` is deliberately absent — it is
|
|
128
|
+
* derived at query time from the injected pricing lookup, so a pricing change
|
|
129
|
+
* never requires rewriting an immutable rollup. */
|
|
130
|
+
type RollupModelRow = Omit<ModelUsageRow, 'unpriced'>;
|
|
131
|
+
/** Per-key group inside a rollup. `label` is absent for the same reason: it is
|
|
132
|
+
* resolved by the admin handler against the live key registry. */
|
|
133
|
+
type RollupApiKeyRow = Omit<ApiKeyUsageRow, 'label'>;
|
|
134
|
+
/** One hour of a day. Only hours that saw traffic are stored. */
|
|
135
|
+
interface RollupHourRow {
|
|
136
|
+
/** LOCAL hour of day, 0–23. */
|
|
137
|
+
hour: number;
|
|
138
|
+
requests: number;
|
|
139
|
+
inputTokens: number;
|
|
140
|
+
outputTokens: number;
|
|
141
|
+
cacheReadTokens: number;
|
|
142
|
+
cacheCreationTokens: number;
|
|
143
|
+
costUsd: number;
|
|
144
|
+
}
|
|
145
|
+
/** The on-disk `usage-YYYY-MM-DD.rollup.json` document. */
|
|
146
|
+
interface UsageDayRollup {
|
|
147
|
+
version: 1;
|
|
148
|
+
/** `YYYY-MM-DD`, LOCAL. Must match the file name. */
|
|
149
|
+
date: string;
|
|
150
|
+
/** Byte size of the shard this was built from; the staleness guard. */
|
|
151
|
+
sourceBytes: number;
|
|
152
|
+
totals: SummableTotals;
|
|
153
|
+
/** Sparse `binIndex -> count` over per-event cache-hit rates. */
|
|
154
|
+
hitRateBins: Record<string, number>;
|
|
155
|
+
byModel: RollupModelRow[];
|
|
156
|
+
byApiKey: RollupApiKeyRow[];
|
|
157
|
+
byHour: RollupHourRow[];
|
|
158
|
+
/** Distinct non-null session ids seen that day (drilldown index). */
|
|
159
|
+
sessionIds: string[];
|
|
160
|
+
/** True when `sessionIds` hit {@link SESSION_INDEX_LIMIT} and is incomplete. */
|
|
161
|
+
sessionIdsTruncated: boolean;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* usageRollupStore — read-or-build the per-day rollup sidecars.
|
|
166
|
+
*
|
|
167
|
+
* A rollup is built LAZILY, the first time a query needs a closed day, and then
|
|
168
|
+
* reused forever. Building it is the only time that day's rows are ever parsed
|
|
169
|
+
* again; every subsequent query for any range containing that day reads a few KB
|
|
170
|
+
* of JSON instead of megabytes of JSONL.
|
|
171
|
+
*
|
|
172
|
+
* STALENESS is checked against the shard's byte size, not its mtime. Shards are
|
|
173
|
+
* append-only, so a size that no longer matches what the rollup was built from
|
|
174
|
+
* means rows were added afterwards — only reachable by inserting an event with a
|
|
175
|
+
* past `ts`, which the store API permits and the tests exercise. A shard that is
|
|
176
|
+
* absent entirely is the PRUNED case: the rollup is then the sole surviving
|
|
177
|
+
* record of that day and is trusted unconditionally.
|
|
178
|
+
*
|
|
179
|
+
* Writes are atomic (temp file + rename) so a crash mid-write cannot leave a
|
|
180
|
+
* half-JSON sidecar that later reads as a corrupt day.
|
|
181
|
+
*
|
|
182
|
+
* @module @omnicross/daemon/usage/usageRollupStore
|
|
183
|
+
*/
|
|
184
|
+
|
|
185
|
+
declare class UsageRollupStore {
|
|
186
|
+
private readonly usageDir;
|
|
187
|
+
private readonly maxCached;
|
|
188
|
+
private readonly cache;
|
|
189
|
+
/** In-flight builds, so a burst of concurrent queries builds a day ONCE. */
|
|
190
|
+
private readonly inFlight;
|
|
191
|
+
constructor(usageDir: string, maxCached?: number);
|
|
192
|
+
/**
|
|
193
|
+
* The rollup for a CLOSED day, building it from the shard when absent or
|
|
194
|
+
* stale. `null` means the day has neither a usable rollup nor a shard — i.e.
|
|
195
|
+
* nothing was ever recorded for it.
|
|
196
|
+
*
|
|
197
|
+
* Callers must not ask for TODAY: today's rows are still arriving, so a rollup
|
|
198
|
+
* of them would be wrong the moment it was written.
|
|
199
|
+
*/
|
|
200
|
+
get(dayKey: string, hasShard?: boolean): Promise<UsageDayRollup | null>;
|
|
201
|
+
/** Forget a day — used when an insert lands in an already-rolled-up day. */
|
|
202
|
+
invalidate(dayKey?: string): void;
|
|
203
|
+
/**
|
|
204
|
+
* Build (or rebuild) a day's rollup from its shard and persist it. Exposed for
|
|
205
|
+
* the prune sweeper, which must guarantee a rollup exists BEFORE it deletes
|
|
206
|
+
* the rows that rollup is derived from.
|
|
207
|
+
*/
|
|
208
|
+
ensure(dayKey: string): Promise<UsageDayRollup | null>;
|
|
209
|
+
private resolve;
|
|
210
|
+
private readSidecar;
|
|
211
|
+
private writeSidecar;
|
|
212
|
+
private remember;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* UsagePruneSweeper — retention for the sharded usage store.
|
|
217
|
+
*
|
|
218
|
+
* Prunes RAW day shards past `retentionDays` and NEVER touches a rollup. That
|
|
219
|
+
* asymmetry is the whole design: aggregates (totals, per-model, per-key, hourly
|
|
220
|
+
* trend) stay complete for the lifetime of the install, while the per-row detail
|
|
221
|
+
* that costs 5 MB a day is bounded. In particular `getSpendByKey().totalUsd` —
|
|
222
|
+
* the LIFETIME figure seeding the outbound key spend policy — keeps summing
|
|
223
|
+
* correctly across pruned days, which a naive "delete old rows" would silently
|
|
224
|
+
* zero, reopening budgets the operator had already spent.
|
|
225
|
+
*
|
|
226
|
+
* ORDERING IS LOAD-BEARING: a shard is only ever deleted AFTER its rollup exists
|
|
227
|
+
* on disk. If the rollup cannot be built, the shard is kept and the day is
|
|
228
|
+
* retried on the next tick. Deleting rows whose aggregate was never computed
|
|
229
|
+
* would destroy that day permanently.
|
|
230
|
+
*
|
|
231
|
+
* Shaped like `audit/AuditPruneSweeper`: `start()` arms an `unref()`ed interval
|
|
232
|
+
* plus one immediate pass, `dispose()` clears it, a re-entrancy guard prevents
|
|
233
|
+
* overlap, and nothing throws — a failed sweep is logged and retried, never
|
|
234
|
+
* fatal to the daemon.
|
|
235
|
+
*
|
|
236
|
+
* @module @omnicross/daemon/usage/UsagePruneSweeper
|
|
237
|
+
*/
|
|
238
|
+
|
|
239
|
+
/** The `usage` config segment. */
|
|
240
|
+
interface UsageRetentionConfig {
|
|
241
|
+
/** Days of RAW rows to keep. `0` or absent ⇒ keep everything. */
|
|
242
|
+
retentionDays?: number;
|
|
243
|
+
}
|
|
244
|
+
declare class UsagePruneSweeper {
|
|
245
|
+
private readonly usageDir;
|
|
246
|
+
private readonly rollups;
|
|
247
|
+
private readonly logger;
|
|
248
|
+
private config;
|
|
249
|
+
private readonly intervalMs;
|
|
250
|
+
/** Injectable clock (ms) for deterministic tests. */
|
|
251
|
+
private readonly now;
|
|
252
|
+
private timer;
|
|
253
|
+
private sweeping;
|
|
254
|
+
constructor(usageDir: string, rollups: UsageRollupStore, logger: Logger, config?: UsageRetentionConfig, intervalMs?: number,
|
|
255
|
+
/** Injectable clock (ms) for deterministic tests. */
|
|
256
|
+
now?: () => number);
|
|
257
|
+
/** Whether raw rows are pruned at all. */
|
|
258
|
+
get enabled(): boolean;
|
|
259
|
+
/** Re-apply config to the live instance (boot + admin PUT hot-reload). */
|
|
260
|
+
configure(config: UsageRetentionConfig): void;
|
|
261
|
+
/**
|
|
262
|
+
* Arm the interval AND run one pass immediately (boot cleanup). No-op when
|
|
263
|
+
* retention is off (keep-everything is the zero-regression default).
|
|
264
|
+
* Idempotent.
|
|
265
|
+
*/
|
|
266
|
+
start(): void;
|
|
267
|
+
/** Clear the interval (daemon shutdown / test teardown). Idempotent. */
|
|
268
|
+
dispose(): void;
|
|
269
|
+
/**
|
|
270
|
+
* One prune pass: for every day strictly OLDER than the retention cutoff that
|
|
271
|
+
* still has a shard, make sure its rollup exists and only then delete the
|
|
272
|
+
* shard. Exposed for tests; never throws. Returns the number of shards
|
|
273
|
+
* removed.
|
|
274
|
+
*/
|
|
275
|
+
sweep(): Promise<number>;
|
|
276
|
+
/** The LOCAL-midnight epoch ms of the current day. */
|
|
277
|
+
private todayMidnight;
|
|
278
|
+
}
|
|
279
|
+
|
|
89
280
|
/**
|
|
90
281
|
* config.ts — the daemon's `config.json` schema + load/save (design D9).
|
|
91
282
|
*
|
|
@@ -411,6 +602,12 @@ interface DaemonConfig {
|
|
|
411
602
|
* so it is never walked by `decryptConfigSecrets`/`encryptConfigSecrets`.
|
|
412
603
|
*/
|
|
413
604
|
logging?: LoggingConfig;
|
|
605
|
+
/**
|
|
606
|
+
* Optional usage-retention config. Absent ⇒ the default 90-day window on RAW
|
|
607
|
+
* usage rows. Per-day ROLLUPS are never pruned regardless — they carry the
|
|
608
|
+
* lifetime per-key spend the outbound key policy seeds from.
|
|
609
|
+
*/
|
|
610
|
+
usage?: UsageRetentionConfig;
|
|
414
611
|
}
|
|
415
612
|
/** Apply defaults to a (possibly absent) admin block: enabled, port 8766,
|
|
416
613
|
* loopback, no token. NOTE: an EXPLICIT `port: 0` is honored as "bind an
|
|
@@ -1421,11 +1618,53 @@ declare class AccountHealthProbeScheduler implements AccountProbeHistoryReader {
|
|
|
1421
1618
|
}
|
|
1422
1619
|
|
|
1423
1620
|
/**
|
|
1424
|
-
*
|
|
1425
|
-
* design D4
|
|
1426
|
-
*
|
|
1427
|
-
*
|
|
1428
|
-
*
|
|
1621
|
+
* auditBodyReader — reconstruct captured bodies out of the per-session shards
|
|
1622
|
+
* (audit-store-sharding, design D4).
|
|
1623
|
+
*
|
|
1624
|
+
* Bodies no longer sit inline on the metadata line, so reading one is an explicit
|
|
1625
|
+
* second step: locate the session shard, replay its delta chain, hand back the
|
|
1626
|
+
* original text. Backs the AUTHED admin body query and the `omnicross audit` CLI
|
|
1627
|
+
* — never an unauthenticated surface (a body can hold prompts and PII).
|
|
1628
|
+
*
|
|
1629
|
+
* Shards are read whole. That is bounded on purpose: a shard holds ONE session's
|
|
1630
|
+
* deltas, which is exactly the thing the delta encoding keeps small.
|
|
1631
|
+
*
|
|
1632
|
+
* Both storage forms are handled transparently: today's plain `.jsonl` (kept
|
|
1633
|
+
* greppable on disk) and an archived `.jsonl.gz` from a rolled-over day.
|
|
1634
|
+
*
|
|
1635
|
+
* @module @omnicross/daemon/audit/auditBodyReader
|
|
1636
|
+
*/
|
|
1637
|
+
|
|
1638
|
+
/** Locate one record's bodies. `ts` narrows the search to a single day directory. */
|
|
1639
|
+
interface AuditBodyQuery {
|
|
1640
|
+
/** The audit record id whose bodies to reconstruct. */
|
|
1641
|
+
id: string;
|
|
1642
|
+
/** The record's session key (its shard). */
|
|
1643
|
+
sessionKey: string;
|
|
1644
|
+
/** The record's timestamp, if known — skips scanning every retained day. */
|
|
1645
|
+
ts?: number;
|
|
1646
|
+
}
|
|
1647
|
+
|
|
1648
|
+
/**
|
|
1649
|
+
* auditReader — read + filter the audit store (request-audit-log design D4/D6,
|
|
1650
|
+
* re-laid-out by audit-store-sharding design D5). Backs the AUTHED admin query
|
|
1651
|
+
* only (records carry IP/UA). Returns NEWEST-FIRST up to a bounded limit.
|
|
1652
|
+
*
|
|
1653
|
+
* Reads BOTH on-disk layouts: the current `audit-YYYY-MM-DD/meta.jsonl` and the
|
|
1654
|
+
* legacy flat `audit-YYYY-MM-DD.jsonl` left behind by an older daemon (still
|
|
1655
|
+
* queryable until TTL prunes it — there is no migration step).
|
|
1656
|
+
*
|
|
1657
|
+
* BOUNDED BY CONSTRUCTION. Days are visited newest-first and each file is walked
|
|
1658
|
+
* BACKWARDS from its tail, so a query touches only as much of the store as it
|
|
1659
|
+
* needs. Two stops apply:
|
|
1660
|
+
* - cross-day: once `limit` rows are in hand, no older day is opened at all.
|
|
1661
|
+
* Exact, because day files never overlap in date.
|
|
1662
|
+
* - within-day: at most `limit + OVERSCAN` rows are taken from one file. The
|
|
1663
|
+
* overscan absorbs the slight append-order skew from a slow request landing
|
|
1664
|
+
* after a fast one that started later.
|
|
1665
|
+
*
|
|
1666
|
+
* A returned record NEVER carries a body — those live in the per-session shards
|
|
1667
|
+
* and are fetched one at a time. `hasBody` says whether one exists.
|
|
1429
1668
|
*
|
|
1430
1669
|
* @module @omnicross/daemon/audit/auditReader
|
|
1431
1670
|
*/
|
|
@@ -1434,6 +1673,8 @@ declare class AccountHealthProbeScheduler implements AccountProbeHistoryReader {
|
|
|
1434
1673
|
interface AuditQuery {
|
|
1435
1674
|
/** Restrict to one outbound key id. */
|
|
1436
1675
|
keyId?: string;
|
|
1676
|
+
/** Restrict to one conversation-session key. */
|
|
1677
|
+
sessionKey?: string;
|
|
1437
1678
|
/** Inclusive lower bound (epoch ms). */
|
|
1438
1679
|
from?: number;
|
|
1439
1680
|
/** Inclusive upper bound (epoch ms). */
|
|
@@ -1458,9 +1699,9 @@ interface AuditStatsQuery {
|
|
|
1458
1699
|
* auditQueryApi — the AUTHED `GET /admin/api/audit?keyId=&from=&to=&limit=`
|
|
1459
1700
|
* handler (request-audit-log, design D6).
|
|
1460
1701
|
*
|
|
1461
|
-
* Audit records carry client IP / user-agent (PII) and
|
|
1462
|
-
* redacted
|
|
1463
|
-
* behind the admin auth gate. This lives in its OWN helper module (the
|
|
1702
|
+
* Audit records carry client IP / user-agent (PII), and the sibling body query
|
|
1703
|
+
* returns redacted request/response snapshots — so unlike the coarse `/health`
|
|
1704
|
+
* boolean they are served ONLY behind the admin auth gate. This lives in its OWN helper module (the
|
|
1464
1705
|
* #4/#8/#10 helper-module convention) so `adminApi.ts` — at its line cap — is not
|
|
1465
1706
|
* touched: `AdminServer.dispatch` routes the path here directly, AFTER its auth
|
|
1466
1707
|
* gate. NEVER unauthenticated, NEVER surfaced on `/health`.
|
|
@@ -1475,6 +1716,14 @@ interface AuditStatsQuery {
|
|
|
1475
1716
|
type AuditQueryReader = (query: AuditQuery) => AuditRecord[];
|
|
1476
1717
|
/** Metadata-only aggregate reader used by the overview. */
|
|
1477
1718
|
type AuditStatsReader = (query: AuditStatsQuery) => Promise<AuditStats> | AuditStats;
|
|
1719
|
+
/** Reconstructs ONE record's bodies from the per-session shard store. */
|
|
1720
|
+
type AuditBodyReader = (query: AuditBodyQuery) => AuditBodyResult;
|
|
1721
|
+
/** Runs the cross-session body compaction over every closed day. */
|
|
1722
|
+
type AuditCompactor = () => {
|
|
1723
|
+
days: number;
|
|
1724
|
+
shards: number;
|
|
1725
|
+
savedBytes: number;
|
|
1726
|
+
};
|
|
1478
1727
|
|
|
1479
1728
|
/**
|
|
1480
1729
|
* billingStatusApi — the AUTHED `GET /admin/api/billing-status` handler
|
|
@@ -1493,6 +1742,254 @@ type AuditStatsReader = (query: AuditStatsQuery) => Promise<AuditStats> | AuditS
|
|
|
1493
1742
|
/** The read surface the AdminServer consumes (bootstrap binds it to the ledger dir). */
|
|
1494
1743
|
type BillingStatusReader = () => BillingDeliveryStatus;
|
|
1495
1744
|
|
|
1745
|
+
type PreparedImageRuntimeGeneration = {
|
|
1746
|
+
readonly id: string;
|
|
1747
|
+
readonly enabled: true;
|
|
1748
|
+
readonly imageApi: ImageApiContributions;
|
|
1749
|
+
readonly hosted: ResponsesImageGenerationContribution;
|
|
1750
|
+
readonly hostedRuntime: HostedImageRuntimePolicy;
|
|
1751
|
+
readonly inspectCapability?: (apiKeyId: string) => Promise<Omit<ImageRuntimeCapabilityInspection, 'generationId'>>;
|
|
1752
|
+
readonly readRuntimeStatus?: () => ImageRuntimeResourceStatus;
|
|
1753
|
+
dispose(): void | Promise<void>;
|
|
1754
|
+
} | {
|
|
1755
|
+
readonly id: string;
|
|
1756
|
+
readonly enabled: false;
|
|
1757
|
+
dispose(): void | Promise<void>;
|
|
1758
|
+
};
|
|
1759
|
+
interface HostedImageRuntimeGenerationLease {
|
|
1760
|
+
readonly generationId: string;
|
|
1761
|
+
/** Compatibility/debug view; callers should prefer the deep methods below. */
|
|
1762
|
+
readonly contribution: ResponsesImageGenerationContribution;
|
|
1763
|
+
inspectRequest(input: ResponsesImageInspectionInput): ResponsesImageAdmission;
|
|
1764
|
+
validateSelection(admission: ResponsesImageAdmission, selection: ResponsesHostedToolSelection): void;
|
|
1765
|
+
openRequest(input: HostedImageOpenRequestInput): Promise<ResponsesImageRequestScope>;
|
|
1766
|
+
release(): Promise<void>;
|
|
1767
|
+
}
|
|
1768
|
+
interface HostedImageRuntimePolicy {
|
|
1769
|
+
readonly providerId: string;
|
|
1770
|
+
readonly imageModel: string;
|
|
1771
|
+
readonly referenceTtlMs: number;
|
|
1772
|
+
readonly maxOutputBytes: number;
|
|
1773
|
+
readonly maxTotalOutputBytes: number;
|
|
1774
|
+
readonly preferredAccountId?: string;
|
|
1775
|
+
readonly preferredAccountGroup?: string;
|
|
1776
|
+
readonly boundAccountFallbackPolicy?: 'strict' | 'pool';
|
|
1777
|
+
}
|
|
1778
|
+
interface HostedImageOpenRequestInput {
|
|
1779
|
+
readonly admission: ResponsesImageAdmission;
|
|
1780
|
+
readonly tenantId: string;
|
|
1781
|
+
readonly requestId: string;
|
|
1782
|
+
readonly sessionKey: string;
|
|
1783
|
+
readonly signal: AbortSignal;
|
|
1784
|
+
readonly authorizedPreviousResponseId?: string;
|
|
1785
|
+
/** Trusted affinity fact forwarded unchanged into the contribution scope. */
|
|
1786
|
+
readonly authorizedPreviousResponseKnownEmpty?: boolean;
|
|
1787
|
+
readonly mainProviderId: string;
|
|
1788
|
+
readonly selectedMainAccountId?: string;
|
|
1789
|
+
}
|
|
1790
|
+
/** Dormant integration seam for a later Native Responses owner. */
|
|
1791
|
+
interface HostedImageContributionFactory {
|
|
1792
|
+
acquire(): Promise<HostedImageRuntimeGenerationLease>;
|
|
1793
|
+
}
|
|
1794
|
+
/** Bind a stable factory to one app-session runtime manager without acquiring. */
|
|
1795
|
+
declare function createHostedImageContributionFactory(manager: ImageRuntimeManager): HostedImageContributionFactory;
|
|
1796
|
+
interface ImageRuntimeGenerationStatus {
|
|
1797
|
+
readonly generationId: string;
|
|
1798
|
+
readonly enabled: boolean;
|
|
1799
|
+
readonly httpLeases: number;
|
|
1800
|
+
readonly hostedLeases: number;
|
|
1801
|
+
}
|
|
1802
|
+
interface ImageRuntimeManagerStatus {
|
|
1803
|
+
readonly disposed: boolean;
|
|
1804
|
+
readonly current: ImageRuntimeGenerationStatus;
|
|
1805
|
+
readonly draining: readonly ImageRuntimeGenerationStatus[];
|
|
1806
|
+
}
|
|
1807
|
+
type ImageRuntimeSafeUnavailableReason = ImageCapabilityUnavailableReason | 'disabled' | 'runtime_unavailable';
|
|
1808
|
+
interface ImageRuntimeCapabilityInspection {
|
|
1809
|
+
readonly generationId: string;
|
|
1810
|
+
readonly enabled: boolean;
|
|
1811
|
+
readonly available: boolean;
|
|
1812
|
+
readonly providerId?: 'codex-subscription';
|
|
1813
|
+
readonly model?: string;
|
|
1814
|
+
readonly reason?: ImageRuntimeSafeUnavailableReason;
|
|
1815
|
+
readonly capabilities?: ImageCapabilities;
|
|
1816
|
+
}
|
|
1817
|
+
interface ImageRuntimeResourceStatus {
|
|
1818
|
+
readonly queue: Readonly<{
|
|
1819
|
+
activeJobs: number;
|
|
1820
|
+
waitingJobs: number;
|
|
1821
|
+
activeAccounts: number;
|
|
1822
|
+
waitingAccounts: number;
|
|
1823
|
+
waitingTenants: number;
|
|
1824
|
+
maxConcurrentJobsPerAccount: number;
|
|
1825
|
+
maxQueuedJobs: number;
|
|
1826
|
+
accepting: boolean;
|
|
1827
|
+
shuttingDown: boolean;
|
|
1828
|
+
}>;
|
|
1829
|
+
readonly temporary: Readonly<{
|
|
1830
|
+
activeScopes: number;
|
|
1831
|
+
totalBytes: number;
|
|
1832
|
+
tenantCount: number;
|
|
1833
|
+
maxActiveScopes: number;
|
|
1834
|
+
maxTotalBytes: number;
|
|
1835
|
+
maxTenantBytes: number;
|
|
1836
|
+
}>;
|
|
1837
|
+
readonly storage: Readonly<{
|
|
1838
|
+
mounts: number;
|
|
1839
|
+
retiredMounts: number;
|
|
1840
|
+
referenceEntries: number;
|
|
1841
|
+
referenceBytes: number;
|
|
1842
|
+
referenceTombstones: number;
|
|
1843
|
+
stateCalls: number;
|
|
1844
|
+
stateResponses: number;
|
|
1845
|
+
stateTombstones: number;
|
|
1846
|
+
pendingReferenceDeletes: number;
|
|
1847
|
+
maxReferenceEntries: number;
|
|
1848
|
+
maxReferenceBytes: number;
|
|
1849
|
+
maxTenantReferenceBytes: number;
|
|
1850
|
+
maxStateCalls: number;
|
|
1851
|
+
maxStateResponses: number;
|
|
1852
|
+
}>;
|
|
1853
|
+
}
|
|
1854
|
+
interface PreparedImageRuntimeChange {
|
|
1855
|
+
readonly generationId: string;
|
|
1856
|
+
publish(): void;
|
|
1857
|
+
rollback(): void;
|
|
1858
|
+
dispose(): Promise<void>;
|
|
1859
|
+
}
|
|
1860
|
+
/** App-session owner for stable forwarders and generation-pinned work. */
|
|
1861
|
+
declare class ImageRuntimeManager {
|
|
1862
|
+
#private;
|
|
1863
|
+
readonly contributions: ImageApiContributions;
|
|
1864
|
+
constructor(initial?: PreparedImageRuntimeGeneration);
|
|
1865
|
+
prepare(generation: PreparedImageRuntimeGeneration): PreparedImageRuntimeChange;
|
|
1866
|
+
acquireHosted(): Promise<HostedImageRuntimeGenerationLease>;
|
|
1867
|
+
inspectCapability(apiKeyId: string): Promise<ImageRuntimeCapabilityInspection>;
|
|
1868
|
+
listAvailableModels(apiKeyId: string): Promise<readonly string[]>;
|
|
1869
|
+
resourceStatus(): ImageRuntimeResourceStatus | undefined;
|
|
1870
|
+
status(): ImageRuntimeManagerStatus;
|
|
1871
|
+
dispose(): Promise<void>;
|
|
1872
|
+
}
|
|
1873
|
+
|
|
1874
|
+
type SafeProvider = 'codex-subscription' | 'unknown' | 'other';
|
|
1875
|
+
type SafeModel = 'gpt-image-2' | 'unknown' | 'other';
|
|
1876
|
+
type SafeErrorCode = ImageGenerationErrorCode | 'none' | 'other';
|
|
1877
|
+
type SafeCountOption = 'unknown' | '0' | '1' | '2-4' | '5+';
|
|
1878
|
+
type SafeBooleanOption = boolean | 'unknown';
|
|
1879
|
+
type SafeQuality = 'auto' | 'low' | 'medium' | 'high' | 'unknown' | 'other';
|
|
1880
|
+
type SafeBackground = 'auto' | 'opaque' | 'transparent' | 'unknown' | 'other';
|
|
1881
|
+
type SafeOutputFormat = 'png' | 'jpeg' | 'webp' | 'unknown' | 'other';
|
|
1882
|
+
declare const IMAGE_CONFIGURATION_AUDIT_FIELDS: readonly ["enablement", "provider", "model", "account", "queue", "temporary", "limits", "retention", "storage", "remote", "evidence"];
|
|
1883
|
+
type ImageConfigurationAuditField = typeof IMAGE_CONFIGURATION_AUDIT_FIELDS[number];
|
|
1884
|
+
/** Values are deliberately limited to safe categories and internal generation ids. */
|
|
1885
|
+
interface ImageConfigurationAuditRecord {
|
|
1886
|
+
readonly outcome: 'applied';
|
|
1887
|
+
readonly fields: readonly ImageConfigurationAuditField[];
|
|
1888
|
+
readonly previousGenerationId?: string;
|
|
1889
|
+
readonly generationId?: string;
|
|
1890
|
+
}
|
|
1891
|
+
interface ImageHistogramSnapshot {
|
|
1892
|
+
readonly count: number;
|
|
1893
|
+
readonly sum: number;
|
|
1894
|
+
/** Non-cumulative fixed buckets; `upperBound:null` is the overflow bucket. */
|
|
1895
|
+
readonly buckets: readonly {
|
|
1896
|
+
readonly upperBound: number | null;
|
|
1897
|
+
readonly count: number;
|
|
1898
|
+
}[];
|
|
1899
|
+
}
|
|
1900
|
+
interface ImageApiMetricDimensions {
|
|
1901
|
+
readonly endpoint: 'images.generate' | 'images.edit';
|
|
1902
|
+
readonly provider: SafeProvider;
|
|
1903
|
+
readonly model: SafeModel;
|
|
1904
|
+
readonly action: 'generate' | 'edit';
|
|
1905
|
+
readonly quality: SafeQuality;
|
|
1906
|
+
readonly background: SafeBackground;
|
|
1907
|
+
readonly outputFormat: SafeOutputFormat;
|
|
1908
|
+
readonly streaming: SafeBooleanOption;
|
|
1909
|
+
readonly requestedOutputs: SafeCountOption;
|
|
1910
|
+
readonly partialImages: SafeCountOption;
|
|
1911
|
+
readonly terminal: 'completed' | 'failed' | 'cancelled';
|
|
1912
|
+
readonly errorCode: SafeErrorCode;
|
|
1913
|
+
}
|
|
1914
|
+
interface ImageExecutionMetricDimensions {
|
|
1915
|
+
readonly provider: SafeProvider;
|
|
1916
|
+
readonly model: SafeModel;
|
|
1917
|
+
readonly action: 'generate' | 'edit' | 'other';
|
|
1918
|
+
readonly quality: SafeQuality;
|
|
1919
|
+
readonly background: SafeBackground;
|
|
1920
|
+
readonly outputFormat: SafeOutputFormat;
|
|
1921
|
+
readonly streaming: SafeBooleanOption;
|
|
1922
|
+
readonly requestedOutputs: SafeCountOption;
|
|
1923
|
+
readonly terminal: 'completed' | 'failed' | 'cancelled' | 'other';
|
|
1924
|
+
readonly errorCode: SafeErrorCode;
|
|
1925
|
+
}
|
|
1926
|
+
interface ImageApiMetricSnapshot {
|
|
1927
|
+
readonly dimensions: ImageApiMetricDimensions;
|
|
1928
|
+
readonly requests: number;
|
|
1929
|
+
readonly inputCount?: ImageHistogramSnapshot;
|
|
1930
|
+
readonly inputBytes?: ImageHistogramSnapshot;
|
|
1931
|
+
readonly referenceOutcomes: Readonly<{
|
|
1932
|
+
hits: number;
|
|
1933
|
+
notFound: number;
|
|
1934
|
+
expired: number;
|
|
1935
|
+
failed: number;
|
|
1936
|
+
}>;
|
|
1937
|
+
readonly cleanupOutcomes: Readonly<{
|
|
1938
|
+
completed: number;
|
|
1939
|
+
failed: number;
|
|
1940
|
+
}>;
|
|
1941
|
+
}
|
|
1942
|
+
interface ImageExecutionMetricSnapshot {
|
|
1943
|
+
readonly dimensions: ImageExecutionMetricDimensions;
|
|
1944
|
+
readonly executions: number;
|
|
1945
|
+
readonly finalLatencyMs: ImageHistogramSnapshot;
|
|
1946
|
+
readonly queueWaitMs?: ImageHistogramSnapshot;
|
|
1947
|
+
readonly generationDurationMs?: ImageHistogramSnapshot;
|
|
1948
|
+
readonly firstPartialLatencyMs?: ImageHistogramSnapshot;
|
|
1949
|
+
readonly inputCount: ImageHistogramSnapshot;
|
|
1950
|
+
readonly inputBytes: ImageHistogramSnapshot;
|
|
1951
|
+
readonly outputCount: ImageHistogramSnapshot;
|
|
1952
|
+
readonly outputBytes: ImageHistogramSnapshot;
|
|
1953
|
+
readonly retryCount?: ImageHistogramSnapshot;
|
|
1954
|
+
readonly authRefreshCount?: ImageHistogramSnapshot;
|
|
1955
|
+
readonly referenceSaveCount?: ImageHistogramSnapshot;
|
|
1956
|
+
readonly retentionRollbackFailures?: ImageHistogramSnapshot;
|
|
1957
|
+
}
|
|
1958
|
+
interface ImageObservabilitySnapshot {
|
|
1959
|
+
readonly apiRequests: readonly ImageApiMetricSnapshot[];
|
|
1960
|
+
readonly executions: readonly ImageExecutionMetricSnapshot[];
|
|
1961
|
+
readonly configurationChanges: readonly ImageConfigurationAuditRecord[];
|
|
1962
|
+
readonly overflow: Readonly<{
|
|
1963
|
+
apiRecords: number;
|
|
1964
|
+
telemetryRecords: number;
|
|
1965
|
+
}>;
|
|
1966
|
+
}
|
|
1967
|
+
interface ImageObservabilityOptions {
|
|
1968
|
+
/** Hard cap applied independently to API and execution dimension maps. */
|
|
1969
|
+
readonly maxDimensionSets?: number;
|
|
1970
|
+
}
|
|
1971
|
+
/** Process-local, metadata-only aggregation for HTTP and hosted Images work. */
|
|
1972
|
+
declare class ImageObservability {
|
|
1973
|
+
#private;
|
|
1974
|
+
readonly telemetrySink: ImageTelemetrySink;
|
|
1975
|
+
readonly audit: (record: ImageApiAuditRecord) => void;
|
|
1976
|
+
constructor(options?: ImageObservabilityOptions);
|
|
1977
|
+
recordApiAudit(record: ImageApiAuditRecord): void;
|
|
1978
|
+
recordTelemetry(record: ImageTelemetryRecord): void;
|
|
1979
|
+
recordConfigurationAudit(record: ImageConfigurationAuditRecord): void;
|
|
1980
|
+
snapshot(): ImageObservabilitySnapshot;
|
|
1981
|
+
reset(): void;
|
|
1982
|
+
}
|
|
1983
|
+
|
|
1984
|
+
interface PreparedServerConfigChange {
|
|
1985
|
+
/** Publish the prepared snapshot. Implementations should make this an infallible swap. */
|
|
1986
|
+
publish(): void | Promise<void>;
|
|
1987
|
+
/** Restore the exact runtime snapshot that preceded publish. */
|
|
1988
|
+
rollback(): void | Promise<void>;
|
|
1989
|
+
/** Release an unpublished or rolled-back replacement. */
|
|
1990
|
+
dispose(): void | Promise<void>;
|
|
1991
|
+
}
|
|
1992
|
+
|
|
1496
1993
|
/**
|
|
1497
1994
|
* autoDisableStore.ts — the daemon's PROCESS-IN-MEMORY auto-disable store.
|
|
1498
1995
|
*
|
|
@@ -1632,9 +2129,16 @@ declare class ConfigFileProviderConfigSource implements ProviderConfigSource {
|
|
|
1632
2129
|
* @module @omnicross/daemon/ports/JsonApiServerSettingsStore
|
|
1633
2130
|
*/
|
|
1634
2131
|
|
|
2132
|
+
interface JsonSettingsDocumentSnapshot {
|
|
2133
|
+
readonly existed: boolean;
|
|
2134
|
+
/** Raw persisted bytes; may contain encrypted secrets and must never enter a DTO or log. */
|
|
2135
|
+
readonly bytes?: Uint8Array;
|
|
2136
|
+
}
|
|
2137
|
+
type AtomicDocumentReplace = (targetPath: string, contents: Uint8Array) => void;
|
|
1635
2138
|
declare class JsonApiServerSettingsStore implements ApiServerSettingsStore {
|
|
1636
2139
|
private readonly configPath;
|
|
1637
2140
|
private readonly box;
|
|
2141
|
+
private readonly atomicReplace;
|
|
1638
2142
|
/**
|
|
1639
2143
|
* @param configPath the daemon config.json whose `server` field is backed.
|
|
1640
2144
|
* @param box OPTIONAL at-rest `SecretBox` (upstream-proxy). When set, the
|
|
@@ -1643,9 +2147,13 @@ declare class JsonApiServerSettingsStore implements ApiServerSettingsStore {
|
|
|
1643
2147
|
* secret-aware — every OTHER server field is non-secret). Null
|
|
1644
2148
|
* ⇒ passthrough (legacy/pure tests unchanged).
|
|
1645
2149
|
*/
|
|
1646
|
-
constructor(configPath: string, box?: SecretBox | null);
|
|
2150
|
+
constructor(configPath: string, box?: SecretBox | null, atomicReplace?: AtomicDocumentReplace);
|
|
1647
2151
|
get<T = unknown>(key: string): Promise<T | undefined>;
|
|
1648
2152
|
set<T = unknown>(key: string, value: T): Promise<void>;
|
|
2153
|
+
/** Capture the exact prior document for an admin transaction rollback. */
|
|
2154
|
+
captureDocumentSnapshot(): JsonSettingsDocumentSnapshot;
|
|
2155
|
+
/** Restore exact prior bytes (including unrelated fields and encrypted secrets). */
|
|
2156
|
+
restoreDocumentSnapshot(snapshot: JsonSettingsDocumentSnapshot): void;
|
|
1649
2157
|
/** Encrypt the proxy passwords + webhook + billing secrets before persisting (no-op without a box). */
|
|
1650
2158
|
private encryptSecrets;
|
|
1651
2159
|
/** Decrypt the proxy passwords + webhook + billing secrets on read (no-op without a box). */
|
|
@@ -1727,7 +2235,31 @@ declare class JsonPricingStore implements PricingStore {
|
|
|
1727
2235
|
private replaceFile;
|
|
1728
2236
|
}
|
|
1729
2237
|
|
|
2238
|
+
interface CodexAuthHelperConfig {
|
|
2239
|
+
command: string;
|
|
2240
|
+
args: string[];
|
|
2241
|
+
}
|
|
2242
|
+
|
|
1730
2243
|
type IntegrationClientId = 'codex' | 'claude';
|
|
2244
|
+
type IntegrationKeyOwnership = 'managed' | 'selected';
|
|
2245
|
+
/** Secret-free pointer to an access key. The plaintext remains in the encrypted key store. */
|
|
2246
|
+
interface IntegrationKeyBinding {
|
|
2247
|
+
keyId: string;
|
|
2248
|
+
ownership: IntegrationKeyOwnership;
|
|
2249
|
+
}
|
|
2250
|
+
/** Redacted access-key state exposed by the integrations admin API. */
|
|
2251
|
+
interface IntegrationKeyBindingStatus {
|
|
2252
|
+
id: string;
|
|
2253
|
+
name: string;
|
|
2254
|
+
keyPrefix: string;
|
|
2255
|
+
ownership: IntegrationKeyOwnership;
|
|
2256
|
+
revealable: boolean;
|
|
2257
|
+
enabled: boolean;
|
|
2258
|
+
revoked: boolean;
|
|
2259
|
+
allowedEndpoints: OutboundPermission[];
|
|
2260
|
+
requiredEndpoints: OutboundPermission[];
|
|
2261
|
+
loopbackOnly: boolean;
|
|
2262
|
+
}
|
|
1731
2263
|
type IntegrationStatusKind = 'not-installed' | 'enabled' | 'configuration-drift' | 'configuration-missing' | 'key-missing';
|
|
1732
2264
|
interface IntegrationClientStatus {
|
|
1733
2265
|
client: IntegrationClientId;
|
|
@@ -1736,6 +2268,8 @@ interface IntegrationClientStatus {
|
|
|
1736
2268
|
installedAt?: number;
|
|
1737
2269
|
gatewayBaseUrl?: string;
|
|
1738
2270
|
message?: string;
|
|
2271
|
+
/** Selected key metadata only; never contains plaintext or the encrypted envelope. */
|
|
2272
|
+
key?: IntegrationKeyBindingStatus;
|
|
1739
2273
|
}
|
|
1740
2274
|
interface IntegrationChangePlan {
|
|
1741
2275
|
client: IntegrationClientId;
|
|
@@ -1775,7 +2309,9 @@ interface IntegrationGatewayKeyRecord {
|
|
|
1775
2309
|
}
|
|
1776
2310
|
interface IntegrationState {
|
|
1777
2311
|
version: 1;
|
|
2312
|
+
/** Legacy shared-key layout. New installs use `keyBindings`; retained for safe migration. */
|
|
1778
2313
|
gatewayKey?: IntegrationGatewayKeyRecord;
|
|
2314
|
+
keyBindings?: Partial<Record<IntegrationClientId, IntegrationKeyBinding>>;
|
|
1779
2315
|
clients: Partial<Record<IntegrationClientId, IntegrationInstallRecord>>;
|
|
1780
2316
|
}
|
|
1781
2317
|
|
|
@@ -1793,27 +2329,38 @@ interface IntegrationManagerOptions {
|
|
|
1793
2329
|
gatewayBaseUrl: string;
|
|
1794
2330
|
keyDb: OutboundKeyDb;
|
|
1795
2331
|
stateStore: IntegrationStateStore;
|
|
2332
|
+
codexAuthHelper?: CodexAuthHelperConfig;
|
|
1796
2333
|
homeDir?: string;
|
|
1797
2334
|
}
|
|
1798
|
-
/** Coordinates
|
|
2335
|
+
/** Coordinates per-client least-privilege keys with reversible native CLI config edits. */
|
|
1799
2336
|
declare class IntegrationManager {
|
|
1800
2337
|
private readonly options;
|
|
1801
2338
|
private readonly homeDir;
|
|
2339
|
+
private readonly codexAuthHelper;
|
|
1802
2340
|
constructor(options: IntegrationManagerOptions);
|
|
1803
2341
|
listStatus(): Promise<IntegrationClientStatus[]>;
|
|
1804
2342
|
plan(client: IntegrationClientId, configPath?: string): Promise<IntegrationChangePlan>;
|
|
1805
2343
|
install(client: IntegrationClientId, configPath?: string): Promise<IntegrationClientStatus>;
|
|
1806
2344
|
repair(client: IntegrationClientId): Promise<IntegrationClientStatus>;
|
|
1807
2345
|
remove(client: IntegrationClientId): Promise<IntegrationClientStatus>;
|
|
2346
|
+
/** Bind a user-confirmed access key and grant only this client's required endpoints. */
|
|
2347
|
+
bindIntegrationKey(client: IntegrationClientId, keyId: string): Promise<IntegrationClientStatus>;
|
|
2348
|
+
/** Rotate every Omnicross-managed client binding; user-selected keys remain untouched. */
|
|
1808
2349
|
rotateGatewayKey(): Promise<{
|
|
1809
|
-
|
|
2350
|
+
keyIds: Partial<Record<IntegrationClientId, string>>;
|
|
1810
2351
|
}>;
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
2352
|
+
/** Resolve the plaintext only for the command-auth helper; callers must not log it. */
|
|
2353
|
+
getIntegrationToken(client: IntegrationClientId): Promise<string>;
|
|
2354
|
+
/** Compatibility alias for callers predating per-client bindings. */
|
|
2355
|
+
getGatewayToken(client?: IntegrationClientId): Promise<string>;
|
|
2356
|
+
private ensureClientKey;
|
|
2357
|
+
private createManagedClientKey;
|
|
2358
|
+
private installedSecret;
|
|
2359
|
+
private rebindInstalledClient;
|
|
1814
2360
|
private statusFor;
|
|
2361
|
+
private boundKeyDetails;
|
|
2362
|
+
private retireManagedKeys;
|
|
1815
2363
|
private defaultConfigPath;
|
|
1816
|
-
private codexAuthPathForConfig;
|
|
1817
2364
|
private renderInstalled;
|
|
1818
2365
|
}
|
|
1819
2366
|
|
|
@@ -1933,6 +2480,11 @@ interface PoolKeyHealth {
|
|
|
1933
2480
|
interface PoolHealthReader {
|
|
1934
2481
|
getKeyHealth(providerId: string): Promise<Record<string, PoolKeyHealth>>;
|
|
1935
2482
|
}
|
|
2483
|
+
interface AdminImagesStatusReader {
|
|
2484
|
+
inspectCapability(apiKeyId: string): Promise<ImageRuntimeCapabilityInspection>;
|
|
2485
|
+
status(): ImageRuntimeManagerStatus;
|
|
2486
|
+
resourceStatus(): ImageRuntimeResourceStatus | undefined;
|
|
2487
|
+
}
|
|
1936
2488
|
/** The live daemon handles the management API operates over. */
|
|
1937
2489
|
interface AdminApiDeps {
|
|
1938
2490
|
/** Path to the daemon's `config.json` (provider catalog + `server` field). */
|
|
@@ -1957,6 +2509,16 @@ interface AdminApiDeps {
|
|
|
1957
2509
|
readonly settingsStore: JsonApiServerSettingsStore;
|
|
1958
2510
|
/** The running outbound server (status + live applyConfig). */
|
|
1959
2511
|
readonly outboundApiServer: OutboundApiServer;
|
|
2512
|
+
/** True only when production composed the hardened per-hop remote resolver. */
|
|
2513
|
+
readonly imageRemoteResolverAvailable?: boolean;
|
|
2514
|
+
/** Optional production Images runtime generation participant. */
|
|
2515
|
+
readonly imageRuntimeConfig?: {
|
|
2516
|
+
prepareConfig(config: ImagesServerConfig): Promise<PreparedServerConfigChange>;
|
|
2517
|
+
};
|
|
2518
|
+
/** Narrow metadata-only reader for authenticated Images capability/status. */
|
|
2519
|
+
readonly imageRuntimeStatus?: AdminImagesStatusReader;
|
|
2520
|
+
/** Metadata-only successful Images configuration audit sink. */
|
|
2521
|
+
readonly imageConfigAudit?: (record: ImageConfigurationAuditRecord) => void;
|
|
1960
2522
|
/** Process-local machine-managed routing leases (optional for light embedders). */
|
|
1961
2523
|
readonly routeLeaseManager?: RouteLeaseManager;
|
|
1962
2524
|
/** Subscription accounts (token-free `listAll`). */
|
|
@@ -2132,6 +2694,13 @@ interface AdminServerDeps extends AdminApiDeps {
|
|
|
2132
2694
|
auditReader?: AuditQueryReader;
|
|
2133
2695
|
/** Metadata-only audit aggregate used by the overview error-rate metric. */
|
|
2134
2696
|
auditStatsReader?: AuditStatsReader;
|
|
2697
|
+
/**
|
|
2698
|
+
* Reconstructs one record's bodies from the per-session shard store
|
|
2699
|
+
* (audit-store-sharding). Absent when audit was never enabled.
|
|
2700
|
+
*/
|
|
2701
|
+
auditBodyReader?: AuditBodyReader;
|
|
2702
|
+
/** Runs cross-session body compaction on demand (audit-store-sharding D8). */
|
|
2703
|
+
auditCompactor?: AuditCompactor;
|
|
2135
2704
|
/**
|
|
2136
2705
|
* OPTIONAL billing delivery-status reader (billing-event-stream, design D5).
|
|
2137
2706
|
* When wired (bootstrap → the ledger dir), the AUTHED `GET /admin/api/billing-status`
|
|
@@ -2174,9 +2743,673 @@ declare class AdminServer {
|
|
|
2174
2743
|
getStatus(): AdminServerStatus;
|
|
2175
2744
|
}
|
|
2176
2745
|
|
|
2746
|
+
type DaemonImagePathArea = 'temporary' | 'artifacts' | 'state' | 'evidence' | 'mountManifest';
|
|
2747
|
+
interface DaemonImagePaths {
|
|
2748
|
+
readonly applicationDataRoot: string;
|
|
2749
|
+
readonly imagesRoot: string;
|
|
2750
|
+
readonly temporaryRoot: string;
|
|
2751
|
+
readonly durableRoot: string;
|
|
2752
|
+
readonly artifactsRoot: string;
|
|
2753
|
+
readonly stateRoot: string;
|
|
2754
|
+
readonly evidenceRoot: string;
|
|
2755
|
+
readonly mountManifestRoot: string;
|
|
2756
|
+
readonly mountManifestPath: string;
|
|
2757
|
+
}
|
|
2758
|
+
interface ImageRootValidationOptions {
|
|
2759
|
+
readonly label?: string;
|
|
2760
|
+
readonly processDirectory?: string;
|
|
2761
|
+
readonly userHome?: string;
|
|
2762
|
+
readonly temporaryDirectory?: string;
|
|
2763
|
+
}
|
|
2764
|
+
interface CreateDaemonImagePathResolverOptions extends ImageRootValidationOptions {
|
|
2765
|
+
readonly configPath: string;
|
|
2766
|
+
readonly storageRoot?: string;
|
|
2767
|
+
}
|
|
2768
|
+
interface VerifiedDaemonImagePath {
|
|
2769
|
+
readonly area: DaemonImagePathArea;
|
|
2770
|
+
readonly absolutePath: string;
|
|
2771
|
+
readonly kind: 'opaque-file' | 'opaque-directory' | 'mount-manifest';
|
|
2772
|
+
}
|
|
2773
|
+
/**
|
|
2774
|
+
* Owns all daemon Images filesystem names. Callers receive opaque capabilities,
|
|
2775
|
+
* never a filename-accepting delete primitive; destructive methods revalidate
|
|
2776
|
+
* the root identity, descendant relationship, basename, and symlink state.
|
|
2777
|
+
*/
|
|
2778
|
+
declare class DaemonImagePathResolver {
|
|
2779
|
+
#private;
|
|
2780
|
+
readonly paths: DaemonImagePaths;
|
|
2781
|
+
constructor(options: CreateDaemonImagePathResolverOptions);
|
|
2782
|
+
createOpaqueFile(area: Exclude<DaemonImagePathArea, 'temporary' | 'mountManifest'>, format?: 'bin' | 'json' | 'tmp'): VerifiedDaemonImagePath;
|
|
2783
|
+
createOpaqueDirectory(area?: Exclude<DaemonImagePathArea, 'mountManifest'>): VerifiedDaemonImagePath;
|
|
2784
|
+
mountManifest(): VerifiedDaemonImagePath;
|
|
2785
|
+
/** Revalidate and return one internal root for store-local bounded I/O. */
|
|
2786
|
+
verifiedRoot(area: DaemonImagePathArea): string;
|
|
2787
|
+
/** Revalidate immediately before unlinking a resolver-issued file capability. */
|
|
2788
|
+
removeFile(target: VerifiedDaemonImagePath): void;
|
|
2789
|
+
/** Only empty opaque directories may be removed until the owned-marker layer is composed. */
|
|
2790
|
+
removeEmptyDirectory(target: VerifiedDaemonImagePath): void;
|
|
2791
|
+
private issue;
|
|
2792
|
+
private verifyDestructiveTarget;
|
|
2793
|
+
}
|
|
2794
|
+
|
|
2795
|
+
interface FileCodexImageCapabilityEvidenceManifestOwnerOptions {
|
|
2796
|
+
readonly paths: DaemonImagePathResolver;
|
|
2797
|
+
readonly maxEntries?: number;
|
|
2798
|
+
readonly now?: () => number;
|
|
2799
|
+
readonly random?: (bytes: number) => Buffer;
|
|
2800
|
+
readonly hmacSalt?: Uint8Array;
|
|
2801
|
+
readonly replaceManifest?: (targetPath: string, contents: Uint8Array) => void;
|
|
2802
|
+
}
|
|
2803
|
+
type FileCodexImageCapabilityEvidenceSourceOptions = Readonly<(FileCodexImageCapabilityEvidenceManifestOwnerOptions & {
|
|
2804
|
+
readonly ttlMs: number;
|
|
2805
|
+
}) | {
|
|
2806
|
+
readonly owner: FileCodexImageCapabilityEvidenceManifestOwner;
|
|
2807
|
+
readonly ttlMs: number;
|
|
2808
|
+
}>;
|
|
2809
|
+
interface FileCodexImageCapabilityEvidenceStatus {
|
|
2810
|
+
readonly entries: number;
|
|
2811
|
+
readonly freshEntries: number;
|
|
2812
|
+
readonly staleEntries: number;
|
|
2813
|
+
readonly bytes: number;
|
|
2814
|
+
}
|
|
2815
|
+
/** Revision-aware manifest owner shared by runtime generations, doctor, and cleanup. */
|
|
2816
|
+
declare class FileCodexImageCapabilityEvidenceManifestOwner {
|
|
2817
|
+
#private;
|
|
2818
|
+
constructor(options: FileCodexImageCapabilityEvidenceManifestOwnerOptions);
|
|
2819
|
+
createSource(ttlMs: number): FileCodexImageCapabilityEvidenceSource;
|
|
2820
|
+
resolveWithTtl(request: CodexImageCapabilityEvidenceRequest, ttlMs: number): Promise<CodexImageCapabilityEvidence>;
|
|
2821
|
+
recordSuccessfulVerificationWithTtl(observation: CodexImageCapabilityObservation, ttlMs: number): Promise<void>;
|
|
2822
|
+
cleanup(now: number, limit: number): Promise<{
|
|
2823
|
+
readonly entriesRemoved: number;
|
|
2824
|
+
readonly bytesRemoved: number;
|
|
2825
|
+
}>;
|
|
2826
|
+
statusWithTtl(ttlMs: number): FileCodexImageCapabilityEvidenceStatus;
|
|
2827
|
+
}
|
|
2828
|
+
/** Immutable TTL view over a revision-aware file-backed evidence manifest owner. */
|
|
2829
|
+
declare class FileCodexImageCapabilityEvidenceSource implements CodexImageCapabilityEvidenceSource {
|
|
2830
|
+
#private;
|
|
2831
|
+
constructor(options: FileCodexImageCapabilityEvidenceSourceOptions);
|
|
2832
|
+
createView(ttlMs: number): FileCodexImageCapabilityEvidenceSource;
|
|
2833
|
+
resolve(request: CodexImageCapabilityEvidenceRequest): Promise<CodexImageCapabilityEvidence>;
|
|
2834
|
+
recordSuccessfulVerification(observation: CodexImageCapabilityObservation): Promise<void>;
|
|
2835
|
+
cleanup(now: number, limit: number): Promise<{
|
|
2836
|
+
readonly entriesRemoved: number;
|
|
2837
|
+
readonly bytesRemoved: number;
|
|
2838
|
+
}>;
|
|
2839
|
+
status(): FileCodexImageCapabilityEvidenceStatus;
|
|
2840
|
+
ttlMs(): number;
|
|
2841
|
+
/** Lifecycle-symmetric no-op; physical safety no longer depends on local leases. */
|
|
2842
|
+
dispose(): void;
|
|
2843
|
+
}
|
|
2844
|
+
|
|
2845
|
+
interface FileImageReferenceStoreLimits {
|
|
2846
|
+
readonly ttlMs: number;
|
|
2847
|
+
readonly maxArtifactBytes: number;
|
|
2848
|
+
readonly maxTotalBytes: number;
|
|
2849
|
+
readonly maxTenantBytes: number;
|
|
2850
|
+
readonly maxEntries: number;
|
|
2851
|
+
readonly maxTombstones: number;
|
|
2852
|
+
readonly tombstoneTtlMs: number;
|
|
2853
|
+
}
|
|
2854
|
+
interface FileImageReferenceStoreOptions {
|
|
2855
|
+
readonly paths: DaemonImagePathResolver;
|
|
2856
|
+
readonly limits: FileImageReferenceStoreLimits;
|
|
2857
|
+
readonly secretBox?: SecretBox;
|
|
2858
|
+
readonly now?: () => number;
|
|
2859
|
+
readonly random?: (bytes: number) => Buffer;
|
|
2860
|
+
readonly replaceManifest?: (targetPath: string, contents: Uint8Array) => void;
|
|
2861
|
+
}
|
|
2862
|
+
interface FileImageReferenceReconciliationResult {
|
|
2863
|
+
readonly metadataRemoved: number;
|
|
2864
|
+
readonly metadataDegradedToProviderReference: number;
|
|
2865
|
+
readonly orphanFilesRemoved: number;
|
|
2866
|
+
readonly incompleteFilesRemoved: number;
|
|
2867
|
+
readonly invalidDescendants: number;
|
|
2868
|
+
}
|
|
2869
|
+
declare class FileImageReferenceStore implements ImageReferenceStore {
|
|
2870
|
+
#private;
|
|
2871
|
+
constructor(options: FileImageReferenceStoreOptions);
|
|
2872
|
+
save(input: ImageReferenceSaveInput): Promise<ImageReferenceMetadata>;
|
|
2873
|
+
/** Generation-bound write entry point; reads and maintenance remain shared. */
|
|
2874
|
+
saveWithLimits(input: ImageReferenceSaveInput, limits: FileImageReferenceStoreLimits): Promise<ImageReferenceMetadata>;
|
|
2875
|
+
/** Updates only app-session maintenance policy; pinned writes pass their own limits. */
|
|
2876
|
+
updateMaintenanceLimits(limits: FileImageReferenceStoreLimits): void;
|
|
2877
|
+
resolve(tenantId: string, referenceId: ImageReferenceId): Promise<ImageReferenceResolution>;
|
|
2878
|
+
delete(tenantId: string, referenceId: ImageReferenceId): Promise<boolean>;
|
|
2879
|
+
/** Daemon-internal cleanup path; accepts only the local reference-domain tenant HMAC. */
|
|
2880
|
+
deleteByHashedTenantKey(tenantKey: string, referenceId: ImageReferenceId): Promise<boolean>;
|
|
2881
|
+
cleanup(now?: number): Promise<number>;
|
|
2882
|
+
status(): {
|
|
2883
|
+
readonly entries: number;
|
|
2884
|
+
readonly bytes: number;
|
|
2885
|
+
readonly tombstones: number;
|
|
2886
|
+
};
|
|
2887
|
+
hasLiveReferenceByHashedTenantKey(tenantKey: string, referenceId: ImageReferenceId, now?: number): Promise<boolean>;
|
|
2888
|
+
reconcileOwnedFiles(maxEntries: number): Promise<FileImageReferenceReconciliationResult>;
|
|
2889
|
+
openArtifact(fileName: string, byteLength: number, signal?: AbortSignal): Promise<ReadableStream<Uint8Array>>;
|
|
2890
|
+
private exclusive;
|
|
2891
|
+
private tenantKey;
|
|
2892
|
+
private newReferenceId;
|
|
2893
|
+
private validateSaveInput;
|
|
2894
|
+
private selectVictims;
|
|
2895
|
+
private nextTombstones;
|
|
2896
|
+
private writeArtifact;
|
|
2897
|
+
private artifactPath;
|
|
2898
|
+
private validArtifact;
|
|
2899
|
+
private removeArtifact;
|
|
2900
|
+
private safeUnlinkArtifactPath;
|
|
2901
|
+
private releaseLease;
|
|
2902
|
+
private manifestPath;
|
|
2903
|
+
private persist;
|
|
2904
|
+
private atomicReplace;
|
|
2905
|
+
private loadManifest;
|
|
2906
|
+
}
|
|
2907
|
+
|
|
2908
|
+
interface FileResponsesImageStateStoreLimits {
|
|
2909
|
+
readonly maxCalls: number;
|
|
2910
|
+
readonly maxResponses: number;
|
|
2911
|
+
readonly maxTombstones: number;
|
|
2912
|
+
readonly tombstoneTtlMs: number;
|
|
2913
|
+
}
|
|
2914
|
+
interface FileResponsesImageStateStoreOptions {
|
|
2915
|
+
readonly paths: DaemonImagePathResolver;
|
|
2916
|
+
readonly limits: FileResponsesImageStateStoreLimits;
|
|
2917
|
+
readonly now?: () => number;
|
|
2918
|
+
readonly random?: (bytes: number) => Buffer;
|
|
2919
|
+
readonly replaceManifest?: (targetPath: string, contents: Uint8Array) => void;
|
|
2920
|
+
}
|
|
2921
|
+
interface PendingResponsesImageReferenceDelete {
|
|
2922
|
+
readonly referenceTenantKey: string;
|
|
2923
|
+
readonly binding: ResponsesImageCallBinding;
|
|
2924
|
+
}
|
|
2925
|
+
/** Durable production implementation of the existing Responses image-state contract. */
|
|
2926
|
+
declare class FileResponsesImageStateStore implements ResponsesImageStateStore {
|
|
2927
|
+
#private;
|
|
2928
|
+
constructor(options: FileResponsesImageStateStoreOptions);
|
|
2929
|
+
commit(input: ResponsesImageStateCommitInput): Promise<readonly ResponsesImageCallBinding[]>;
|
|
2930
|
+
/** Generation-bound write entry point; reads and maintenance remain shared. */
|
|
2931
|
+
commitWithLimits(input: ResponsesImageStateCommitInput, limits: FileResponsesImageStateStoreLimits): Promise<readonly ResponsesImageCallBinding[]>;
|
|
2932
|
+
/** Updates only app-session maintenance policy; pinned commits pass their own limits. */
|
|
2933
|
+
updateMaintenanceLimits(limits: FileResponsesImageStateStoreLimits): void;
|
|
2934
|
+
resolveCall(tenantId: string, callId: ResponsesImageCallId): Promise<ResponsesImageCallResolution>;
|
|
2935
|
+
resolveResponse(tenantId: string, responseId: string): Promise<ResponsesImageResponseResolution>;
|
|
2936
|
+
deleteCall(tenantId: string, callId: ResponsesImageCallId): Promise<ResponsesImageCallBinding | undefined>;
|
|
2937
|
+
deleteResponse(tenantId: string, responseId: string): Promise<boolean>;
|
|
2938
|
+
cleanup(now?: number): Promise<readonly ResponsesImageCallBinding[]>;
|
|
2939
|
+
pendingReferenceDeletes(limit?: number): readonly PendingResponsesImageReferenceDelete[];
|
|
2940
|
+
acknowledgeReferenceDeletes(completed: readonly PendingResponsesImageReferenceDelete[]): Promise<number>;
|
|
2941
|
+
reconcileBrokenReferenceLinks(hasLiveReference: (referenceTenantKey: string, referenceId: ResponsesImageCallBinding['referenceId']) => Promise<boolean>, maxEntries: number): Promise<readonly ResponsesImageCallBinding[]>;
|
|
2942
|
+
status(): {
|
|
2943
|
+
readonly calls: number;
|
|
2944
|
+
readonly responses: number;
|
|
2945
|
+
readonly tombstones: number;
|
|
2946
|
+
readonly pendingReferenceDeletes: number;
|
|
2947
|
+
};
|
|
2948
|
+
private exclusive;
|
|
2949
|
+
private failure;
|
|
2950
|
+
private assertCommit;
|
|
2951
|
+
private tenantKey;
|
|
2952
|
+
private rememberTombstone;
|
|
2953
|
+
private enqueuePendingReferenceDelete;
|
|
2954
|
+
private hasTombstone;
|
|
2955
|
+
private prunedTombstones;
|
|
2956
|
+
private pruneTombstonesInPlace;
|
|
2957
|
+
private sameTombstones;
|
|
2958
|
+
private touch;
|
|
2959
|
+
private releaseCall;
|
|
2960
|
+
private releaseResponse;
|
|
2961
|
+
private manifestPath;
|
|
2962
|
+
private persist;
|
|
2963
|
+
private atomicReplace;
|
|
2964
|
+
private loadManifest;
|
|
2965
|
+
}
|
|
2966
|
+
|
|
2967
|
+
interface ImageStorageMountBackend {
|
|
2968
|
+
readonly id: string;
|
|
2969
|
+
readonly createdAt: number;
|
|
2970
|
+
readonly resolver: DaemonImagePathResolver;
|
|
2971
|
+
readonly references: FileImageReferenceStore;
|
|
2972
|
+
readonly responsesState: FileResponsesImageStateStore;
|
|
2973
|
+
}
|
|
2974
|
+
interface ImageStorageMountCatalogOptions {
|
|
2975
|
+
readonly pathOptions: Omit<CreateDaemonImagePathResolverOptions, 'storageRoot'>;
|
|
2976
|
+
readonly activeStorageRoot?: string;
|
|
2977
|
+
readonly referenceLimits: FileImageReferenceStoreLimits;
|
|
2978
|
+
readonly responsesStateLimits: FileResponsesImageStateStoreLimits;
|
|
2979
|
+
readonly secretBox?: SecretBox;
|
|
2980
|
+
readonly now?: () => number;
|
|
2981
|
+
readonly random?: (bytes: number) => Buffer;
|
|
2982
|
+
readonly replaceCatalog?: (targetPath: string, contents: Uint8Array) => void;
|
|
2983
|
+
readonly reconcileCorruptManifests?: boolean;
|
|
2984
|
+
}
|
|
2985
|
+
interface ImageStorageMountPolicy {
|
|
2986
|
+
readonly referenceLimits: FileImageReferenceStoreLimits;
|
|
2987
|
+
readonly responsesStateLimits: FileResponsesImageStateStoreLimits;
|
|
2988
|
+
}
|
|
2989
|
+
interface PreparedImageStorageMountActivation {
|
|
2990
|
+
readonly backend: ImageStorageMountBackend;
|
|
2991
|
+
publish(): ImageStorageMountBackend;
|
|
2992
|
+
rollback(): void;
|
|
2993
|
+
dispose(): void;
|
|
2994
|
+
}
|
|
2995
|
+
/** Owns the durable-root set independently from any one runtime generation. */
|
|
2996
|
+
declare class ImageStorageMountCatalog {
|
|
2997
|
+
#private;
|
|
2998
|
+
constructor(options: ImageStorageMountCatalogOptions);
|
|
2999
|
+
active(): ImageStorageMountBackend;
|
|
3000
|
+
mountsForRead(): readonly ImageStorageMountBackend[];
|
|
3001
|
+
status(): {
|
|
3002
|
+
readonly mounts: number;
|
|
3003
|
+
readonly retiredMounts: number;
|
|
3004
|
+
};
|
|
3005
|
+
startupReconciliationStatus(): {
|
|
3006
|
+
readonly corruptManifestsQuarantined: number;
|
|
3007
|
+
};
|
|
3008
|
+
utilization(): {
|
|
3009
|
+
readonly referenceEntries: number;
|
|
3010
|
+
readonly referenceBytes: number;
|
|
3011
|
+
readonly referenceTombstones: number;
|
|
3012
|
+
readonly stateCalls: number;
|
|
3013
|
+
readonly stateResponses: number;
|
|
3014
|
+
readonly stateTombstones: number;
|
|
3015
|
+
readonly pendingReferenceDeletes: number;
|
|
3016
|
+
};
|
|
3017
|
+
/** Pins a backend object until its owning runtime generation drains. */
|
|
3018
|
+
retainBackend(backend: ImageStorageMountBackend): () => void;
|
|
3019
|
+
activate(storageRoot?: string): ImageStorageMountBackend;
|
|
3020
|
+
/** Prepare a validated backend without changing the catalog's active mount. */
|
|
3021
|
+
prepareActivation(storageRoot?: string, policy?: ImageStorageMountPolicy): PreparedImageStorageMountActivation;
|
|
3022
|
+
retireEmptyMount(mountId: string): boolean;
|
|
3023
|
+
private createResolver;
|
|
3024
|
+
private createBackend;
|
|
3025
|
+
private applyMaintenancePolicy;
|
|
3026
|
+
private newMountId;
|
|
3027
|
+
private isManifestError;
|
|
3028
|
+
private quarantineManifest;
|
|
3029
|
+
private isVerifiedEmpty;
|
|
3030
|
+
private catalogPath;
|
|
3031
|
+
private persist;
|
|
3032
|
+
private atomicReplace;
|
|
3033
|
+
private loadCatalog;
|
|
3034
|
+
}
|
|
3035
|
+
declare class MountedImageReferenceStore implements ImageReferenceStore {
|
|
3036
|
+
private readonly catalog;
|
|
3037
|
+
private readonly writeBackend?;
|
|
3038
|
+
private readonly writeLimits?;
|
|
3039
|
+
constructor(catalog: ImageStorageMountCatalog, writeBackend?: ImageStorageMountBackend | undefined, writeLimits?: FileImageReferenceStoreLimits | undefined);
|
|
3040
|
+
bindWriteBackend(backend: ImageStorageMountBackend, limits: FileImageReferenceStoreLimits): MountedImageReferenceStore;
|
|
3041
|
+
status(): Readonly<{
|
|
3042
|
+
referenceEntries: number;
|
|
3043
|
+
referenceBytes: number;
|
|
3044
|
+
referenceTombstones: number;
|
|
3045
|
+
stateCalls: number;
|
|
3046
|
+
stateResponses: number;
|
|
3047
|
+
stateTombstones: number;
|
|
3048
|
+
pendingReferenceDeletes: number;
|
|
3049
|
+
mounts: number;
|
|
3050
|
+
retiredMounts: number;
|
|
3051
|
+
}>;
|
|
3052
|
+
save(input: ImageReferenceSaveInput): Promise<_omnicross_contracts_image_generation_types.ImageReferenceMetadata>;
|
|
3053
|
+
resolve(tenantId: string, referenceId: ImageReferenceId): Promise<ImageReferenceResolution>;
|
|
3054
|
+
delete(tenantId: string, referenceId: ImageReferenceId): Promise<boolean>;
|
|
3055
|
+
deleteByHashedTenantKey(tenantKey: string, referenceId: ImageReferenceId): Promise<boolean>;
|
|
3056
|
+
cleanup(now?: number): Promise<number>;
|
|
3057
|
+
}
|
|
3058
|
+
declare class MountedResponsesImageStateStore implements ResponsesImageStateStore {
|
|
3059
|
+
private readonly catalog;
|
|
3060
|
+
private readonly writeBackend?;
|
|
3061
|
+
private readonly writeLimits?;
|
|
3062
|
+
constructor(catalog: ImageStorageMountCatalog, writeBackend?: ImageStorageMountBackend | undefined, writeLimits?: FileResponsesImageStateStoreLimits | undefined);
|
|
3063
|
+
bindWriteBackend(backend: ImageStorageMountBackend, limits: FileResponsesImageStateStoreLimits): MountedResponsesImageStateStore;
|
|
3064
|
+
commit(input: ResponsesImageStateCommitInput): Promise<readonly ResponsesImageCallBinding[]>;
|
|
3065
|
+
resolveCall(tenantId: string, callId: ResponsesImageCallId): Promise<ResponsesImageCallResolution>;
|
|
3066
|
+
resolveResponse(tenantId: string, responseId: string): Promise<ResponsesImageResponseResolution>;
|
|
3067
|
+
deleteCall(tenantId: string, callId: ResponsesImageCallId): Promise<ResponsesImageCallBinding | undefined>;
|
|
3068
|
+
deleteResponse(tenantId: string, responseId: string): Promise<boolean>;
|
|
3069
|
+
cleanup(now?: number): Promise<readonly ResponsesImageCallBinding[]>;
|
|
3070
|
+
}
|
|
3071
|
+
|
|
3072
|
+
interface ImageDoctorLocalSnapshot {
|
|
3073
|
+
readonly config: Readonly<{
|
|
3074
|
+
enabled: boolean;
|
|
3075
|
+
provider: 'codex-subscription';
|
|
3076
|
+
model: string;
|
|
3077
|
+
valid: boolean;
|
|
3078
|
+
errorCount: number;
|
|
3079
|
+
}>;
|
|
3080
|
+
readonly roots: Readonly<{
|
|
3081
|
+
valid: boolean;
|
|
3082
|
+
verifiedAreas: number;
|
|
3083
|
+
expectedAreas: number;
|
|
3084
|
+
}>;
|
|
3085
|
+
readonly stores: Readonly<{
|
|
3086
|
+
valid: boolean;
|
|
3087
|
+
mounts: number;
|
|
3088
|
+
retiredMounts: number;
|
|
3089
|
+
referenceEntries: number;
|
|
3090
|
+
referenceBytes: number;
|
|
3091
|
+
stateCalls: number;
|
|
3092
|
+
stateResponses: number;
|
|
3093
|
+
corruptManifestsQuarantined: number;
|
|
3094
|
+
}>;
|
|
3095
|
+
readonly permissions: Readonly<{
|
|
3096
|
+
valid: boolean;
|
|
3097
|
+
rows: number;
|
|
3098
|
+
legacyRows: number;
|
|
3099
|
+
invalidRows: number;
|
|
3100
|
+
imagesAuthorizedRows: number;
|
|
3101
|
+
}>;
|
|
3102
|
+
readonly account: Readonly<{
|
|
3103
|
+
present: boolean;
|
|
3104
|
+
usable: boolean;
|
|
3105
|
+
reason: 'ready' | 'missing' | 'unavailable';
|
|
3106
|
+
}>;
|
|
3107
|
+
readonly evidence: Readonly<FileCodexImageCapabilityEvidenceStatus & {
|
|
3108
|
+
valid: boolean;
|
|
3109
|
+
}>;
|
|
3110
|
+
}
|
|
3111
|
+
type ImageDoctorLiveFailureCode = 'images_disabled' | 'codex_account_unavailable' | 'evidence_store_unavailable' | 'evidence_persist_failed' | ImageGenerationErrorCode;
|
|
3112
|
+
type ImageDoctorLiveResult = Readonly<{
|
|
3113
|
+
ok: true;
|
|
3114
|
+
code: 'verified';
|
|
3115
|
+
model: 'gpt-image-2';
|
|
3116
|
+
quality: 'low';
|
|
3117
|
+
outputFormat: 'png';
|
|
3118
|
+
freshEvidenceEntries: number;
|
|
3119
|
+
}> | Readonly<{
|
|
3120
|
+
ok: false;
|
|
3121
|
+
code: ImageDoctorLiveFailureCode;
|
|
3122
|
+
}>;
|
|
3123
|
+
interface ImageDoctorService {
|
|
3124
|
+
inspectLocal(config: ImagesServerConfig): Promise<ImageDoctorLocalSnapshot>;
|
|
3125
|
+
verifyLive(config: ImagesServerConfig, signal: AbortSignal): Promise<ImageDoctorLiveResult>;
|
|
3126
|
+
}
|
|
3127
|
+
|
|
3128
|
+
interface ImageTemporaryBudgetStatus {
|
|
3129
|
+
readonly activeScopes: number;
|
|
3130
|
+
readonly totalBytes: number;
|
|
3131
|
+
readonly tenantCount: number;
|
|
3132
|
+
}
|
|
3133
|
+
/** Process-local atomic budget shared by every scope in one pinned runtime generation. */
|
|
3134
|
+
declare class DaemonImageTemporaryBudget implements ImageTemporaryResourceBudget {
|
|
3135
|
+
#private;
|
|
3136
|
+
constructor(config: ImagesServerConfig['temporary']);
|
|
3137
|
+
acquireScope(tenantId: string): ImageTemporaryResourceBudgetLease;
|
|
3138
|
+
status(): ImageTemporaryBudgetStatus;
|
|
3139
|
+
}
|
|
3140
|
+
interface DaemonImageTemporaryResourceFactoryOptions {
|
|
3141
|
+
readonly paths: DaemonImagePathResolver;
|
|
3142
|
+
readonly config: ImagesServerConfig['temporary'];
|
|
3143
|
+
readonly activeScopes?: DaemonImageActiveScopeRegistry;
|
|
3144
|
+
}
|
|
3145
|
+
/** App-session registry shared by all runtime generations and recurring cleanup. */
|
|
3146
|
+
declare class DaemonImageActiveScopeRegistry {
|
|
3147
|
+
#private;
|
|
3148
|
+
constructor(paths: DaemonImagePathResolver);
|
|
3149
|
+
register(privateDirectory: string): () => void;
|
|
3150
|
+
isActive(privateDirectory: string): boolean;
|
|
3151
|
+
status(): {
|
|
3152
|
+
readonly activeDirectories: number;
|
|
3153
|
+
};
|
|
3154
|
+
}
|
|
3155
|
+
/** Binds the private root, fixed owner marker, tenant, and shared budget in one seam. */
|
|
3156
|
+
declare class DaemonImageTemporaryResourceFactory {
|
|
3157
|
+
#private;
|
|
3158
|
+
readonly budget: DaemonImageTemporaryBudget;
|
|
3159
|
+
constructor(options: DaemonImageTemporaryResourceFactoryOptions);
|
|
3160
|
+
readonly createResourceScope: (limits: ImageApiLimits, signal: AbortSignal, tenantId: string) => Promise<ImageRequestResourceScope>;
|
|
3161
|
+
}
|
|
3162
|
+
|
|
3163
|
+
interface ImageStartupReconcilerOptions {
|
|
3164
|
+
readonly catalog: ImageStorageMountCatalog;
|
|
3165
|
+
readonly temporaryPaths: DaemonImagePathResolver;
|
|
3166
|
+
readonly staleTemporaryAfterMs: number;
|
|
3167
|
+
readonly activeTemporaryScopes?: Pick<DaemonImageActiveScopeRegistry, 'isActive'>;
|
|
3168
|
+
readonly maxMountsPerPass?: number;
|
|
3169
|
+
readonly maxEntriesPerMount?: number;
|
|
3170
|
+
readonly maxTemporaryDirectoriesPerPass?: number;
|
|
3171
|
+
readonly now?: () => number;
|
|
3172
|
+
}
|
|
3173
|
+
interface ImageStartupReconciliationResult {
|
|
3174
|
+
readonly corruptManifestsQuarantined: number;
|
|
3175
|
+
readonly mountsVisited: number;
|
|
3176
|
+
readonly stateBindingsRemoved: number;
|
|
3177
|
+
readonly brokenBindingsRemoved: number;
|
|
3178
|
+
readonly referenceEntriesRemoved: number;
|
|
3179
|
+
readonly metadataRemoved: number;
|
|
3180
|
+
readonly metadataDegradedToProviderReference: number;
|
|
3181
|
+
readonly orphanFilesRemoved: number;
|
|
3182
|
+
readonly incompleteFilesRemoved: number;
|
|
3183
|
+
readonly transactionFilesRemoved: number;
|
|
3184
|
+
readonly temporaryDirectoriesRemoved: number;
|
|
3185
|
+
readonly foreignTemporaryDirectoriesSkipped: number;
|
|
3186
|
+
readonly activeTemporaryDirectoriesSkipped: number;
|
|
3187
|
+
readonly invalidDescendantsSkipped: number;
|
|
3188
|
+
readonly pendingReferenceDeletes: number;
|
|
3189
|
+
}
|
|
3190
|
+
/** One bounded, synchronous-filesystem startup pass; it never follows symlinks. */
|
|
3191
|
+
declare class ImageStartupReconciler {
|
|
3192
|
+
#private;
|
|
3193
|
+
constructor(options: ImageStartupReconcilerOptions);
|
|
3194
|
+
run(): Promise<ImageStartupReconciliationResult>;
|
|
3195
|
+
private hasLiveReference;
|
|
3196
|
+
private removeTransactionFiles;
|
|
3197
|
+
private removeStaleTemporaryDirectories;
|
|
3198
|
+
private removeTreeWithoutFollowingSymlinks;
|
|
3199
|
+
}
|
|
3200
|
+
|
|
3201
|
+
interface BoundedImageEvidenceCleanup {
|
|
3202
|
+
cleanup(now: number, limit: number): Promise<{
|
|
3203
|
+
readonly entriesRemoved: number;
|
|
3204
|
+
readonly bytesRemoved: number;
|
|
3205
|
+
}>;
|
|
3206
|
+
}
|
|
3207
|
+
interface ImageCleanupTimer {
|
|
3208
|
+
unref(): unknown;
|
|
3209
|
+
}
|
|
3210
|
+
interface ImageCleanupServiceOptions {
|
|
3211
|
+
readonly reconciler: ImageStartupReconciler;
|
|
3212
|
+
readonly catalog: ImageStorageMountCatalog;
|
|
3213
|
+
readonly intervalMs: number;
|
|
3214
|
+
readonly maxEvidenceEntriesPerPass?: number;
|
|
3215
|
+
readonly maxRetiredMountsPerPass?: number;
|
|
3216
|
+
readonly evidence?: BoundedImageEvidenceCleanup;
|
|
3217
|
+
readonly now?: () => number;
|
|
3218
|
+
readonly scheduleInterval?: (callback: () => void, intervalMs: number) => ImageCleanupTimer;
|
|
3219
|
+
readonly clearScheduledInterval?: (timer: ImageCleanupTimer) => void;
|
|
3220
|
+
}
|
|
3221
|
+
interface ImageCleanupPolicy {
|
|
3222
|
+
readonly reconciler: ImageStartupReconciler;
|
|
3223
|
+
readonly intervalMs: number;
|
|
3224
|
+
readonly evidence?: BoundedImageEvidenceCleanup;
|
|
3225
|
+
}
|
|
3226
|
+
interface PreparedImageCleanupPolicyChange {
|
|
3227
|
+
publish(): void;
|
|
3228
|
+
rollback(): void;
|
|
3229
|
+
dispose(): void;
|
|
3230
|
+
}
|
|
3231
|
+
interface ImageCleanupServicePassResult {
|
|
3232
|
+
readonly stateBindingsRemoved: number;
|
|
3233
|
+
readonly brokenBindingsRemoved: number;
|
|
3234
|
+
readonly referenceEntriesRemoved: number;
|
|
3235
|
+
readonly orphanFilesRemoved: number;
|
|
3236
|
+
readonly incompleteFilesRemoved: number;
|
|
3237
|
+
readonly transactionFilesRemoved: number;
|
|
3238
|
+
readonly temporaryDirectoriesRemoved: number;
|
|
3239
|
+
readonly evidenceEntriesRemoved: number;
|
|
3240
|
+
readonly evidenceBytesRemoved: number;
|
|
3241
|
+
readonly evidenceCleanupFailures: number;
|
|
3242
|
+
readonly retiredMountsRemoved: number;
|
|
3243
|
+
readonly referenceEntries: number;
|
|
3244
|
+
readonly referenceBytes: number;
|
|
3245
|
+
readonly referenceTombstones: number;
|
|
3246
|
+
readonly stateCalls: number;
|
|
3247
|
+
readonly stateResponses: number;
|
|
3248
|
+
readonly stateTombstones: number;
|
|
3249
|
+
readonly pendingReferenceDeletes: number;
|
|
3250
|
+
readonly finishedAt: number;
|
|
3251
|
+
}
|
|
3252
|
+
interface ImageCleanupServiceStatus extends ImageCleanupServicePassResult {
|
|
3253
|
+
readonly running: boolean;
|
|
3254
|
+
readonly passesCompleted: number;
|
|
3255
|
+
readonly passFailures: number;
|
|
3256
|
+
}
|
|
3257
|
+
/** Unref'ed recurring lifecycle owner with one non-overlapping bounded pass. */
|
|
3258
|
+
declare class ImageCleanupService {
|
|
3259
|
+
#private;
|
|
3260
|
+
constructor(options: ImageCleanupServiceOptions);
|
|
3261
|
+
start(): void;
|
|
3262
|
+
private schedule;
|
|
3263
|
+
/** Prepares a hot-reloadable cadence/reconciler/evidence snapshot. */
|
|
3264
|
+
preparePolicy(policy: ImageCleanupPolicy): PreparedImageCleanupPolicyChange;
|
|
3265
|
+
stop(): Promise<void>;
|
|
3266
|
+
reset(): Promise<void>;
|
|
3267
|
+
runOnce(): Promise<ImageCleanupServicePassResult>;
|
|
3268
|
+
status(): ImageCleanupServiceStatus;
|
|
3269
|
+
private performPass;
|
|
3270
|
+
}
|
|
3271
|
+
|
|
3272
|
+
interface TrustedImageApiRuntimeResolverOptions {
|
|
3273
|
+
readonly config: ImagesServerConfig;
|
|
3274
|
+
readonly referenceStore: ImageReferenceStore;
|
|
3275
|
+
/** Persistent private daemon key material. It is copied on construction. */
|
|
3276
|
+
readonly hmacKey: Uint8Array;
|
|
3277
|
+
/** Present only when the host has proved the complete remote-fetch policy. */
|
|
3278
|
+
readonly provenRemoteResolver?: RemoteImageAssetResolver;
|
|
3279
|
+
}
|
|
3280
|
+
interface TrustedImageApiRuntimeResolver {
|
|
3281
|
+
readonly resolve: ImageApiRuntimeResolver;
|
|
3282
|
+
dispose(): void;
|
|
3283
|
+
}
|
|
3284
|
+
/**
|
|
3285
|
+
* Resolves only daemon-authenticated route state. Named outbound keys retain
|
|
3286
|
+
* their stable key-id tenant; ephemeral Route Lease traffic falls back to the
|
|
3287
|
+
* authenticated lease id and remains isolated from other leases. Request
|
|
3288
|
+
* headers, including an inbound bearer, are deliberately outside the resolver's
|
|
3289
|
+
* identity inputs.
|
|
3290
|
+
*/
|
|
3291
|
+
declare function createTrustedImageApiRuntimeResolver(options: TrustedImageApiRuntimeResolverOptions): TrustedImageApiRuntimeResolver;
|
|
3292
|
+
|
|
3293
|
+
interface DaemonImageExecutionSchedulerOptions {
|
|
3294
|
+
readonly config: ImagesServerConfig['queue'];
|
|
3295
|
+
/** Persistent private daemon key material; copied on construction. */
|
|
3296
|
+
readonly hmacKey: Uint8Array;
|
|
3297
|
+
}
|
|
3298
|
+
interface DaemonImageExecutionSchedulerStatus {
|
|
3299
|
+
readonly activeJobs: number;
|
|
3300
|
+
readonly waitingJobs: number;
|
|
3301
|
+
readonly activeAccounts: number;
|
|
3302
|
+
readonly waitingAccounts: number;
|
|
3303
|
+
readonly waitingTenants: number;
|
|
3304
|
+
readonly maxConcurrentJobsPerAccount: number;
|
|
3305
|
+
readonly maxQueuedJobs: number;
|
|
3306
|
+
readonly accepting: boolean;
|
|
3307
|
+
readonly shuttingDown: boolean;
|
|
3308
|
+
}
|
|
3309
|
+
/**
|
|
3310
|
+
* Snapshot-bound image admission with per-account active caps and fair tenant
|
|
3311
|
+
* rotation under one bounded global waiting population.
|
|
3312
|
+
*/
|
|
3313
|
+
declare class DaemonImageExecutionScheduler implements ImageExecutionScheduler {
|
|
3314
|
+
#private;
|
|
3315
|
+
constructor(options: DaemonImageExecutionSchedulerOptions);
|
|
3316
|
+
deriveAccountKey(selectedAccountId: string): ImageExecutionAccountKey;
|
|
3317
|
+
acquire(request: ImageExecutionSchedulerRequest): Promise<ImageExecutionSchedulerGrant>;
|
|
3318
|
+
status(): DaemonImageExecutionSchedulerStatus;
|
|
3319
|
+
/** Stop new admissions while allowing already queued and active work to drain. */
|
|
3320
|
+
retire(): void;
|
|
3321
|
+
/** Reject waiters and cancel active grants. Safe to call repeatedly. */
|
|
3322
|
+
shutdown(): void;
|
|
3323
|
+
}
|
|
3324
|
+
|
|
3325
|
+
interface ImageRuntimeGenerationSharedStorage {
|
|
3326
|
+
/** Resolver associated with the catalog's already-active mount. */
|
|
3327
|
+
readonly paths: DaemonImagePathResolver;
|
|
3328
|
+
readonly referenceStore: MountedImageReferenceStore;
|
|
3329
|
+
readonly stateStore: MountedResponsesImageStateStore;
|
|
3330
|
+
}
|
|
3331
|
+
interface ImageRuntimeMetadataObservability {
|
|
3332
|
+
readonly telemetrySink?: ImageTelemetrySink;
|
|
3333
|
+
readonly audit?: (record: ImageApiAuditRecord) => void | Promise<void>;
|
|
3334
|
+
}
|
|
3335
|
+
/**
|
|
3336
|
+
* Explicit deterministic Tier-A seam. It replaces only the provider inside an
|
|
3337
|
+
* otherwise-production runtime generation and is never inferred from config.
|
|
3338
|
+
*/
|
|
3339
|
+
interface SyntheticVerifiedImageProviderTestSeam {
|
|
3340
|
+
readonly label: 'synthetic-verified-image-provider-test-only';
|
|
3341
|
+
createProvider(context: Readonly<{
|
|
3342
|
+
generationId: string;
|
|
3343
|
+
scheduler: ImageExecutionScheduler;
|
|
3344
|
+
now: () => number;
|
|
3345
|
+
referenceStore: MountedImageReferenceStore;
|
|
3346
|
+
stateStore: MountedResponsesImageStateStore;
|
|
3347
|
+
}>): ImageProvider;
|
|
3348
|
+
}
|
|
3349
|
+
interface ImageRuntimeGenerationFactoryOptions {
|
|
3350
|
+
readonly generationId: string;
|
|
3351
|
+
readonly config: ImagesServerConfig;
|
|
3352
|
+
readonly subscriptionAccounts: Pick<SubscriptionAccountService, 'getStrategy'>;
|
|
3353
|
+
readonly storage: ImageRuntimeGenerationSharedStorage;
|
|
3354
|
+
readonly provenRemoteResolver?: RemoteImageAssetResolver;
|
|
3355
|
+
readonly observability?: ImageRuntimeMetadataObservability;
|
|
3356
|
+
readonly createRequestId?: () => string;
|
|
3357
|
+
readonly createCallId?: () => `ig_${string}`;
|
|
3358
|
+
readonly now?: () => number;
|
|
3359
|
+
/** Test seam; production loads the persistent daemon-local HMAC salt. */
|
|
3360
|
+
readonly privateHmacKey?: Uint8Array;
|
|
3361
|
+
/** App-session owner used to protect live directories across generations. */
|
|
3362
|
+
readonly activeTemporaryScopes?: DaemonImageActiveScopeRegistry;
|
|
3363
|
+
/** Immutable TTL view scoped to this runtime generation. */
|
|
3364
|
+
readonly evidenceSource?: FileCodexImageCapabilityEvidenceSource;
|
|
3365
|
+
/** App-session catalog lease retained until this generation is disposed. */
|
|
3366
|
+
readonly releaseStorageBackend?: () => void;
|
|
3367
|
+
/** Test-only Tier-A provider; production leaves this absent. */
|
|
3368
|
+
readonly testOnlySyntheticVerifiedProvider?: SyntheticVerifiedImageProviderTestSeam;
|
|
3369
|
+
}
|
|
3370
|
+
interface ProductionImageRuntimeComponents {
|
|
3371
|
+
readonly providerRegistry: ImageProviderRegistry;
|
|
3372
|
+
readonly orchestrator: ImageOrchestrator;
|
|
3373
|
+
readonly scheduler: DaemonImageExecutionScheduler;
|
|
3374
|
+
readonly evidenceSource: FileCodexImageCapabilityEvidenceSource;
|
|
3375
|
+
readonly temporaryResources: DaemonImageTemporaryResourceFactory;
|
|
3376
|
+
readonly runtimeResolver: TrustedImageApiRuntimeResolver;
|
|
3377
|
+
readonly referenceStore: MountedImageReferenceStore;
|
|
3378
|
+
readonly stateStore: MountedResponsesImageStateStore;
|
|
3379
|
+
}
|
|
3380
|
+
type ProductionImageRuntimeGeneration = PreparedImageRuntimeGeneration & {
|
|
3381
|
+
readonly components?: ProductionImageRuntimeComponents;
|
|
3382
|
+
};
|
|
3383
|
+
/**
|
|
3384
|
+
* Builds a complete generation without changing the storage catalog's active
|
|
3385
|
+
* mount. Catalog activation must be composed by a failure-atomic transaction.
|
|
3386
|
+
*/
|
|
3387
|
+
declare function createImageRuntimeGeneration(options: ImageRuntimeGenerationFactoryOptions): ProductionImageRuntimeGeneration;
|
|
3388
|
+
|
|
3389
|
+
/**
|
|
3390
|
+
* JsonOutboundKeyDb — the daemon's file-backed `OutboundKeyDb` port impl
|
|
3391
|
+
* (design D3).
|
|
3392
|
+
*
|
|
3393
|
+
* Durable storage for named outbound API keys, backed by a json file (a sibling
|
|
3394
|
+
* of `config.json`, e.g. `keys.json`) holding an `OutboundKeyDbRow[]`. This port
|
|
3395
|
+
* provides ONLY storage — it never generates secrets nor hashes. Core's
|
|
3396
|
+
* `createNamedKey(db, name)` calls `outboundApiKeysCreate` with the sha256
|
|
3397
|
+
* `keyHash` + display `keyPrefix` and returns the one-time plaintext; the hot
|
|
3398
|
+
* auth path uses core's `hashKey(presented)` + `outboundApiKeysGetByHash`.
|
|
3399
|
+
*
|
|
3400
|
+
* OPTIONAL reversible storage: when constructed with a `SecretBox`, each created
|
|
3401
|
+
* key ALSO persists its plaintext as a `keySecret` `enc:` envelope, powering the
|
|
3402
|
+
* operator "view key" affordance (`outboundApiKeysReveal`). The hash remains the
|
|
3403
|
+
* auth index; without a box the store is hash-only (byte-identical to legacy).
|
|
3404
|
+
*
|
|
3405
|
+
* @module @omnicross/daemon/ports/JsonOutboundKeyDb
|
|
3406
|
+
*/
|
|
3407
|
+
|
|
3408
|
+
type AtomicFileReplace = (targetPath: string, contents: string) => void;
|
|
2177
3409
|
declare class JsonOutboundKeyDb implements OutboundKeyDb {
|
|
2178
3410
|
private readonly keysPath;
|
|
2179
3411
|
private readonly secretBox?;
|
|
3412
|
+
private readonly atomicReplace;
|
|
2180
3413
|
/**
|
|
2181
3414
|
* @param secretBox OPTIONAL reversible-secret codec. When present, a created
|
|
2182
3415
|
* key's plaintext is persisted as a `keySecret` `enc:` envelope (enabling the
|
|
@@ -2185,7 +3418,7 @@ declare class JsonOutboundKeyDb implements OutboundKeyDb {
|
|
|
2185
3418
|
* always returns `null`. Existing 1-arg call sites (tests, lightweight
|
|
2186
3419
|
* embedders) keep working.
|
|
2187
3420
|
*/
|
|
2188
|
-
constructor(keysPath: string, secretBox?: SecretBox | undefined);
|
|
3421
|
+
constructor(keysPath: string, secretBox?: SecretBox | undefined, atomicReplace?: AtomicFileReplace);
|
|
2189
3422
|
outboundApiKeysList(): Promise<OutboundKeyDbRow[]>;
|
|
2190
3423
|
outboundApiKeysGetByHash(hash: string): Promise<OutboundKeyDbRow | null>;
|
|
2191
3424
|
outboundApiKeysCreate(input: {
|
|
@@ -2195,7 +3428,7 @@ declare class JsonOutboundKeyDb implements OutboundKeyDb {
|
|
|
2195
3428
|
keyPrefix: string;
|
|
2196
3429
|
createdAt?: number;
|
|
2197
3430
|
kind?: 'client' | 'integration';
|
|
2198
|
-
allowedEndpoints?:
|
|
3431
|
+
allowedEndpoints?: OutboundPermission[];
|
|
2199
3432
|
loopbackOnly?: boolean;
|
|
2200
3433
|
plaintext?: string;
|
|
2201
3434
|
}): Promise<OutboundKeyDbRow>;
|
|
@@ -2204,6 +3437,7 @@ declare class JsonOutboundKeyDb implements OutboundKeyDb {
|
|
|
2204
3437
|
outboundApiKeysRevoke(id: string): Promise<boolean>;
|
|
2205
3438
|
outboundApiKeysTouchLastUsed(id: string): Promise<boolean>;
|
|
2206
3439
|
outboundApiKeysSetEnabled(id: string, enabled: boolean): Promise<boolean>;
|
|
3440
|
+
outboundApiKeysSetPermissions(id: string, permissions: OutboundPermission[]): Promise<boolean>;
|
|
2207
3441
|
outboundApiKeysSetMaxConcurrency(id: string, maxConcurrency: number | null): Promise<boolean>;
|
|
2208
3442
|
outboundApiKeysSetPolicy(id: string, policy: OutboundKeyPolicy): Promise<boolean>;
|
|
2209
3443
|
outboundApiKeysMarkActivated(id: string, activatedAt: number): Promise<boolean>;
|
|
@@ -2295,17 +3529,28 @@ declare class AccountHealthSweeper {
|
|
|
2295
3529
|
}
|
|
2296
3530
|
|
|
2297
3531
|
/**
|
|
2298
|
-
* AuditPruneSweeper —
|
|
2299
|
-
* design D4
|
|
2300
|
-
*
|
|
2301
|
-
*
|
|
2302
|
-
*
|
|
3532
|
+
* AuditPruneSweeper — retention + archiving for the audit store
|
|
3533
|
+
* (request-audit-log design D4, extended by audit-store-sharding design D6).
|
|
3534
|
+
*
|
|
3535
|
+
* Two jobs run on the same hourly tick:
|
|
3536
|
+
*
|
|
3537
|
+
* - PRUNE. Remove every day older than `retentionDays`. A current day is a
|
|
3538
|
+
* DIRECTORY (removed recursively), a legacy day is a flat file (unlinked with
|
|
3539
|
+
* its stats sidecar). Either way it stays a whole-day removal — never a
|
|
3540
|
+
* line-level rewrite of a live file, which jsonl makes awkward.
|
|
3541
|
+
* - ARCHIVE. First run the cross-session compaction pass (see
|
|
3542
|
+
* {@link compactAuditDay}), then gzip the body shards of any day that is no
|
|
3543
|
+
* longer today. The
|
|
3544
|
+
* current day stays PLAIN TEXT so it can still be tailed and grepped while
|
|
3545
|
+
* debugging; once a day is closed nothing appends to it again, so rewriting
|
|
3546
|
+
* and compressing it is safe and buys a large multiple on top of the delta.
|
|
3547
|
+
* `meta.jsonl` is NEVER compressed — it is the query hot path.
|
|
2303
3548
|
*
|
|
2304
3549
|
* Modeled on the #8 `AccountHealthProbeScheduler` / `AccountHealthSweeper`:
|
|
2305
3550
|
* `start()` arms an `unref()`ed interval, `dispose()` clears it, a single-sweep
|
|
2306
|
-
* re-entrancy guard prevents overlap.
|
|
2307
|
-
*
|
|
2308
|
-
*
|
|
3551
|
+
* re-entrancy guard prevents overlap. Both jobs also run once at boot. Disabled
|
|
3552
|
+
* config means armed-off, so audit-off is byte-identical zero regression. Never
|
|
3553
|
+
* throws.
|
|
2309
3554
|
*
|
|
2310
3555
|
* @module @omnicross/daemon/audit/AuditPruneSweeper
|
|
2311
3556
|
*/
|
|
@@ -2319,6 +3564,7 @@ declare class AuditPruneSweeper {
|
|
|
2319
3564
|
private readonly now;
|
|
2320
3565
|
private timer;
|
|
2321
3566
|
private sweeping;
|
|
3567
|
+
private archiving;
|
|
2322
3568
|
constructor(auditDir: string, logger: Logger, config: AuditConfig, intervalMs?: number,
|
|
2323
3569
|
/** Injectable clock (ms) for deterministic tests. */
|
|
2324
3570
|
now?: () => number);
|
|
@@ -2327,32 +3573,102 @@ declare class AuditPruneSweeper {
|
|
|
2327
3573
|
/** Re-apply config to the live instance (boot + admin PUT hot-reload). */
|
|
2328
3574
|
configure(config: AuditConfig): void;
|
|
2329
3575
|
/**
|
|
2330
|
-
* Arm the
|
|
2331
|
-
*
|
|
3576
|
+
* Arm the interval AND run one pass immediately (boot cleanup). No-op when
|
|
3577
|
+
* audit is disabled (zero regression). Idempotent.
|
|
2332
3578
|
*/
|
|
2333
3579
|
start(): void;
|
|
2334
3580
|
/** Clear the interval (daemon shutdown / test teardown). Idempotent. */
|
|
2335
3581
|
dispose(): void;
|
|
3582
|
+
/** Prune first, then archive — never spend CPU compressing a day about to go. */
|
|
3583
|
+
private runOnce;
|
|
3584
|
+
/** The LOCAL-midnight epoch ms of the current day. */
|
|
3585
|
+
private todayMidnight;
|
|
2336
3586
|
/**
|
|
2337
|
-
* One prune:
|
|
2338
|
-
*
|
|
2339
|
-
*
|
|
3587
|
+
* One prune: remove every audit day strictly OLDER than the retention cutoff
|
|
3588
|
+
* (`now - retentionDays` days, at local-midnight granularity). Exposed for
|
|
3589
|
+
* tests; never throws. Returns the number of days removed.
|
|
2340
3590
|
*/
|
|
2341
3591
|
sweep(): Promise<number>;
|
|
3592
|
+
/**
|
|
3593
|
+
* Gzip the body shards of every CLOSED day (anything before today). Today is
|
|
3594
|
+
* deliberately left as plain text so it stays greppable while it is the day you
|
|
3595
|
+
* are debugging. Exposed for tests; never throws. Returns shards compressed.
|
|
3596
|
+
*/
|
|
3597
|
+
archive(): Promise<number>;
|
|
3598
|
+
/** Gzip up to `budget` plain shards in one day's `bodies/` directory. */
|
|
3599
|
+
private archiveDay;
|
|
3600
|
+
}
|
|
3601
|
+
|
|
3602
|
+
/**
|
|
3603
|
+
* usageMigrate — the ONE-SHOT fold of the legacy flat `usage-events.jsonl` into
|
|
3604
|
+
* per-LOCAL-day shards plus their immutable rollups.
|
|
3605
|
+
*
|
|
3606
|
+
* Runs at bootstrap, BEFORE any listener is bound, so no request can be served
|
|
3607
|
+
* against a half-migrated store. It streams: the legacy file is never held in
|
|
3608
|
+
* memory as one string (that is the exact shape that put the old store on course
|
|
3609
|
+
* for V8's ~512 MB string ceiling), and each day's rollup is accumulated during
|
|
3610
|
+
* the same pass that writes its shard, so the whole migration is a single read.
|
|
3611
|
+
*
|
|
3612
|
+
* Writes are BATCHED per day (see {@link FLUSH_BYTES}). Awaiting a write per row
|
|
3613
|
+
* turned 199k rows into ~17 s of boot on a real 157 MB store, which the desktop
|
|
3614
|
+
* shell — waiting on the admin port — is right to find suspicious; batching cuts
|
|
3615
|
+
* it to a few seconds without changing what lands on disk.
|
|
3616
|
+
*
|
|
3617
|
+
* CRASH SAFETY. Everything is built inside `usage/.migrating/` and only moved
|
|
3618
|
+
* into place once the row count reconciles. A crash before the commit leaves the
|
|
3619
|
+
* legacy file untouched and the scratch directory to be wiped and rebuilt on the
|
|
3620
|
+
* next attempt. A crash DURING the commit leaves some shards in place and the
|
|
3621
|
+
* legacy file still present; the next attempt refuses to run (a target shard
|
|
3622
|
+
* already exists) and says so, rather than overwriting or double-counting rows.
|
|
3623
|
+
*
|
|
3624
|
+
* RECONCILIATION is the point of the exercise: `linesRead` must equal
|
|
3625
|
+
* `rowsWritten + skipped`, where `skipped` is lines the shared row guard rejects.
|
|
3626
|
+
* If it does not, nothing is committed and the legacy file stays exactly where it
|
|
3627
|
+
* was. Silently losing usage rows would corrupt lifetime key spend, which is the
|
|
3628
|
+
* one number in this store that can never be reconstructed.
|
|
3629
|
+
*
|
|
3630
|
+
* @module @omnicross/daemon/usage/usageMigrate
|
|
3631
|
+
*/
|
|
3632
|
+
|
|
3633
|
+
interface UsageMigrationResult {
|
|
3634
|
+
/** True only when the legacy file was folded in and removed. */
|
|
3635
|
+
migrated: boolean;
|
|
3636
|
+
/** Non-empty lines read from the legacy file. */
|
|
3637
|
+
linesRead: number;
|
|
3638
|
+
/** Rows that passed the guard and were written to a shard. */
|
|
3639
|
+
rowsWritten: number;
|
|
3640
|
+
/** Lines the guard rejected (torn tail, hand-edited, foreign writer). */
|
|
3641
|
+
skipped: number;
|
|
3642
|
+
/** Distinct LOCAL days produced. */
|
|
3643
|
+
days: number;
|
|
3644
|
+
/** Present when the migration declined or aborted; nothing was committed. */
|
|
3645
|
+
reason?: string;
|
|
2342
3646
|
}
|
|
2343
3647
|
|
|
2344
3648
|
/**
|
|
2345
|
-
* AuditWriter — the daemon's file-backed audit sink (request-audit-log
|
|
2346
|
-
* D4/D5
|
|
2347
|
-
*
|
|
3649
|
+
* AuditWriter — the daemon's file-backed audit sink (request-audit-log design
|
|
3650
|
+
* D4/D5, re-laid-out by audit-store-sharding design D2). Registered as
|
|
3651
|
+
* `@omnicross/core`'s audit sink when audit is enabled; its {@link record} is
|
|
3652
|
+
* what `recordAudit` hands each assembled record to.
|
|
2348
3653
|
*
|
|
2349
|
-
* FIRE-AND-FORGET (hard constraint): {@link record} DEFERS
|
|
3654
|
+
* FIRE-AND-FORGET (hard constraint): {@link record} DEFERS all fs work off the
|
|
2350
3655
|
* caller's stack (an injectable `defer`, default a zero-delay timer — the
|
|
2351
3656
|
* `UsageRecorder` precedent) and returns immediately, so the relay response path
|
|
2352
|
-
* never waits on disk I/O.
|
|
2353
|
-
*
|
|
2354
|
-
*
|
|
2355
|
-
*
|
|
3657
|
+
* never waits on disk I/O. Delta encoding rides that same deferred tick and is
|
|
3658
|
+
* pure memory, so it can never stall a response either. A write error is
|
|
3659
|
+
* swallowed + logged (a failing audit store must never affect a relay).
|
|
3660
|
+
*
|
|
3661
|
+
* Each record is split across TWO destinations under `audit/audit-YYYY-MM-DD/`:
|
|
3662
|
+
*
|
|
3663
|
+
* - `meta.jsonl` — one small JSON line WITHOUT bodies. This is what queries and
|
|
3664
|
+
* the stats sidecar read, so a listing no longer pays for megabytes of prompt.
|
|
3665
|
+
* - `bodies/<sessionKey>.jsonl` — the captured bodies, sharded per conversation
|
|
3666
|
+
* and delta-encoded against the session's previous turn.
|
|
3667
|
+
*
|
|
3668
|
+
* The two writes are INDEPENDENT: the metadata line is the canonical record, so a
|
|
3669
|
+
* body-shard failure is logged and the metadata still lands. When a shard write
|
|
3670
|
+
* fails the session's encoding base is dropped, otherwise the next turn would
|
|
3671
|
+
* chain a delta onto a base no reader can find.
|
|
2356
3672
|
*
|
|
2357
3673
|
* @module @omnicross/daemon/audit/AuditWriter
|
|
2358
3674
|
*/
|
|
@@ -2360,23 +3676,37 @@ declare class AuditPruneSweeper {
|
|
|
2360
3676
|
declare class AuditWriter {
|
|
2361
3677
|
private readonly auditDir;
|
|
2362
3678
|
private readonly logger;
|
|
2363
|
-
/** Deferral used by `record()` to schedule the
|
|
3679
|
+
/** Deferral used by `record()` to schedule the writes off the caller's path. */
|
|
2364
3680
|
private readonly defer;
|
|
2365
|
-
|
|
3681
|
+
/** Day directories already created this process (avoids an mkdir per record). */
|
|
3682
|
+
private readonly ensuredDirs;
|
|
3683
|
+
/** Per-session encoding bases. Memory-only; a miss simply writes a full snapshot. */
|
|
3684
|
+
private readonly bases;
|
|
2366
3685
|
constructor(auditDir: string, logger: Logger,
|
|
2367
|
-
/** Deferral used by `record()` to schedule the
|
|
3686
|
+
/** Deferral used by `record()` to schedule the writes off the caller's path. */
|
|
2368
3687
|
defer?: (fn: () => void) => void);
|
|
2369
3688
|
/**
|
|
2370
|
-
* Enqueue one record
|
|
2371
|
-
*
|
|
3689
|
+
* Enqueue one record. Returns IMMEDIATELY (fire-and-forget); every fs write and
|
|
3690
|
+
* the delta encoding happen on the deferred tick. A failure is logged, never thrown.
|
|
2372
3691
|
*/
|
|
2373
3692
|
record(record: AuditRecord): void;
|
|
3693
|
+
/** Drop all retained encoding bases (config reload / shutdown / test teardown). */
|
|
3694
|
+
reset(): void;
|
|
2374
3695
|
/**
|
|
2375
|
-
* Append synchronously — the awaitable form tests use to assert
|
|
2376
|
-
*
|
|
2377
|
-
* store's lazy file creation).
|
|
3696
|
+
* Append synchronously — the awaitable form tests use to assert a line landed.
|
|
3697
|
+
* Writes the metadata line first (canonical), then the body shard.
|
|
2378
3698
|
*/
|
|
2379
3699
|
appendNow(record: AuditRecord): void;
|
|
3700
|
+
/** Create a directory once per process and remember it. */
|
|
3701
|
+
private ensureDir;
|
|
3702
|
+
/** Write the body-free metadata line + refresh the exact-count sidecar. */
|
|
3703
|
+
private appendMeta;
|
|
3704
|
+
/**
|
|
3705
|
+
* Write the delta-encoded body shard for one record. A no-op when nothing was
|
|
3706
|
+
* captured or when the session key is missing/unsafe — in which case the body
|
|
3707
|
+
* is dropped rather than written to an unvalidated path.
|
|
3708
|
+
*/
|
|
3709
|
+
private appendBody;
|
|
2380
3710
|
}
|
|
2381
3711
|
|
|
2382
3712
|
/**
|
|
@@ -2715,6 +4045,16 @@ interface DaemonPaths {
|
|
|
2715
4045
|
* never invoke a real package manager. Absent → the real `exec`-based runner.
|
|
2716
4046
|
*/
|
|
2717
4047
|
cliCommandRunner?: CommandRunner;
|
|
4048
|
+
/** TEST/COMPOSITION SEAM: prepared Images runtime generation for config transactions. */
|
|
4049
|
+
imageRuntimeConfig?: AdminApiDeps['imageRuntimeConfig'];
|
|
4050
|
+
/** TEST/COMPOSITION SEAM: metadata-only Images status reader. */
|
|
4051
|
+
imageRuntimeStatus?: AdminApiDeps['imageRuntimeStatus'];
|
|
4052
|
+
/** TEST/COMPOSITION SEAM: metadata-only successful Images config audit sink. */
|
|
4053
|
+
imageConfigAudit?: AdminApiDeps['imageConfigAudit'];
|
|
4054
|
+
/** TEST ONLY: deterministic Tier-A provider inside the production Images composition. */
|
|
4055
|
+
testOnlySyntheticVerifiedImageProvider?: SyntheticVerifiedImageProviderTestSeam;
|
|
4056
|
+
/** TEST SEAM: inject an atomic settings replacement fault. */
|
|
4057
|
+
settingsAtomicReplace?: (targetPath: string, contents: Uint8Array) => void;
|
|
2718
4058
|
}
|
|
2719
4059
|
/** The constructed daemon handles the CLI commands operate on. */
|
|
2720
4060
|
interface Daemon {
|
|
@@ -2723,6 +4063,18 @@ interface Daemon {
|
|
|
2723
4063
|
readonly llmConfig: ConfigFileProviderConfigSource;
|
|
2724
4064
|
readonly keyDb: JsonOutboundKeyDb;
|
|
2725
4065
|
readonly settingsStore: JsonApiServerSettingsStore;
|
|
4066
|
+
/** App-session extension-operation registry shared with the resident proxy. */
|
|
4067
|
+
readonly openAIOperationRegistry: OpenAIOperationRegistry;
|
|
4068
|
+
/** Stable Images forwarders and generation lifecycle owner for this app session. */
|
|
4069
|
+
readonly imageRuntimeManager: ImageRuntimeManager;
|
|
4070
|
+
/** Bounded process-local metadata aggregation shared by every runtime generation. */
|
|
4071
|
+
readonly imageObservability: ImageObservability;
|
|
4072
|
+
/** Startup reconciliation plus recurring bounded cleanup for Images state. */
|
|
4073
|
+
readonly imageCleanupService: ImageCleanupService;
|
|
4074
|
+
/** Local-only diagnostics plus the explicit consuming Images verifier. */
|
|
4075
|
+
readonly imageDoctor: ImageDoctorService;
|
|
4076
|
+
/** Generation-pinned hosted-image lease factory used by Native Responses. */
|
|
4077
|
+
readonly hostedImageContributionFactory: HostedImageContributionFactory;
|
|
2726
4078
|
readonly providerProxy: ProviderProxy;
|
|
2727
4079
|
readonly routeLeaseManager: RouteLeaseManager;
|
|
2728
4080
|
readonly outboundApiServer: OutboundApiServer;
|
|
@@ -2800,6 +4152,17 @@ interface Daemon {
|
|
|
2800
4152
|
* in cleanup.
|
|
2801
4153
|
*/
|
|
2802
4154
|
readonly auditPruneSweeper: AuditPruneSweeper;
|
|
4155
|
+
/**
|
|
4156
|
+
* Retention for RAW usage rows (rollups are kept forever). Armed by `start`;
|
|
4157
|
+
* `launch` leaves it off — a short-lived boot has no business pruning.
|
|
4158
|
+
*/
|
|
4159
|
+
readonly usagePruneSweeper: UsagePruneSweeper;
|
|
4160
|
+
/**
|
|
4161
|
+
* Fold a legacy flat `usage-events.jsonl` into day shards. MUST be awaited
|
|
4162
|
+
* before any listener binds: it moves files the query path reads, and a
|
|
4163
|
+
* request served mid-migration would see a partial store.
|
|
4164
|
+
*/
|
|
4165
|
+
readonly migrateUsageStore: () => Promise<UsageMigrationResult>;
|
|
2803
4166
|
/**
|
|
2804
4167
|
* Durable-first billing publisher (billing-event-stream) — appends each event
|
|
2805
4168
|
* to `billing/billing-YYYY-MM-DD.jsonl` FIRST, then best-effort POSTs it.
|
|
@@ -2816,11 +4179,6 @@ interface Daemon {
|
|
|
2816
4179
|
*/
|
|
2817
4180
|
readonly billingRetrySweeper: BillingRetrySweeper;
|
|
2818
4181
|
}
|
|
2819
|
-
/**
|
|
2820
|
-
* Construct the standalone daemon from a loaded config + on-disk paths. Does NOT
|
|
2821
|
-
* start the listeners — the `start` command awaits `providerProxy.start()` then
|
|
2822
|
-
* `outboundApiServer.applyConfig(...)`.
|
|
2823
|
-
*/
|
|
2824
4182
|
declare function buildDaemon(config: DaemonConfig, paths: DaemonPaths): Daemon;
|
|
2825
4183
|
/** Reset the core singletons (tests / teardown only). Re-exported for the suite.
|
|
2826
4184
|
*
|
|
@@ -2906,11 +4264,24 @@ declare function buildHealthReport(deps: HealthReportDeps): HealthReport;
|
|
|
2906
4264
|
* - SINK: always the console; PLUS an optional append-only file stream when
|
|
2907
4265
|
* `file` is set (lazy-open; a write/open error is swallowed → the daemon never
|
|
2908
4266
|
* crashes on a logging failure, it just falls back to the console).
|
|
4267
|
+
* - ROTATION: the file sink is size-capped (`maxFileBytes`, default 8 MB) and
|
|
4268
|
+
* keeps `maxFiles` generations (default 5) as `<file>.1` … `<file>.N`. An
|
|
4269
|
+
* UNBOUNDED append-only log is how a long-lived daemon quietly fills a disk,
|
|
4270
|
+
* so the cap is on by default rather than opt-in. Lines emitted mid-rotation
|
|
4271
|
+
* are queued (bounded) and flushed into the fresh generation, never dropped
|
|
4272
|
+
* silently unless the queue itself overflows.
|
|
2909
4273
|
*
|
|
2910
4274
|
* ZERO-REGRESSION DEFAULT: `new ConfigurableLogger()` (no config) = console +
|
|
2911
4275
|
* all levels + text = behaviorally byte-identical to the legacy `ConsoleLogger`
|
|
2912
4276
|
* (same `console` method per level, same `(message[, meta])` / error arg shape).
|
|
2913
4277
|
*
|
|
4278
|
+
* NOTE — the daemon no longer CONSTRUCTS it that way. `bootstrap.ts` defaults the
|
|
4279
|
+
* file sink on (`<configDir>/logs/daemon.log`, level `info`, format `json`)
|
|
4280
|
+
* whenever the config omits it: the desktop app discards the daemon's stdout, so
|
|
4281
|
+
* a console-only logger meant a crash left no evidence at all. The unconfigured
|
|
4282
|
+
* CONSTRUCTOR default above is still console-only — only the daemon's wiring
|
|
4283
|
+
* changed.
|
|
4284
|
+
*
|
|
2914
4285
|
* CAUTION (per the #3 host:port-only-logging precedent): the JSON serializer
|
|
2915
4286
|
* reduces an `Error` to `{ message, stack }` and spreads a plain `meta` object,
|
|
2916
4287
|
* but it is NOT a secret redactor — call sites remain responsible for not passing
|
|
@@ -2923,8 +4294,16 @@ declare class ConfigurableLogger implements Logger {
|
|
|
2923
4294
|
private readonly threshold;
|
|
2924
4295
|
private readonly format;
|
|
2925
4296
|
private readonly filePath;
|
|
4297
|
+
private readonly maxFileBytes;
|
|
4298
|
+
private readonly maxFiles;
|
|
2926
4299
|
private fileStream;
|
|
2927
4300
|
private fileDisabled;
|
|
4301
|
+
/** Bytes in the CURRENT generation — seeded from the file on open. */
|
|
4302
|
+
private fileBytes;
|
|
4303
|
+
/** True from the moment a rotation starts until the fresh stream is live. */
|
|
4304
|
+
private rotating;
|
|
4305
|
+
/** Lines emitted while `rotating`; flushed into the new generation. */
|
|
4306
|
+
private rotateQueue;
|
|
2928
4307
|
constructor(cfg?: LoggingConfig);
|
|
2929
4308
|
info(message: string, meta?: Record<string, unknown> | Error | object): void;
|
|
2930
4309
|
warn(message: string, meta?: Record<string, unknown> | Error | object): void;
|
|
@@ -2944,7 +4323,22 @@ declare class ConfigurableLogger implements Logger {
|
|
|
2944
4323
|
private writeConsole;
|
|
2945
4324
|
/** Append one line to the file sink; a failure disables the sink (swallowed). */
|
|
2946
4325
|
private writeFile;
|
|
2947
|
-
/**
|
|
4326
|
+
/** Write ONE prepared line to the live generation, rotating once it is full. */
|
|
4327
|
+
private emitLine;
|
|
4328
|
+
/**
|
|
4329
|
+
* Close the full generation, shift `<file>.N-1` → `<file>.N` (oldest dropped),
|
|
4330
|
+
* then reopen. Renames run in the `end()` callback so nothing still buffered in
|
|
4331
|
+
* the stream lands in the wrong generation. Any failure disables the sink
|
|
4332
|
+
* rather than throwing — the console sink is unaffected either way.
|
|
4333
|
+
*/
|
|
4334
|
+
private rotate;
|
|
4335
|
+
/** `<file>.N` unlinked, `<file>.k` → `<file>.k+1`, `<file>` → `<file>.1`. */
|
|
4336
|
+
private shiftGenerations;
|
|
4337
|
+
/**
|
|
4338
|
+
* Lazily open the append-only file stream; disable the sink on any error. The
|
|
4339
|
+
* byte counter is seeded from the file already on disk so a restart cannot
|
|
4340
|
+
* reset an almost-full generation back to zero and blow past the cap.
|
|
4341
|
+
*/
|
|
2948
4342
|
private getFileStream;
|
|
2949
4343
|
private consoleFn;
|
|
2950
4344
|
/** `{ ts, level, msg, ...meta }` (+ `error` when present) as a single line. */
|
|
@@ -3034,4 +4428,4 @@ declare function mapCcrToOmnicross(ccr: CcrConfig): {
|
|
|
3034
4428
|
notes: string[];
|
|
3035
4429
|
};
|
|
3036
4430
|
|
|
3037
|
-
export { type AdminApiDeps, AdminServer, type AdminServerDeps, type AdminServerStatus, type CcrConfig, type CcrProvider, type CcrRouter, ConfigFileProviderConfigSource, ConfigurableLogger, ConsoleLogger, DEFAULT_ADMIN_PORT, type Daemon, type DaemonAdminConfig, type DaemonApiFormat, type DaemonConfig, type DaemonPaths, type DaemonProviderConfig, type HealthReportDeps, JsonApiServerSettingsStore, JsonOutboundKeyDb, JsonSubscriptionCredentialStore, type ResolvedAdminConfig, buildDaemon, buildHealthReport, handleAdminApi, inferApiFormat, loadConfig, mapCcrToOmnicross, parseCcrConfig, resetDaemonSingletonsForTests, resolveAdminConfig, saveConfig, validateConfig };
|
|
4431
|
+
export { type AdminApiDeps, AdminServer, type AdminServerDeps, type AdminServerStatus, type CcrConfig, type CcrProvider, type CcrRouter, ConfigFileProviderConfigSource, ConfigurableLogger, ConsoleLogger, DEFAULT_ADMIN_PORT, type Daemon, type DaemonAdminConfig, type DaemonApiFormat, type DaemonConfig, type DaemonPaths, type DaemonProviderConfig, type HealthReportDeps, type HostedImageContributionFactory, type HostedImageRuntimeGenerationLease, type ImageApiMetricDimensions, type ImageApiMetricSnapshot, type ImageExecutionMetricDimensions, type ImageExecutionMetricSnapshot, type ImageHistogramSnapshot, ImageObservability, type ImageObservabilityOptions, type ImageObservabilitySnapshot, type ImageRuntimeCapabilityInspection, type ImageRuntimeGenerationFactoryOptions, type ImageRuntimeGenerationSharedStorage, ImageRuntimeManager, type ImageRuntimeManagerStatus, type ImageRuntimeMetadataObservability, type ImageRuntimeResourceStatus, type ImageRuntimeSafeUnavailableReason, JsonApiServerSettingsStore, JsonOutboundKeyDb, JsonSubscriptionCredentialStore, type PreparedImageRuntimeChange, type PreparedImageRuntimeGeneration, type ProductionImageRuntimeComponents, type ProductionImageRuntimeGeneration, type ResolvedAdminConfig, type TrustedImageApiRuntimeResolver, type TrustedImageApiRuntimeResolverOptions, buildDaemon, buildHealthReport, createHostedImageContributionFactory, createImageRuntimeGeneration, createTrustedImageApiRuntimeResolver, handleAdminApi, inferApiFormat, loadConfig, mapCcrToOmnicross, parseCcrConfig, resetDaemonSingletonsForTests, resolveAdminConfig, saveConfig, validateConfig };
|