@stage-labs/metro 0.1.0-beta.322 → 0.1.0-beta.323
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/claude/session.ts +3 -2
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/box-gateway.ts +3 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-api.ts +5 -4
- package/runtime/node_modules/@metro-labs/inference/src/delivered.ts +7 -2
- package/runtime/node_modules/@metro-labs/inference/src/model-config.ts +21 -10
- package/runtime/runtime.json +1 -1
- package/runtime/sdk-runner/runner.json +1 -1
package/package.json
CHANGED
|
@@ -11,11 +11,12 @@ import { isRecord } from '@metro-labs/core/is-record';
|
|
|
11
11
|
import { readJson, writeJson } from '@metro-labs/core/secure-fs';
|
|
12
12
|
import { METRO_VERSION } from '@metro-labs/core/version';
|
|
13
13
|
import { agentsDir, listAgentFiles } from '../agents/files.js';
|
|
14
|
-
import {
|
|
14
|
+
import { primaryRoute, type ModelSetup } from '@metro-labs/inference/model-config';
|
|
15
15
|
import { effectiveSetup } from '../gateway/effective.js';
|
|
16
16
|
import { inSessionScope } from './memory.js';
|
|
17
17
|
import { runningAsRoot } from '../metro-user/privilege.js';
|
|
18
18
|
import { agentUser, agentUserExpected, agentViewDir, asAgent, claudeHome } from '../agent-user/user.js';
|
|
19
|
+
import { routeReadiness } from '../gateway/box-gateway.js';
|
|
19
20
|
|
|
20
21
|
function sessionEnv(): Record<string, string> {
|
|
21
22
|
const user = agentUser();
|
|
@@ -133,7 +134,7 @@ const NO_MODEL = "the organization routes this agent to no model yet: choose one
|
|
|
133
134
|
function credentialReady(deps: SessionDeps): string | null {
|
|
134
135
|
try {
|
|
135
136
|
const primary = primaryRoute((deps.model ?? (() => effectiveSetup()))());
|
|
136
|
-
return primary === null ? NO_MODEL :
|
|
137
|
+
return primary === null ? NO_MODEL : routeReadiness(primary);
|
|
137
138
|
} catch (err) {
|
|
138
139
|
return `the model setup is not readable (${errMsg(err)})`;
|
|
139
140
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
2
2
|
import { log } from '@metro-labs/core/log';
|
|
3
3
|
import { handleGatewayRequest, type GatewayDeps } from '@metro-labs/inference/gateway';
|
|
4
|
+
import { notReady, setupNotReady, type Route } from '@metro-labs/inference/model-config';
|
|
4
5
|
import { agentsDir } from '../agents/files.js';
|
|
5
6
|
import { agentIdForKey } from '../agents/keys.js';
|
|
6
7
|
import { reregisterMcpToken } from '../mcp-server/link.js';
|
|
@@ -13,6 +14,8 @@ type Handler = (req: IncomingMessage, res: ServerResponse) => boolean;
|
|
|
13
14
|
|
|
14
15
|
const identify = (key: string): boolean => agentIdForKey(key) !== undefined;
|
|
15
16
|
|
|
17
|
+
export const routeReadiness = (route: Route): string | null => (gatewayUrl() === null ? notReady(route) : setupNotReady(route));
|
|
18
|
+
|
|
16
19
|
export const boxGateway = (): GatewayDeps => ({
|
|
17
20
|
setup: () => effectiveSetup(),
|
|
18
21
|
identify,
|
|
@@ -4,7 +4,8 @@ import { ApiError } from '@metro-labs/http/api-error';
|
|
|
4
4
|
import { apiFailure, apiSession, cors, sendJson } from '@metro-labs/http/api-http';
|
|
5
5
|
import { effectiveSetup } from './effective.js';
|
|
6
6
|
import { chainStatus } from '@metro-labs/inference/fallback';
|
|
7
|
-
import { helperSetup,
|
|
7
|
+
import { helperSetup, primaryRoute, type Connection, type ModelSetup } from '@metro-labs/inference/model-config';
|
|
8
|
+
import { routeReadiness } from './box-gateway.js';
|
|
8
9
|
import { orgModels, type SyncStatus } from './org-models.js';
|
|
9
10
|
import { lastServed } from '@metro-labs/inference/served';
|
|
10
11
|
import { refreshUsage, type UsageDeps } from '@metro-labs/inference/usage-refresh';
|
|
@@ -29,8 +30,8 @@ function publicConnection(c: Connection): Record<string, unknown> {
|
|
|
29
30
|
provider: c.provider,
|
|
30
31
|
label: c.label,
|
|
31
32
|
model: c.model,
|
|
32
|
-
hasKey: c.apiKey !== '',
|
|
33
|
-
subscription: c.claudeToken !== '',
|
|
33
|
+
hasKey: c.apiKey !== '' || c.auth === 'key',
|
|
34
|
+
subscription: c.claudeToken !== '' || c.auth === 'subscription',
|
|
34
35
|
region: c.region,
|
|
35
36
|
zdr: c.zdr,
|
|
36
37
|
signedOut: c.signedOut,
|
|
@@ -52,7 +53,7 @@ function helpersBody(setup: ModelSetup): Record<string, unknown> | null {
|
|
|
52
53
|
export function settingsBody(deps: ModelApiDeps): Record<string, unknown> {
|
|
53
54
|
const setup = setupOf(deps);
|
|
54
55
|
const primary = primaryRoute(setup);
|
|
55
|
-
const reason = primary === null ? NO_MODEL :
|
|
56
|
+
const reason = primary === null ? NO_MODEL : routeReadiness(primary);
|
|
56
57
|
return {
|
|
57
58
|
source: setup.source,
|
|
58
59
|
sync: (deps.syncStatus ?? (() => orgModels()?.syncStatus() ?? NO_SYNC))(),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { readEffort, type RouteEffort } from '@metro-labs/core/effort';
|
|
2
2
|
import { isRecord } from '@metro-labs/core/is-record';
|
|
3
|
-
import { emptySetup, isProvider, type ModelSetup, type Account, type Chain, type CodexAccess, type Connection, type Fallback, type Provider, type RouteSource } from './model-config.js';
|
|
3
|
+
import { emptySetup, isProvider, type AuthKind, type ModelSetup, type Account, type Chain, type CodexAccess, type Connection, type Fallback, type Provider, type RouteSource } from './model-config.js';
|
|
4
4
|
|
|
5
5
|
export interface DeliveredRoute {
|
|
6
6
|
primary: Fallback | null;
|
|
@@ -69,6 +69,10 @@ function credentialOf(provider: Provider, raw: unknown): Credential {
|
|
|
69
69
|
return { apiKey: text(raw.apiKey), claudeToken: provider === 'anthropic' ? text(raw.claudeToken) : '', codex: null };
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
const AUTH_KINDS: readonly AuthKind[] = ['key', 'subscription', 'codex'];
|
|
73
|
+
|
|
74
|
+
const authOf = (raw: unknown): AuthKind | null => AUTH_KINDS.find((kind) => kind === raw) ?? null;
|
|
75
|
+
|
|
72
76
|
export function deliveredConnection(raw: unknown): Connection | null {
|
|
73
77
|
if (!isRecord(raw) || !isProvider(raw.provider)) return null;
|
|
74
78
|
const id = text(raw.id);
|
|
@@ -86,7 +90,8 @@ export function deliveredConnection(raw: unknown): Connection | null {
|
|
|
86
90
|
codex,
|
|
87
91
|
claudeToken,
|
|
88
92
|
account: accountOf(raw.account),
|
|
89
|
-
signedOut: codex === null && apiKey === '' && claudeToken === '',
|
|
93
|
+
signedOut: typeof raw.signedOut === 'boolean' ? raw.signedOut : codex === null && apiKey === '' && claudeToken === '',
|
|
94
|
+
auth: authOf(raw.auth),
|
|
90
95
|
};
|
|
91
96
|
}
|
|
92
97
|
|
|
@@ -31,8 +31,11 @@ export interface Connection {
|
|
|
31
31
|
claudeToken: string;
|
|
32
32
|
account: Account;
|
|
33
33
|
signedOut: boolean;
|
|
34
|
+
auth?: AuthKind | null;
|
|
34
35
|
}
|
|
35
36
|
|
|
37
|
+
export type AuthKind = 'key' | 'subscription' | 'codex';
|
|
38
|
+
|
|
36
39
|
export interface Fallback {
|
|
37
40
|
connection: string;
|
|
38
41
|
model: string;
|
|
@@ -77,28 +80,36 @@ export function primaryRoute(setup: ModelSetup): Route | null {
|
|
|
77
80
|
return null;
|
|
78
81
|
}
|
|
79
82
|
|
|
80
|
-
|
|
81
|
-
|
|
83
|
+
type Check = [(r: Route) => boolean, string, 'credential' | 'setup'];
|
|
84
|
+
|
|
85
|
+
const CHECKS: Record<Provider, Check[]> = {
|
|
86
|
+
anthropic: [[(r) => r.connection.apiKey === '' && r.connection.claudeToken === '', "Anthropic has no API key or Claude subscription: add one on the organization's Gateway page.", 'credential']],
|
|
82
87
|
bedrock: [
|
|
83
|
-
[(r) => r.connection.apiKey === '', "Bedrock has no API key: add it on the organization's Gateway page."],
|
|
84
|
-
[(r) => r.connection.region === '', "Bedrock needs a region: add it on the organization's Gateway page."],
|
|
88
|
+
[(r) => r.connection.apiKey === '', "Bedrock has no API key: add it on the organization's Gateway page.", 'credential'],
|
|
89
|
+
[(r) => r.connection.region === '', "Bedrock needs a region: add it on the organization's Gateway page.", 'setup'],
|
|
85
90
|
],
|
|
86
91
|
openrouter: [
|
|
87
|
-
[(r) => r.connection.apiKey === '', "OpenRouter has no API key: add it on the organization's Gateway page."],
|
|
88
|
-
[(r) => r.model === '', "OpenRouter needs a model id: choose one on the organization's Gateway page."],
|
|
92
|
+
[(r) => r.connection.apiKey === '', "OpenRouter has no API key: add it on the organization's Gateway page.", 'credential'],
|
|
93
|
+
[(r) => r.model === '', "OpenRouter needs a model id: choose one on the organization's Gateway page.", 'setup'],
|
|
89
94
|
],
|
|
90
95
|
codex: [
|
|
91
|
-
[(r) => r.connection.codex === null, CODEX_SIGN_IN],
|
|
92
|
-
[(r) => r.model === '', "Codex needs a model id: choose one on the organization's Gateway page."],
|
|
96
|
+
[(r) => r.connection.codex === null, CODEX_SIGN_IN, 'credential'],
|
|
97
|
+
[(r) => r.model === '', "Codex needs a model id: choose one on the organization's Gateway page.", 'setup'],
|
|
93
98
|
],
|
|
94
99
|
routeway: [
|
|
95
|
-
[(r) => r.connection.apiKey === '', "Routeway has no API key: add it on the organization's Gateway page."],
|
|
96
|
-
[(r) => r.model === '', "Routeway needs a model id: choose one on the organization's Gateway page."],
|
|
100
|
+
[(r) => r.connection.apiKey === '', "Routeway has no API key: add it on the organization's Gateway page.", 'credential'],
|
|
101
|
+
[(r) => r.model === '', "Routeway needs a model id: choose one on the organization's Gateway page.", 'setup'],
|
|
97
102
|
],
|
|
98
103
|
};
|
|
99
104
|
|
|
100
105
|
export const notReady = (route: Route): string | null => CHECKS[route.connection.provider].find(([missing]) => missing(route))?.[1] ?? null;
|
|
101
106
|
|
|
107
|
+
export function setupNotReady(route: Route): string | null {
|
|
108
|
+
const checks = CHECKS[route.connection.provider];
|
|
109
|
+
if (route.connection.signedOut) return checks.find(([, , kind]) => kind === 'credential')?.[1] ?? null;
|
|
110
|
+
return checks.find(([missing, , kind]) => kind === 'setup' && missing(route))?.[1] ?? null;
|
|
111
|
+
}
|
|
112
|
+
|
|
102
113
|
export const isSmallModel = (requested: string): boolean => SMALL_RE.test(requested);
|
|
103
114
|
|
|
104
115
|
const DEFAULTS: Record<Provider, (requested: string, model: string) => string> = {
|
package/runtime/runtime.json
CHANGED