@opengeni/api-router 0.15.4 → 0.15.6
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-ICRZC3JH.js → chunk-MN7BW3UV.js} +21 -58
- package/dist/{chunk-ICRZC3JH.js.map → chunk-MN7BW3UV.js.map} +1 -1
- package/dist/index.js +1 -1
- package/dist/sandbox/channel-a.d.ts +2 -0
- package/package.json +7 -7
- package/src/routes/sessions.ts +16 -35
- package/src/sandbox/channel-a.ts +7 -2
package/dist/index.js
CHANGED
|
@@ -2,11 +2,13 @@ import { type Settings } from "@opengeni/config";
|
|
|
2
2
|
import type { Session } from "@opengeni/contracts";
|
|
3
3
|
import { type Database, type LeaseSnapshot } from "@opengeni/db";
|
|
4
4
|
import { type EventBus } from "@opengeni/events";
|
|
5
|
+
import { type Observability } from "@opengeni/observability";
|
|
5
6
|
import { SandboxChannelAService, type RoutingSandboxSession } from "@opengeni/runtime/sandbox";
|
|
6
7
|
export type ChannelAServices = {
|
|
7
8
|
db: Database;
|
|
8
9
|
settings: Settings;
|
|
9
10
|
bus: EventBus;
|
|
11
|
+
observability?: Observability | undefined;
|
|
10
12
|
};
|
|
11
13
|
export type ChannelAContext = {
|
|
12
14
|
accountId: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opengeni/api-router",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.6",
|
|
4
4
|
"description": "OpenGeni HTTP surface: the Hono adapter/router (createApp), routes, MCP HTTP transport, and HTTP access adapters over @opengeni/core. An engine-distribution surface — its runtime closure includes engine-internal packages.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -46,14 +46,14 @@
|
|
|
46
46
|
"@opengeni/codex": "^0.2.9",
|
|
47
47
|
"@opengeni/config": "^0.9.1",
|
|
48
48
|
"@opengeni/contracts": "^0.28.1",
|
|
49
|
-
"@opengeni/core": "^0.16.
|
|
50
|
-
"@opengeni/db": "^0.
|
|
51
|
-
"@opengeni/documents": "^0.2.
|
|
52
|
-
"@opengeni/events": "^0.3.
|
|
49
|
+
"@opengeni/core": "^0.16.3",
|
|
50
|
+
"@opengeni/db": "^0.19.0",
|
|
51
|
+
"@opengeni/documents": "^0.2.64",
|
|
52
|
+
"@opengeni/events": "^0.3.55",
|
|
53
53
|
"@opengeni/github": "^0.4.13",
|
|
54
54
|
"@opengeni/network": "^0.1.1",
|
|
55
|
-
"@opengeni/observability": "^0.
|
|
56
|
-
"@opengeni/runtime": "^0.
|
|
55
|
+
"@opengeni/observability": "^0.4.0",
|
|
56
|
+
"@opengeni/runtime": "^0.16.0",
|
|
57
57
|
"@opengeni/storage": "^0.2.50",
|
|
58
58
|
"@temporalio/client": "^1.17.0",
|
|
59
59
|
"better-auth": "^1.6.14",
|
package/src/routes/sessions.ts
CHANGED
|
@@ -179,6 +179,7 @@ type SessionRouteDeps = ApiRouteDeps & Pick<ViewerServices, "establishSandboxSes
|
|
|
179
179
|
|
|
180
180
|
export function registerSessionRoutes(app: Hono, deps: SessionRouteDeps): void {
|
|
181
181
|
const { settings, db, bus, workflowClient, objectStorage } = deps;
|
|
182
|
+
const channelAServices = { db, settings, bus, observability: deps.observability };
|
|
182
183
|
const workspaceCaptureManifestCache = new WorkspaceCaptureManifestCache();
|
|
183
184
|
const ptyIdentity = (pty: SandboxOpenPtySessionRow): SandboxPtyProcessIdentity => ({
|
|
184
185
|
leaseId: pty.leaseId,
|
|
@@ -2047,54 +2048,42 @@ export function registerSessionRoutes(app: Hono, deps: SessionRouteDeps): void {
|
|
|
2047
2048
|
app.post("/v1/workspaces/:workspaceId/sessions/:sessionId/fs/list", async (c) => {
|
|
2048
2049
|
const ctx = await channelAPreamble(c, "files:read");
|
|
2049
2050
|
const req = await parseChannelABody(c, FsListRequest);
|
|
2050
|
-
const out = await withChannelA(
|
|
2051
|
-
service.fsList(req),
|
|
2052
|
-
);
|
|
2051
|
+
const out = await withChannelA(channelAServices, ctx, ({ service }) => service.fsList(req));
|
|
2053
2052
|
return c.json(out);
|
|
2054
2053
|
});
|
|
2055
2054
|
|
|
2056
2055
|
app.post("/v1/workspaces/:workspaceId/sessions/:sessionId/fs/read", async (c) => {
|
|
2057
2056
|
const ctx = await channelAPreamble(c, "files:read");
|
|
2058
2057
|
const req = await parseChannelABody(c, FsReadRequest);
|
|
2059
|
-
const out = await withChannelA(
|
|
2060
|
-
service.fsRead(req),
|
|
2061
|
-
);
|
|
2058
|
+
const out = await withChannelA(channelAServices, ctx, ({ service }) => service.fsRead(req));
|
|
2062
2059
|
return c.json(out);
|
|
2063
2060
|
});
|
|
2064
2061
|
|
|
2065
2062
|
app.post("/v1/workspaces/:workspaceId/sessions/:sessionId/fs/write", async (c) => {
|
|
2066
2063
|
const ctx = await channelAPreamble(c, "files:write");
|
|
2067
2064
|
const req = await parseChannelABody(c, FsWriteRequest);
|
|
2068
|
-
const out = await withChannelA(
|
|
2069
|
-
service.fsWrite(req),
|
|
2070
|
-
);
|
|
2065
|
+
const out = await withChannelA(channelAServices, ctx, ({ service }) => service.fsWrite(req));
|
|
2071
2066
|
return c.json(out);
|
|
2072
2067
|
});
|
|
2073
2068
|
|
|
2074
2069
|
app.post("/v1/workspaces/:workspaceId/sessions/:sessionId/fs/delete", async (c) => {
|
|
2075
2070
|
const ctx = await channelAPreamble(c, "files:write");
|
|
2076
2071
|
const req = await parseChannelABody(c, FsDeleteRequest);
|
|
2077
|
-
const out = await withChannelA(
|
|
2078
|
-
service.fsDelete(req),
|
|
2079
|
-
);
|
|
2072
|
+
const out = await withChannelA(channelAServices, ctx, ({ service }) => service.fsDelete(req));
|
|
2080
2073
|
return c.json(out);
|
|
2081
2074
|
});
|
|
2082
2075
|
|
|
2083
2076
|
app.post("/v1/workspaces/:workspaceId/sessions/:sessionId/fs/move", async (c) => {
|
|
2084
2077
|
const ctx = await channelAPreamble(c, "files:write");
|
|
2085
2078
|
const req = await parseChannelABody(c, FsMoveRequest);
|
|
2086
|
-
const out = await withChannelA(
|
|
2087
|
-
service.fsMove(req),
|
|
2088
|
-
);
|
|
2079
|
+
const out = await withChannelA(channelAServices, ctx, ({ service }) => service.fsMove(req));
|
|
2089
2080
|
return c.json(out);
|
|
2090
2081
|
});
|
|
2091
2082
|
|
|
2092
2083
|
app.post("/v1/workspaces/:workspaceId/sessions/:sessionId/fs/mkdir", async (c) => {
|
|
2093
2084
|
const ctx = await channelAPreamble(c, "files:write");
|
|
2094
2085
|
const req = await parseChannelABody(c, FsMkdirRequest);
|
|
2095
|
-
const out = await withChannelA(
|
|
2096
|
-
service.fsMkdir(req),
|
|
2097
|
-
);
|
|
2086
|
+
const out = await withChannelA(channelAServices, ctx, ({ service }) => service.fsMkdir(req));
|
|
2098
2087
|
return c.json(out);
|
|
2099
2088
|
});
|
|
2100
2089
|
|
|
@@ -2102,36 +2091,28 @@ export function registerSessionRoutes(app: Hono, deps: SessionRouteDeps): void {
|
|
|
2102
2091
|
app.post("/v1/workspaces/:workspaceId/sessions/:sessionId/git/status", async (c) => {
|
|
2103
2092
|
const ctx = await channelAPreamble(c, "files:read");
|
|
2104
2093
|
const req = await parseChannelABody(c, GitStatusRequest);
|
|
2105
|
-
const out = await withChannelA(
|
|
2106
|
-
service.gitStatus(req),
|
|
2107
|
-
);
|
|
2094
|
+
const out = await withChannelA(channelAServices, ctx, ({ service }) => service.gitStatus(req));
|
|
2108
2095
|
return c.json(out);
|
|
2109
2096
|
});
|
|
2110
2097
|
|
|
2111
2098
|
app.post("/v1/workspaces/:workspaceId/sessions/:sessionId/git/diff", async (c) => {
|
|
2112
2099
|
const ctx = await channelAPreamble(c, "files:read");
|
|
2113
2100
|
const req = await parseChannelABody(c, GitDiffRequest);
|
|
2114
|
-
const out = await withChannelA(
|
|
2115
|
-
service.gitDiff(req),
|
|
2116
|
-
);
|
|
2101
|
+
const out = await withChannelA(channelAServices, ctx, ({ service }) => service.gitDiff(req));
|
|
2117
2102
|
return c.json(out);
|
|
2118
2103
|
});
|
|
2119
2104
|
|
|
2120
2105
|
app.post("/v1/workspaces/:workspaceId/sessions/:sessionId/git/log", async (c) => {
|
|
2121
2106
|
const ctx = await channelAPreamble(c, "files:read");
|
|
2122
2107
|
const req = await parseChannelABody(c, GitLogRequest);
|
|
2123
|
-
const out = await withChannelA(
|
|
2124
|
-
service.gitLog(req),
|
|
2125
|
-
);
|
|
2108
|
+
const out = await withChannelA(channelAServices, ctx, ({ service }) => service.gitLog(req));
|
|
2126
2109
|
return c.json(out);
|
|
2127
2110
|
});
|
|
2128
2111
|
|
|
2129
2112
|
app.post("/v1/workspaces/:workspaceId/sessions/:sessionId/git/show", async (c) => {
|
|
2130
2113
|
const ctx = await channelAPreamble(c, "files:read");
|
|
2131
2114
|
const req = await parseChannelABody(c, GitShowRequest);
|
|
2132
|
-
const out = await withChannelA(
|
|
2133
|
-
service.gitShow(req),
|
|
2134
|
-
);
|
|
2115
|
+
const out = await withChannelA(channelAServices, ctx, ({ service }) => service.gitShow(req));
|
|
2135
2116
|
return c.json(out);
|
|
2136
2117
|
});
|
|
2137
2118
|
|
|
@@ -2196,7 +2177,7 @@ export function registerSessionRoutes(app: Hono, deps: SessionRouteDeps): void {
|
|
|
2196
2177
|
app.post("/v1/workspaces/:workspaceId/sessions/:sessionId/terminal/exec", async (c) => {
|
|
2197
2178
|
const ctx = await channelAPreamble(c, "terminal:attach");
|
|
2198
2179
|
const req = await parseChannelABody(c, TerminalExecRequest);
|
|
2199
|
-
const out = await withChannelA(
|
|
2180
|
+
const out = await withChannelA(channelAServices, ctx, ({ service }) =>
|
|
2200
2181
|
service.terminalExec(req),
|
|
2201
2182
|
);
|
|
2202
2183
|
return c.json(out);
|
|
@@ -2213,7 +2194,7 @@ export function registerSessionRoutes(app: Hono, deps: SessionRouteDeps): void {
|
|
|
2213
2194
|
});
|
|
2214
2195
|
}
|
|
2215
2196
|
const ptyId = crypto.randomUUID();
|
|
2216
|
-
const out = await withChannelA(
|
|
2197
|
+
const out = await withChannelA(channelAServices, ctx, async (handle) => {
|
|
2217
2198
|
if (!handle.lease) {
|
|
2218
2199
|
throw new HTTPException(409, {
|
|
2219
2200
|
message: "durable interactive terminals require a session-home provider lease",
|
|
@@ -2324,7 +2305,7 @@ export function registerSessionRoutes(app: Hono, deps: SessionRouteDeps): void {
|
|
|
2324
2305
|
throw new HTTPException(404, { message: "pty not found or closed" });
|
|
2325
2306
|
}
|
|
2326
2307
|
let seq = 1;
|
|
2327
|
-
await withChannelA(
|
|
2308
|
+
await withChannelA(channelAServices, ctx, async (handle) => {
|
|
2328
2309
|
await adoptPtyProcess(ctx, handle, pty);
|
|
2329
2310
|
let output: string;
|
|
2330
2311
|
try {
|
|
@@ -2387,7 +2368,7 @@ export function registerSessionRoutes(app: Hono, deps: SessionRouteDeps): void {
|
|
|
2387
2368
|
if (!pty) {
|
|
2388
2369
|
throw new HTTPException(404, { message: "pty not found or closed" });
|
|
2389
2370
|
}
|
|
2390
|
-
await withChannelA(
|
|
2371
|
+
await withChannelA(channelAServices, ctx, async (handle) => {
|
|
2391
2372
|
await adoptPtyProcess(ctx, handle, pty);
|
|
2392
2373
|
await handle.service.ptyResize(req, pty.execSessionId);
|
|
2393
2374
|
const updated = await updatePtySessionActivity(db, {
|
|
@@ -2418,7 +2399,7 @@ export function registerSessionRoutes(app: Hono, deps: SessionRouteDeps): void {
|
|
|
2418
2399
|
});
|
|
2419
2400
|
// Idempotent: closing an already-closed/absent PTY is a 204 no-op.
|
|
2420
2401
|
if (pty) {
|
|
2421
|
-
await withChannelA(
|
|
2402
|
+
await withChannelA(channelAServices, ctx, async (handle) => {
|
|
2422
2403
|
await adoptPtyProcess(ctx, handle, pty);
|
|
2423
2404
|
await handle.service.ptyClose(req, pty.execSessionId);
|
|
2424
2405
|
const terminal = await getRetainedProcess(db, {
|
package/src/sandbox/channel-a.ts
CHANGED
|
@@ -40,6 +40,7 @@ import {
|
|
|
40
40
|
type LeaseSnapshot,
|
|
41
41
|
} from "@opengeni/db";
|
|
42
42
|
import { appendAndPublishEvents, type EventBus } from "@opengeni/events";
|
|
43
|
+
import { sandboxOperationMetricObserver, type Observability } from "@opengeni/observability";
|
|
43
44
|
import { HTTPException } from "hono/http-exception";
|
|
44
45
|
|
|
45
46
|
import {
|
|
@@ -67,6 +68,7 @@ export type ChannelAServices = {
|
|
|
67
68
|
db: Database;
|
|
68
69
|
settings: Settings;
|
|
69
70
|
bus: EventBus;
|
|
71
|
+
observability?: Observability | undefined;
|
|
70
72
|
};
|
|
71
73
|
|
|
72
74
|
export type ChannelAContext = {
|
|
@@ -103,6 +105,9 @@ export async function withChannelA<T>(
|
|
|
103
105
|
fn: (handle: ChannelAHandle) => Promise<T>,
|
|
104
106
|
): Promise<T> {
|
|
105
107
|
const { db, settings, bus } = services;
|
|
108
|
+
const onSandboxOperation = services.observability
|
|
109
|
+
? sandboxOperationMetricObserver(services.observability)
|
|
110
|
+
: undefined;
|
|
106
111
|
const { accountId, workspaceId, session } = ctx;
|
|
107
112
|
|
|
108
113
|
if (session.sandboxBackend === "none") {
|
|
@@ -204,7 +209,7 @@ export async function withChannelA<T>(
|
|
|
204
209
|
backendId: "selfhosted",
|
|
205
210
|
};
|
|
206
211
|
const routed = wrapChannelABoxWithRouting(
|
|
207
|
-
{ db, settings, bus },
|
|
212
|
+
{ db, settings, bus, ...(onSandboxOperation ? { onSandboxOperation } : {}) },
|
|
208
213
|
{
|
|
209
214
|
accountId,
|
|
210
215
|
workspaceId,
|
|
@@ -354,7 +359,7 @@ export async function withChannelA<T>(
|
|
|
354
359
|
// routing may be dormant, but its direct mutation admission is mandatory for
|
|
355
360
|
// every persistable provider write.
|
|
356
361
|
const routed = wrapChannelABoxWithRouting(
|
|
357
|
-
{ db, settings, bus },
|
|
362
|
+
{ db, settings, bus, ...(onSandboxOperation ? { onSandboxOperation } : {}) },
|
|
358
363
|
{
|
|
359
364
|
accountId,
|
|
360
365
|
workspaceId,
|