@volter/twin-openrouter 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 (56) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +131 -0
  3. package/client/openrouter-mirror.css +131 -0
  4. package/client/openrouter-mirror.tsx +221 -0
  5. package/dist/client/openrouter-mirror.bundle.js +235 -0
  6. package/dist/client/openrouter-mirror.css +131 -0
  7. package/dist/client/openrouter-mirror.d.ts +1 -0
  8. package/dist/client/openrouter-mirror.js +95 -0
  9. package/dist/client/openrouter-mirror.tsx +221 -0
  10. package/dist/src/cli.d.ts +2 -0
  11. package/dist/src/cli.js +31 -0
  12. package/dist/src/index.d.ts +15 -0
  13. package/dist/src/index.js +66 -0
  14. package/dist/src/openrouter-budget.d.ts +58 -0
  15. package/dist/src/openrouter-budget.js +133 -0
  16. package/dist/src/openrouter-capabilities.d.ts +3 -0
  17. package/dist/src/openrouter-capabilities.js +720 -0
  18. package/dist/src/openrouter-conformance.d.ts +11 -0
  19. package/dist/src/openrouter-conformance.js +53 -0
  20. package/dist/src/openrouter-connector.d.ts +90 -0
  21. package/dist/src/openrouter-connector.js +245 -0
  22. package/dist/src/openrouter-local-generation.d.ts +17 -0
  23. package/dist/src/openrouter-local-generation.js +271 -0
  24. package/dist/src/openrouter-mirror-ui.d.ts +12 -0
  25. package/dist/src/openrouter-mirror-ui.js +78 -0
  26. package/dist/src/openrouter-models.d.ts +39 -0
  27. package/dist/src/openrouter-models.js +78 -0
  28. package/dist/src/openrouter-scenario.d.ts +36 -0
  29. package/dist/src/openrouter-scenario.js +145 -0
  30. package/dist/src/openrouter-server.d.ts +20 -0
  31. package/dist/src/openrouter-server.js +144 -0
  32. package/dist/src/openrouter-stub.d.ts +8 -0
  33. package/dist/src/openrouter-stub.js +58 -0
  34. package/dist/src/openrouter-twin.d.ts +4 -0
  35. package/dist/src/openrouter-twin.js +1506 -0
  36. package/dist/src/openrouter-types.d.ts +50 -0
  37. package/dist/src/openrouter-types.js +1 -0
  38. package/dist/test-fixtures/openrouter-openapi-operations.SOURCE.md +16 -0
  39. package/dist/test-fixtures/openrouter-openapi-operations.json +1041 -0
  40. package/package.json +71 -0
  41. package/src/cli.ts +29 -0
  42. package/src/index.ts +108 -0
  43. package/src/openrouter-budget.ts +159 -0
  44. package/src/openrouter-capabilities.ts +861 -0
  45. package/src/openrouter-conformance.ts +60 -0
  46. package/src/openrouter-connector.ts +264 -0
  47. package/src/openrouter-local-generation.ts +207 -0
  48. package/src/openrouter-mirror-ui.ts +84 -0
  49. package/src/openrouter-models.ts +118 -0
  50. package/src/openrouter-scenario.ts +156 -0
  51. package/src/openrouter-server.ts +158 -0
  52. package/src/openrouter-stub.ts +60 -0
  53. package/src/openrouter-twin.ts +1441 -0
  54. package/src/openrouter-types.ts +49 -0
  55. package/test-fixtures/openrouter-openapi-operations.SOURCE.md +16 -0
  56. package/test-fixtures/openrouter-openapi-operations.json +1041 -0
@@ -0,0 +1,11 @@
1
+ export type OpenRouterConformanceReport = {
2
+ ok: boolean;
3
+ checksRun: number;
4
+ violations: Array<{
5
+ check: string;
6
+ detail: string;
7
+ }>;
8
+ };
9
+ export declare function checkOpenRouterConformance(opts?: {
10
+ root?: string;
11
+ }): Promise<OpenRouterConformanceReport>;
@@ -0,0 +1,53 @@
1
+ import { mkdtempSync, rmSync } from 'node:fs';
2
+ import { tmpdir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ import { handleOpenRouterTwinRequest } from "./openrouter-twin.js";
5
+ export async function checkOpenRouterConformance(opts = {}) {
6
+ const root = opts.root ?? mkdtempSync(join(tmpdir(), 'openrouter-conf-'));
7
+ const owned = opts.root === undefined;
8
+ const violations = [];
9
+ let checksRun = 0;
10
+ const fail = (check, detail) => violations.push({ check, detail });
11
+ const h = (method, path, body, sseSink) => handleOpenRouterTwinRequest({ method, path, body: body === undefined ? undefined : JSON.stringify(body), root, ...(sseSink ? { sseSink } : {}) });
12
+ try {
13
+ checksRun++;
14
+ const chat = await h('POST', '/api/v1/chat/completions', { model: 'openai/gpt-4o', messages: [{ role: 'user', content: 'hello' }] });
15
+ const cb = chat.body;
16
+ if (chat.status !== 200 || cb.object !== 'chat.completion' || cb.choices?.[0]?.message?.role !== 'assistant')
17
+ fail('chat.envelope', 'invalid chat completion envelope');
18
+ if (typeof cb.usage?.cost !== 'number')
19
+ fail('chat.usage', 'OpenRouter usage cost missing');
20
+ checksRun++;
21
+ const events = [];
22
+ await h('POST', '/api/v1/chat/completions', { model: 'openai/gpt-4o', stream: true, messages: [{ role: 'user', content: 'stream' }] }, (event) => events.push(event));
23
+ if (!events.some((event) => event.data?.object === 'chat.completion.chunk') || events.at(-1)?.done !== true)
24
+ fail('streaming', 'missing chunk or [DONE]');
25
+ checksRun++;
26
+ const models = await h('GET', '/api/v1/models');
27
+ if (!Array.isArray(models.body.data) || models.body.data.length === 0)
28
+ fail('models.list', 'missing models data');
29
+ const one = await h('GET', '/api/v1/models/openai/gpt-4o');
30
+ if (one.body.data?.id !== 'openai/gpt-4o')
31
+ fail('models.retrieve', 'missing model');
32
+ checksRun++;
33
+ const gen = await h('GET', `/api/v1/generation?id=${encodeURIComponent(cb.id)}`);
34
+ if (gen.body.data?.model !== 'openai/gpt-4o')
35
+ fail('generation.stats', 'generation lookup failed');
36
+ checksRun++;
37
+ const bad = await h('POST', '/api/v1/chat/completions', { model: 'openai/gpt-4o', messages: [] });
38
+ if (bad.status !== 400 || !bad.body.error)
39
+ fail('error.envelope', 'bad chat request did not return error envelope');
40
+ checksRun++;
41
+ const resp = await h('POST', '/api/v1/responses', { model: 'openai/gpt-4o', input: 'hi' });
42
+ if (resp.status !== 200 || resp.body.object !== 'response')
43
+ fail('responses.create', 'responses envelope invalid');
44
+ const embed = await h('POST', '/api/v1/embeddings', { model: 'm', input: 'hi' });
45
+ if (embed.status !== 200 || !Array.isArray(embed.body.data?.[0]?.embedding))
46
+ fail('embeddings.create', 'embeddings envelope invalid');
47
+ }
48
+ finally {
49
+ if (owned)
50
+ rmSync(root, { recursive: true, force: true });
51
+ }
52
+ return { ok: violations.length === 0, checksRun, violations };
53
+ }
@@ -0,0 +1,90 @@
1
+ import type { PerformContext, PushOutcome, RemoteExecute, TwinAction } from '@volter/world-core';
2
+ import { OpenRouterBudget, type OpenRouterBudgetOptions } from './openrouter-budget.js';
3
+ export type OpenRouterExecute = (req: {
4
+ method: string;
5
+ path: string;
6
+ body?: unknown;
7
+ }) => Promise<{
8
+ status: number;
9
+ data: unknown;
10
+ }>;
11
+ /** The real OpenRouter API host. The ONLY place this pack names it as a target. */
12
+ export declare const OPENROUTER_API_BASE = "https://openrouter.ai";
13
+ /**
14
+ * THE CHOKE POINT — the one place this pack builds an `OpenRouterExecute` that really reaches
15
+ * openrouter.ai, and therefore the one place the rate budget has to be enforced.
16
+ *
17
+ * Reads the API key from `OPENROUTER_API_KEY` (never a literal, never a credentials file) and sends it
18
+ * as `Authorization: Bearer …`, which is how OpenRouter authenticates its OpenAI-compatible surface.
19
+ *
20
+ * EVERY request is guarded: the budget is charged BEFORE it goes out (`checkBudget`, which THROWS
21
+ * instead of returning once the ceiling or a persisted cooldown says stop) and the response is fed
22
+ * back (`recordCall`) so a `Retry-After` / 429 / rate-limit-exhaustion signal becomes a persisted
23
+ * cooldown that makes every later call fail fast WITHOUT touching OpenRouter. There is deliberately
24
+ * NO option to disable the guard.
25
+ *
26
+ * No vendor SDK is imported: real-transport `fetch` keeps this pack SDK-free at runtime, which the
27
+ * architecture guardrail requires.
28
+ */
29
+ export declare function liveOpenRouterExecute(opts?: {
30
+ apiKey?: string;
31
+ baseUrl?: string;
32
+ fetchImpl?: typeof fetch;
33
+ /** An existing budget to share across executes. Omit and one is constructed. Cannot be null. */
34
+ budget?: OpenRouterBudget;
35
+ /** Construction options for the default budget (ledger path, clock). Cannot loosen it. */
36
+ budgetOptions?: OpenRouterBudgetOptions;
37
+ }): OpenRouterExecute;
38
+ export declare function pullOpenRouterState(execute: OpenRouterExecute, opts?: {
39
+ root?: string;
40
+ occurredAt?: string;
41
+ }): Promise<void>;
42
+ /**
43
+ * D7 consumer-facing pull entry point: gather all OpenRouter domains (the model catalog and the account's provisioning keys) and
44
+ * fold them into the twin as ONE observation, returning the standard result shape. Idempotent:
45
+ * a re-pull of identical state appends no new deltas.
46
+ */
47
+ export declare function syncOpenRouterFromReal(execute: OpenRouterExecute, opts?: {
48
+ root?: string;
49
+ occurredAt?: string;
50
+ }): Promise<{
51
+ observed: number;
52
+ deltasAppended: number;
53
+ }>;
54
+ export declare function openRouterRequestForAction(action: TwinAction): {
55
+ method: string;
56
+ path: string;
57
+ body?: unknown;
58
+ } | null;
59
+ export declare function pushOpenRouterAction(action: TwinAction, execute: OpenRouterExecute, opts?: {
60
+ root?: string;
61
+ occurredAt?: string;
62
+ }): Promise<boolean>;
63
+ export declare function pushPendingOpenRouterActions(execute: OpenRouterExecute, opts?: {
64
+ root?: string;
65
+ occurredAt?: string;
66
+ }): Promise<number>;
67
+ /**
68
+ * An `OpenRouterExecute` over the kernel's executor. At a REAL boundary the kernel sets the sealed
69
+ * credential over these headers (executor.ts); at the twin's own wire any credential is one.
70
+ *
71
+ * The paths carry OpenRouter's `/api` prefix, which `collectOpenRouterState` already uses and the
72
+ * twin's own router accepts alongside the bare `/v1` form.
73
+ */
74
+ export declare function openRouterExecuteOver(execute: RemoteExecute): OpenRouterExecute;
75
+ /** The refresh adapter: pull the account's state through the executor and fold it into the root. */
76
+ export declare function syncOpenRouterFromRemote(execute: RemoteExecute, opts?: {
77
+ root?: string;
78
+ origin?: string;
79
+ occurredAt?: string;
80
+ }): Promise<{
81
+ observed: number;
82
+ deltasAppended: number;
83
+ }>;
84
+ /**
85
+ * The perform adapter. OpenRouter's provisioning surface is what a world can really write:
86
+ * `pushOpenRouterAction` maps the one crossing operation it models and answers `false` for the
87
+ * rest, which are the twin's OWN record (a generation is a completion this twin produced; there is
88
+ * nothing at OpenRouter to create). Saying so is the honest outcome, never a silent success.
89
+ */
90
+ export declare function performOpenRouterAction(execute: RemoteExecute, action: TwinAction, _ctx: PerformContext): Promise<PushOutcome>;
@@ -0,0 +1,245 @@
1
+ // OpenRouter CONNECTOR — pull/push against an INJECTED `OpenRouterExecute`, so every test and every
2
+ // capability verify runs offline against a deterministic fake.
3
+ //
4
+ // `liveOpenRouterExecute` below is the pack's ONE construction site for a real, network-calling
5
+ // execute — the choke point the rate budget sits inside. Before it existed there was nowhere for a
6
+ // guard to live: the pack only ever received an execute, so a budget could only be something the
7
+ // caller remembered to opt into, and "the caller remembered" is exactly the assumption that cost a
8
+ // ~4.5-day Figma token lockout (2026-07-25). See `openrouter-budget.ts` for the numbers, and for the
9
+ // honest statement that none of OpenRouter's published figures governs this connector exactly.
10
+ //
11
+ // The injected contract is UNCHANGED: everything below still takes a plain `OpenRouterExecute`.
12
+ import { confirmAction, deployableEntries, observeResources } from '@volter/world-core';
13
+ import { OpenRouterBudget, OpenRouterBudgetError, openRouterCallWeight } from "./openrouter-budget.js";
14
+ const DEFAULT_OCCURRED_AT = '1970-01-01T00:00:00.000Z';
15
+ /** The real OpenRouter API host. The ONLY place this pack names it as a target. */
16
+ export const OPENROUTER_API_BASE = 'https://openrouter.ai';
17
+ /**
18
+ * THE CHOKE POINT — the one place this pack builds an `OpenRouterExecute` that really reaches
19
+ * openrouter.ai, and therefore the one place the rate budget has to be enforced.
20
+ *
21
+ * Reads the API key from `OPENROUTER_API_KEY` (never a literal, never a credentials file) and sends it
22
+ * as `Authorization: Bearer …`, which is how OpenRouter authenticates its OpenAI-compatible surface.
23
+ *
24
+ * EVERY request is guarded: the budget is charged BEFORE it goes out (`checkBudget`, which THROWS
25
+ * instead of returning once the ceiling or a persisted cooldown says stop) and the response is fed
26
+ * back (`recordCall`) so a `Retry-After` / 429 / rate-limit-exhaustion signal becomes a persisted
27
+ * cooldown that makes every later call fail fast WITHOUT touching OpenRouter. There is deliberately
28
+ * NO option to disable the guard.
29
+ *
30
+ * No vendor SDK is imported: real-transport `fetch` keeps this pack SDK-free at runtime, which the
31
+ * architecture guardrail requires.
32
+ */
33
+ export function liveOpenRouterExecute(opts = {}) {
34
+ const apiKey = opts.apiKey ?? process.env.OPENROUTER_API_KEY ?? '';
35
+ if (!apiKey)
36
+ throw new Error('OPENROUTER_API_KEY is not set — the live OpenRouter client needs an API key');
37
+ const baseUrl = opts.baseUrl ?? OPENROUTER_API_BASE;
38
+ const doFetch = opts.fetchImpl ?? fetch;
39
+ // There is no value a caller can pass to end up with an unguarded execute. `null`/`undefined` (or
40
+ // omitting it) build the default budget; anything that is not a REAL `OpenRouterBudget` is refused
41
+ // loudly rather than trusted — a duck-typed stand-in with a no-op `checkBudget` would otherwise be
42
+ // the one clean way around the guard.
43
+ if (opts.budget !== undefined && opts.budget !== null && !(opts.budget instanceof OpenRouterBudget)) {
44
+ throw new Error('liveOpenRouterExecute: `budget` must be an OpenRouterBudget — refusing to build a live OpenRouter client around an unverified rate guard');
45
+ }
46
+ // The default ledger is keyed by a hash of THIS key — OpenRouter's limits are per key / per
47
+ // account, so a cwd-scoped ledger would hand the same key a fresh allowance per checkout/CI leg.
48
+ const budget = opts.budget instanceof OpenRouterBudget
49
+ ? opts.budget
50
+ : new OpenRouterBudget({ token: apiKey, ...(opts.budgetOptions ?? {}) });
51
+ return async (req) => {
52
+ requireVendorPath(req.path);
53
+ const method = (req.method || 'GET').toUpperCase();
54
+ const weight = openRouterCallWeight(method, req.path);
55
+ // THROWS instead of calling. Nothing below this line runs when the budget refuses.
56
+ const reservation = budget.checkBudget(weight);
57
+ const res = await doFetch(new URL(req.path, baseUrl).toString(), {
58
+ method,
59
+ headers: {
60
+ Authorization: `Bearer ${apiKey}`,
61
+ Accept: 'application/json',
62
+ ...(req.body === undefined ? {} : { 'Content-Type': 'application/json' }),
63
+ },
64
+ ...(req.body === undefined ? {} : { body: JSON.stringify(req.body) }),
65
+ });
66
+ const headers = lowerCasedHeaders(res.headers);
67
+ const text = await res.text();
68
+ let data = null;
69
+ try {
70
+ data = text ? JSON.parse(text) : null;
71
+ }
72
+ catch {
73
+ data = { raw: text };
74
+ }
75
+ // Settles the reservation and, on a back-off signal, arms the cooldown. May itself throw (a
76
+ // Retry-After beyond the cap is not something to sleep off) — the cooldown is persisted first
77
+ // either way, so the refusal survives the throw.
78
+ // recordCall may THROW after arming the cooldown (a back-off beyond the cap). On a refused
79
+ // call that louder refusal wins; an answer OpenRouter ACCEPTED is kept, so a write that landed is
80
+ // never recorded as failed and performed again on retry.
81
+ try {
82
+ budget.recordCall(weight, headers, { status: res.status, reservation });
83
+ }
84
+ catch (error) {
85
+ if (!(error instanceof OpenRouterBudgetError) || !res.ok)
86
+ throw error;
87
+ }
88
+ return { status: res.status, data };
89
+ };
90
+ }
91
+ /**
92
+ * Refuse a `path` that is not a same-origin ABSOLUTE PATH.
93
+ *
94
+ * `new URL(path, base)` treats `https://evil.test/x` AND the protocol-relative `//evil.test/x` as
95
+ * absolute and silently retargets the host — and this factory attaches the credential
96
+ * unconditionally, so a caller-supplied absolute path would exfiltrate it to an arbitrary server.
97
+ * Figma's guarded client sidesteps this by concatenating rather than resolving; this execute takes an
98
+ * arbitrary `{ path }` from its caller, so it validates instead. (§9 finding, 2026-07-26.)
99
+ *
100
+ * `/twin/...` is refused for a different reason: those are the twin's OWN seed routes
101
+ * (`*RequestForAction` builds them), which the real vendor has never heard of. Sending one live is a
102
+ * guaranteed 404 that still burns budget and puts the live credential on the wire for nothing.
103
+ */
104
+ function requireVendorPath(path) {
105
+ if (typeof path !== 'string' || !/^\/(?!\/)/.test(path)) {
106
+ throw new Error(`liveOpenRouterExecute: path must be a same-origin absolute path beginning with a single "/" (got ${String(path)}) — refusing to let a caller-supplied absolute URL retarget the host with the credential attached`);
107
+ }
108
+ if (path === '/twin' || path.startsWith('/twin/')) {
109
+ throw new Error(`liveOpenRouterExecute: refusing to send the twin-only path ${path} to real OpenRouter — that route exists only in the local twin`);
110
+ }
111
+ }
112
+ /** Response headers as a plain lower-cased record — what the kernel's back-off reader expects. */
113
+ function lowerCasedHeaders(h) {
114
+ const out = {};
115
+ h.forEach((v, k) => { out[k.toLowerCase()] = v; });
116
+ return out;
117
+ }
118
+ /** Fetch OpenRouter's model catalog and provisioning keys through the injected client, mapped to SyncResource[] (no fold). */
119
+ async function collectOpenRouterState(execute) {
120
+ const resources = [];
121
+ const models = await execute({ method: 'GET', path: '/api/v1/models' });
122
+ const catalog = models.data.data ?? [];
123
+ for (const model of catalog) {
124
+ if (typeof model.id === 'string')
125
+ resources.push({ type: 'model', id: `model_${model.id}`, fields: model });
126
+ }
127
+ // The PROVISIONING keys (`GET /api/v1/keys`) — the one part of an OpenRouter account a world both
128
+ // WRITES and can read back, so it is the pull that makes the mirror a mirror rather than a
129
+ // catalog copy. The vendor addresses a key by its `hash` and never returns the prefixed form, so
130
+ // the subject id is rebuilt exactly as `createApiKey` mints it.
131
+ const keys = await execute({ method: 'GET', path: '/api/v1/keys' });
132
+ for (const key of keys.data.data ?? []) {
133
+ const hash = typeof key.hash === 'string' ? key.hash : typeof key.id === 'string' ? key.id : null;
134
+ if (!hash)
135
+ continue;
136
+ resources.push({ type: 'api_key', id: `key_${hash}`, fields: { ...key, id: `key_${hash}`, hash } });
137
+ }
138
+ return resources;
139
+ }
140
+ export async function pullOpenRouterState(execute, opts = {}) {
141
+ const resources = await collectOpenRouterState(execute);
142
+ const at = opts.occurredAt ?? DEFAULT_OCCURRED_AT;
143
+ observeResources('openrouter', resources, { ...(opts.root !== undefined ? { root: opts.root } : {}), at, batch: `obs:openrouter:${at}` });
144
+ }
145
+ /**
146
+ * D7 consumer-facing pull entry point: gather all OpenRouter domains (the model catalog and the account's provisioning keys) and
147
+ * fold them into the twin as ONE observation, returning the standard result shape. Idempotent:
148
+ * a re-pull of identical state appends no new deltas.
149
+ */
150
+ export async function syncOpenRouterFromReal(execute, opts = {}) {
151
+ const resources = await collectOpenRouterState(execute);
152
+ const at = opts.occurredAt ?? DEFAULT_OCCURRED_AT;
153
+ const result = observeResources('openrouter', resources, { ...(opts.root !== undefined ? { root: opts.root } : {}), at, batch: `obs:openrouter:${at}` });
154
+ return { observed: resources.length, deltasAppended: result.appended };
155
+ }
156
+ export function openRouterRequestForAction(action) {
157
+ if (action.operation === 'generation.record') {
158
+ const stat = action.fields;
159
+ return { method: 'GET', path: `/api/v1/generation?id=${encodeURIComponent(stat.id.replace(/^generation_/, ''))}` };
160
+ }
161
+ return null;
162
+ }
163
+ export async function pushOpenRouterAction(action, execute, opts = {}) {
164
+ const req = openRouterRequestForAction(action);
165
+ if (!req)
166
+ return false;
167
+ const result = await execute(req);
168
+ if (result.status < 200 || result.status >= 300)
169
+ return false;
170
+ // The vendor's OWN id for the subject, and the receipt that says the crossing happened — protocol
171
+ // 2's confirmation carries both, so a later read can tell a deployed subject from a local one.
172
+ const answered = result.data?.data;
173
+ const vendorSubjectId = typeof answered?.id === 'string' ? answered.id : action.subject.id;
174
+ confirmAction({
175
+ service: 'openrouter',
176
+ actionId: action.id,
177
+ subject: action.subject,
178
+ fields: action.fields ?? {},
179
+ occurredAt: opts.occurredAt ?? DEFAULT_OCCURRED_AT,
180
+ vendorSubjectId,
181
+ receipt: { status: 'deployed' },
182
+ ...(opts.root !== undefined ? { root: opts.root } : {}),
183
+ });
184
+ return true;
185
+ }
186
+ export async function pushPendingOpenRouterActions(execute, opts = {}) {
187
+ let pushed = 0;
188
+ for (const action of deployableEntries('openrouter', opts.root)) {
189
+ if (await pushOpenRouterAction(action, execute, opts))
190
+ pushed++;
191
+ }
192
+ return pushed;
193
+ }
194
+ // ── PROTOCOL 2: the pack's half of the real state system ────────────────────────────────────
195
+ /**
196
+ * An `OpenRouterExecute` over the kernel's executor. At a REAL boundary the kernel sets the sealed
197
+ * credential over these headers (executor.ts); at the twin's own wire any credential is one.
198
+ *
199
+ * The paths carry OpenRouter's `/api` prefix, which `collectOpenRouterState` already uses and the
200
+ * twin's own router accepts alongside the bare `/v1` form.
201
+ */
202
+ export function openRouterExecuteOver(execute) {
203
+ return async (req) => {
204
+ const res = await execute({
205
+ method: req.method,
206
+ path: req.path,
207
+ headers: { accept: 'application/json', 'content-type': 'application/json', authorization: 'Bearer twin' },
208
+ ...(req.body === undefined ? {} : { body: JSON.stringify(req.body) }),
209
+ });
210
+ let data = {};
211
+ try {
212
+ data = JSON.parse(res.body || '{}');
213
+ }
214
+ catch {
215
+ data = {};
216
+ }
217
+ return { status: res.status, data };
218
+ };
219
+ }
220
+ /** The refresh adapter: pull the account's state through the executor and fold it into the root. */
221
+ export async function syncOpenRouterFromRemote(execute, opts = {}) {
222
+ return syncOpenRouterFromReal(openRouterExecuteOver(execute), {
223
+ ...(opts.root !== undefined ? { root: opts.root } : {}),
224
+ occurredAt: opts.occurredAt ?? new Date().toISOString(),
225
+ });
226
+ }
227
+ /**
228
+ * The perform adapter. OpenRouter's provisioning surface is what a world can really write:
229
+ * `pushOpenRouterAction` maps the one crossing operation it models and answers `false` for the
230
+ * rest, which are the twin's OWN record (a generation is a completion this twin produced; there is
231
+ * nothing at OpenRouter to create). Saying so is the honest outcome, never a silent success.
232
+ */
233
+ export async function performOpenRouterAction(execute, action, _ctx) {
234
+ const client = openRouterExecuteOver(execute);
235
+ const req = openRouterRequestForAction(action);
236
+ if (!req) {
237
+ const op = action.operation ?? `${action.subject.type}.update`;
238
+ return { externalId: action.subject.id, data: { performed: false, reason: `${op} is the twin's own record — nothing at OpenRouter to write` } };
239
+ }
240
+ const result = await client(req);
241
+ if (result.status < 200 || result.status >= 300) {
242
+ throw new Error(`openrouter ${req.method} ${req.path} refused: HTTP ${result.status}`);
243
+ }
244
+ return { externalId: action.subject.id, data: (result.data ?? {}) };
245
+ }
@@ -0,0 +1,17 @@
1
+ /** Optional pack-owned transport to a separately World-owned loopback generation service. */
2
+ export declare const LOCAL_GENERATION_BODY_LIMIT: number;
3
+ export type LocalGenerationFetch = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
4
+ export declare function localGenerationOrigin(value: string): string;
5
+ /** Read only the opt-in generation request body; ordinary twin routes retain their old path. */
6
+ export declare function readLocalGenerationBody(request: Request): Promise<{
7
+ bytes: Uint8Array<ArrayBuffer>;
8
+ text: string;
9
+ }>;
10
+ /** One pull from the caller means at most one read from the local response. */
11
+ export declare function forwardLocalGeneration(args: {
12
+ origin: string;
13
+ request: Request;
14
+ pathname: string;
15
+ body: Uint8Array<ArrayBuffer>;
16
+ fetchImpl?: LocalGenerationFetch;
17
+ }): Promise<Response>;