modelrig 0.0.1 → 0.1.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.
Files changed (220) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +115 -8
  3. package/dist/adapters/anthropic.d.ts +46 -0
  4. package/dist/adapters/anthropic.d.ts.map +1 -0
  5. package/dist/adapters/anthropic.js +202 -0
  6. package/dist/adapters/deepseek.d.ts +41 -0
  7. package/dist/adapters/deepseek.d.ts.map +1 -0
  8. package/dist/adapters/deepseek.js +163 -0
  9. package/dist/adapters/gemini.d.ts +44 -0
  10. package/dist/adapters/gemini.d.ts.map +1 -0
  11. package/dist/adapters/gemini.js +222 -0
  12. package/dist/adapters/grok.d.ts +44 -0
  13. package/dist/adapters/grok.d.ts.map +1 -0
  14. package/dist/adapters/grok.js +168 -0
  15. package/dist/adapters/openai-compat.d.ts +74 -0
  16. package/dist/adapters/openai-compat.d.ts.map +1 -0
  17. package/dist/adapters/openai-compat.js +187 -0
  18. package/dist/adapters/openai.d.ts +49 -0
  19. package/dist/adapters/openai.d.ts.map +1 -0
  20. package/dist/adapters/openai.js +222 -0
  21. package/dist/bakeoff/engine.d.ts +52 -0
  22. package/dist/bakeoff/engine.d.ts.map +1 -0
  23. package/dist/bakeoff/engine.js +163 -0
  24. package/dist/bakeoff/replay.d.ts +52 -0
  25. package/dist/bakeoff/replay.d.ts.map +1 -0
  26. package/dist/bakeoff/replay.js +94 -0
  27. package/dist/bakeoff/samples.d.ts +62 -0
  28. package/dist/bakeoff/samples.d.ts.map +1 -0
  29. package/dist/bakeoff/samples.js +128 -0
  30. package/dist/bakeoff/stats.d.ts +13 -0
  31. package/dist/bakeoff/stats.d.ts.map +1 -0
  32. package/dist/bakeoff/stats.js +48 -0
  33. package/dist/bakeoff/types.d.ts +37 -0
  34. package/dist/bakeoff/types.d.ts.map +1 -0
  35. package/dist/bakeoff/types.js +10 -0
  36. package/dist/cli.d.ts +27 -0
  37. package/dist/cli.d.ts.map +1 -0
  38. package/dist/cli.js +572 -0
  39. package/dist/config.d.ts +104 -0
  40. package/dist/config.d.ts.map +1 -0
  41. package/dist/config.js +148 -0
  42. package/dist/feedback/coverage.d.ts +32 -0
  43. package/dist/feedback/coverage.d.ts.map +1 -0
  44. package/dist/feedback/coverage.js +95 -0
  45. package/dist/feedback/feedback.d.ts +35 -0
  46. package/dist/feedback/feedback.d.ts.map +1 -0
  47. package/dist/feedback/feedback.js +39 -0
  48. package/dist/index.d.ts +155 -0
  49. package/dist/index.d.ts.map +1 -0
  50. package/dist/index.js +448 -0
  51. package/dist/init/finder.d.ts +79 -0
  52. package/dist/init/finder.d.ts.map +1 -0
  53. package/dist/init/finder.js +297 -0
  54. package/dist/init/run.d.ts +99 -0
  55. package/dist/init/run.d.ts.map +1 -0
  56. package/dist/init/run.js +281 -0
  57. package/dist/init/signatures.d.ts +39 -0
  58. package/dist/init/signatures.d.ts.map +1 -0
  59. package/dist/init/signatures.js +116 -0
  60. package/dist/matrix/aggregate.d.ts +89 -0
  61. package/dist/matrix/aggregate.d.ts.map +1 -0
  62. package/dist/matrix/aggregate.js +186 -0
  63. package/dist/query/grammar.d.ts +35 -0
  64. package/dist/query/grammar.d.ts.map +1 -0
  65. package/dist/query/grammar.js +279 -0
  66. package/dist/query/run.d.ts +8 -0
  67. package/dist/query/run.d.ts.map +1 -0
  68. package/dist/query/run.js +21 -0
  69. package/dist/registry/build.d.ts +126 -0
  70. package/dist/registry/build.d.ts.map +1 -0
  71. package/dist/registry/build.js +192 -0
  72. package/dist/registry/call-notes.d.ts +11 -0
  73. package/dist/registry/call-notes.d.ts.map +1 -0
  74. package/dist/registry/call-notes.js +72 -0
  75. package/dist/registry/claims.d.ts +63 -0
  76. package/dist/registry/claims.d.ts.map +1 -0
  77. package/dist/registry/claims.js +107 -0
  78. package/dist/registry/facts.d.ts +32 -0
  79. package/dist/registry/facts.d.ts.map +1 -0
  80. package/dist/registry/facts.js +120 -0
  81. package/dist/registry/fingerprint.d.ts +20 -0
  82. package/dist/registry/fingerprint.d.ts.map +1 -0
  83. package/dist/registry/fingerprint.js +150 -0
  84. package/dist/registry/parity.d.ts +26 -0
  85. package/dist/registry/parity.d.ts.map +1 -0
  86. package/dist/registry/parity.js +41 -0
  87. package/dist/registry/pricing.d.ts +19 -0
  88. package/dist/registry/pricing.d.ts.map +1 -0
  89. package/dist/registry/pricing.js +69 -0
  90. package/dist/registry/rows.d.ts +16 -0
  91. package/dist/registry/rows.d.ts.map +1 -0
  92. package/dist/registry/rows.js +57 -0
  93. package/dist/registry/seed.d.ts +43 -0
  94. package/dist/registry/seed.d.ts.map +1 -0
  95. package/dist/registry/seed.js +97 -0
  96. package/dist/registry/select.d.ts +68 -0
  97. package/dist/registry/select.d.ts.map +1 -0
  98. package/dist/registry/select.js +141 -0
  99. package/dist/registry/staleness.d.ts +18 -0
  100. package/dist/registry/staleness.d.ts.map +1 -0
  101. package/dist/registry/staleness.js +39 -0
  102. package/dist/routes/draft.d.ts +83 -0
  103. package/dist/routes/draft.d.ts.map +1 -0
  104. package/dist/routes/draft.js +135 -0
  105. package/dist/routes/loader.d.ts +26 -0
  106. package/dist/routes/loader.d.ts.map +1 -0
  107. package/dist/routes/loader.js +436 -0
  108. package/dist/routes/manifest.d.ts +24 -0
  109. package/dist/routes/manifest.d.ts.map +1 -0
  110. package/dist/routes/manifest.js +118 -0
  111. package/dist/routes/serveability.d.ts +52 -0
  112. package/dist/routes/serveability.d.ts.map +1 -0
  113. package/dist/routes/serveability.js +73 -0
  114. package/dist/routes/template.d.ts +18 -0
  115. package/dist/routes/template.d.ts.map +1 -0
  116. package/dist/routes/template.js +94 -0
  117. package/dist/run/cost.d.ts +26 -0
  118. package/dist/run/cost.d.ts.map +1 -0
  119. package/dist/run/cost.js +41 -0
  120. package/dist/run/envelope.d.ts +21 -0
  121. package/dist/run/envelope.d.ts.map +1 -0
  122. package/dist/run/envelope.js +45 -0
  123. package/dist/run/grounding.d.ts +56 -0
  124. package/dist/run/grounding.d.ts.map +1 -0
  125. package/dist/run/grounding.js +89 -0
  126. package/dist/run/repair.d.ts +36 -0
  127. package/dist/run/repair.d.ts.map +1 -0
  128. package/dist/run/repair.js +61 -0
  129. package/dist/run/resolve.d.ts +26 -0
  130. package/dist/run/resolve.d.ts.map +1 -0
  131. package/dist/run/resolve.js +81 -0
  132. package/dist/run/retry.d.ts +19 -0
  133. package/dist/run/retry.d.ts.map +1 -0
  134. package/dist/run/retry.js +50 -0
  135. package/dist/run/run.d.ts +41 -0
  136. package/dist/run/run.d.ts.map +1 -0
  137. package/dist/run/run.js +0 -0
  138. package/dist/run/validate.d.ts +15 -0
  139. package/dist/run/validate.d.ts.map +1 -0
  140. package/dist/run/validate.js +56 -0
  141. package/dist/savings/emit.d.ts +25 -0
  142. package/dist/savings/emit.d.ts.map +1 -0
  143. package/dist/savings/emit.js +75 -0
  144. package/dist/savings/odometer.d.ts +32 -0
  145. package/dist/savings/odometer.d.ts.map +1 -0
  146. package/dist/savings/odometer.js +70 -0
  147. package/dist/savings/swap-execute.d.ts +44 -0
  148. package/dist/savings/swap-execute.d.ts.map +1 -0
  149. package/dist/savings/swap-execute.js +87 -0
  150. package/dist/savings/swaps-client.d.ts +41 -0
  151. package/dist/savings/swaps-client.d.ts.map +1 -0
  152. package/dist/savings/swaps-client.js +81 -0
  153. package/dist/telemetry/capture.d.ts +28 -0
  154. package/dist/telemetry/capture.d.ts.map +1 -0
  155. package/dist/telemetry/capture.js +74 -0
  156. package/dist/telemetry/exporter.d.ts +62 -0
  157. package/dist/telemetry/exporter.d.ts.map +1 -0
  158. package/dist/telemetry/exporter.js +233 -0
  159. package/dist/telemetry/ingest-protocol.d.ts +161 -0
  160. package/dist/telemetry/ingest-protocol.d.ts.map +1 -0
  161. package/dist/telemetry/ingest-protocol.js +163 -0
  162. package/dist/telemetry/sinks.d.ts +54 -0
  163. package/dist/telemetry/sinks.d.ts.map +1 -0
  164. package/dist/telemetry/sinks.js +89 -0
  165. package/dist/telemetry/store.d.ts +38 -0
  166. package/dist/telemetry/store.d.ts.map +1 -0
  167. package/dist/telemetry/store.js +332 -0
  168. package/dist/telemetry/supabase-exporter.d.ts +64 -0
  169. package/dist/telemetry/supabase-exporter.d.ts.map +1 -0
  170. package/dist/telemetry/supabase-exporter.js +211 -0
  171. package/dist/tenancy/api-key-format.d.ts +67 -0
  172. package/dist/tenancy/api-key-format.d.ts.map +1 -0
  173. package/dist/tenancy/api-key-format.js +91 -0
  174. package/dist/tenancy/constants.d.ts +23 -0
  175. package/dist/tenancy/constants.d.ts.map +1 -0
  176. package/dist/tenancy/constants.js +25 -0
  177. package/dist/tenancy/scrub.d.ts +47 -0
  178. package/dist/tenancy/scrub.d.ts.map +1 -0
  179. package/dist/tenancy/scrub.js +85 -0
  180. package/dist/types/failures-teaching.d.ts +56 -0
  181. package/dist/types/failures-teaching.d.ts.map +1 -0
  182. package/dist/types/failures-teaching.js +177 -0
  183. package/dist/types/failures.d.ts +27 -0
  184. package/dist/types/failures.d.ts.map +1 -0
  185. package/dist/types/failures.js +41 -0
  186. package/dist/types/provider.d.ts +79 -0
  187. package/dist/types/provider.d.ts.map +1 -0
  188. package/dist/types/provider.js +20 -0
  189. package/dist/types/route.d.ts +127 -0
  190. package/dist/types/route.d.ts.map +1 -0
  191. package/dist/types/route.js +6 -0
  192. package/dist/types/telemetry.d.ts +89 -0
  193. package/dist/types/telemetry.d.ts.map +1 -0
  194. package/dist/types/telemetry.js +6 -0
  195. package/dist/watch/budget.d.ts +19 -0
  196. package/dist/watch/budget.d.ts.map +1 -0
  197. package/dist/watch/budget.js +35 -0
  198. package/dist/watch/evaluate.d.ts +66 -0
  199. package/dist/watch/evaluate.d.ts.map +1 -0
  200. package/dist/watch/evaluate.js +94 -0
  201. package/dist/watch/proposals.d.ts +73 -0
  202. package/dist/watch/proposals.d.ts.map +1 -0
  203. package/dist/watch/proposals.js +169 -0
  204. package/dist/watch/run-record.d.ts +64 -0
  205. package/dist/watch/run-record.d.ts.map +1 -0
  206. package/dist/watch/run-record.js +86 -0
  207. package/dist/watch/run-reported.d.ts +20 -0
  208. package/dist/watch/run-reported.d.ts.map +1 -0
  209. package/dist/watch/run-reported.js +53 -0
  210. package/dist/watch/run.d.ts +40 -0
  211. package/dist/watch/run.d.ts.map +1 -0
  212. package/dist/watch/run.js +44 -0
  213. package/dist/watch/state-store.d.ts +48 -0
  214. package/dist/watch/state-store.d.ts.map +1 -0
  215. package/dist/watch/state-store.js +86 -0
  216. package/dist/watch/state.d.ts +21 -0
  217. package/dist/watch/state.d.ts.map +1 -0
  218. package/dist/watch/state.js +93 -0
  219. package/package.json +50 -14
  220. package/index.js +0 -3
@@ -0,0 +1,332 @@
1
+ "use strict";
2
+ /**
3
+ * SQLite telemetry store — spec §5, plan §2. Package-owned storage; zero
4
+ * InferWealth Supabase involvement. Schema created idempotently on open;
5
+ * PRAGMA user_version tracks migrations (v2: fingerprint_cell, Phase 2;
6
+ * v3: captures table + served_variant/repaired_by, Phase 3).
7
+ *
8
+ * PRIVACY (Phase 3 WS4): the `captures` table is created here but NEVER read
9
+ * here — capture access lives exclusively behind telemetry/capture.ts's
10
+ * separate handle. ExportableTelemetryStore MUST NOT gain capture accessors;
11
+ * the export-isolation tests are the tripwire.
12
+ */
13
+ var __importDefault = (this && this.__importDefault) || function (mod) {
14
+ return (mod && mod.__esModule) ? mod : { "default": mod };
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.openTelemetryStore = openTelemetryStore;
18
+ const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
19
+ const node_fs_1 = require("node:fs");
20
+ const node_path_1 = require("node:path");
21
+ const SCHEMA_SQL = `
22
+ CREATE TABLE IF NOT EXISTS inferences (
23
+ id TEXT PRIMARY KEY,
24
+ ts TEXT NOT NULL,
25
+ route TEXT NOT NULL,
26
+ route_version INTEGER NOT NULL,
27
+ provider TEXT NOT NULL,
28
+ model TEXT NOT NULL,
29
+ requested_tier TEXT NOT NULL,
30
+ served_tier TEXT NOT NULL,
31
+ tokens_in INTEGER NOT NULL,
32
+ tokens_out INTEGER NOT NULL,
33
+ tokens_cached INTEGER NOT NULL,
34
+ cost_estimate_usd REAL NOT NULL,
35
+ latency_ms INTEGER NOT NULL,
36
+ ttfb_ms INTEGER,
37
+ failure_class TEXT,
38
+ attempts_json TEXT NOT NULL,
39
+ tags_json TEXT NOT NULL,
40
+ fingerprint_cell TEXT,
41
+ served_variant TEXT,
42
+ repaired_by TEXT,
43
+ pricing_missing INTEGER NOT NULL DEFAULT 0
44
+ );
45
+ CREATE INDEX IF NOT EXISTS idx_inferences_route_ts ON inferences (route, ts);
46
+
47
+ CREATE TABLE IF NOT EXISTS captures (
48
+ inference_id TEXT PRIMARY KEY,
49
+ ts TEXT NOT NULL,
50
+ route TEXT NOT NULL,
51
+ route_version INTEGER NOT NULL,
52
+ variables_json TEXT NOT NULL,
53
+ output_text TEXT NOT NULL
54
+ );
55
+ CREATE INDEX IF NOT EXISTS idx_captures_route_ts ON captures (route, ts);
56
+
57
+ CREATE TABLE IF NOT EXISTS envelopes (
58
+ envelope_key TEXT PRIMARY KEY,
59
+ budget_usd REAL NOT NULL,
60
+ spent_usd REAL NOT NULL DEFAULT 0,
61
+ status TEXT NOT NULL DEFAULT 'open' CHECK (status IN ('open', 'exhausted'))
62
+ );
63
+
64
+ CREATE TABLE IF NOT EXISTS export_state (
65
+ source_id TEXT PRIMARY KEY,
66
+ last_rowid INTEGER NOT NULL DEFAULT 0
67
+ );
68
+
69
+ CREATE TABLE IF NOT EXISTS feedback (
70
+ id TEXT PRIMARY KEY,
71
+ ts TEXT NOT NULL,
72
+ inference_id TEXT,
73
+ episode_key TEXT,
74
+ verdict TEXT NOT NULL CHECK (verdict IN ('up', 'down')),
75
+ score REAL,
76
+ note TEXT,
77
+ source TEXT NOT NULL,
78
+ -- v7 (Phase 6 WS2): the verdict's own tenant and author. NULL org_id means
79
+ -- "whatever org this exporter is", which is the right answer for every
80
+ -- self-hosted rig; a non-null one is a verdict recorded THROUGH a server on
81
+ -- behalf of another org, and the exporter must not overwrite it (QA-P6-010).
82
+ org_id TEXT,
83
+ created_by TEXT
84
+ );
85
+ CREATE INDEX IF NOT EXISTS idx_feedback_inference ON feedback (inference_id);
86
+ CREATE INDEX IF NOT EXISTS idx_feedback_episode ON feedback (episode_key);
87
+ `;
88
+ function toFeedbackRow(r) {
89
+ return {
90
+ id: r.id,
91
+ ts: r.ts,
92
+ inferenceId: r.inference_id,
93
+ episodeKey: r.episode_key,
94
+ verdict: r.verdict,
95
+ score: r.score,
96
+ note: r.note,
97
+ source: r.source,
98
+ // `?? null` rather than a bare read: a v6 database returns `undefined`
99
+ // for a column that did not exist when its rows were written, and
100
+ // `undefined` would serialize to a MISSING key on the wire rather than an
101
+ // explicit null — which reads to the server as "no opinion" instead of
102
+ // "no org", a distinction that matters exactly once and expensively.
103
+ orgId: r.org_id ?? null,
104
+ createdBy: r.created_by ?? null,
105
+ };
106
+ }
107
+ function toTelemetryRow(r) {
108
+ return {
109
+ id: r.id,
110
+ ts: r.ts,
111
+ route: r.route,
112
+ routeVersion: r.route_version,
113
+ provider: r.provider,
114
+ model: r.model,
115
+ requestedTier: r.requested_tier,
116
+ servedTier: r.served_tier,
117
+ tokensIn: r.tokens_in,
118
+ tokensOut: r.tokens_out,
119
+ tokensCached: r.tokens_cached,
120
+ costEstimateUsd: r.cost_estimate_usd,
121
+ latencyMs: r.latency_ms,
122
+ ttfbMs: r.ttfb_ms,
123
+ failureClass: r.failure_class,
124
+ attemptsJson: r.attempts_json,
125
+ tagsJson: r.tags_json,
126
+ fingerprintCell: r.fingerprint_cell,
127
+ servedVariant: r.served_variant,
128
+ repairedBy: r.repaired_by,
129
+ pricingMissing: r.pricing_missing !== 0,
130
+ };
131
+ }
132
+ function toEnvelopeRow(r) {
133
+ return {
134
+ envelopeKey: r.envelope_key,
135
+ budgetUsd: r.budget_usd,
136
+ spentUsd: r.spent_usd,
137
+ status: r.status,
138
+ };
139
+ }
140
+ function openTelemetryStore(dbPath) {
141
+ (0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(dbPath), { recursive: true });
142
+ const db = new better_sqlite3_1.default(dbPath);
143
+ db.pragma("journal_mode = WAL");
144
+ db.exec(SCHEMA_SQL);
145
+ // Migrations, idempotent on every open. Fresh databases get the full v2
146
+ // schema from SCHEMA_SQL above; v1 databases (tables pre-existing, so the
147
+ // CREATEs were no-ops) gain the fingerprint_cell column additively —
148
+ // existing rows are untouched and read back with fingerprint_cell = NULL.
149
+ if (db.pragma("user_version", { simple: true }) < 2) {
150
+ const columns = db.prepare("PRAGMA table_info(inferences)").all();
151
+ if (!columns.some((c) => c.name === "fingerprint_cell")) {
152
+ db.exec("ALTER TABLE inferences ADD COLUMN fingerprint_cell TEXT");
153
+ }
154
+ db.pragma("user_version = 2");
155
+ }
156
+ // v3 (Phase 3): captures table (from SCHEMA_SQL above — a no-op CREATE on
157
+ // fresh databases) + the variant/repair columns, additive and nullable.
158
+ if (db.pragma("user_version", { simple: true }) < 3) {
159
+ const columns = db.prepare("PRAGMA table_info(inferences)").all();
160
+ if (!columns.some((c) => c.name === "served_variant")) {
161
+ db.exec("ALTER TABLE inferences ADD COLUMN served_variant TEXT");
162
+ }
163
+ if (!columns.some((c) => c.name === "repaired_by")) {
164
+ db.exec("ALTER TABLE inferences ADD COLUMN repaired_by TEXT");
165
+ }
166
+ db.pragma("user_version = 3");
167
+ }
168
+ // v4 (addendum A1): pricing_missing flag — additive; old rows read false.
169
+ if (db.pragma("user_version", { simple: true }) < 4) {
170
+ const columns = db.prepare("PRAGMA table_info(inferences)").all();
171
+ if (!columns.some((c) => c.name === "pricing_missing")) {
172
+ db.exec("ALTER TABLE inferences ADD COLUMN pricing_missing INTEGER NOT NULL DEFAULT 0");
173
+ }
174
+ db.pragma("user_version = 4");
175
+ }
176
+ // v5 (Phase 4 WS2): feedback table — purely additive; SCHEMA_SQL's
177
+ // CREATE IF NOT EXISTS handles fresh AND migrating databases identically,
178
+ // so the bump only stamps the version.
179
+ if (db.pragma("user_version", { simple: true }) < 5) {
180
+ db.pragma("user_version = 5");
181
+ }
182
+ // v6 (Phase 4 WS3): ttfb_ms — additive, nullable; old rows read null.
183
+ if (db.pragma("user_version", { simple: true }) < 6) {
184
+ const columns = db.prepare("PRAGMA table_info(inferences)").all();
185
+ if (!columns.some((c) => c.name === "ttfb_ms")) {
186
+ db.exec("ALTER TABLE inferences ADD COLUMN ttfb_ms INTEGER");
187
+ }
188
+ db.pragma("user_version = 6");
189
+ }
190
+ // v7 (Phase 6 WS2): feedback.org_id + feedback.created_by — additive,
191
+ // nullable. Old verdicts read null on both, which is TRUE of them: they were
192
+ // recorded before either concept existed, and backfilling a guess into an
193
+ // unrecomputable record is exactly what 00017 refused to do.
194
+ if (db.pragma("user_version", { simple: true }) < 7) {
195
+ const columns = db.prepare("PRAGMA table_info(feedback)").all();
196
+ if (!columns.some((c) => c.name === "org_id")) {
197
+ db.exec("ALTER TABLE feedback ADD COLUMN org_id TEXT");
198
+ }
199
+ if (!columns.some((c) => c.name === "created_by")) {
200
+ db.exec("ALTER TABLE feedback ADD COLUMN created_by TEXT");
201
+ }
202
+ db.pragma("user_version = 7");
203
+ }
204
+ const insertInference = db.prepare(`
205
+ INSERT INTO inferences (
206
+ id, ts, route, route_version, provider, model, requested_tier, served_tier,
207
+ tokens_in, tokens_out, tokens_cached, cost_estimate_usd, latency_ms, ttfb_ms,
208
+ failure_class, attempts_json, tags_json, fingerprint_cell, served_variant, repaired_by, pricing_missing
209
+ ) VALUES (
210
+ @id, @ts, @route, @route_version, @provider, @model, @requested_tier, @served_tier,
211
+ @tokens_in, @tokens_out, @tokens_cached, @cost_estimate_usd, @latency_ms, @ttfb_ms,
212
+ @failure_class, @attempts_json, @tags_json, @fingerprint_cell, @served_variant, @repaired_by, @pricing_missing
213
+ )
214
+ `);
215
+ const selectByTag = db.prepare(`
216
+ SELECT * FROM inferences
217
+ WHERE json_extract(tags_json, '$.' || ?) = ?
218
+ ORDER BY ts, rowid
219
+ `);
220
+ const selectEnvelope = db.prepare("SELECT * FROM envelopes WHERE envelope_key = ?");
221
+ const insertEnvelope = db.prepare(`
222
+ INSERT INTO envelopes (envelope_key, budget_usd, spent_usd, status)
223
+ VALUES (?, ?, 0, 'open')
224
+ ON CONFLICT (envelope_key) DO NOTHING
225
+ `);
226
+ const insertFeedback = db.prepare(`
227
+ INSERT INTO feedback (
228
+ id, ts, inference_id, episode_key, verdict, score, note, source, org_id, created_by
229
+ )
230
+ VALUES (
231
+ @id, @ts, @inference_id, @episode_key, @verdict, @score, @note, @source, @org_id, @created_by
232
+ )
233
+ `);
234
+ const accrueEnvelope = db.prepare(`
235
+ UPDATE envelopes
236
+ SET spent_usd = spent_usd + ?,
237
+ status = CASE WHEN spent_usd + ? >= budget_usd THEN 'exhausted' ELSE status END
238
+ WHERE envelope_key = ?
239
+ `);
240
+ return {
241
+ record(row) {
242
+ insertInference.run({
243
+ id: row.id,
244
+ ts: row.ts,
245
+ route: row.route,
246
+ route_version: row.routeVersion,
247
+ provider: row.provider,
248
+ model: row.model,
249
+ requested_tier: row.requestedTier,
250
+ served_tier: row.servedTier,
251
+ tokens_in: row.tokensIn,
252
+ tokens_out: row.tokensOut,
253
+ tokens_cached: row.tokensCached,
254
+ cost_estimate_usd: row.costEstimateUsd,
255
+ latency_ms: row.latencyMs,
256
+ ttfb_ms: row.ttfbMs,
257
+ failure_class: row.failureClass,
258
+ attempts_json: row.attemptsJson,
259
+ tags_json: row.tagsJson,
260
+ fingerprint_cell: row.fingerprintCell,
261
+ served_variant: row.servedVariant,
262
+ repaired_by: row.repairedBy,
263
+ pricing_missing: row.pricingMissing ? 1 : 0,
264
+ });
265
+ },
266
+ recordFeedback(row) {
267
+ insertFeedback.run({
268
+ id: row.id,
269
+ ts: row.ts,
270
+ inference_id: row.inferenceId,
271
+ episode_key: row.episodeKey,
272
+ verdict: row.verdict,
273
+ score: row.score,
274
+ note: row.note,
275
+ source: row.source,
276
+ org_id: row.orgId,
277
+ created_by: row.createdBy,
278
+ });
279
+ },
280
+ readFeedbackAfter(afterRowid, limit) {
281
+ const rows = db
282
+ .prepare("SELECT rowid AS __rowid, * FROM feedback WHERE rowid > ? ORDER BY rowid LIMIT ?")
283
+ .all(afterRowid, limit);
284
+ return rows.map((r) => ({ rowid: r.__rowid, row: toFeedbackRow(r) }));
285
+ },
286
+ listFeedback(limit) {
287
+ const rows = db
288
+ .prepare("SELECT * FROM feedback ORDER BY ts DESC, rowid DESC LIMIT ?")
289
+ .all(limit);
290
+ return rows.map(toFeedbackRow);
291
+ },
292
+ queryByTag(tag, value) {
293
+ return selectByTag.all(tag, value).map(toTelemetryRow);
294
+ },
295
+ getEnvelope(key) {
296
+ const row = selectEnvelope.get(key);
297
+ return row ? toEnvelopeRow(row) : null;
298
+ },
299
+ openEnvelope(key, budgetUsd) {
300
+ insertEnvelope.run(key, budgetUsd);
301
+ return toEnvelopeRow(selectEnvelope.get(key));
302
+ },
303
+ accrue(key, usd) {
304
+ const result = accrueEnvelope.run(usd, usd, key);
305
+ if (result.changes === 0) {
306
+ throw new Error(`accrue on unopened envelope "${key}" — call openEnvelope first`);
307
+ }
308
+ return toEnvelopeRow(selectEnvelope.get(key));
309
+ },
310
+ readInferencesAfter(afterRowid, limit) {
311
+ const rows = db
312
+ .prepare("SELECT rowid AS __rowid, * FROM inferences WHERE rowid > ? ORDER BY rowid LIMIT ?")
313
+ .all(afterRowid, limit);
314
+ return rows.map((r) => ({ rowid: r.__rowid, row: toTelemetryRow(r) }));
315
+ },
316
+ listEnvelopes() {
317
+ const rows = db.prepare("SELECT * FROM envelopes ORDER BY envelope_key").all();
318
+ return rows.map(toEnvelopeRow);
319
+ },
320
+ getExportWatermark(sourceId) {
321
+ const row = db.prepare("SELECT last_rowid FROM export_state WHERE source_id = ?").get(sourceId);
322
+ return row?.last_rowid ?? 0;
323
+ },
324
+ setExportWatermark(sourceId, lastRowid) {
325
+ db.prepare(`INSERT INTO export_state (source_id, last_rowid) VALUES (?, ?)
326
+ ON CONFLICT (source_id) DO UPDATE SET last_rowid = excluded.last_rowid`).run(sourceId, lastRowid);
327
+ },
328
+ close() {
329
+ db.close();
330
+ },
331
+ };
332
+ }
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Async batch exporter: SQLite telemetry → ModelRig Supabase project.
3
+ * Spec §5 (cloud sink) + §6 criteria 8–9. Fire-and-forget from the run loop:
4
+ * the inference path NEVER blocks on, or fails because of, this sink.
5
+ * At-least-once delivery with idempotent ids; unexported rows queue locally in
6
+ * SQLite (the inferences table itself, behind a rowid watermark) and flush on
7
+ * recovery; watermark bookkeeping per source instance.
8
+ *
9
+ * Transport is raw PostgREST over fetch (dependency-light, service-role key).
10
+ */
11
+ import type { RigManifest } from "../routes/manifest";
12
+ import type { ServeabilityWarning } from "../routes/serveability";
13
+ import type { ExportableTelemetryStore } from "./store";
14
+ export interface ExportedRouteMirror {
15
+ readonly route: string;
16
+ readonly version: number;
17
+ readonly candidates: ReadonlyArray<{
18
+ readonly provider: string;
19
+ readonly model: string;
20
+ }>;
21
+ /** Capture opt-in FLAG (Phase 3 WS4) — status only; capture content never
22
+ * has a cloud surface. */
23
+ readonly capture?: boolean;
24
+ }
25
+ export interface ExporterConfig {
26
+ readonly supabaseUrl: string;
27
+ /** Service-role key — server-side only; never reaches the console. */
28
+ readonly serviceRoleKey: string;
29
+ readonly sourceId: string;
30
+ /**
31
+ * Tenant for every row this exporter writes (Phase 6 WS1). Defaults to the
32
+ * house org, which is the right answer for a self-hosted control plane —
33
+ * there is exactly one org there and everything belongs to it.
34
+ *
35
+ * This is DIRECT mode only. In api mode the server resolves org and project
36
+ * from the `rig_sk_` key and stamps them itself: a client that could choose
37
+ * its own tenant would not be a tenant boundary (T3).
38
+ */
39
+ readonly orgId?: string;
40
+ /** Finer scope inside the org. Null is legitimate — not every rig has one. */
41
+ readonly projectId?: string | null;
42
+ readonly batchSize?: number;
43
+ readonly intervalMs?: number;
44
+ /** Loaded route bundles, mirrored to the `routes` table for the console. */
45
+ readonly routes?: readonly ExportedRouteMirror[];
46
+ /** Rig manifest, mirrored to the `rig_manifests` table for the console
47
+ * (dimension NAMES only — tag values never leave the telemetry rows). */
48
+ readonly manifest?: RigManifest | null;
49
+ /** Inert-candidate warnings for THIS deployment (F1) — mirrored onto the
50
+ * manifest row so console /setup can say "2 candidates inert here, set
51
+ * XAI_API_KEY" instead of the fact living only in a boot log. Names only:
52
+ * route, provider, model, env var. */
53
+ readonly serveability?: readonly ServeabilityWarning[];
54
+ /** Injectable for tests; defaults to global fetch. */
55
+ readonly fetchFn?: typeof fetch;
56
+ }
57
+ export interface Exporter {
58
+ start(): void;
59
+ /** Flush pending rows now (used by tests and process-exit hooks). */
60
+ flush(): Promise<void>;
61
+ stop(): Promise<void>;
62
+ }
63
+ export declare function createSupabaseExporter(store: ExportableTelemetryStore, config: ExporterConfig): Exporter;
64
+ //# sourceMappingURL=supabase-exporter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"supabase-exporter.d.ts","sourceRoot":"","sources":["../../src/telemetry/supabase-exporter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAIlE,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,SAAS,CAAC;AAExD,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,aAAa,CAAC;QAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC1F;8BAC0B;IAC1B,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,sEAAsE;IACtE,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B;;;;;;;;OAQG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,8EAA8E;IAC9E,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,4EAA4E;IAC5E,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,mBAAmB,EAAE,CAAC;IACjD;6EACyE;IACzE,QAAQ,CAAC,QAAQ,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IACvC;;;0CAGsC;IACtC,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,mBAAmB,EAAE,CAAC;IACvD,sDAAsD;IACtD,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;CACjC;AAED,MAAM,WAAW,QAAQ;IACvB,KAAK,IAAI,IAAI,CAAC;IACd,qEAAqE;IACrE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAiFD,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,wBAAwB,EAC/B,MAAM,EAAE,cAAc,GACrB,QAAQ,CA4KV"}
@@ -0,0 +1,211 @@
1
+ "use strict";
2
+ /**
3
+ * Async batch exporter: SQLite telemetry → ModelRig Supabase project.
4
+ * Spec §5 (cloud sink) + §6 criteria 8–9. Fire-and-forget from the run loop:
5
+ * the inference path NEVER blocks on, or fails because of, this sink.
6
+ * At-least-once delivery with idempotent ids; unexported rows queue locally in
7
+ * SQLite (the inferences table itself, behind a rowid watermark) and flush on
8
+ * recovery; watermark bookkeeping per source instance.
9
+ *
10
+ * Transport is raw PostgREST over fetch (dependency-light, service-role key).
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.createSupabaseExporter = createSupabaseExporter;
14
+ const constants_1 = require("../tenancy/constants");
15
+ const DEFAULT_BATCH_SIZE = 200;
16
+ const DEFAULT_INTERVAL_MS = 10000;
17
+ /** After a sink failure, skip this many intervals, doubling per consecutive
18
+ * failure (simple exponential backoff, capped). */
19
+ const BACKOFF_MAX_SKIPS = 32;
20
+ function toRemote(row, tenancy) {
21
+ return {
22
+ ...tenancy,
23
+ id: row.id,
24
+ ts: row.ts,
25
+ route: row.route,
26
+ route_version: row.routeVersion,
27
+ provider: row.provider,
28
+ model: row.model,
29
+ requested_tier: row.requestedTier,
30
+ served_tier: row.servedTier,
31
+ tokens_in: row.tokensIn,
32
+ tokens_out: row.tokensOut,
33
+ tokens_cached: row.tokensCached,
34
+ cost_estimate_usd: row.costEstimateUsd,
35
+ latency_ms: row.latencyMs,
36
+ ttfb_ms: row.ttfbMs,
37
+ failure_class: row.failureClass,
38
+ attempts: JSON.parse(row.attemptsJson),
39
+ tags: JSON.parse(row.tagsJson),
40
+ fingerprint_cell: row.fingerprintCell,
41
+ served_variant: row.servedVariant,
42
+ repaired_by: row.repairedBy,
43
+ pricing_missing: row.pricingMissing,
44
+ };
45
+ }
46
+ function feedbackToRemote(row, tenancy) {
47
+ return {
48
+ org_id: tenancy.org_id,
49
+ project_id: tenancy.project_id,
50
+ id: row.id,
51
+ ts: row.ts,
52
+ inference_id: row.inferenceId,
53
+ episode_key: row.episodeKey,
54
+ verdict: row.verdict,
55
+ score: row.score,
56
+ note: row.note,
57
+ source: row.source,
58
+ };
59
+ }
60
+ function createSupabaseExporter(store, config) {
61
+ const batchSize = config.batchSize ?? DEFAULT_BATCH_SIZE;
62
+ const intervalMs = config.intervalMs ?? DEFAULT_INTERVAL_MS;
63
+ const fetchFn = config.fetchFn ?? fetch;
64
+ const base = config.supabaseUrl.replace(/\/$/, "");
65
+ const tenancy = {
66
+ org_id: config.orgId ?? constants_1.HOUSE_ORG_ID,
67
+ project_id: config.projectId ?? null,
68
+ source_id: config.sourceId,
69
+ };
70
+ let timer = null;
71
+ let flushing = false;
72
+ let skipsRemaining = 0;
73
+ let consecutiveFailures = 0;
74
+ let routesMirrored = false;
75
+ let manifestMirrored = false;
76
+ async function post(table, body, upsert) {
77
+ const response = await fetchFn(`${base}/rest/v1/${table}`, {
78
+ method: "POST",
79
+ headers: {
80
+ "content-type": "application/json",
81
+ apikey: config.serviceRoleKey,
82
+ authorization: `Bearer ${config.serviceRoleKey}`,
83
+ prefer: upsert
84
+ ? "resolution=merge-duplicates,return=minimal"
85
+ : "resolution=ignore-duplicates,return=minimal",
86
+ },
87
+ body: JSON.stringify(body),
88
+ });
89
+ if (!response.ok) {
90
+ throw new Error(`sink POST ${table} → ${response.status} ${await response.text().catch(() => "")}`);
91
+ }
92
+ }
93
+ async function flushOnce() {
94
+ // Routes mirror first (idempotent upsert; retried until it succeeds once).
95
+ if (!routesMirrored && config.routes !== undefined && config.routes.length > 0) {
96
+ await post("routes", config.routes.map((r) => ({
97
+ org_id: tenancy.org_id,
98
+ route: r.route,
99
+ version: r.version,
100
+ candidates: r.candidates,
101
+ capture: r.capture ?? false,
102
+ updated_at: new Date().toISOString(),
103
+ })), true);
104
+ routesMirrored = true;
105
+ }
106
+ // Manifest mirror (idempotent upsert, once per process — like routes).
107
+ if (!manifestMirrored && config.manifest !== undefined && config.manifest !== null) {
108
+ await post("rig_manifests", [
109
+ {
110
+ org_id: tenancy.org_id,
111
+ source_id: config.sourceId,
112
+ name: config.manifest.name,
113
+ dimensions: config.manifest.dimensions,
114
+ contribution: config.manifest.contribution,
115
+ // F1: names only — route, provider/model, and the env var to set.
116
+ serveability: (config.serveability ?? []).map((w) => ({
117
+ route: w.route,
118
+ inert: w.inert.map((c) => ({ provider: c.provider, model: c.model })),
119
+ serveable_count: w.serveableCount,
120
+ fix_hint: w.fixHint,
121
+ })),
122
+ updated_at: new Date().toISOString(),
123
+ },
124
+ ], true);
125
+ manifestMirrored = true;
126
+ }
127
+ // Inferences past the watermark, in batches. Watermark advances ONLY
128
+ // after a confirmed write (at-least-once; remote id conflicts ignored).
129
+ for (;;) {
130
+ const watermark = store.getExportWatermark(config.sourceId);
131
+ const pending = store.readInferencesAfter(watermark, batchSize);
132
+ if (pending.length === 0)
133
+ break;
134
+ await post("inferences", pending.map(({ row }) => toRemote(row, tenancy)), false);
135
+ store.setExportWatermark(config.sourceId, pending[pending.length - 1].rowid);
136
+ await post("export_watermarks", [{ org_id: tenancy.org_id, source_id: config.sourceId, last_exported_ts: new Date().toISOString() }], true);
137
+ if (pending.length < batchSize)
138
+ break;
139
+ }
140
+ // Feedback mirror (Phase 4 WS2): same at-least-once watermark dance as
141
+ // inferences, behind its own export_state key so the two streams never
142
+ // block each other.
143
+ const feedbackSource = `${config.sourceId}:feedback`;
144
+ for (;;) {
145
+ const watermark = store.getExportWatermark(feedbackSource);
146
+ const pending = store.readFeedbackAfter(watermark, batchSize);
147
+ if (pending.length === 0)
148
+ break;
149
+ await post("feedback", pending.map(({ row }) => feedbackToRemote(row, tenancy)), false);
150
+ store.setExportWatermark(feedbackSource, pending[pending.length - 1].rowid);
151
+ if (pending.length < batchSize)
152
+ break;
153
+ }
154
+ // Envelopes: small table, full upsert each flush.
155
+ const envelopes = store.listEnvelopes();
156
+ if (envelopes.length > 0) {
157
+ await post("envelopes", envelopes.map((e) => ({
158
+ org_id: tenancy.org_id,
159
+ envelope_key: e.envelopeKey,
160
+ budget_usd: e.budgetUsd,
161
+ spent_usd: e.spentUsd,
162
+ status: e.status,
163
+ exported_at: new Date().toISOString(),
164
+ })), true);
165
+ }
166
+ }
167
+ async function guardedFlush() {
168
+ if (flushing)
169
+ return;
170
+ flushing = true;
171
+ try {
172
+ await flushOnce();
173
+ consecutiveFailures = 0;
174
+ }
175
+ catch (err) {
176
+ // Swallow-and-log, ALWAYS — sink failures must never reach the run loop.
177
+ consecutiveFailures += 1;
178
+ skipsRemaining = Math.min(2 ** consecutiveFailures, BACKOFF_MAX_SKIPS);
179
+ console.warn(`[modelrig exporter] sink flush failed (attempt ${consecutiveFailures}, backing off ` +
180
+ `${skipsRemaining} intervals): ${err instanceof Error ? err.message : String(err)}`);
181
+ }
182
+ finally {
183
+ flushing = false;
184
+ }
185
+ }
186
+ return {
187
+ start() {
188
+ if (timer !== null)
189
+ return;
190
+ timer = setInterval(() => {
191
+ if (skipsRemaining > 0) {
192
+ skipsRemaining -= 1;
193
+ return;
194
+ }
195
+ void guardedFlush();
196
+ }, intervalMs);
197
+ // Never keep the process alive for telemetry export.
198
+ timer.unref?.();
199
+ },
200
+ async flush() {
201
+ await guardedFlush();
202
+ },
203
+ async stop() {
204
+ if (timer !== null) {
205
+ clearInterval(timer);
206
+ timer = null;
207
+ }
208
+ await guardedFlush();
209
+ },
210
+ };
211
+ }
@@ -0,0 +1,67 @@
1
+ /**
2
+ * The `rig_sk_` key FORMAT — Phase 6 WS2.
3
+ *
4
+ * Shared knowledge, deliberately: the SDK builds the header that presents a
5
+ * key, modelrig-server resolves one, and the oracle decides whether the value
6
+ * it was handed is a key at all. Three packages agreeing on a prefix by
7
+ * copying a string literal is how N1's "one name per concept" fails in
8
+ * practice, so the string lives here and they all import it.
9
+ *
10
+ * What is NOT here is MINTING. Generating a secret and writing its hash is the
11
+ * control plane's job; an SDK that could do it would imply a client could
12
+ * issue itself a credential, which is the shape of the thing this scheme
13
+ * exists to prevent. `mintApiKey` lives in modelrig-server.
14
+ */
15
+ /**
16
+ * The one place this prefix is spelled. Grep-visible on purpose: the
17
+ * guardrails C2 rule keys off it, and so does the exporter's sink selection.
18
+ */
19
+ export declare const API_KEY_PREFIX = "rig_sk_";
20
+ /** 32 bytes = 256 bits, as base64url. The minting side uses this to generate;
21
+ * this side uses its ENCODED length to recognise. See 00020's header for why
22
+ * that number carries the argument that a plain sha-256 at rest is sound. */
23
+ export declare const API_KEY_SECRET_BYTES = 32;
24
+ /** base64url length of API_KEY_SECRET_BYTES — the minimum a real key's body has. */
25
+ export declare const API_KEY_BODY_MIN_LENGTH = 43;
26
+ /**
27
+ * The granted authorities. Mirrors migration 00020's CHECK constraint — a
28
+ * test pins that the two agree, because a scope that exists here and not
29
+ * there is a key that cannot be written, and one that exists there and not
30
+ * here is an authority nothing enforces.
31
+ */
32
+ export declare const API_KEY_SCOPES: readonly ["ingest", "query", "feedback", "decision", "oracle-act"];
33
+ export type ApiKeyScope = (typeof API_KEY_SCOPES)[number];
34
+ /** sha256 of the WHOLE presented string, prefix included — so a caller cannot
35
+ * strip the prefix and land on a different row. */
36
+ export declare function hashApiKeySecret(secret: string): string;
37
+ /**
38
+ * Cheap shape check, used to decide which credential branch a bearer token
39
+ * takes before any I/O happens.
40
+ *
41
+ * The minimum body length matters: without it the bare string `rig_sk_` would
42
+ * take the api-key branch and cost a database round trip per garbage request,
43
+ * which is a free amplification primitive on an unauthenticated surface.
44
+ */
45
+ export declare function looksLikeApiKey(presented: string): boolean;
46
+ /**
47
+ * What a key may be called in a log line, an error, or a boot banner.
48
+ *
49
+ * Returns the placeholder form for a non-key so that a caller who reaches for
50
+ * this with the wrong variable prints nothing useful rather than printing the
51
+ * whole thing. C2's leak-gate is the belt; this is the braces.
52
+ */
53
+ export declare function redactApiKey(presented: string): string;
54
+ /**
55
+ * Narrow an untrusted scope list to the known set (T7: a jsonb/array column
56
+ * is not proof of shape — narrow at the boundary).
57
+ *
58
+ * Unknown entries are DROPPED, not rejected: a row written by a future
59
+ * migration with a scope this build predates should still authorize the
60
+ * scopes this build understands, rather than failing closed on everything.
61
+ * Returns declaration order so two equal grant sets compare equal.
62
+ */
63
+ export declare function parseScopes(raw: unknown): ApiKeyScope[];
64
+ /** An empty grant set authorizes nothing — stated as code so it cannot be
65
+ * accidentally re-derived as "no scopes means unrestricted". */
66
+ export declare function hasScope(granted: readonly ApiKeyScope[], required: ApiKeyScope): boolean;
67
+ //# sourceMappingURL=api-key-format.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-key-format.d.ts","sourceRoot":"","sources":["../../src/tenancy/api-key-format.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH;;;GAGG;AACH,eAAO,MAAM,cAAc,YAAY,CAAC;AAExC;;6EAE6E;AAC7E,eAAO,MAAM,oBAAoB,KAAK,CAAC;AAEvC,oFAAoF;AACpF,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAE1C;;;;;GAKG;AACH,eAAO,MAAM,cAAc,oEAAqE,CAAC;AAEjG,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AAE1D;mDACmD;AACnD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAI1D;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAGtD;AAED;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,OAAO,GAAG,WAAW,EAAE,CAIvD;AAED;gEACgE;AAChE,wBAAgB,QAAQ,CAAC,OAAO,EAAE,SAAS,WAAW,EAAE,EAAE,QAAQ,EAAE,WAAW,GAAG,OAAO,CAExF"}