@stage-labs/metro 0.1.0-beta.310 → 0.1.0-beta.312

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 (34) hide show
  1. package/dist/claude-env.js +1 -1
  2. package/dist/route.js +1 -1
  3. package/package.json +1 -1
  4. package/runtime/node_modules/@metro-labs/core/package.json +1 -0
  5. package/runtime/node_modules/@metro-labs/core/src/claude-token.ts +3 -0
  6. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/view.ts +1 -1
  7. package/runtime/node_modules/@metro-labs/daemon/src/boot/boot.ts +1 -3
  8. package/runtime/node_modules/@metro-labs/daemon/src/claude/api.ts +1 -36
  9. package/runtime/node_modules/@metro-labs/daemon/src/claude/session.ts +4 -13
  10. package/runtime/node_modules/@metro-labs/daemon/src/gateway/delivered.ts +16 -9
  11. package/runtime/node_modules/@metro-labs/daemon/src/gateway/effective.ts +4 -15
  12. package/runtime/node_modules/@metro-labs/daemon/src/gateway/fallback.ts +5 -4
  13. package/runtime/node_modules/@metro-labs/daemon/src/gateway/forward.ts +6 -0
  14. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gateway.ts +66 -67
  15. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-api.ts +18 -32
  16. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-config.ts +15 -46
  17. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-leftovers.ts +24 -3
  18. package/runtime/node_modules/@metro-labs/daemon/src/gateway/routeway.ts +19 -0
  19. package/runtime/node_modules/@metro-labs/daemon/src/gateway/subscription.ts +1 -61
  20. package/runtime/node_modules/@metro-labs/daemon/src/gateway/usage-refresh.ts +11 -55
  21. package/runtime/node_modules/@metro-labs/daemon/src/gateway/usage.ts +1 -66
  22. package/runtime/node_modules/@metro-labs/http/src/api-http.ts +1 -1
  23. package/runtime/node_modules/@metro-labs/http/src/connector-tools.ts +7 -1
  24. package/runtime/runtime.json +1 -1
  25. package/runtime/sdk-runner/node_modules/@metro-labs/core/package.json +1 -0
  26. package/runtime/sdk-runner/node_modules/@metro-labs/core/src/claude-token.ts +3 -0
  27. package/runtime/sdk-runner/runner.json +1 -1
  28. package/runtime/node_modules/@metro-labs/daemon/src/claude/login.ts +0 -243
  29. package/runtime/node_modules/@metro-labs/daemon/src/claude/onboarding.ts +0 -38
  30. package/runtime/node_modules/@metro-labs/daemon/src/claude/usage-probe.ts +0 -128
  31. package/runtime/node_modules/@metro-labs/daemon/src/gateway/claude-logins.ts +0 -192
  32. package/runtime/node_modules/@metro-labs/daemon/src/gateway/claude-store.ts +0 -59
  33. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-lists.ts +0 -40
  34. package/runtime/node_modules/@metro-labs/daemon/src/gateway/provider-models.ts +0 -77
@@ -1,40 +0,0 @@
1
- import { errMsg, log } from '@metro-labs/core/log';
2
-
3
- export const LIST_TTL_MS = 60 * 60_000;
4
- export const RETRY_MS = 5 * 60_000;
5
-
6
- export interface ListCache<T> {
7
- peek: (key: string) => T[] | null;
8
- refresh: (key: string, load: () => Promise<T[]>) => Promise<void>;
9
- }
10
-
11
- const clears = new Set<() => void>();
12
-
13
- export function listCache<T>(name: string, now: () => number = Date.now): ListCache<T> {
14
- const kept = new Map<string, { at: number; list: T[] }>();
15
- const tried = new Map<string, number>();
16
- clears.add(() => {
17
- kept.clear();
18
- tried.clear();
19
- });
20
- const fresh = (key: string): boolean => {
21
- const hit = kept.get(key);
22
- return hit !== undefined && now() - hit.at < LIST_TTL_MS;
23
- };
24
- return {
25
- peek: (key) => kept.get(key)?.list ?? null,
26
- async refresh(key, load) {
27
- if (fresh(key) || now() - (tried.get(key) ?? Number.NEGATIVE_INFINITY) < RETRY_MS) return;
28
- tried.set(key, now());
29
- try {
30
- kept.set(key, { at: now(), list: await load() });
31
- } catch (err) {
32
- log.warn({ list: name, err: errMsg(err) }, 'model-lists: the provider would not list its models, so the last list it gave stands');
33
- }
34
- },
35
- };
36
- }
37
-
38
- export function forgetModelLists(): void {
39
- for (const clear of clears) clear();
40
- }
@@ -1,77 +0,0 @@
1
- import type { IncomingHttpHeaders } from 'node:http';
2
- import { isRecord } from '@metro-labs/core/is-record';
3
- import { GatewayError, OAUTH_BETA } from './forward.js';
4
- import { listCache } from './model-lists.js';
5
- import { stringOf } from '@metro-labs/http/api-http';
6
-
7
- export const ANTHROPIC_API = 'https://api.anthropic.com';
8
- const ANTHROPIC_VERSION = '2023-06-01';
9
- const LIST_MAX = 1000;
10
- const LIST_TIMEOUT_MS = 10_000;
11
- const BEARER = 'Bearer ';
12
-
13
- export interface ProviderModel {
14
- id: string;
15
- name: string;
16
- }
17
-
18
- interface Dated extends ProviderModel {
19
- created: string;
20
- }
21
-
22
- const KNOWN_CLAUDE: ProviderModel[] = [
23
- { id: 'claude-sonnet-5-5', name: 'Claude Sonnet 5.5' },
24
- { id: 'claude-opus-5-5', name: 'Claude Opus 5.5' },
25
- { id: 'claude-fable-5-1', name: 'Claude Fable 5.1' },
26
- { id: 'claude-opus-5', name: 'Claude Opus 5' },
27
- { id: 'claude-sonnet-5', name: 'Claude Sonnet 5' },
28
- { id: 'claude-haiku-4-5', name: 'Claude Haiku 4.5' },
29
- ];
30
-
31
- const claudeLists = listCache<ProviderModel>('anthropic');
32
-
33
- const newestFirst = (list: Dated[]): ProviderModel[] =>
34
- list.sort((a, b) => b.created.localeCompare(a.created) || a.id.localeCompare(b.id)).map(({ id, name }) => ({ id, name }));
35
-
36
- function claudeEntry(entry: unknown): Dated[] {
37
- if (!isRecord(entry) || stringOf(entry.id) === '') return [];
38
- const type = stringOf(entry.type);
39
- if (type !== '' && type !== 'model') return [];
40
- const id = stringOf(entry.id);
41
- return [{ id, name: stringOf(entry.display_name) || id, created: stringOf(entry.created_at) }];
42
- }
43
-
44
- async function listClaude(auth: Record<string, string>, what: string, base: string, fetchImpl: typeof fetch): Promise<ProviderModel[]> {
45
- const res = await fetchImpl(`${base}/v1/models?limit=${String(LIST_MAX)}`, {
46
- headers: { ...auth, 'anthropic-version': ANTHROPIC_VERSION, accept: 'application/json' },
47
- redirect: 'manual',
48
- signal: AbortSignal.timeout(LIST_TIMEOUT_MS),
49
- });
50
- if (!res.ok) throw new GatewayError(res.status, 'api_error', `Anthropic would not list its models with ${what} (${String(res.status)})`);
51
- const body: unknown = await res.json();
52
- const data = isRecord(body) && Array.isArray(body.data) ? body.data : null;
53
- if (data === null) throw new GatewayError(502, 'api_error', 'Anthropic answered with no model list');
54
- return newestFirst(data.flatMap(claudeEntry));
55
- }
56
-
57
- const loginKey = (base: string): string => `login:${base}`;
58
-
59
- const first = (value: string | string[] | undefined): string => (Array.isArray(value) ? value[0] : value)?.trim() ?? '';
60
-
61
- function loginAuth(headers: IncomingHttpHeaders): Record<string, string> | null {
62
- const bearer = first(headers.authorization);
63
- if (bearer.startsWith(BEARER) && bearer.length > BEARER.length) return { authorization: bearer, 'anthropic-beta': OAUTH_BETA };
64
- const key = first(headers['x-api-key']);
65
- return key === '' ? null : { 'x-api-key': key };
66
- }
67
-
68
- export function refreshLoginModels(headers: IncomingHttpHeaders, base = ANTHROPIC_API, fetchImpl: typeof fetch = fetch): Promise<void> {
69
- const auth = loginAuth(headers);
70
- if (auth === null) return Promise.resolve();
71
- return claudeLists.refresh(loginKey(base), () => listClaude(auth, 'the Claude Code login', base, fetchImpl));
72
- }
73
-
74
- export function claudeModels(base = ANTHROPIC_API): ProviderModel[] {
75
- const live = claudeLists.peek(loginKey(base)) ?? [];
76
- return live.length > 0 ? live : KNOWN_CLAUDE;
77
- }