offrouter-core 0.2.0 → 0.2.2

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 (139) hide show
  1. package/dist/audit.d.ts +137 -0
  2. package/dist/audit.d.ts.map +1 -0
  3. package/dist/audit.js +320 -0
  4. package/dist/audit.js.map +1 -0
  5. package/dist/auth/credential-chain.d.ts +84 -0
  6. package/dist/auth/credential-chain.d.ts.map +1 -0
  7. package/dist/auth/credential-chain.js +150 -0
  8. package/dist/auth/credential-chain.js.map +1 -0
  9. package/dist/auth/index.d.ts +10 -0
  10. package/dist/auth/index.d.ts.map +1 -0
  11. package/dist/auth/index.js +7 -0
  12. package/dist/auth/index.js.map +1 -0
  13. package/dist/auth/keychain.d.ts +60 -0
  14. package/dist/auth/keychain.d.ts.map +1 -0
  15. package/dist/auth/keychain.js +104 -0
  16. package/dist/auth/keychain.js.map +1 -0
  17. package/dist/auth/oauth-pkce.d.ts +138 -0
  18. package/dist/auth/oauth-pkce.d.ts.map +1 -0
  19. package/dist/auth/oauth-pkce.js +375 -0
  20. package/dist/auth/oauth-pkce.js.map +1 -0
  21. package/dist/auth/oauth-server.d.ts +36 -0
  22. package/dist/auth/oauth-server.d.ts.map +1 -0
  23. package/dist/auth/oauth-server.js +210 -0
  24. package/dist/auth/oauth-server.js.map +1 -0
  25. package/dist/config.d.ts +76 -0
  26. package/dist/config.d.ts.map +1 -0
  27. package/dist/config.js +355 -0
  28. package/dist/config.js.map +1 -0
  29. package/dist/delegation.d.ts +123 -0
  30. package/dist/delegation.d.ts.map +1 -0
  31. package/dist/delegation.js +455 -0
  32. package/dist/delegation.js.map +1 -0
  33. package/dist/index.d.ts +41 -0
  34. package/dist/index.d.ts.map +1 -0
  35. package/dist/index.js +26 -0
  36. package/dist/index.js.map +1 -0
  37. package/dist/policy.d.ts +45 -0
  38. package/dist/policy.d.ts.map +1 -0
  39. package/dist/policy.js +318 -0
  40. package/dist/policy.js.map +1 -0
  41. package/{src/protocol.ts → dist/protocol.d.ts} +2 -2
  42. package/dist/protocol.d.ts.map +1 -0
  43. package/dist/protocol.js +6 -0
  44. package/dist/protocol.js.map +1 -0
  45. package/dist/providers/adapter.d.ts +137 -0
  46. package/dist/providers/adapter.d.ts.map +1 -0
  47. package/dist/providers/adapter.js +163 -0
  48. package/dist/providers/adapter.js.map +1 -0
  49. package/dist/providers/catalog-data.d.ts +128 -0
  50. package/dist/providers/catalog-data.d.ts.map +1 -0
  51. package/dist/providers/catalog-data.js +397 -0
  52. package/dist/providers/catalog-data.js.map +1 -0
  53. package/dist/providers/catalog.d.ts +63 -0
  54. package/dist/providers/catalog.d.ts.map +1 -0
  55. package/dist/providers/catalog.js +394 -0
  56. package/dist/providers/catalog.js.map +1 -0
  57. package/dist/providers/fake.d.ts +46 -0
  58. package/dist/providers/fake.d.ts.map +1 -0
  59. package/dist/providers/fake.js +234 -0
  60. package/dist/providers/fake.js.map +1 -0
  61. package/dist/providers/openai-compatible.d.ts +65 -0
  62. package/dist/providers/openai-compatible.d.ts.map +1 -0
  63. package/dist/providers/openai-compatible.js +434 -0
  64. package/dist/providers/openai-compatible.js.map +1 -0
  65. package/dist/proxy/responses-mapper.d.ts +366 -0
  66. package/dist/proxy/responses-mapper.d.ts.map +1 -0
  67. package/dist/proxy/responses-mapper.js +517 -0
  68. package/dist/proxy/responses-mapper.js.map +1 -0
  69. package/dist/proxy/responses-server.d.ts +29 -0
  70. package/dist/proxy/responses-server.d.ts.map +1 -0
  71. package/dist/proxy/responses-server.js +360 -0
  72. package/dist/proxy/responses-server.js.map +1 -0
  73. package/dist/router.d.ts +18 -0
  74. package/dist/router.d.ts.map +1 -0
  75. package/dist/router.js +296 -0
  76. package/dist/router.js.map +1 -0
  77. package/dist/schemas.d.ts +560 -0
  78. package/dist/schemas.d.ts.map +1 -0
  79. package/{src/schemas.ts → dist/schemas.js} +83 -103
  80. package/dist/schemas.js.map +1 -0
  81. package/dist/secrets.d.ts +112 -0
  82. package/dist/secrets.d.ts.map +1 -0
  83. package/dist/secrets.js +326 -0
  84. package/dist/secrets.js.map +1 -0
  85. package/dist/types.d.ts +117 -0
  86. package/dist/types.d.ts.map +1 -0
  87. package/dist/types.js +6 -0
  88. package/dist/types.js.map +1 -0
  89. package/dist/usage-file.d.ts +59 -0
  90. package/dist/usage-file.d.ts.map +1 -0
  91. package/dist/usage-file.js +316 -0
  92. package/dist/usage-file.js.map +1 -0
  93. package/dist/usage.d.ts +235 -0
  94. package/dist/usage.d.ts.map +1 -0
  95. package/dist/usage.js +517 -0
  96. package/dist/usage.js.map +1 -0
  97. package/package.json +9 -4
  98. package/src/audit.test.ts +0 -302
  99. package/src/audit.ts +0 -553
  100. package/src/auth/credential-chain.test.ts +0 -326
  101. package/src/auth/credential-chain.ts +0 -235
  102. package/src/auth/index.ts +0 -45
  103. package/src/auth/keychain.test.ts +0 -265
  104. package/src/auth/keychain.ts +0 -168
  105. package/src/auth/oauth-pkce.test.ts +0 -329
  106. package/src/auth/oauth-pkce.ts +0 -571
  107. package/src/auth/oauth-server.test.ts +0 -83
  108. package/src/auth/oauth-server.ts +0 -296
  109. package/src/config.test.ts +0 -479
  110. package/src/config.ts +0 -505
  111. package/src/delegation.test.ts +0 -368
  112. package/src/delegation.ts +0 -605
  113. package/src/index.ts +0 -280
  114. package/src/policy.test.ts +0 -634
  115. package/src/policy.ts +0 -420
  116. package/src/providers/adapter.test.ts +0 -293
  117. package/src/providers/adapter.ts +0 -328
  118. package/src/providers/catalog-data.test.ts +0 -258
  119. package/src/providers/catalog-data.ts +0 -498
  120. package/src/providers/catalog.test.ts +0 -84
  121. package/src/providers/catalog.ts +0 -483
  122. package/src/providers/fake.ts +0 -312
  123. package/src/providers/openai-compatible.test.ts +0 -366
  124. package/src/providers/openai-compatible.ts +0 -554
  125. package/src/proxy/responses-mapper.test.ts +0 -290
  126. package/src/proxy/responses-mapper.ts +0 -736
  127. package/src/proxy/responses-server.test.ts +0 -322
  128. package/src/proxy/responses-server.ts +0 -469
  129. package/src/router.test.ts +0 -699
  130. package/src/router.ts +0 -352
  131. package/src/schemas.test.ts +0 -291
  132. package/src/secrets.test.ts +0 -271
  133. package/src/secrets.ts +0 -461
  134. package/src/types.ts +0 -173
  135. package/src/usage-file.test.ts +0 -243
  136. package/src/usage-file.ts +0 -435
  137. package/src/usage.test.ts +0 -335
  138. package/src/usage.ts +0 -859
  139. package/tsconfig.json +0 -9
@@ -0,0 +1,326 @@
1
+ /**
2
+ * Secret store boundary for OffRouter V1.
3
+ *
4
+ * Secrets are stored only by OffRouter under user control:
5
+ * - FileSecretStore: `secrets.json` fallback (chmod 0600 on POSIX)
6
+ * - KeychainSecretStore: OS keychain boundary/stub for real runs
7
+ *
8
+ * Never scrapes, copies, or reverse-engineers credentials from other CLIs.
9
+ * No network access. No live credential discovery.
10
+ */
11
+ import { readFileSync } from "node:fs";
12
+ import { mkdir, readFile, rename, unlink, writeFile, chmod, } from "node:fs/promises";
13
+ import { dirname } from "node:path";
14
+ import { randomBytes } from "node:crypto";
15
+ /** Placeholder substituted for secret material in log-safe strings. */
16
+ /** Placeholder substituted for secret material in log-safe strings. */
17
+ export const REDACTED = "[REDACTED]";
18
+ const DEFAULT_KEYCHAIN_SERVICE = "offrouter";
19
+ const DEFAULT_ACCOUNT_PREFIX = "provider:";
20
+ /**
21
+ * Common API-key / token shapes seen in provider error messages.
22
+ * Patterns are intentionally conservative (long alphabetic payload)
23
+ * to avoid eating short unrelated tokens while still catching leaks.
24
+ */
25
+ const API_KEY_PATTERNS = [
26
+ // Anthropic: sk-ant-...
27
+ /\bsk-ant-[a-zA-Z0-9_-]{16,}\b/g,
28
+ // OpenAI / OpenRouter-style: sk-... (including sk-live-, sk-proj-, etc.)
29
+ /\bsk-[a-zA-Z0-9_-]{16,}\b/g,
30
+ // Google API keys
31
+ /\bAIza[0-9A-Za-z_-]{20,}\b/g,
32
+ // Bearer tokens in Authorization headers / log lines
33
+ /\bBearer\s+[A-Za-z0-9._\-+=/]{16,}\b/gi,
34
+ // OpenAI project / service-account style
35
+ /\bsk-proj-[a-zA-Z0-9_-]{16,}\b/g,
36
+ ];
37
+ function assertProviderId(providerId) {
38
+ if (typeof providerId !== "string" || providerId.trim() === "") {
39
+ throw new TypeError("providerId must be a non-empty string");
40
+ }
41
+ if (providerId !== providerId.trim()) {
42
+ throw new TypeError("providerId must not have leading/trailing whitespace");
43
+ }
44
+ }
45
+ function assertSecret(secret) {
46
+ if (typeof secret !== "string" || secret.length === 0) {
47
+ throw new TypeError("secret must be a non-empty string");
48
+ }
49
+ }
50
+ /**
51
+ * Standalone redaction for log/audit paths that do not hold a store.
52
+ * Optionally rejects known secret values (longest-first to avoid partials).
53
+ */
54
+ export function redact(value, knownSecrets = []) {
55
+ if (typeof value !== "string" || value.length === 0) {
56
+ return value == null ? "" : String(value ?? "");
57
+ }
58
+ let out = value;
59
+ const known = [...knownSecrets]
60
+ .filter((s) => typeof s === "string" && s.length > 0)
61
+ .sort((a, b) => b.length - a.length);
62
+ for (const secret of known) {
63
+ if (!out.includes(secret))
64
+ continue;
65
+ // Split/join avoids RegExp special-character issues in secret material.
66
+ out = out.split(secret).join(REDACTED);
67
+ }
68
+ for (const pattern of API_KEY_PATTERNS) {
69
+ // Reset lastIndex for global regexes reused across calls.
70
+ pattern.lastIndex = 0;
71
+ out = out.replace(pattern, REDACTED);
72
+ }
73
+ return out;
74
+ }
75
+ /**
76
+ * File-backed secret store for tests and user-approved fallback.
77
+ * Writes `$filePath` with mode `0o600` on POSIX after every mutation.
78
+ */
79
+ export class FileSecretStore {
80
+ filePath;
81
+ #fs;
82
+ #cache = null;
83
+ /** Provider ids currently known (for toJSON without leaking values). */
84
+ #providerIds = [];
85
+ constructor(options) {
86
+ if (!options?.filePath || typeof options.filePath !== "string") {
87
+ throw new TypeError("FileSecretStore requires options.filePath");
88
+ }
89
+ this.filePath = options.filePath;
90
+ this.#fs = {
91
+ mkdir,
92
+ readFile,
93
+ rename,
94
+ unlink,
95
+ writeFile,
96
+ chmod,
97
+ ...options.fileSystem,
98
+ };
99
+ }
100
+ async get(providerId) {
101
+ assertProviderId(providerId);
102
+ const doc = await this.#load();
103
+ const value = doc.secrets[providerId];
104
+ return value === undefined ? undefined : value;
105
+ }
106
+ async set(providerId, secret) {
107
+ assertProviderId(providerId);
108
+ assertSecret(secret);
109
+ const doc = await this.#load();
110
+ doc.secrets[providerId] = secret;
111
+ await this.#persist(doc);
112
+ }
113
+ async delete(providerId) {
114
+ assertProviderId(providerId);
115
+ const doc = await this.#load();
116
+ if (!(providerId in doc.secrets)) {
117
+ return;
118
+ }
119
+ delete doc.secrets[providerId];
120
+ await this.#persist(doc);
121
+ }
122
+ async listProviders() {
123
+ const doc = await this.#load();
124
+ return Object.keys(doc.secrets).sort((a, b) => a.localeCompare(b));
125
+ }
126
+ redact(value) {
127
+ const known = this.#cache
128
+ ? Object.values(this.#cache.secrets)
129
+ : this.#loadKnownSecretsSync();
130
+ return redact(value, known);
131
+ }
132
+ toJSON() {
133
+ return {
134
+ kind: "file",
135
+ filePath: this.filePath,
136
+ providers: [...this.#providerIds],
137
+ // loaded reflects whether secrets were read into memory; never the values.
138
+ loaded: this.#cache !== null,
139
+ };
140
+ }
141
+ async #load() {
142
+ if (this.#cache) {
143
+ return this.#cache;
144
+ }
145
+ try {
146
+ const raw = await this.#fs.readFile(this.filePath, "utf8");
147
+ const parsed = parseSecretDocument(raw, this.filePath);
148
+ this.#remember(parsed);
149
+ return parsed;
150
+ }
151
+ catch (err) {
152
+ if (err &&
153
+ typeof err === "object" &&
154
+ "code" in err &&
155
+ err.code === "ENOENT") {
156
+ const empty = { version: 1, secrets: {} };
157
+ this.#remember(empty);
158
+ return empty;
159
+ }
160
+ throw err;
161
+ }
162
+ }
163
+ async #persist(doc) {
164
+ await this.#fs.mkdir(dirname(this.filePath), { recursive: true });
165
+ const payload = `${JSON.stringify({ version: 1, secrets: doc.secrets }, null, 2)}\n`;
166
+ // Write via temp + rename so a crash mid-write cannot corrupt the store.
167
+ const tmp = `${this.filePath}.${randomBytes(8).toString("hex")}.tmp`;
168
+ try {
169
+ await this.#fs.writeFile(tmp, payload, { encoding: "utf8", mode: 0o600 });
170
+ // Re-assert 0600 on platforms that honor mode bits (no-op semantics on win).
171
+ await this.#fs.chmod(tmp, 0o600);
172
+ await this.#fs.rename(tmp, this.filePath);
173
+ // rename may preserve source perms, but assert again on final path for
174
+ // files that previously existed with looser permissions.
175
+ await this.#fs.chmod(this.filePath, 0o600);
176
+ this.#remember(doc);
177
+ }
178
+ catch (err) {
179
+ await this.#fs.unlink(tmp).catch((unlinkErr) => {
180
+ if (unlinkErr &&
181
+ typeof unlinkErr === "object" &&
182
+ "code" in unlinkErr &&
183
+ unlinkErr.code === "ENOENT") {
184
+ return;
185
+ }
186
+ throw unlinkErr;
187
+ });
188
+ throw err;
189
+ }
190
+ }
191
+ #remember(doc) {
192
+ this.#cache = doc;
193
+ this.#providerIds = Object.keys(doc.secrets).sort((a, b) => a.localeCompare(b));
194
+ }
195
+ #loadKnownSecretsSync() {
196
+ try {
197
+ const raw = readFileSync(this.filePath, "utf8");
198
+ const parsed = parseSecretDocument(raw, this.filePath);
199
+ this.#remember(parsed);
200
+ return Object.values(parsed.secrets);
201
+ }
202
+ catch {
203
+ // redact() must be non-throwing. Pattern-based redaction still runs.
204
+ return [];
205
+ }
206
+ }
207
+ }
208
+ /**
209
+ * Raised when the OS keychain/keyring backend is not available or not wired.
210
+ * Callers should fall back to FileSecretStore only with explicit user approval.
211
+ */
212
+ export class KeychainUnavailableError extends Error {
213
+ code = "keychain_unavailable";
214
+ constructor(message, options) {
215
+ super(message, options);
216
+ this.name = "KeychainUnavailableError";
217
+ }
218
+ }
219
+ /**
220
+ * OS keychain boundary for real runs.
221
+ *
222
+ * V1 ships this as a typed stub: it never scrapes other CLIs and does not
223
+ * perform network I/O. Get/set/delete/list reject with KeychainUnavailableError
224
+ * until a platform backend is wired. Prefer this store at the seams of real
225
+ * runs once keychain support lands; tests and dry runs use FileSecretStore.
226
+ */
227
+ export class KeychainSecretStore {
228
+ service;
229
+ accountPrefix;
230
+ constructor(options = {}) {
231
+ this.service = options.service?.trim() || DEFAULT_KEYCHAIN_SERVICE;
232
+ this.accountPrefix =
233
+ options.accountPrefix === undefined
234
+ ? DEFAULT_ACCOUNT_PREFIX
235
+ : options.accountPrefix;
236
+ }
237
+ async get(providerId) {
238
+ assertProviderId(providerId);
239
+ this.#reject("get");
240
+ }
241
+ async set(providerId, secret) {
242
+ assertProviderId(providerId);
243
+ assertSecret(secret);
244
+ this.#reject("set");
245
+ }
246
+ async delete(providerId) {
247
+ assertProviderId(providerId);
248
+ this.#reject("delete");
249
+ }
250
+ async listProviders() {
251
+ this.#reject("listProviders");
252
+ }
253
+ redact(value) {
254
+ // Stub holds no secrets in-process; still scrub common API key shapes.
255
+ return redact(value);
256
+ }
257
+ toJSON() {
258
+ return {
259
+ kind: "keychain",
260
+ service: this.service,
261
+ accountPrefix: this.accountPrefix,
262
+ available: false,
263
+ };
264
+ }
265
+ #reject(operation) {
266
+ throw new KeychainUnavailableError(`KeychainSecretStore.${operation} is unavailable: OS keychain backend is not wired in this OffRouter build. ` +
267
+ `OffRouter will not scrape other CLI credential stores. Use FileSecretStore for tests, or approve an ` +
268
+ `explicit file fallback for real runs when keychain is unavailable.`);
269
+ }
270
+ }
271
+ /**
272
+ * Retrieve a provider token from the secret store.
273
+ * Returns undefined if no token is stored or the store is unavailable.
274
+ */
275
+ export async function getProviderToken(providerId, store) {
276
+ try {
277
+ const value = await store.get(providerId);
278
+ if (typeof value === "string" && value.length > 0) {
279
+ return value;
280
+ }
281
+ }
282
+ catch {
283
+ // KeychainUnavailableError or other errors: treat as missing.
284
+ }
285
+ return undefined;
286
+ }
287
+ /**
288
+ * Store a provider token in the secret store.
289
+ * Throws if the store is unavailable (e.g., KeychainUnavailableError).
290
+ */
291
+ export async function setProviderToken(providerId, token, store) {
292
+ await store.set(providerId, token);
293
+ }
294
+ function parseSecretDocument(raw, filePath) {
295
+ let parsed;
296
+ try {
297
+ parsed = JSON.parse(raw);
298
+ }
299
+ catch (err) {
300
+ const message = err instanceof Error ? err.message : String(err);
301
+ throw new Error(`Invalid secrets file ${filePath}: ${message}`, {
302
+ cause: err,
303
+ });
304
+ }
305
+ if (typeof parsed !== "object" ||
306
+ parsed === null ||
307
+ Array.isArray(parsed)) {
308
+ throw new Error(`Invalid secrets file ${filePath}: expected a JSON object`);
309
+ }
310
+ const record = parsed;
311
+ const secretsRaw = record.secrets;
312
+ if (typeof secretsRaw !== "object" ||
313
+ secretsRaw === null ||
314
+ Array.isArray(secretsRaw)) {
315
+ throw new Error(`Invalid secrets file ${filePath}: missing object field "secrets"`);
316
+ }
317
+ const secrets = {};
318
+ for (const [key, value] of Object.entries(secretsRaw)) {
319
+ if (typeof value !== "string") {
320
+ throw new Error(`Invalid secrets file ${filePath}: secret for "${key}" must be a string`);
321
+ }
322
+ secrets[key] = value;
323
+ }
324
+ return { version: 1, secrets };
325
+ }
326
+ //# sourceMappingURL=secrets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secrets.js","sourceRoot":"","sources":["../src/secrets.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,117 @@
1
+ /**
2
+ * OffRouter V1 routing types.
3
+ * Aligned with docs/contracts/v1-routing-contract.md
4
+ */
5
+ export type AuthTier = "subscription" | "local" | "api-key";
6
+ export type AuthScope = "first-party" | "third-party" | "unknown";
7
+ export type SubscriptionStatus = "active" | "near-limit" | "exhausted" | "rate-limited" | "expired" | "unconfigured" | "unknown";
8
+ export type ProviderHealth = "healthy" | "degraded" | "dead";
9
+ export type FallbackReason = "primary" | "subscription_exhausted" | "subscription_degraded" | "subscription_unconfigured" | "subscription_scope_denied" | "local_preferred" | "api_key_fallback" | "policy_denied" | "provider_error";
10
+ export type HarnessKind = "claude" | "codex" | "gemini" | "grok" | "cursor" | "pi" | "other";
11
+ export type TaskKind = "explain" | "code" | "review" | "plan" | "research" | "image" | "tools" | "other";
12
+ export type RiskLevel = "low" | "medium" | "high";
13
+ export type PolicyDenialCode = "profile_not_allowlisted" | "work_profile_denied" | "provider_denied" | "project_untrusted" | "privacy_label_denied" | "local_only_required" | "cost_cap_exceeded" | "api_key_blocked_by_subscription_first_policy" | "subscription_scope_denied" | "capability_missing";
14
+ export type PolicyDenialTarget = "profile" | "provider" | "model" | "workspace" | "candidate";
15
+ export interface PolicyDenial {
16
+ code: PolicyDenialCode;
17
+ target: PolicyDenialTarget;
18
+ id: string;
19
+ message: string;
20
+ }
21
+ export interface ProviderCandidate {
22
+ providerId: string;
23
+ modelId: string;
24
+ displayName?: string;
25
+ authTier: AuthTier;
26
+ authScope: AuthScope;
27
+ subscriptionStatus?: SubscriptionStatus;
28
+ health: ProviderHealth;
29
+ accountId?: string;
30
+ subscriptionQuotaPercentRemaining?: number;
31
+ supportsTools: boolean;
32
+ supportsStreaming: boolean;
33
+ supportsJson: boolean;
34
+ supportsImages: boolean;
35
+ contextWindowTokens?: number;
36
+ maxOutputTokens?: number;
37
+ estimatedCostUsd?: number;
38
+ }
39
+ export interface HarnessRef {
40
+ kind: HarnessKind;
41
+ profile: string;
42
+ }
43
+ /**
44
+ * Route request carries prompt preview + digest only.
45
+ * Full prompt text belongs in delegation requests after policy allows a provider.
46
+ */
47
+ export interface RouteTask {
48
+ promptPreview: string;
49
+ promptDigest: string;
50
+ kind: TaskKind;
51
+ risk: RiskLevel;
52
+ requiresTools?: boolean;
53
+ requiresImages?: boolean;
54
+ requiresStreaming?: boolean;
55
+ requiresJson?: boolean;
56
+ }
57
+ export interface WorkspaceRef {
58
+ cwd: string;
59
+ trusted: boolean;
60
+ privacyLabel?: string;
61
+ }
62
+ export interface RouteConstraints {
63
+ maxCostUsd?: number;
64
+ localOnly?: boolean;
65
+ allowApiKeyFallback?: boolean;
66
+ subscriptionFirst?: boolean;
67
+ }
68
+ export interface RouteRequest {
69
+ protocolVersion: "offrouter.route.v1";
70
+ requestId: string;
71
+ harness: HarnessRef;
72
+ task: RouteTask;
73
+ workspace: WorkspaceRef;
74
+ constraints: RouteConstraints;
75
+ }
76
+ export interface SelectedRoute {
77
+ provider: string;
78
+ model: string;
79
+ authTier: AuthTier;
80
+ authScope?: AuthScope;
81
+ accountId?: string;
82
+ }
83
+ export interface FallbackChainEntry {
84
+ provider: string;
85
+ model: string;
86
+ authTier: AuthTier;
87
+ reason: FallbackReason;
88
+ }
89
+ export interface RouteDecision {
90
+ protocolVersion: "offrouter.route.v1";
91
+ decisionId: string;
92
+ requestId: string;
93
+ /** Selected route when routing succeeds; omitted/null when blocked. */
94
+ route?: SelectedRoute | null;
95
+ /** Short machine-oriented reason code for the decision. */
96
+ reason: string;
97
+ /** Human-readable explanation of why this route (or block) was chosen. */
98
+ explanation: string;
99
+ fallbackChain?: FallbackChainEntry[];
100
+ policyDenials?: PolicyDenial[];
101
+ confidence?: number;
102
+ /** Audit-safe summary; must not contain raw prompt text. */
103
+ auditSummary?: string;
104
+ blocked?: boolean;
105
+ needsConfiguration?: boolean;
106
+ /** Warning when the selected route is a near-limit subscription with no healthy alternative. */
107
+ nearLimitWarning?: string;
108
+ }
109
+ /**
110
+ * Outcome of evaluatePolicy: filtered candidates plus structured denials.
111
+ * Policy engine is implemented in a later task; this type is the contract edge.
112
+ */
113
+ export interface PolicyDecision {
114
+ allowed: ProviderCandidate[];
115
+ denials: PolicyDenial[];
116
+ }
117
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,MAAM,QAAQ,GAAG,cAAc,GAAG,OAAO,GAAG,SAAS,CAAC;AAE5D,MAAM,MAAM,SAAS,GAAG,aAAa,GAAG,aAAa,GAAG,SAAS,CAAC;AAElE,MAAM,MAAM,kBAAkB,GAC1B,QAAQ,GACR,YAAY,GACZ,WAAW,GACX,cAAc,GACd,SAAS,GACT,cAAc,GACd,SAAS,CAAC;AAEd,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,UAAU,GAAG,MAAM,CAAC;AAE7D,MAAM,MAAM,cAAc,GACtB,SAAS,GACT,wBAAwB,GACxB,uBAAuB,GACvB,2BAA2B,GAC3B,2BAA2B,GAC3B,iBAAiB,GACjB,kBAAkB,GAClB,eAAe,GACf,gBAAgB,CAAC;AAErB,MAAM,MAAM,WAAW,GACrB,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,GAAG,IAAI,GAAG,OAAO,CAAC;AAErE,MAAM,MAAM,QAAQ,GAChB,SAAS,GACT,MAAM,GACN,QAAQ,GACR,MAAM,GACN,UAAU,GACV,OAAO,GACP,OAAO,GACP,OAAO,CAAC;AAEZ,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;AAElD,MAAM,MAAM,gBAAgB,GACxB,yBAAyB,GACzB,qBAAqB,GACrB,iBAAiB,GACjB,mBAAmB,GACnB,sBAAsB,GACtB,qBAAqB,GACrB,mBAAmB,GACnB,8CAA8C,GAC9C,2BAA2B,GAC3B,oBAAoB,CAAC;AAEzB,MAAM,MAAM,kBAAkB,GAC5B,SAAS,GAAG,UAAU,GAAG,OAAO,GAAG,WAAW,GAAG,WAAW,CAAC;AAE/D,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,gBAAgB,CAAC;IACvB,MAAM,EAAE,kBAAkB,CAAC;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,QAAQ,CAAC;IACnB,SAAS,EAAE,SAAS,CAAC;IACrB,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;IACxC,MAAM,EAAE,cAAc,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iCAAiC,CAAC,EAAE,MAAM,CAAC;IAC3C,aAAa,EAAE,OAAO,CAAC;IACvB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,YAAY,EAAE,OAAO,CAAC;IACtB,cAAc,EAAE,OAAO,CAAC;IACxB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,MAAM,WAAW,SAAS;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,QAAQ,CAAC;IACf,IAAI,EAAE,SAAS,CAAC;IAChB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,gBAAgB;IAC/B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,MAAM,WAAW,YAAY;IAC3B,eAAe,EAAE,oBAAoB,CAAC;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,UAAU,CAAC;IACpB,IAAI,EAAE,SAAS,CAAC;IAChB,SAAS,EAAE,YAAY,CAAC;IACxB,WAAW,EAAE,gBAAgB,CAAC;CAC/B;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,QAAQ,CAAC;IACnB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,QAAQ,CAAC;IACnB,MAAM,EAAE,cAAc,CAAC;CACxB;AAED,MAAM,WAAW,aAAa;IAC5B,eAAe,EAAE,oBAAoB,CAAC;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,uEAAuE;IACvE,KAAK,CAAC,EAAE,aAAa,GAAG,IAAI,CAAC;IAC7B,2DAA2D;IAC3D,MAAM,EAAE,MAAM,CAAC;IACf,0EAA0E;IAC1E,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,kBAAkB,EAAE,CAAC;IACrC,aAAa,CAAC,EAAE,YAAY,EAAE,CAAC;IAC/B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4DAA4D;IAC5D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,gGAAgG;IAChG,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,iBAAiB,EAAE,CAAC;IAC7B,OAAO,EAAE,YAAY,EAAE,CAAC;CACzB"}
package/dist/types.js ADDED
@@ -0,0 +1,6 @@
1
+ /**
2
+ * OffRouter V1 routing types.
3
+ * Aligned with docs/contracts/v1-routing-contract.md
4
+ */
5
+ export {};
6
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
@@ -0,0 +1,59 @@
1
+ import { chmod, mkdir, readFile, rename, unlink, writeFile } from "node:fs/promises";
2
+ import { type AccountUsageSnapshot, type PaidFallbackExplanation, type ProviderUsageSnapshot, type RecordUsageResult, type UsageEvent, type UsageStore } from "./usage.js";
3
+ export { DEFAULT_NEAR_LIMIT_THRESHOLD } from "./usage.js";
4
+ export { InMemoryUsageStore } from "./usage.js";
5
+ export type { UsageStore } from "./usage.js";
6
+ export interface FileUsageStoreFileSystem {
7
+ mkdir: typeof mkdir;
8
+ readFile: typeof readFile;
9
+ rename: typeof rename;
10
+ unlink: typeof unlink;
11
+ writeFile: typeof writeFile;
12
+ chmod: typeof chmod;
13
+ }
14
+ export interface FileUsageStoreOptions {
15
+ /** Absolute path to usage.json (typically `$OFFROUTER_HOME/state/usage.json`). */
16
+ filePath: string;
17
+ /** Injectable file operations for failure-path tests. Defaults to node fs. */
18
+ fileSystem?: Partial<FileUsageStoreFileSystem>;
19
+ /** Injectable clock for tests. */
20
+ now?: () => Date;
21
+ /**
22
+ * In-memory store that owns provider health/quota logic. Account usage is
23
+ * persisted separately and mirrored onto this delegate. Useful for tests.
24
+ */
25
+ delegate?: UsageStore;
26
+ }
27
+ /**
28
+ * File-backed UsageStore. Persists per-account quota usage atomically and
29
+ * delegates provider health/quota logic to an in-memory engine.
30
+ */
31
+ export declare class FileUsageStore implements UsageStore {
32
+ #private;
33
+ readonly filePath: string;
34
+ constructor(options: FileUsageStoreOptions);
35
+ record(event: UsageEvent): Promise<RecordUsageResult>;
36
+ getProvider(providerId: string): Promise<ProviderUsageSnapshot | undefined>;
37
+ listProviders(): Promise<ProviderUsageSnapshot[]>;
38
+ setSubscriptionQuota(providerId: string, input: {
39
+ limit?: number;
40
+ used?: number;
41
+ status?: import("./types.js").SubscriptionStatus;
42
+ modelId?: string;
43
+ at?: Date;
44
+ }): Promise<ProviderUsageSnapshot>;
45
+ explainPaidFallback(providerId: string, options?: {
46
+ hasApiKeyCandidate?: boolean;
47
+ }): Promise<PaidFallbackExplanation>;
48
+ recordUsage(providerId: string, accountId: string, input: {
49
+ used?: number;
50
+ limit?: number;
51
+ at?: Date;
52
+ }): Promise<AccountUsageSnapshot>;
53
+ isNearLimit(providerId: string, accountId: string, threshold?: number): Promise<boolean>;
54
+ getNearLimitAccounts(threshold?: number): Promise<AccountUsageSnapshot[]>;
55
+ getAccountUsage(providerId: string, accountId: string): Promise<AccountUsageSnapshot | undefined>;
56
+ listAccountUsage(): Promise<AccountUsageSnapshot[]>;
57
+ toJSON(): Record<string, unknown>;
58
+ }
59
+ //# sourceMappingURL=usage-file.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usage-file.d.ts","sourceRoot":"","sources":["../src/usage-file.ts"],"names":[],"mappings":"AAoBA,OAAO,EACL,KAAK,EACL,KAAK,EACL,QAAQ,EACR,MAAM,EACN,MAAM,EACN,SAAS,EACV,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EAIL,KAAK,oBAAoB,EACzB,KAAK,uBAAuB,EAC5B,KAAK,qBAAqB,EAC1B,KAAK,iBAAiB,EACtB,KAAK,UAAU,EACf,KAAK,UAAU,EAChB,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,4BAA4B,EAAE,MAAM,YAAY,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAChD,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,MAAM,WAAW,wBAAwB;IACvC,KAAK,EAAE,OAAO,KAAK,CAAC;IACpB,QAAQ,EAAE,OAAO,QAAQ,CAAC;IAC1B,MAAM,EAAE,OAAO,MAAM,CAAC;IACtB,MAAM,EAAE,OAAO,MAAM,CAAC;IACtB,SAAS,EAAE,OAAO,SAAS,CAAC;IAC5B,KAAK,EAAE,OAAO,KAAK,CAAC;CACrB;AAED,MAAM,WAAW,qBAAqB;IACpC,kFAAkF;IAClF,QAAQ,EAAE,MAAM,CAAC;IACjB,8EAA8E;IAC9E,UAAU,CAAC,EAAE,OAAO,CAAC,wBAAwB,CAAC,CAAC;IAC/C,kCAAkC;IAClC,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;IACjB;;;OAGG;IACH,QAAQ,CAAC,EAAE,UAAU,CAAC;CACvB;AAoFD;;;GAGG;AACH,qBAAa,cAAe,YAAW,UAAU;;IAC/C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;gBASd,OAAO,EAAE,qBAAqB;IA6I1C,MAAM,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAIrD,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,GAAG,SAAS,CAAC;IAI3E,aAAa,IAAI,OAAO,CAAC,qBAAqB,EAAE,CAAC;IAIjD,oBAAoB,CAClB,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE;QACL,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,OAAO,YAAY,EAAE,kBAAkB,CAAC;QACjD,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,EAAE,CAAC,EAAE,IAAI,CAAC;KACX,GACA,OAAO,CAAC,qBAAqB,CAAC;IAIjC,mBAAmB,CACjB,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE;QAAE,kBAAkB,CAAC,EAAE,OAAO,CAAA;KAAE,GACzC,OAAO,CAAC,uBAAuB,CAAC;IAM7B,WAAW,CACf,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,EAAE,CAAC,EAAE,IAAI,CAAA;KAAE,GAClD,OAAO,CAAC,oBAAoB,CAAC;IAuB1B,WAAW,CACf,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,OAAO,CAAC;IASb,oBAAoB,CACxB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,oBAAoB,EAAE,CAAC;IAgB5B,eAAe,CACnB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,oBAAoB,GAAG,SAAS,CAAC;IAStC,gBAAgB,IAAI,OAAO,CAAC,oBAAoB,EAAE,CAAC;IAezD,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAQlC"}