@opengeni/api-router 0.7.3 → 0.9.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.
- package/dist/app.js +1 -1
- package/dist/{chunk-EYYTFA7N.js → chunk-QOYQBYHM.js} +1920 -264
- package/dist/chunk-QOYQBYHM.js.map +1 -0
- package/dist/index.js +1 -1
- package/package.json +12 -11
- package/src/app.ts +37 -15
- package/src/codex-redemption-security.ts +96 -0
- package/src/integrations/oauth-client.ts +147 -61
- package/src/mcp/server.ts +62 -8
- package/src/mcp/session-view.ts +102 -1
- package/src/mcp/toolspace.ts +389 -124
- package/src/model-catalog.ts +337 -0
- package/src/routes/codex.ts +866 -14
- package/src/routes/files.ts +153 -0
- package/src/routes/sessions.ts +155 -10
- package/src/routes/workspaces.ts +36 -2
- package/src/sandbox/viewer.ts +7 -1
- package/dist/chunk-EYYTFA7N.js.map +0 -1
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
import {
|
|
2
|
+
configuredModels,
|
|
3
|
+
configuredProviders,
|
|
4
|
+
withCodexCatalogProvider,
|
|
5
|
+
type ConfiguredModel,
|
|
6
|
+
type Settings,
|
|
7
|
+
} from "@opengeni/config";
|
|
8
|
+
import {
|
|
9
|
+
ClientModel,
|
|
10
|
+
WorkspaceModelCatalogResponse,
|
|
11
|
+
evaluateWorkspaceModelPolicy,
|
|
12
|
+
type ModelAvailabilityV1,
|
|
13
|
+
type ModelCredentialReadinessV1,
|
|
14
|
+
type WorkspaceModelCatalogResponse as WorkspaceModelCatalogResponseType,
|
|
15
|
+
type WorkspaceModelPolicyContract,
|
|
16
|
+
} from "@opengeni/contracts";
|
|
17
|
+
|
|
18
|
+
export type ModelAvailabilityObservation = {
|
|
19
|
+
status: "available" | "degraded" | "unavailable";
|
|
20
|
+
reason: "not_entitled" | "provider_unhealthy" | null;
|
|
21
|
+
checkedAt: string;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export type ModelCredentialReadinessObservation =
|
|
25
|
+
| { status: "ready"; checkedAt: string }
|
|
26
|
+
| {
|
|
27
|
+
status: "not_ready";
|
|
28
|
+
reason: "prerequisites_missing" | "needs_reauth";
|
|
29
|
+
checkedAt: string;
|
|
30
|
+
}
|
|
31
|
+
| { status: "error"; reason: "resolver_error"; checkedAt: string };
|
|
32
|
+
|
|
33
|
+
export const MODEL_CREDENTIAL_READINESS_OBSERVATION_MAX_AGE_MS = 5 * 60_000;
|
|
34
|
+
|
|
35
|
+
/** Static, client-safe definition projection. No provider secret is reachable. */
|
|
36
|
+
export function projectClientModel(model: ConfiguredModel): ClientModel {
|
|
37
|
+
return ClientModel.parse({
|
|
38
|
+
id: model.id,
|
|
39
|
+
label: model.label,
|
|
40
|
+
provider: model.providerId,
|
|
41
|
+
providerLabel: model.providerLabel,
|
|
42
|
+
api: model.api,
|
|
43
|
+
...(model.contextWindowTokens === undefined
|
|
44
|
+
? {}
|
|
45
|
+
: { contextWindowTokens: model.contextWindowTokens }),
|
|
46
|
+
schemaVersion: model.schemaVersion,
|
|
47
|
+
aliases: model.aliases,
|
|
48
|
+
deployment: model.deployment,
|
|
49
|
+
executionLimits: model.executionLimits,
|
|
50
|
+
credentialSource: model.credentialSource,
|
|
51
|
+
billing: model.billing,
|
|
52
|
+
capabilities: model.capabilities,
|
|
53
|
+
...(model.pricing === undefined ? {} : { pricing: model.pricing }),
|
|
54
|
+
definitionVersion: model.definitionVersion,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function modelDefinitionRunnable(model: ConfiguredModel): boolean {
|
|
59
|
+
return (
|
|
60
|
+
model.capabilities.inputModalities.includes("text") &&
|
|
61
|
+
model.capabilities.outputModalities.includes("text") &&
|
|
62
|
+
model.capabilities.transports.sse.runnable
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function observedCredentialReadiness(input: {
|
|
67
|
+
observation: ModelCredentialReadinessObservation | undefined;
|
|
68
|
+
basis: "connection" | "resolver";
|
|
69
|
+
nowMs: number;
|
|
70
|
+
maxAgeMs: number;
|
|
71
|
+
}): ModelCredentialReadinessV1 {
|
|
72
|
+
if (!input.observation) {
|
|
73
|
+
return {
|
|
74
|
+
status: "not_ready",
|
|
75
|
+
reason: "prerequisites_missing",
|
|
76
|
+
basis: input.basis,
|
|
77
|
+
checkedAt: null,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
const checkedAtMs = Date.parse(input.observation.checkedAt);
|
|
81
|
+
if (!Number.isFinite(checkedAtMs)) {
|
|
82
|
+
return {
|
|
83
|
+
status: "error",
|
|
84
|
+
reason: "resolver_error",
|
|
85
|
+
basis: input.basis,
|
|
86
|
+
checkedAt: null,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
const checkedAt = new Date(checkedAtMs).toISOString();
|
|
90
|
+
if (Math.abs(input.nowMs - checkedAtMs) > input.maxAgeMs) {
|
|
91
|
+
return {
|
|
92
|
+
status: "not_ready",
|
|
93
|
+
reason: "observation_stale",
|
|
94
|
+
basis: input.basis,
|
|
95
|
+
checkedAt,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
if (input.observation.status === "ready") {
|
|
99
|
+
return { status: "ready", reason: null, basis: input.basis, checkedAt };
|
|
100
|
+
}
|
|
101
|
+
if (input.observation.status === "not_ready") {
|
|
102
|
+
return {
|
|
103
|
+
status: "not_ready",
|
|
104
|
+
reason:
|
|
105
|
+
input.observation.reason === "needs_reauth" ? "needs_reauth" : "prerequisites_missing",
|
|
106
|
+
basis: input.basis,
|
|
107
|
+
checkedAt,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
status: "error",
|
|
112
|
+
reason: "resolver_error",
|
|
113
|
+
basis: input.basis,
|
|
114
|
+
checkedAt,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function credentialReadinessFor(input: {
|
|
119
|
+
model: ConfiguredModel;
|
|
120
|
+
provider: ReturnType<typeof configuredProviders>[number] | undefined;
|
|
121
|
+
codexSubscriptionActive: boolean;
|
|
122
|
+
observation: ModelCredentialReadinessObservation | undefined;
|
|
123
|
+
nowMs: number;
|
|
124
|
+
maxAgeMs: number;
|
|
125
|
+
}): ModelCredentialReadinessV1 {
|
|
126
|
+
const source = input.model.credentialSource;
|
|
127
|
+
if (source.kind === "connected_subscription") {
|
|
128
|
+
return input.codexSubscriptionActive
|
|
129
|
+
? { status: "ready", reason: null, basis: "connection", checkedAt: null }
|
|
130
|
+
: {
|
|
131
|
+
status: "not_ready",
|
|
132
|
+
reason: "needs_reauth",
|
|
133
|
+
basis: "connection",
|
|
134
|
+
checkedAt: null,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
if (source.kind === "deployment" && source.mechanism === "api_key") {
|
|
138
|
+
return input.provider?.apiKey
|
|
139
|
+
? { status: "ready", reason: null, basis: "configuration", checkedAt: null }
|
|
140
|
+
: {
|
|
141
|
+
status: "not_ready",
|
|
142
|
+
reason: "missing_credential",
|
|
143
|
+
basis: "configuration",
|
|
144
|
+
checkedAt: null,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
return observedCredentialReadiness({
|
|
148
|
+
observation: input.observation,
|
|
149
|
+
basis: source.kind === "workspace_connection" ? "connection" : "resolver",
|
|
150
|
+
nowMs: input.nowMs,
|
|
151
|
+
maxAgeMs: input.maxAgeMs,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function isXaiGrokModel(model: ConfiguredModel): boolean {
|
|
156
|
+
return model.providerId === "xai" && model.id.startsWith("xai/grok-");
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function observationTimestamp(observation: ModelAvailabilityObservation | undefined): {
|
|
160
|
+
checkedAt: string | null;
|
|
161
|
+
checkedAtMs: number | null;
|
|
162
|
+
} {
|
|
163
|
+
if (!observation || typeof observation.checkedAt !== "string") {
|
|
164
|
+
return { checkedAt: null, checkedAtMs: null };
|
|
165
|
+
}
|
|
166
|
+
const checkedAtMs = Date.parse(observation.checkedAt);
|
|
167
|
+
if (!Number.isFinite(checkedAtMs)) {
|
|
168
|
+
return { checkedAt: null, checkedAtMs: null };
|
|
169
|
+
}
|
|
170
|
+
return { checkedAt: new Date(checkedAtMs).toISOString(), checkedAtMs };
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function xaiGrokAvailabilityFor(input: {
|
|
174
|
+
observation: ModelAvailabilityObservation | undefined;
|
|
175
|
+
nowMs: number;
|
|
176
|
+
maxAgeMs: number;
|
|
177
|
+
}): ModelAvailabilityV1 {
|
|
178
|
+
const { checkedAt, checkedAtMs } = observationTimestamp(input.observation);
|
|
179
|
+
const freshSuccessfulObservation =
|
|
180
|
+
input.observation?.status === "available" &&
|
|
181
|
+
input.observation.reason === null &&
|
|
182
|
+
checkedAtMs !== null &&
|
|
183
|
+
checkedAtMs <= input.nowMs &&
|
|
184
|
+
input.nowMs - checkedAtMs <= input.maxAgeMs;
|
|
185
|
+
|
|
186
|
+
if (freshSuccessfulObservation) {
|
|
187
|
+
return {
|
|
188
|
+
status: "available",
|
|
189
|
+
selectable: true,
|
|
190
|
+
reason: null,
|
|
191
|
+
checkedAt,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return {
|
|
196
|
+
status: "unavailable",
|
|
197
|
+
selectable: false,
|
|
198
|
+
reason:
|
|
199
|
+
input.observation?.status === "unavailable"
|
|
200
|
+
? (input.observation.reason ?? "provider_unhealthy")
|
|
201
|
+
: "provider_unhealthy",
|
|
202
|
+
checkedAt,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function availabilityFor(input: {
|
|
207
|
+
model: ConfiguredModel;
|
|
208
|
+
credentialReadiness: ModelCredentialReadinessV1;
|
|
209
|
+
policy: WorkspaceModelPolicyContract | null;
|
|
210
|
+
observation?: ModelAvailabilityObservation | undefined;
|
|
211
|
+
nowMs: number;
|
|
212
|
+
maxAgeMs: number;
|
|
213
|
+
}): ModelAvailabilityV1 {
|
|
214
|
+
if (!modelDefinitionRunnable(input.model)) {
|
|
215
|
+
return {
|
|
216
|
+
status: "unavailable",
|
|
217
|
+
selectable: false,
|
|
218
|
+
reason: "unsupported",
|
|
219
|
+
checkedAt: null,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
if (input.credentialReadiness.status !== "ready") {
|
|
223
|
+
return {
|
|
224
|
+
status: "unavailable",
|
|
225
|
+
selectable: false,
|
|
226
|
+
reason:
|
|
227
|
+
input.credentialReadiness.reason === "missing_credential"
|
|
228
|
+
? "missing_credential"
|
|
229
|
+
: input.credentialReadiness.reason === "needs_reauth"
|
|
230
|
+
? "needs_reauth"
|
|
231
|
+
: "credential_not_ready",
|
|
232
|
+
checkedAt: input.credentialReadiness.checkedAt,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
if (
|
|
236
|
+
!evaluateWorkspaceModelPolicy(input.policy, {
|
|
237
|
+
providerId: input.model.providerId,
|
|
238
|
+
modelId: input.model.id,
|
|
239
|
+
}).allowed
|
|
240
|
+
) {
|
|
241
|
+
return {
|
|
242
|
+
status: "unavailable",
|
|
243
|
+
selectable: false,
|
|
244
|
+
reason: "policy_blocked",
|
|
245
|
+
checkedAt: null,
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
if (isXaiGrokModel(input.model)) {
|
|
249
|
+
return xaiGrokAvailabilityFor({
|
|
250
|
+
observation: input.observation,
|
|
251
|
+
nowMs: input.nowMs,
|
|
252
|
+
maxAgeMs: input.maxAgeMs,
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
if (!input.observation) {
|
|
256
|
+
// Credential readiness and policy are known-good, but no current
|
|
257
|
+
// provider-health observation is available. Unknown is intentionally
|
|
258
|
+
// selectable; the execution boundary rechecks all authoritative gates.
|
|
259
|
+
return { status: "unknown", selectable: true, reason: null, checkedAt: null };
|
|
260
|
+
}
|
|
261
|
+
if (input.observation.status === "unavailable") {
|
|
262
|
+
return {
|
|
263
|
+
status: "unavailable",
|
|
264
|
+
selectable: false,
|
|
265
|
+
reason: input.observation.reason ?? "provider_unhealthy",
|
|
266
|
+
checkedAt: input.observation.checkedAt,
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
return {
|
|
270
|
+
status: input.observation.status,
|
|
271
|
+
selectable: true,
|
|
272
|
+
reason: null,
|
|
273
|
+
checkedAt: input.observation.checkedAt,
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Authenticated workspace catalog. Codex definitions are included only when
|
|
279
|
+
* the deployment enables that connection type; concrete readiness is supplied
|
|
280
|
+
* by the existing metadata-only DB seam. API-key presence establishes only
|
|
281
|
+
* local configuration readiness. Federated/token credentials require a fresh,
|
|
282
|
+
* successful typed resolver observation; credential health and provider health
|
|
283
|
+
* are separate inputs and neither is fabricated.
|
|
284
|
+
*/
|
|
285
|
+
export function buildWorkspaceModelCatalog(input: {
|
|
286
|
+
settings: Settings;
|
|
287
|
+
policy: WorkspaceModelPolicyContract | null;
|
|
288
|
+
codexSubscriptionActive: boolean;
|
|
289
|
+
credentialReadinessObservations?:
|
|
290
|
+
| Readonly<Record<string, ModelCredentialReadinessObservation>>
|
|
291
|
+
| undefined;
|
|
292
|
+
observations?: Readonly<Record<string, ModelAvailabilityObservation>> | undefined;
|
|
293
|
+
now?: Date | undefined;
|
|
294
|
+
credentialReadinessMaxAgeMs?: number | undefined;
|
|
295
|
+
}): WorkspaceModelCatalogResponseType {
|
|
296
|
+
const catalogSettings = input.settings.codexSubscriptionEnabled
|
|
297
|
+
? withCodexCatalogProvider(input.settings)
|
|
298
|
+
: input.settings;
|
|
299
|
+
const providers = new Map(
|
|
300
|
+
configuredProviders(catalogSettings).map((provider) => [provider.id, provider]),
|
|
301
|
+
);
|
|
302
|
+
const requestedNowMs = input.now?.getTime();
|
|
303
|
+
const nowMs =
|
|
304
|
+
typeof requestedNowMs === "number" && Number.isFinite(requestedNowMs)
|
|
305
|
+
? requestedNowMs
|
|
306
|
+
: Date.now();
|
|
307
|
+
const maxAgeMs =
|
|
308
|
+
typeof input.credentialReadinessMaxAgeMs === "number" &&
|
|
309
|
+
Number.isFinite(input.credentialReadinessMaxAgeMs) &&
|
|
310
|
+
input.credentialReadinessMaxAgeMs >= 0
|
|
311
|
+
? input.credentialReadinessMaxAgeMs
|
|
312
|
+
: MODEL_CREDENTIAL_READINESS_OBSERVATION_MAX_AGE_MS;
|
|
313
|
+
const models = configuredModels(catalogSettings).map((model) => {
|
|
314
|
+
const provider = providers.get(model.providerId);
|
|
315
|
+
const credentialReadiness = credentialReadinessFor({
|
|
316
|
+
model,
|
|
317
|
+
provider,
|
|
318
|
+
codexSubscriptionActive: input.codexSubscriptionActive,
|
|
319
|
+
observation: input.credentialReadinessObservations?.[model.definitionVersion],
|
|
320
|
+
nowMs,
|
|
321
|
+
maxAgeMs,
|
|
322
|
+
});
|
|
323
|
+
return {
|
|
324
|
+
...projectClientModel(model),
|
|
325
|
+
credentialReadiness,
|
|
326
|
+
availability: availabilityFor({
|
|
327
|
+
model,
|
|
328
|
+
credentialReadiness,
|
|
329
|
+
policy: input.policy,
|
|
330
|
+
observation: input.observations?.[model.definitionVersion],
|
|
331
|
+
nowMs,
|
|
332
|
+
maxAgeMs,
|
|
333
|
+
}),
|
|
334
|
+
};
|
|
335
|
+
});
|
|
336
|
+
return WorkspaceModelCatalogResponse.parse({ models });
|
|
337
|
+
}
|