@opengeni/api-router 0.2.1 → 0.3.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-XSYUDIX3.js → chunk-SVBM6RM6.js} +77 -26
- package/dist/chunk-SVBM6RM6.js.map +1 -0
- package/dist/index.js +1 -1
- package/package.json +13 -13
- package/src/mcp/server.ts +9 -1
- package/src/routes/sessions.ts +83 -31
- package/src/sandbox/viewer.ts +23 -0
- package/dist/chunk-XSYUDIX3.js.map +0 -1
package/dist/app.js
CHANGED
|
@@ -267,6 +267,7 @@ import { requireLimit as requireLimit8 } from "@opengeni/core";
|
|
|
267
267
|
// src/mcp/server.ts
|
|
268
268
|
import {
|
|
269
269
|
CreateScheduledTaskRequest,
|
|
270
|
+
SessionMcpCredentialUpdateInput,
|
|
270
271
|
WorkspaceEnvironmentVariableName,
|
|
271
272
|
UpdateScheduledTaskRequest
|
|
272
273
|
} from "@opengeni/contracts";
|
|
@@ -931,6 +932,9 @@ function registerWorkspaceOrchestrationTools(server, deps, grant, can, json) {
|
|
|
931
932
|
goal: z4.unknown().optional(),
|
|
932
933
|
resources: z4.array(z4.unknown()).optional(),
|
|
933
934
|
tools: z4.array(z4.unknown()).optional(),
|
|
935
|
+
// Per-session third-party MCP servers. Credential header values are
|
|
936
|
+
// accepted only at create and never appear in responses/events.
|
|
937
|
+
mcpServers: z4.array(z4.unknown()).optional(),
|
|
934
938
|
environmentId: z4.string().uuid().optional(),
|
|
935
939
|
model: z4.string().min(1).optional(),
|
|
936
940
|
reasoningEffort: z4.string().optional(),
|
|
@@ -989,12 +993,16 @@ function registerWorkspaceOrchestrationTools(server, deps, grant, can, json) {
|
|
|
989
993
|
description: "Post a user message into an existing session; the session queues a turn and resumes if idle.",
|
|
990
994
|
inputSchema: {
|
|
991
995
|
sessionId: z4.string().uuid(),
|
|
992
|
-
text: z4.string().min(1)
|
|
996
|
+
text: z4.string().min(1),
|
|
997
|
+
// Header-value rotation only. URL/name/tool settings are immutable
|
|
998
|
+
// after create; core enforces mcp_servers:attach on this field.
|
|
999
|
+
mcpCredentialUpdates: z4.array(z4.unknown()).optional()
|
|
993
1000
|
}
|
|
994
|
-
}, async ({ sessionId, text }) => {
|
|
1001
|
+
}, async ({ sessionId, text, mcpCredentialUpdates }) => {
|
|
995
1002
|
const { accepted, turn } = await acceptSessionUserMessage(deps, grant, grant.workspaceId, sessionId, {
|
|
996
1003
|
text,
|
|
997
|
-
toolsProvided: false
|
|
1004
|
+
toolsProvided: false,
|
|
1005
|
+
mcpCredentialUpdates: (mcpCredentialUpdates ?? []).map((update) => SessionMcpCredentialUpdateInput.parse(update))
|
|
998
1006
|
});
|
|
999
1007
|
return json({ event: accepted, turnId: turn.id });
|
|
1000
1008
|
});
|
|
@@ -4257,7 +4265,7 @@ import {
|
|
|
4257
4265
|
updatePtySessionActivity,
|
|
4258
4266
|
updateQueuedSessionTurn
|
|
4259
4267
|
} from "@opengeni/db";
|
|
4260
|
-
import { appendAndPublishEvents as appendAndPublishEvents4 } from "@opengeni/events";
|
|
4268
|
+
import { appendAndPublishEvents as appendAndPublishEvents4, coalesceSessionEventDeltas } from "@opengeni/events";
|
|
4261
4269
|
|
|
4262
4270
|
// src/sandbox/channel-a.ts
|
|
4263
4271
|
import { applyGitAuthPointerEnvironment, hasGitHubRepositorySelection, stableSandboxEnvironmentForRun } from "@opengeni/config";
|
|
@@ -4579,6 +4587,12 @@ function viewerHeartbeatIntervalMs(settings) {
|
|
|
4579
4587
|
async function dropEstablishedHandle2(established) {
|
|
4580
4588
|
void established;
|
|
4581
4589
|
}
|
|
4590
|
+
function resolveActiveDesktopTransport(selfhostedActive, interactive) {
|
|
4591
|
+
if (selfhostedActive) {
|
|
4592
|
+
return { transport: "relay-frames", client: "frames", mode: "read-only" };
|
|
4593
|
+
}
|
|
4594
|
+
return { transport: "vnc-ws", client: "novnc", mode: interactive ? "interactive" : "read-only" };
|
|
4595
|
+
}
|
|
4582
4596
|
async function mintDesktopStream(services, input) {
|
|
4583
4597
|
const { db, settings, bus } = services;
|
|
4584
4598
|
const { accountId, workspaceId, session } = input;
|
|
@@ -4883,7 +4897,7 @@ function viewerIdAsUuid(rawViewerId) {
|
|
|
4883
4897
|
}
|
|
4884
4898
|
|
|
4885
4899
|
// src/routes/sessions.ts
|
|
4886
|
-
import { settingsWithEnabledCapabilityMcpServers } from "@opengeni/core";
|
|
4900
|
+
import { settingsWithEnabledCapabilityMcpServers, settingsWithSessionMcpServerMetadata } from "@opengeni/core";
|
|
4887
4901
|
import {
|
|
4888
4902
|
normalizeResources,
|
|
4889
4903
|
validateFileResources,
|
|
@@ -5138,9 +5152,16 @@ function registerSessionRoutes(app, deps) {
|
|
|
5138
5152
|
await requireAccessGrant12(c, deps, workspaceId, "sessions:read");
|
|
5139
5153
|
const sessionId = c.req.param("sessionId");
|
|
5140
5154
|
await assertSessionExists(db, workspaceId, sessionId);
|
|
5141
|
-
const after =
|
|
5142
|
-
const
|
|
5143
|
-
|
|
5155
|
+
const after = eventSequence(c.req.query("after"), 0);
|
|
5156
|
+
const before = optionalEventSequence(c.req.query("before"));
|
|
5157
|
+
const compact = compactEvents(c.req.query("compact"));
|
|
5158
|
+
const limit = eventListLimit(c.req.query("limit"), compact ? 5e3 : 2e3);
|
|
5159
|
+
const events = await listSessionEvents3(db, workspaceId, sessionId, {
|
|
5160
|
+
after,
|
|
5161
|
+
...before !== void 0 ? { before } : {},
|
|
5162
|
+
limit
|
|
5163
|
+
});
|
|
5164
|
+
return c.json(compact ? coalesceSessionEventDeltas(events) : events);
|
|
5144
5165
|
});
|
|
5145
5166
|
app.get("/v1/workspaces/:workspaceId/sessions/:sessionId/events/stream", async (c) => {
|
|
5146
5167
|
const workspaceId = c.req.param("workspaceId");
|
|
@@ -5166,14 +5187,17 @@ function registerSessionRoutes(app, deps) {
|
|
|
5166
5187
|
const existing = await requireQueuedTurnForApi(db, workspaceId, sessionId, turnId);
|
|
5167
5188
|
const payload = UpdateSessionTurnRequest.parse(await c.req.json());
|
|
5168
5189
|
assertConfiguredModel(settings, payload.model);
|
|
5169
|
-
const
|
|
5190
|
+
const session = await requireSession2(db, workspaceId, sessionId);
|
|
5191
|
+
const runtimeSettings = settingsWithSessionMcpServerMetadata(
|
|
5192
|
+
await settingsWithEnabledCapabilityMcpServers(db, workspaceId, settings),
|
|
5193
|
+
session.mcpServers
|
|
5194
|
+
);
|
|
5170
5195
|
const resources = payload.resources !== void 0 ? normalizeResources(payload.resources) : existing.resources;
|
|
5171
5196
|
const tools = payload.tools !== void 0 ? validateToolRefs(payload.tools, runtimeSettings) : existing.tools;
|
|
5172
5197
|
if (resources.some((resource) => resource.kind === "file") && !objectStorage) {
|
|
5173
5198
|
throw new HTTPException15(503, { message: "object storage is not configured" });
|
|
5174
5199
|
}
|
|
5175
5200
|
await validateFileResources(db, workspaceId, resources);
|
|
5176
|
-
const session = await requireSession2(db, workspaceId, sessionId);
|
|
5177
5201
|
await validateGitHubRepositorySelection(db, workspaceId, [...session.resources, ...resources]);
|
|
5178
5202
|
const turn = await updateQueuedSessionTurn(db, workspaceId, turnId, {
|
|
5179
5203
|
...payload.prompt !== void 0 ? { prompt: payload.prompt.trim() } : {},
|
|
@@ -5234,6 +5258,7 @@ function registerSessionRoutes(app, deps) {
|
|
|
5234
5258
|
toolsProvided: userMessagePayloadHasOwnProperty(rawEvent, "tools"),
|
|
5235
5259
|
model: event.payload.model ?? null,
|
|
5236
5260
|
reasoningEffort: event.payload.reasoningEffort ?? null,
|
|
5261
|
+
mcpCredentialUpdates: event.payload.mcpCredentialUpdates ?? [],
|
|
5237
5262
|
...event.clientEventId ? { clientEventId: event.clientEventId } : {}
|
|
5238
5263
|
});
|
|
5239
5264
|
return c.json(accepted2, 202);
|
|
@@ -5361,19 +5386,13 @@ function registerSessionRoutes(app, deps) {
|
|
|
5361
5386
|
} : {}
|
|
5362
5387
|
});
|
|
5363
5388
|
let responseCapabilities = capabilities;
|
|
5364
|
-
if (
|
|
5365
|
-
const activeSandbox = await getSandbox2(db, workspaceId, session.activeSandboxId);
|
|
5366
|
-
|
|
5367
|
-
|
|
5368
|
-
|
|
5369
|
-
|
|
5370
|
-
|
|
5371
|
-
transport: "relay-frames",
|
|
5372
|
-
client: "frames",
|
|
5373
|
-
mode: "read-only"
|
|
5374
|
-
}
|
|
5375
|
-
};
|
|
5376
|
-
}
|
|
5389
|
+
if (capabilities.DesktopStream.transport !== null) {
|
|
5390
|
+
const activeSandbox = session.activeSandboxId ? await getSandbox2(db, workspaceId, session.activeSandboxId) : null;
|
|
5391
|
+
const wire = resolveActiveDesktopTransport(activeSandbox?.kind === "selfhosted", settings.sandboxDesktopInteractive !== false);
|
|
5392
|
+
responseCapabilities = {
|
|
5393
|
+
...capabilities,
|
|
5394
|
+
DesktopStream: { ...capabilities.DesktopStream, ...wire }
|
|
5395
|
+
};
|
|
5377
5396
|
}
|
|
5378
5397
|
return c.json(responseCapabilities);
|
|
5379
5398
|
});
|
|
@@ -5497,8 +5516,13 @@ function registerSessionRoutes(app, deps) {
|
|
|
5497
5516
|
streamToken: stream?.token ?? null,
|
|
5498
5517
|
streamExpiresAt: stream?.expiresAt ?? null,
|
|
5499
5518
|
resolution: stream?.resolution ?? null,
|
|
5500
|
-
|
|
5501
|
-
|
|
5519
|
+
// Transport MUST match where the pixels were minted: a selfhosted-active box
|
|
5520
|
+
// serves the RELAY framebuffer (relay-frames/frames), a Modal box serves noVNC
|
|
5521
|
+
// (vnc-ws/novnc). Hardcoding vnc-ws here handed a machine's relay URL to the
|
|
5522
|
+
// noVNC renderer (and vice-versa on the swap-away case) → "closed before it
|
|
5523
|
+
// opened". Key off the SAME selfhostedActive the mint routed on.
|
|
5524
|
+
transport: stream ? selfhostedActive ? "relay-frames" : "vnc-ws" : null,
|
|
5525
|
+
client: stream ? selfhostedActive ? "frames" : "novnc" : null,
|
|
5502
5526
|
// The REAL PTY terminal address (pty-ws), null when degraded.
|
|
5503
5527
|
terminalUrl: terminal?.url ?? null,
|
|
5504
5528
|
terminalToken: terminal?.token ?? null,
|
|
@@ -5727,6 +5751,33 @@ function registerSessionRoutes(app, deps) {
|
|
|
5727
5751
|
return c.body(null, 204);
|
|
5728
5752
|
});
|
|
5729
5753
|
}
|
|
5754
|
+
function eventListLimit(raw, max = 2e3) {
|
|
5755
|
+
const limit = Number(raw ?? 500);
|
|
5756
|
+
if (!Number.isFinite(limit)) {
|
|
5757
|
+
return 500;
|
|
5758
|
+
}
|
|
5759
|
+
return Math.min(max, Math.max(1, Math.floor(limit)));
|
|
5760
|
+
}
|
|
5761
|
+
function compactEvents(raw) {
|
|
5762
|
+
return raw === "1" || raw === "true";
|
|
5763
|
+
}
|
|
5764
|
+
function eventSequence(raw, fallback) {
|
|
5765
|
+
const sequence = Number(raw ?? fallback);
|
|
5766
|
+
if (!Number.isFinite(sequence)) {
|
|
5767
|
+
return fallback;
|
|
5768
|
+
}
|
|
5769
|
+
return Math.floor(sequence);
|
|
5770
|
+
}
|
|
5771
|
+
function optionalEventSequence(raw) {
|
|
5772
|
+
if (raw === void 0) {
|
|
5773
|
+
return void 0;
|
|
5774
|
+
}
|
|
5775
|
+
const sequence = Number(raw);
|
|
5776
|
+
if (!Number.isFinite(sequence)) {
|
|
5777
|
+
return void 0;
|
|
5778
|
+
}
|
|
5779
|
+
return Math.floor(sequence);
|
|
5780
|
+
}
|
|
5730
5781
|
function hasOwnProperty(value, key) {
|
|
5731
5782
|
return Boolean(value && typeof value === "object" && Object.prototype.hasOwnProperty.call(value, key));
|
|
5732
5783
|
}
|
|
@@ -6328,4 +6379,4 @@ export {
|
|
|
6328
6379
|
withDefaultEnabledCapabilityMcpTools,
|
|
6329
6380
|
workflowIdForSession3 as workflowIdForSession
|
|
6330
6381
|
};
|
|
6331
|
-
//# sourceMappingURL=chunk-
|
|
6382
|
+
//# sourceMappingURL=chunk-SVBM6RM6.js.map
|