@omnicross/contracts 0.1.1 → 0.1.3
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/account-tokens-types.d.cts +181 -1
- package/dist/account-tokens-types.d.ts +181 -1
- package/dist/audit-types.cjs +36 -0
- package/dist/audit-types.d.cts +98 -0
- package/dist/audit-types.d.ts +98 -0
- package/dist/audit-types.js +11 -0
- package/dist/billing-types.cjs +33 -0
- package/dist/billing-types.d.cts +98 -0
- package/dist/billing-types.d.ts +98 -0
- package/dist/billing-types.js +8 -0
- package/dist/canonical-models.d.cts +1 -1
- package/dist/canonical-models.d.ts +1 -1
- package/dist/endpoint-resolver.d.cts +1 -1
- package/dist/endpoint-resolver.d.ts +1 -1
- package/dist/health-logging-types.cjs +32 -0
- package/dist/health-logging-types.d.cts +68 -0
- package/dist/health-logging-types.d.ts +68 -0
- package/dist/health-logging-types.js +7 -0
- package/dist/index.cjs +53 -0
- package/dist/index.d.cts +9 -2
- package/dist/index.d.ts +9 -2
- package/dist/index.js +46 -0
- package/dist/{llm-config-D1jKQLVp.d.ts → llm-config-CKOaFFdy.d.ts} +8 -1
- package/dist/{llm-config-CQjOimv2.d.cts → llm-config-DeWNx1ig.d.cts} +8 -1
- package/dist/llm-config.d.cts +1 -1
- package/dist/llm-config.d.ts +1 -1
- package/dist/pricing-types.cjs +30 -0
- package/dist/pricing-types.d.cts +84 -0
- package/dist/pricing-types.d.ts +84 -0
- package/dist/pricing-types.js +5 -0
- package/dist/provider-presets/index.d.cts +2 -2
- package/dist/provider-presets/index.d.ts +2 -2
- package/dist/thinking-config.d.cts +1 -1
- package/dist/thinking-config.d.ts +1 -1
- package/dist/usage-stats-types.cjs +18 -0
- package/dist/usage-stats-types.d.cts +164 -0
- package/dist/usage-stats-types.d.ts +164 -0
- package/dist/usage-stats-types.js +0 -0
- package/dist/voucher-types.cjs +32 -0
- package/dist/voucher-types.d.cts +153 -0
- package/dist/voucher-types.d.ts +153 -0
- package/dist/voucher-types.js +7 -0
- package/dist/webhook-types.cjs +40 -0
- package/dist/webhook-types.d.cts +122 -0
- package/dist/webhook-types.d.ts +122 -0
- package/dist/webhook-types.js +14 -0
- package/package.json +36 -1
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { UsageEngineOrigin } from './usage-types.cjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Usage-statistics DTOs — provider-generic stats row + aggregation views.
|
|
5
|
+
*
|
|
6
|
+
* The persisted usage-event row shape and the aggregate query views shared by
|
|
7
|
+
* embedders that record per-request usage. Storage itself is host-owned (the
|
|
8
|
+
* serving core only defines the seam); `engineOrigin` uses the OPEN
|
|
9
|
+
* `UsageEngineOrigin` union from `usage-types` so hosts may narrow it to their
|
|
10
|
+
* own closed engine list at their boundary.
|
|
11
|
+
*
|
|
12
|
+
* @module usage-stats-types
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* One captured LLM request, as persisted by the host store.
|
|
17
|
+
*
|
|
18
|
+
* `messageId` is nullable because indirect call paths (e.g. subagent-internal
|
|
19
|
+
* requests) may not map to a host message row. `parentMessageId` is
|
|
20
|
+
* best-effort — populated when the caller knows the parent, NULL otherwise.
|
|
21
|
+
*/
|
|
22
|
+
interface UsageEventRecord {
|
|
23
|
+
id: string;
|
|
24
|
+
ts: number;
|
|
25
|
+
messageId: string | null;
|
|
26
|
+
parentMessageId: string | null;
|
|
27
|
+
sessionId: string | null;
|
|
28
|
+
providerId: string;
|
|
29
|
+
model: string;
|
|
30
|
+
apiKeyId: string | null;
|
|
31
|
+
engineOrigin: UsageEngineOrigin;
|
|
32
|
+
inputTokens: number;
|
|
33
|
+
outputTokens: number;
|
|
34
|
+
cacheReadTokens: number;
|
|
35
|
+
cacheCreationTokens: number;
|
|
36
|
+
reasoningTokens: number;
|
|
37
|
+
costUsd: number;
|
|
38
|
+
/** Hypothetical-input-cost − actual-cost difference attributable to cache_read tokens. */
|
|
39
|
+
costSavedByCacheUsd: number;
|
|
40
|
+
/** JSON-serialised raw provider usage object. Kept for forensics. */
|
|
41
|
+
rawUsage: string | null;
|
|
42
|
+
/** Host run-correlation id (owning agent-run, when the host has one). Additive / nullable. */
|
|
43
|
+
runId?: string | null;
|
|
44
|
+
/** Host event-correlation id for this LLM call. NULL when unknown. Additive / nullable. */
|
|
45
|
+
eventId?: string | null;
|
|
46
|
+
}
|
|
47
|
+
/** Input shape for inserting a new event. `id` and `ts` are filled by the recorder/store. */
|
|
48
|
+
type UsageEventInput = Omit<UsageEventRecord, 'id' | 'ts'> & {
|
|
49
|
+
ts?: number;
|
|
50
|
+
};
|
|
51
|
+
/** Date range for queries. Both bounds are unix-millis; `endTs` is exclusive. */
|
|
52
|
+
interface UsageDateRange {
|
|
53
|
+
startTs: number;
|
|
54
|
+
endTs: number;
|
|
55
|
+
}
|
|
56
|
+
interface UsageQueryParams {
|
|
57
|
+
range: UsageDateRange;
|
|
58
|
+
/** Optional filter — restrict to a particular session. */
|
|
59
|
+
sessionId?: string;
|
|
60
|
+
}
|
|
61
|
+
/** Aggregated totals over a date range. */
|
|
62
|
+
interface UsageTotals {
|
|
63
|
+
inputTokens: number;
|
|
64
|
+
outputTokens: number;
|
|
65
|
+
cacheReadTokens: number;
|
|
66
|
+
cacheCreationTokens: number;
|
|
67
|
+
reasoningTokens: number;
|
|
68
|
+
costUsd: number;
|
|
69
|
+
costSavedByCacheUsd: number;
|
|
70
|
+
/** Number of events contributing to these totals. */
|
|
71
|
+
eventCount: number;
|
|
72
|
+
}
|
|
73
|
+
/** Granularity for a usage time-series query. `week` is NOT a bucket — a "week" view is a `day` bucket over a 7-day range. */
|
|
74
|
+
type UsageTimeBucket = 'hour' | 'day' | 'month';
|
|
75
|
+
/**
|
|
76
|
+
* One bucket of a usage time-series (trend chart). Buckets are query-time
|
|
77
|
+
* aggregations over the daemon's LOCAL-time boundaries and every bucket in the
|
|
78
|
+
* requested range is present (empty buckets are zero-filled), ascending by
|
|
79
|
+
* `bucketStartTs`.
|
|
80
|
+
*/
|
|
81
|
+
interface UsageTimeSeriesBucket {
|
|
82
|
+
/** Unix-millis of the LOCAL-time bucket boundary (hour start / local midnight / local 1st-of-month). */
|
|
83
|
+
bucketStartTs: number;
|
|
84
|
+
/** Frozen locale-agnostic label from LOCAL parts: hour `MM-DD HH:00`, day `YYYY-MM-DD`, month `YYYY-MM`. */
|
|
85
|
+
label: string;
|
|
86
|
+
/** Event count in the bucket (0 for a zero-filled bucket). */
|
|
87
|
+
requests: number;
|
|
88
|
+
inputTokens: number;
|
|
89
|
+
outputTokens: number;
|
|
90
|
+
cacheReadTokens: number;
|
|
91
|
+
cacheCreationTokens: number;
|
|
92
|
+
costUsd: number;
|
|
93
|
+
}
|
|
94
|
+
/** One row of the per-model breakdown. */
|
|
95
|
+
interface ModelUsageRow {
|
|
96
|
+
providerId: string;
|
|
97
|
+
model: string;
|
|
98
|
+
eventCount: number;
|
|
99
|
+
inputTokens: number;
|
|
100
|
+
outputTokens: number;
|
|
101
|
+
cacheReadTokens: number;
|
|
102
|
+
cacheCreationTokens: number;
|
|
103
|
+
costUsd: number;
|
|
104
|
+
costSavedByCacheUsd: number;
|
|
105
|
+
/** True when no pricing row exists for (providerId, model) — UIs may show an "unpriced" badge. */
|
|
106
|
+
unpriced: boolean;
|
|
107
|
+
}
|
|
108
|
+
/** One row of the per-API-key breakdown. NULL apiKeyId is mapped to a sentinel. */
|
|
109
|
+
interface ApiKeyUsageRow {
|
|
110
|
+
/** `null` represents the unattributed sentinel group. */
|
|
111
|
+
apiKeyId: string | null;
|
|
112
|
+
/** Display label resolved by the host store (its key registry, or an "unattributed" fallback). */
|
|
113
|
+
label: string;
|
|
114
|
+
providerId: string | null;
|
|
115
|
+
eventCount: number;
|
|
116
|
+
inputTokens: number;
|
|
117
|
+
outputTokens: number;
|
|
118
|
+
costUsd: number;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Cumulative cache stats for ONE session — a SUM over the session's usage
|
|
122
|
+
* events. `hitRate` is the cost-oriented aggregate `ΣcacheRead / Σ(prompt-side
|
|
123
|
+
* tokens)` where the prompt-side total is `input + cacheRead + cacheCreation`
|
|
124
|
+
* (output excluded).
|
|
125
|
+
*/
|
|
126
|
+
interface SessionCacheStats {
|
|
127
|
+
sessionId: string;
|
|
128
|
+
/** Σ uncached prompt tokens (cache miss). */
|
|
129
|
+
inputTokens: number;
|
|
130
|
+
/** Σ cache-read (hit) tokens. */
|
|
131
|
+
cacheReadTokens: number;
|
|
132
|
+
/** Σ cache-creation (write) tokens — Anthropic; 0 for auto-caching providers. */
|
|
133
|
+
cacheCreationTokens: number;
|
|
134
|
+
/** Σ output tokens (not part of the hit-rate denominator). */
|
|
135
|
+
outputTokens: number;
|
|
136
|
+
/** Number of usage-event rows for the session. */
|
|
137
|
+
eventCount: number;
|
|
138
|
+
/**
|
|
139
|
+
* ΣcacheRead / Σ(input + cacheRead + cacheCreation), in [0, 1]. 0 when the
|
|
140
|
+
* session has no prompt-side tokens yet (avoids divide-by-zero).
|
|
141
|
+
*/
|
|
142
|
+
hitRate: number;
|
|
143
|
+
}
|
|
144
|
+
/** One row in the message-level list (used by message-drilldown UI components). */
|
|
145
|
+
interface MessageUsageRow {
|
|
146
|
+
id: string;
|
|
147
|
+
ts: number;
|
|
148
|
+
messageId: string | null;
|
|
149
|
+
parentMessageId: string | null;
|
|
150
|
+
sessionId: string | null;
|
|
151
|
+
providerId: string;
|
|
152
|
+
model: string;
|
|
153
|
+
apiKeyId: string | null;
|
|
154
|
+
engineOrigin: UsageEngineOrigin;
|
|
155
|
+
inputTokens: number;
|
|
156
|
+
outputTokens: number;
|
|
157
|
+
cacheReadTokens: number;
|
|
158
|
+
cacheCreationTokens: number;
|
|
159
|
+
reasoningTokens: number;
|
|
160
|
+
costUsd: number;
|
|
161
|
+
costSavedByCacheUsd: number;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export type { ApiKeyUsageRow, MessageUsageRow, ModelUsageRow, SessionCacheStats, UsageDateRange, UsageEventInput, UsageEventRecord, UsageQueryParams, UsageTimeBucket, UsageTimeSeriesBucket, UsageTotals };
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { UsageEngineOrigin } from './usage-types.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Usage-statistics DTOs — provider-generic stats row + aggregation views.
|
|
5
|
+
*
|
|
6
|
+
* The persisted usage-event row shape and the aggregate query views shared by
|
|
7
|
+
* embedders that record per-request usage. Storage itself is host-owned (the
|
|
8
|
+
* serving core only defines the seam); `engineOrigin` uses the OPEN
|
|
9
|
+
* `UsageEngineOrigin` union from `usage-types` so hosts may narrow it to their
|
|
10
|
+
* own closed engine list at their boundary.
|
|
11
|
+
*
|
|
12
|
+
* @module usage-stats-types
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* One captured LLM request, as persisted by the host store.
|
|
17
|
+
*
|
|
18
|
+
* `messageId` is nullable because indirect call paths (e.g. subagent-internal
|
|
19
|
+
* requests) may not map to a host message row. `parentMessageId` is
|
|
20
|
+
* best-effort — populated when the caller knows the parent, NULL otherwise.
|
|
21
|
+
*/
|
|
22
|
+
interface UsageEventRecord {
|
|
23
|
+
id: string;
|
|
24
|
+
ts: number;
|
|
25
|
+
messageId: string | null;
|
|
26
|
+
parentMessageId: string | null;
|
|
27
|
+
sessionId: string | null;
|
|
28
|
+
providerId: string;
|
|
29
|
+
model: string;
|
|
30
|
+
apiKeyId: string | null;
|
|
31
|
+
engineOrigin: UsageEngineOrigin;
|
|
32
|
+
inputTokens: number;
|
|
33
|
+
outputTokens: number;
|
|
34
|
+
cacheReadTokens: number;
|
|
35
|
+
cacheCreationTokens: number;
|
|
36
|
+
reasoningTokens: number;
|
|
37
|
+
costUsd: number;
|
|
38
|
+
/** Hypothetical-input-cost − actual-cost difference attributable to cache_read tokens. */
|
|
39
|
+
costSavedByCacheUsd: number;
|
|
40
|
+
/** JSON-serialised raw provider usage object. Kept for forensics. */
|
|
41
|
+
rawUsage: string | null;
|
|
42
|
+
/** Host run-correlation id (owning agent-run, when the host has one). Additive / nullable. */
|
|
43
|
+
runId?: string | null;
|
|
44
|
+
/** Host event-correlation id for this LLM call. NULL when unknown. Additive / nullable. */
|
|
45
|
+
eventId?: string | null;
|
|
46
|
+
}
|
|
47
|
+
/** Input shape for inserting a new event. `id` and `ts` are filled by the recorder/store. */
|
|
48
|
+
type UsageEventInput = Omit<UsageEventRecord, 'id' | 'ts'> & {
|
|
49
|
+
ts?: number;
|
|
50
|
+
};
|
|
51
|
+
/** Date range for queries. Both bounds are unix-millis; `endTs` is exclusive. */
|
|
52
|
+
interface UsageDateRange {
|
|
53
|
+
startTs: number;
|
|
54
|
+
endTs: number;
|
|
55
|
+
}
|
|
56
|
+
interface UsageQueryParams {
|
|
57
|
+
range: UsageDateRange;
|
|
58
|
+
/** Optional filter — restrict to a particular session. */
|
|
59
|
+
sessionId?: string;
|
|
60
|
+
}
|
|
61
|
+
/** Aggregated totals over a date range. */
|
|
62
|
+
interface UsageTotals {
|
|
63
|
+
inputTokens: number;
|
|
64
|
+
outputTokens: number;
|
|
65
|
+
cacheReadTokens: number;
|
|
66
|
+
cacheCreationTokens: number;
|
|
67
|
+
reasoningTokens: number;
|
|
68
|
+
costUsd: number;
|
|
69
|
+
costSavedByCacheUsd: number;
|
|
70
|
+
/** Number of events contributing to these totals. */
|
|
71
|
+
eventCount: number;
|
|
72
|
+
}
|
|
73
|
+
/** Granularity for a usage time-series query. `week` is NOT a bucket — a "week" view is a `day` bucket over a 7-day range. */
|
|
74
|
+
type UsageTimeBucket = 'hour' | 'day' | 'month';
|
|
75
|
+
/**
|
|
76
|
+
* One bucket of a usage time-series (trend chart). Buckets are query-time
|
|
77
|
+
* aggregations over the daemon's LOCAL-time boundaries and every bucket in the
|
|
78
|
+
* requested range is present (empty buckets are zero-filled), ascending by
|
|
79
|
+
* `bucketStartTs`.
|
|
80
|
+
*/
|
|
81
|
+
interface UsageTimeSeriesBucket {
|
|
82
|
+
/** Unix-millis of the LOCAL-time bucket boundary (hour start / local midnight / local 1st-of-month). */
|
|
83
|
+
bucketStartTs: number;
|
|
84
|
+
/** Frozen locale-agnostic label from LOCAL parts: hour `MM-DD HH:00`, day `YYYY-MM-DD`, month `YYYY-MM`. */
|
|
85
|
+
label: string;
|
|
86
|
+
/** Event count in the bucket (0 for a zero-filled bucket). */
|
|
87
|
+
requests: number;
|
|
88
|
+
inputTokens: number;
|
|
89
|
+
outputTokens: number;
|
|
90
|
+
cacheReadTokens: number;
|
|
91
|
+
cacheCreationTokens: number;
|
|
92
|
+
costUsd: number;
|
|
93
|
+
}
|
|
94
|
+
/** One row of the per-model breakdown. */
|
|
95
|
+
interface ModelUsageRow {
|
|
96
|
+
providerId: string;
|
|
97
|
+
model: string;
|
|
98
|
+
eventCount: number;
|
|
99
|
+
inputTokens: number;
|
|
100
|
+
outputTokens: number;
|
|
101
|
+
cacheReadTokens: number;
|
|
102
|
+
cacheCreationTokens: number;
|
|
103
|
+
costUsd: number;
|
|
104
|
+
costSavedByCacheUsd: number;
|
|
105
|
+
/** True when no pricing row exists for (providerId, model) — UIs may show an "unpriced" badge. */
|
|
106
|
+
unpriced: boolean;
|
|
107
|
+
}
|
|
108
|
+
/** One row of the per-API-key breakdown. NULL apiKeyId is mapped to a sentinel. */
|
|
109
|
+
interface ApiKeyUsageRow {
|
|
110
|
+
/** `null` represents the unattributed sentinel group. */
|
|
111
|
+
apiKeyId: string | null;
|
|
112
|
+
/** Display label resolved by the host store (its key registry, or an "unattributed" fallback). */
|
|
113
|
+
label: string;
|
|
114
|
+
providerId: string | null;
|
|
115
|
+
eventCount: number;
|
|
116
|
+
inputTokens: number;
|
|
117
|
+
outputTokens: number;
|
|
118
|
+
costUsd: number;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Cumulative cache stats for ONE session — a SUM over the session's usage
|
|
122
|
+
* events. `hitRate` is the cost-oriented aggregate `ΣcacheRead / Σ(prompt-side
|
|
123
|
+
* tokens)` where the prompt-side total is `input + cacheRead + cacheCreation`
|
|
124
|
+
* (output excluded).
|
|
125
|
+
*/
|
|
126
|
+
interface SessionCacheStats {
|
|
127
|
+
sessionId: string;
|
|
128
|
+
/** Σ uncached prompt tokens (cache miss). */
|
|
129
|
+
inputTokens: number;
|
|
130
|
+
/** Σ cache-read (hit) tokens. */
|
|
131
|
+
cacheReadTokens: number;
|
|
132
|
+
/** Σ cache-creation (write) tokens — Anthropic; 0 for auto-caching providers. */
|
|
133
|
+
cacheCreationTokens: number;
|
|
134
|
+
/** Σ output tokens (not part of the hit-rate denominator). */
|
|
135
|
+
outputTokens: number;
|
|
136
|
+
/** Number of usage-event rows for the session. */
|
|
137
|
+
eventCount: number;
|
|
138
|
+
/**
|
|
139
|
+
* ΣcacheRead / Σ(input + cacheRead + cacheCreation), in [0, 1]. 0 when the
|
|
140
|
+
* session has no prompt-side tokens yet (avoids divide-by-zero).
|
|
141
|
+
*/
|
|
142
|
+
hitRate: number;
|
|
143
|
+
}
|
|
144
|
+
/** One row in the message-level list (used by message-drilldown UI components). */
|
|
145
|
+
interface MessageUsageRow {
|
|
146
|
+
id: string;
|
|
147
|
+
ts: number;
|
|
148
|
+
messageId: string | null;
|
|
149
|
+
parentMessageId: string | null;
|
|
150
|
+
sessionId: string | null;
|
|
151
|
+
providerId: string;
|
|
152
|
+
model: string;
|
|
153
|
+
apiKeyId: string | null;
|
|
154
|
+
engineOrigin: UsageEngineOrigin;
|
|
155
|
+
inputTokens: number;
|
|
156
|
+
outputTokens: number;
|
|
157
|
+
cacheReadTokens: number;
|
|
158
|
+
cacheCreationTokens: number;
|
|
159
|
+
reasoningTokens: number;
|
|
160
|
+
costUsd: number;
|
|
161
|
+
costSavedByCacheUsd: number;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export type { ApiKeyUsageRow, MessageUsageRow, ModelUsageRow, SessionCacheStats, UsageDateRange, UsageEventInput, UsageEventRecord, UsageQueryParams, UsageTimeBucket, UsageTimeSeriesBucket, UsageTotals };
|
|
File without changes
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/voucher-types.ts
|
|
21
|
+
var voucher_types_exports = {};
|
|
22
|
+
__export(voucher_types_exports, {
|
|
23
|
+
DEFAULT_VOUCHER_CONFIG: () => DEFAULT_VOUCHER_CONFIG
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(voucher_types_exports);
|
|
26
|
+
var DEFAULT_VOUCHER_CONFIG = {
|
|
27
|
+
enabled: false
|
|
28
|
+
};
|
|
29
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
30
|
+
0 && (module.exports = {
|
|
31
|
+
DEFAULT_VOUCHER_CONFIG
|
|
32
|
+
});
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Voucher (redemption-card) contracts (voucher-redemption #9, design D2/D8).
|
|
3
|
+
*
|
|
4
|
+
* A voucher is a redeemable credit/renewal card layered on top of the outbound
|
|
5
|
+
* key-policy (#4): an admin generates a card (`CC_<entropy>`), a key-holder
|
|
6
|
+
* redeems it to raise their key's `totalCostLimitUsd` (credit) or extend its
|
|
7
|
+
* `expiresAt` (renewal). Dependency-light shapes shared across `@omnicross/*`:
|
|
8
|
+
*
|
|
9
|
+
* - `VoucherRecord` — the FROZEN stored card. It is a CREDENTIAL RECORD but holds
|
|
10
|
+
* NO plaintext code: only the sha256 `codeHash` (the redeem lookup key) + a
|
|
11
|
+
* short display `codePrefix`. The plaintext `CC_…` is returned ONCE at
|
|
12
|
+
* generation and NEVER persisted or logged. A secret-scan test asserts no
|
|
13
|
+
* `CC_` plaintext survives in a written record.
|
|
14
|
+
* - `VoucherGrant` — the ABSOLUTE target recorded on the card at redeem time so
|
|
15
|
+
* the apply step (`outboundApiKeysSetPolicy`) is idempotently re-appliable
|
|
16
|
+
* after a crash between the CAS flip and the apply (design D4).
|
|
17
|
+
* - `VoucherInfo` — the admin-safe DTO (prefix + status + value + caps only,
|
|
18
|
+
* NEVER the `codeHash`).
|
|
19
|
+
* - `VoucherConfig` — the `voucher` config segment. `enabled` default OFF ⇒ the
|
|
20
|
+
* redeem endpoint is inert + no key is ever mutated ⇒ byte-identical zero
|
|
21
|
+
* regression, purely additive on #4.
|
|
22
|
+
*
|
|
23
|
+
* @module voucher-types
|
|
24
|
+
*/
|
|
25
|
+
/** A card either adds USD credit or extends the key's lifetime. */
|
|
26
|
+
type VoucherType = 'credit' | 'renewal';
|
|
27
|
+
/** Card lifecycle. A card leaves `unredeemed` exactly once (CAS, design D4). */
|
|
28
|
+
type VoucherStatus = 'unredeemed' | 'redeemed' | 'revoked';
|
|
29
|
+
/**
|
|
30
|
+
* The ABSOLUTE grant a redemption applies to a key (design D4/D5). Recorded on
|
|
31
|
+
* the voucher AT the CAS flip so re-applying it after an interrupted redeem is a
|
|
32
|
+
* no-op (idempotent to the absolute value → never double-spends). A `credit`
|
|
33
|
+
* card records `totalCostLimitUsd`; a `renewal` card records `expiresAt`.
|
|
34
|
+
*/
|
|
35
|
+
interface VoucherGrant {
|
|
36
|
+
/** Absolute new `totalCostLimitUsd` for the key (credit cards). */
|
|
37
|
+
totalCostLimitUsd?: number;
|
|
38
|
+
/** Absolute new `expiresAt` (epoch ms) for the key (renewal cards). */
|
|
39
|
+
expiresAt?: number;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* One stored redemption card (design D2, FROZEN). Holds a HASH of the code
|
|
43
|
+
* (never the plaintext) + a display prefix. The `granted*` fields are the
|
|
44
|
+
* absolute target recorded at redeem time (idempotent re-apply, design D4).
|
|
45
|
+
*/
|
|
46
|
+
interface VoucherRecord {
|
|
47
|
+
/** Card id (independent of the code). */
|
|
48
|
+
id: string;
|
|
49
|
+
/** sha256 of the `CC_<entropy>` code — the redeem lookup key. NEVER plaintext. */
|
|
50
|
+
codeHash: string;
|
|
51
|
+
/** Short display prefix for the admin list (e.g. `CC_AB…`). NEVER the full code. */
|
|
52
|
+
codePrefix: string;
|
|
53
|
+
/** Credit (adds USD) or renewal (extends expiry). */
|
|
54
|
+
type: VoucherType;
|
|
55
|
+
/** `credit`: USD added to the key's `totalCostLimitUsd`. */
|
|
56
|
+
creditUsd?: number;
|
|
57
|
+
/** `renewal`: days added to the key's `expiresAt`. */
|
|
58
|
+
renewalDays?: number;
|
|
59
|
+
/** Anti-abuse cap on the RESULTING key `totalCostLimitUsd` (design D5). */
|
|
60
|
+
maxTotalCostLimitUsd?: number;
|
|
61
|
+
/** Anti-abuse cap on the RESULTING key lifetime, in days from now (design D5). */
|
|
62
|
+
maxExpiryDays?: number;
|
|
63
|
+
/** Lifecycle status. */
|
|
64
|
+
status: VoucherStatus;
|
|
65
|
+
/** Epoch ms the card was generated. */
|
|
66
|
+
createdAt: number;
|
|
67
|
+
/** Epoch ms the card was redeemed (set on the CAS flip). */
|
|
68
|
+
redeemedAt?: number;
|
|
69
|
+
/** The key id that redeemed the card (single-key binding). */
|
|
70
|
+
redeemedByKeyId?: string;
|
|
71
|
+
/**
|
|
72
|
+
* Whether the grant has been APPLIED to the key (design D4, revised). The CAS
|
|
73
|
+
* flip sets this `false`; the apply sets it `true` after a successful
|
|
74
|
+
* `outboundApiKeysSetPolicy`. `redeemed && grantApplied !== true` means "flipped
|
|
75
|
+
* but not (yet) applied" — the apply (of the recorded ABSOLUTE below) re-runs on
|
|
76
|
+
* the next redeem for the key, before that redeem computes anything. It is
|
|
77
|
+
* `grantApplied` that says "no work left"; the apply itself is idempotent
|
|
78
|
+
* (re-applying the same absolute is a no-op), so a crash between the apply and
|
|
79
|
+
* this mark never double-credits.
|
|
80
|
+
*/
|
|
81
|
+
grantApplied?: boolean;
|
|
82
|
+
/**
|
|
83
|
+
* The recorded ABSOLUTE `totalCostLimitUsd` target (the intended final key
|
|
84
|
+
* value), computed at flip time from the CURRENT policy read INSIDE the per-key
|
|
85
|
+
* mutex. This is the AUTHORITATIVE apply source on BOTH the first pass and the
|
|
86
|
+
* reconcile/replay path — re-applying it is idempotent (never double-credits).
|
|
87
|
+
* Because redemptions for a key are serialized and a stranded card is reconciled
|
|
88
|
+
* before the next card computes, the recorded absolute is never stale.
|
|
89
|
+
*/
|
|
90
|
+
grantedTotalCostLimitUsd?: number;
|
|
91
|
+
/** The recorded ABSOLUTE `expiresAt` target (see `grantedTotalCostLimitUsd`). */
|
|
92
|
+
grantedExpiresAt?: number;
|
|
93
|
+
/** Epoch ms the card was revoked. */
|
|
94
|
+
revokedAt?: number;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* The admin-safe voucher DTO (design D2). NEVER carries the `codeHash` — the
|
|
98
|
+
* admin sees the display prefix + status + value + caps only. This is the ONLY
|
|
99
|
+
* projection an admin GET returns.
|
|
100
|
+
*/
|
|
101
|
+
interface VoucherInfo {
|
|
102
|
+
id: string;
|
|
103
|
+
codePrefix: string;
|
|
104
|
+
type: VoucherType;
|
|
105
|
+
creditUsd?: number;
|
|
106
|
+
renewalDays?: number;
|
|
107
|
+
maxTotalCostLimitUsd?: number;
|
|
108
|
+
maxExpiryDays?: number;
|
|
109
|
+
status: VoucherStatus;
|
|
110
|
+
createdAt: number;
|
|
111
|
+
redeemedAt?: number;
|
|
112
|
+
redeemedByKeyId?: string;
|
|
113
|
+
grantApplied?: boolean;
|
|
114
|
+
grantedTotalCostLimitUsd?: number;
|
|
115
|
+
grantedExpiresAt?: number;
|
|
116
|
+
revokedAt?: number;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* The one-time create result: `plaintextOnce` (the `CC_…` code) is shown exactly
|
|
120
|
+
* once at generation and never again — only its hash is stored (design D3).
|
|
121
|
+
*/
|
|
122
|
+
interface VoucherCreated {
|
|
123
|
+
id: string;
|
|
124
|
+
codePrefix: string;
|
|
125
|
+
type: VoucherType;
|
|
126
|
+
createdAt: number;
|
|
127
|
+
/** The plaintext `CC_…` code — the ONLY time it crosses the wire. */
|
|
128
|
+
plaintextOnce: string;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* The result a successful redeem returns to the key-holder (design D2). Reveals
|
|
132
|
+
* ONLY this key's own new balance/expiry — never any other card or key.
|
|
133
|
+
*/
|
|
134
|
+
interface VoucherRedeemResult {
|
|
135
|
+
type: VoucherType;
|
|
136
|
+
/** The key's new absolute `totalCostLimitUsd` (credit cards). */
|
|
137
|
+
totalCostLimitUsd?: number;
|
|
138
|
+
/** The key's new absolute `expiresAt` (epoch ms) (renewal cards). */
|
|
139
|
+
expiresAt?: number;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* The `voucher` config segment (design D8), normalized like `audit`/`billing`.
|
|
143
|
+
* `enabled` default FALSE ⇒ the redeem endpoint rejects + no admin generate ⇒ no
|
|
144
|
+
* key is ever mutated ⇒ byte-identical zero regression, purely additive on #4.
|
|
145
|
+
*/
|
|
146
|
+
interface VoucherConfig {
|
|
147
|
+
/** Master switch; default FALSE (zero regression, inert product). */
|
|
148
|
+
enabled: boolean;
|
|
149
|
+
}
|
|
150
|
+
/** Frozen defaults for the `voucher` segment (SSOT). */
|
|
151
|
+
declare const DEFAULT_VOUCHER_CONFIG: VoucherConfig;
|
|
152
|
+
|
|
153
|
+
export { DEFAULT_VOUCHER_CONFIG, type VoucherConfig, type VoucherCreated, type VoucherGrant, type VoucherInfo, type VoucherRecord, type VoucherRedeemResult, type VoucherStatus, type VoucherType };
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Voucher (redemption-card) contracts (voucher-redemption #9, design D2/D8).
|
|
3
|
+
*
|
|
4
|
+
* A voucher is a redeemable credit/renewal card layered on top of the outbound
|
|
5
|
+
* key-policy (#4): an admin generates a card (`CC_<entropy>`), a key-holder
|
|
6
|
+
* redeems it to raise their key's `totalCostLimitUsd` (credit) or extend its
|
|
7
|
+
* `expiresAt` (renewal). Dependency-light shapes shared across `@omnicross/*`:
|
|
8
|
+
*
|
|
9
|
+
* - `VoucherRecord` — the FROZEN stored card. It is a CREDENTIAL RECORD but holds
|
|
10
|
+
* NO plaintext code: only the sha256 `codeHash` (the redeem lookup key) + a
|
|
11
|
+
* short display `codePrefix`. The plaintext `CC_…` is returned ONCE at
|
|
12
|
+
* generation and NEVER persisted or logged. A secret-scan test asserts no
|
|
13
|
+
* `CC_` plaintext survives in a written record.
|
|
14
|
+
* - `VoucherGrant` — the ABSOLUTE target recorded on the card at redeem time so
|
|
15
|
+
* the apply step (`outboundApiKeysSetPolicy`) is idempotently re-appliable
|
|
16
|
+
* after a crash between the CAS flip and the apply (design D4).
|
|
17
|
+
* - `VoucherInfo` — the admin-safe DTO (prefix + status + value + caps only,
|
|
18
|
+
* NEVER the `codeHash`).
|
|
19
|
+
* - `VoucherConfig` — the `voucher` config segment. `enabled` default OFF ⇒ the
|
|
20
|
+
* redeem endpoint is inert + no key is ever mutated ⇒ byte-identical zero
|
|
21
|
+
* regression, purely additive on #4.
|
|
22
|
+
*
|
|
23
|
+
* @module voucher-types
|
|
24
|
+
*/
|
|
25
|
+
/** A card either adds USD credit or extends the key's lifetime. */
|
|
26
|
+
type VoucherType = 'credit' | 'renewal';
|
|
27
|
+
/** Card lifecycle. A card leaves `unredeemed` exactly once (CAS, design D4). */
|
|
28
|
+
type VoucherStatus = 'unredeemed' | 'redeemed' | 'revoked';
|
|
29
|
+
/**
|
|
30
|
+
* The ABSOLUTE grant a redemption applies to a key (design D4/D5). Recorded on
|
|
31
|
+
* the voucher AT the CAS flip so re-applying it after an interrupted redeem is a
|
|
32
|
+
* no-op (idempotent to the absolute value → never double-spends). A `credit`
|
|
33
|
+
* card records `totalCostLimitUsd`; a `renewal` card records `expiresAt`.
|
|
34
|
+
*/
|
|
35
|
+
interface VoucherGrant {
|
|
36
|
+
/** Absolute new `totalCostLimitUsd` for the key (credit cards). */
|
|
37
|
+
totalCostLimitUsd?: number;
|
|
38
|
+
/** Absolute new `expiresAt` (epoch ms) for the key (renewal cards). */
|
|
39
|
+
expiresAt?: number;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* One stored redemption card (design D2, FROZEN). Holds a HASH of the code
|
|
43
|
+
* (never the plaintext) + a display prefix. The `granted*` fields are the
|
|
44
|
+
* absolute target recorded at redeem time (idempotent re-apply, design D4).
|
|
45
|
+
*/
|
|
46
|
+
interface VoucherRecord {
|
|
47
|
+
/** Card id (independent of the code). */
|
|
48
|
+
id: string;
|
|
49
|
+
/** sha256 of the `CC_<entropy>` code — the redeem lookup key. NEVER plaintext. */
|
|
50
|
+
codeHash: string;
|
|
51
|
+
/** Short display prefix for the admin list (e.g. `CC_AB…`). NEVER the full code. */
|
|
52
|
+
codePrefix: string;
|
|
53
|
+
/** Credit (adds USD) or renewal (extends expiry). */
|
|
54
|
+
type: VoucherType;
|
|
55
|
+
/** `credit`: USD added to the key's `totalCostLimitUsd`. */
|
|
56
|
+
creditUsd?: number;
|
|
57
|
+
/** `renewal`: days added to the key's `expiresAt`. */
|
|
58
|
+
renewalDays?: number;
|
|
59
|
+
/** Anti-abuse cap on the RESULTING key `totalCostLimitUsd` (design D5). */
|
|
60
|
+
maxTotalCostLimitUsd?: number;
|
|
61
|
+
/** Anti-abuse cap on the RESULTING key lifetime, in days from now (design D5). */
|
|
62
|
+
maxExpiryDays?: number;
|
|
63
|
+
/** Lifecycle status. */
|
|
64
|
+
status: VoucherStatus;
|
|
65
|
+
/** Epoch ms the card was generated. */
|
|
66
|
+
createdAt: number;
|
|
67
|
+
/** Epoch ms the card was redeemed (set on the CAS flip). */
|
|
68
|
+
redeemedAt?: number;
|
|
69
|
+
/** The key id that redeemed the card (single-key binding). */
|
|
70
|
+
redeemedByKeyId?: string;
|
|
71
|
+
/**
|
|
72
|
+
* Whether the grant has been APPLIED to the key (design D4, revised). The CAS
|
|
73
|
+
* flip sets this `false`; the apply sets it `true` after a successful
|
|
74
|
+
* `outboundApiKeysSetPolicy`. `redeemed && grantApplied !== true` means "flipped
|
|
75
|
+
* but not (yet) applied" — the apply (of the recorded ABSOLUTE below) re-runs on
|
|
76
|
+
* the next redeem for the key, before that redeem computes anything. It is
|
|
77
|
+
* `grantApplied` that says "no work left"; the apply itself is idempotent
|
|
78
|
+
* (re-applying the same absolute is a no-op), so a crash between the apply and
|
|
79
|
+
* this mark never double-credits.
|
|
80
|
+
*/
|
|
81
|
+
grantApplied?: boolean;
|
|
82
|
+
/**
|
|
83
|
+
* The recorded ABSOLUTE `totalCostLimitUsd` target (the intended final key
|
|
84
|
+
* value), computed at flip time from the CURRENT policy read INSIDE the per-key
|
|
85
|
+
* mutex. This is the AUTHORITATIVE apply source on BOTH the first pass and the
|
|
86
|
+
* reconcile/replay path — re-applying it is idempotent (never double-credits).
|
|
87
|
+
* Because redemptions for a key are serialized and a stranded card is reconciled
|
|
88
|
+
* before the next card computes, the recorded absolute is never stale.
|
|
89
|
+
*/
|
|
90
|
+
grantedTotalCostLimitUsd?: number;
|
|
91
|
+
/** The recorded ABSOLUTE `expiresAt` target (see `grantedTotalCostLimitUsd`). */
|
|
92
|
+
grantedExpiresAt?: number;
|
|
93
|
+
/** Epoch ms the card was revoked. */
|
|
94
|
+
revokedAt?: number;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* The admin-safe voucher DTO (design D2). NEVER carries the `codeHash` — the
|
|
98
|
+
* admin sees the display prefix + status + value + caps only. This is the ONLY
|
|
99
|
+
* projection an admin GET returns.
|
|
100
|
+
*/
|
|
101
|
+
interface VoucherInfo {
|
|
102
|
+
id: string;
|
|
103
|
+
codePrefix: string;
|
|
104
|
+
type: VoucherType;
|
|
105
|
+
creditUsd?: number;
|
|
106
|
+
renewalDays?: number;
|
|
107
|
+
maxTotalCostLimitUsd?: number;
|
|
108
|
+
maxExpiryDays?: number;
|
|
109
|
+
status: VoucherStatus;
|
|
110
|
+
createdAt: number;
|
|
111
|
+
redeemedAt?: number;
|
|
112
|
+
redeemedByKeyId?: string;
|
|
113
|
+
grantApplied?: boolean;
|
|
114
|
+
grantedTotalCostLimitUsd?: number;
|
|
115
|
+
grantedExpiresAt?: number;
|
|
116
|
+
revokedAt?: number;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* The one-time create result: `plaintextOnce` (the `CC_…` code) is shown exactly
|
|
120
|
+
* once at generation and never again — only its hash is stored (design D3).
|
|
121
|
+
*/
|
|
122
|
+
interface VoucherCreated {
|
|
123
|
+
id: string;
|
|
124
|
+
codePrefix: string;
|
|
125
|
+
type: VoucherType;
|
|
126
|
+
createdAt: number;
|
|
127
|
+
/** The plaintext `CC_…` code — the ONLY time it crosses the wire. */
|
|
128
|
+
plaintextOnce: string;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* The result a successful redeem returns to the key-holder (design D2). Reveals
|
|
132
|
+
* ONLY this key's own new balance/expiry — never any other card or key.
|
|
133
|
+
*/
|
|
134
|
+
interface VoucherRedeemResult {
|
|
135
|
+
type: VoucherType;
|
|
136
|
+
/** The key's new absolute `totalCostLimitUsd` (credit cards). */
|
|
137
|
+
totalCostLimitUsd?: number;
|
|
138
|
+
/** The key's new absolute `expiresAt` (epoch ms) (renewal cards). */
|
|
139
|
+
expiresAt?: number;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* The `voucher` config segment (design D8), normalized like `audit`/`billing`.
|
|
143
|
+
* `enabled` default FALSE ⇒ the redeem endpoint rejects + no admin generate ⇒ no
|
|
144
|
+
* key is ever mutated ⇒ byte-identical zero regression, purely additive on #4.
|
|
145
|
+
*/
|
|
146
|
+
interface VoucherConfig {
|
|
147
|
+
/** Master switch; default FALSE (zero regression, inert product). */
|
|
148
|
+
enabled: boolean;
|
|
149
|
+
}
|
|
150
|
+
/** Frozen defaults for the `voucher` segment (SSOT). */
|
|
151
|
+
declare const DEFAULT_VOUCHER_CONFIG: VoucherConfig;
|
|
152
|
+
|
|
153
|
+
export { DEFAULT_VOUCHER_CONFIG, type VoucherConfig, type VoucherCreated, type VoucherGrant, type VoucherInfo, type VoucherRecord, type VoucherRedeemResult, type VoucherStatus, type VoucherType };
|