@opengeni/runtime 0.6.0 → 0.7.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/{chunk-BHA2SU3H.js → chunk-RYDCVTMW.js} +1959 -208
- package/dist/chunk-RYDCVTMW.js.map +1 -0
- package/dist/{index-otZkoLYq.d.ts → index-DIiiwQY2.d.ts} +693 -55
- package/dist/index.d.ts +293 -121
- package/dist/index.js +1819 -786
- package/dist/index.js.map +1 -1
- package/dist/sandbox/index.d.ts +1 -1
- package/dist/sandbox/index.js +79 -3
- package/package.json +21 -20
- package/src/codex-tool-search.ts +185 -31
- package/src/context-compaction.ts +78 -243
- package/src/history-sanitizer.ts +172 -20
- package/src/index.ts +2573 -897
- package/src/metrics.ts +28 -2
- package/src/sandbox/capabilities.ts +12 -3
- package/src/sandbox/channel-a.ts +420 -72
- package/src/sandbox/display-stack.ts +18 -4
- package/src/sandbox/errors.ts +13 -0
- package/src/sandbox/index.ts +398 -112
- package/src/sandbox/op-correlation.ts +70 -0
- package/src/sandbox/providers/index.ts +9 -3
- package/src/sandbox/providers/modal.ts +99 -24
- package/src/sandbox/providers/selfhosted.ts +1 -4
- package/src/sandbox/recording.ts +146 -90
- package/src/sandbox/routing/backend-resolver.ts +144 -10
- package/src/sandbox/routing/routing-session.ts +129 -25
- package/src/sandbox/select.ts +60 -8
- package/src/sandbox/selfhosted/capabilities.ts +59 -25
- package/src/sandbox/selfhosted/control-rpc.ts +160 -28
- package/src/sandbox/selfhosted/fault-rendering.ts +226 -0
- package/src/sandbox/selfhosted/op-observer.ts +54 -0
- package/src/sandbox/selfhosted/op-stream.ts +801 -0
- package/src/sandbox/selfhosted/op-testing.ts +468 -0
- package/src/sandbox/selfhosted/op-transport.ts +151 -0
- package/src/sandbox/selfhosted/retry-policy.ts +177 -0
- package/src/sandbox/selfhosted/session.ts +498 -55
- package/src/sandbox/selfhosted/testing.ts +60 -19
- package/src/sandbox/stream-port.ts +17 -5
- package/src/sandbox/stream-token.ts +8 -2
- package/src/sandbox/terminal-server.ts +18 -4
- package/src/sandbox-computer.ts +332 -70
- package/src/usage-telemetry.ts +73 -0
- package/dist/chunk-BHA2SU3H.js.map +0 -1
- package/src/image-history.ts +0 -149
|
@@ -72,7 +72,10 @@ export class MockAgentResponder implements ControlRpc {
|
|
|
72
72
|
this.hostname = opts.hostname ?? "mock-machine";
|
|
73
73
|
this.execHandler = opts.exec ?? ((req) => defaultEcho(req, this.hostname));
|
|
74
74
|
for (const [path, content] of Object.entries(opts.files ?? {})) {
|
|
75
|
-
this.files.set(
|
|
75
|
+
this.files.set(
|
|
76
|
+
normalize(path),
|
|
77
|
+
typeof content === "string" ? encoder.encode(content) : content,
|
|
78
|
+
);
|
|
76
79
|
}
|
|
77
80
|
}
|
|
78
81
|
|
|
@@ -87,13 +90,27 @@ export class MockAgentResponder implements ControlRpc {
|
|
|
87
90
|
return bytes ? decoder.decode(bytes) : undefined;
|
|
88
91
|
}
|
|
89
92
|
|
|
90
|
-
async request(
|
|
93
|
+
async request(
|
|
94
|
+
subject: string,
|
|
95
|
+
req: ControlRequest,
|
|
96
|
+
_opts: { timeoutMs: number },
|
|
97
|
+
): Promise<ControlResponse> {
|
|
91
98
|
this.requests.push({ subject, req });
|
|
92
99
|
if (!this.online) {
|
|
93
|
-
return errorResponse(
|
|
100
|
+
return errorResponse(
|
|
101
|
+
req.requestId,
|
|
102
|
+
ErrorCode.ERROR_CODE_AGENT_OFFLINE,
|
|
103
|
+
"the enrolled agent is offline",
|
|
104
|
+
false,
|
|
105
|
+
);
|
|
94
106
|
}
|
|
95
107
|
if (this.draining) {
|
|
96
|
-
return errorResponse(
|
|
108
|
+
return errorResponse(
|
|
109
|
+
req.requestId,
|
|
110
|
+
ErrorCode.ERROR_CODE_DRAINING,
|
|
111
|
+
"the agent is draining",
|
|
112
|
+
true,
|
|
113
|
+
);
|
|
97
114
|
}
|
|
98
115
|
const op = req.op;
|
|
99
116
|
if (!op) {
|
|
@@ -101,7 +118,10 @@ export class MockAgentResponder implements ControlRpc {
|
|
|
101
118
|
}
|
|
102
119
|
switch (op.$case) {
|
|
103
120
|
case "ping":
|
|
104
|
-
return ok(req.requestId, {
|
|
121
|
+
return ok(req.requestId, {
|
|
122
|
+
$case: "ping",
|
|
123
|
+
ping: { nonce: op.ping.nonce, agentMonotonicMs: "0" },
|
|
124
|
+
});
|
|
105
125
|
case "exec": {
|
|
106
126
|
const res = await this.execHandler(op.exec);
|
|
107
127
|
return ok(req.requestId, { $case: "exec", exec: res });
|
|
@@ -109,7 +129,12 @@ export class MockAgentResponder implements ControlRpc {
|
|
|
109
129
|
case "fsRead": {
|
|
110
130
|
const bytes = this.files.get(normalize(op.fsRead.path));
|
|
111
131
|
if (!bytes) {
|
|
112
|
-
return errorResponse(
|
|
132
|
+
return errorResponse(
|
|
133
|
+
req.requestId,
|
|
134
|
+
ErrorCode.ERROR_CODE_NOT_FOUND,
|
|
135
|
+
`no such file: ${op.fsRead.path}`,
|
|
136
|
+
false,
|
|
137
|
+
);
|
|
113
138
|
}
|
|
114
139
|
const res: FsReadResponse = { content: bytes, totalSize: String(bytes.length) };
|
|
115
140
|
return ok(req.requestId, { $case: "fsRead", fsRead: res });
|
|
@@ -147,16 +172,16 @@ export class MockAgentResponder implements ControlRpc {
|
|
|
147
172
|
const bytes = this.files.get(normalize(op.fsStat.path));
|
|
148
173
|
const res: FsStatResponse = bytes
|
|
149
174
|
? {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
175
|
+
exists: true,
|
|
176
|
+
entry: {
|
|
177
|
+
name: normalize(op.fsStat.path).split("/").pop() ?? "",
|
|
178
|
+
path: op.fsStat.path,
|
|
179
|
+
kind: FsEntryKind.FS_ENTRY_KIND_FILE,
|
|
180
|
+
size: String(bytes.length),
|
|
181
|
+
modifiedMs: "0",
|
|
182
|
+
mode: 0o644,
|
|
183
|
+
},
|
|
184
|
+
}
|
|
160
185
|
: { exists: false, entry: undefined };
|
|
161
186
|
return ok(req.requestId, { $case: "fsStat", fsStat: res });
|
|
162
187
|
}
|
|
@@ -170,7 +195,13 @@ export class MockAgentResponder implements ControlRpc {
|
|
|
170
195
|
return ok(req.requestId, {
|
|
171
196
|
$case: "desktopEnsure",
|
|
172
197
|
desktopEnsure: {
|
|
173
|
-
channel: {
|
|
198
|
+
channel: {
|
|
199
|
+
channelId: "mock-desktop",
|
|
200
|
+
workspaceId: "",
|
|
201
|
+
agentId: "",
|
|
202
|
+
kind: 1,
|
|
203
|
+
port: 6080,
|
|
204
|
+
},
|
|
174
205
|
display: { id: ":99", width: 1024, height: 768, virtual: true },
|
|
175
206
|
},
|
|
176
207
|
});
|
|
@@ -188,7 +219,12 @@ export class MockAgentResponder implements ControlRpc {
|
|
|
188
219
|
});
|
|
189
220
|
}
|
|
190
221
|
default:
|
|
191
|
-
return errorResponse(
|
|
222
|
+
return errorResponse(
|
|
223
|
+
req.requestId,
|
|
224
|
+
ErrorCode.ERROR_CODE_UNSUPPORTED,
|
|
225
|
+
`mock does not implement ${op.$case}`,
|
|
226
|
+
false,
|
|
227
|
+
);
|
|
192
228
|
}
|
|
193
229
|
}
|
|
194
230
|
}
|
|
@@ -211,7 +247,12 @@ function ok(requestId: string, result: NonNullable<ControlResponse["result"]>):
|
|
|
211
247
|
return { requestId, error: undefined, result };
|
|
212
248
|
}
|
|
213
249
|
|
|
214
|
-
function errorResponse(
|
|
250
|
+
function errorResponse(
|
|
251
|
+
requestId: string,
|
|
252
|
+
code: ErrorCode,
|
|
253
|
+
message: string,
|
|
254
|
+
retryable: boolean,
|
|
255
|
+
): ControlResponse {
|
|
215
256
|
const error: AgentError = { code, message, retryable, detail: {} };
|
|
216
257
|
return { requestId, error, result: undefined };
|
|
217
258
|
}
|
|
@@ -48,7 +48,10 @@ type PortResolvableSession = {
|
|
|
48
48
|
* `transport:null` (a value, never a crash) — a headless-only provider or a
|
|
49
49
|
* transient tunnel failure must not fail the whole handshake. */
|
|
50
50
|
export class StreamPortUnavailableError extends Error {
|
|
51
|
-
constructor(
|
|
51
|
+
constructor(
|
|
52
|
+
message: string,
|
|
53
|
+
readonly cause?: unknown,
|
|
54
|
+
) {
|
|
52
55
|
super(message);
|
|
53
56
|
this.name = "StreamPortUnavailableError";
|
|
54
57
|
}
|
|
@@ -103,7 +106,11 @@ const DEFAULT_RESOLUTION: [number, number] = [1280, 800];
|
|
|
103
106
|
* the holder + validated at the in-box websockify edge).
|
|
104
107
|
*/
|
|
105
108
|
export function buildStreamUrl(endpoint: ExposedPortEndpoint): string {
|
|
106
|
-
if (
|
|
109
|
+
if (
|
|
110
|
+
typeof endpoint.host !== "string" ||
|
|
111
|
+
endpoint.host.length === 0 ||
|
|
112
|
+
typeof endpoint.port !== "number"
|
|
113
|
+
) {
|
|
107
114
|
throw new StreamPortUnavailableError(
|
|
108
115
|
`provider returned a malformed exposed-port endpoint (host=${String(endpoint.host)}, port=${String(endpoint.port)})`,
|
|
109
116
|
);
|
|
@@ -112,13 +119,18 @@ export function buildStreamUrl(endpoint: ExposedPortEndpoint): string {
|
|
|
112
119
|
const scheme = tls ? "wss" : "ws";
|
|
113
120
|
const defaultPort = tls ? 443 : 80;
|
|
114
121
|
// Bracket a bare IPv6 host (urlForExposedPort parity).
|
|
115
|
-
const host =
|
|
122
|
+
const host =
|
|
123
|
+
endpoint.host.includes(":") && !endpoint.host.startsWith("[")
|
|
124
|
+
? `[${endpoint.host}]`
|
|
125
|
+
: endpoint.host;
|
|
116
126
|
// The path: default the root `/` (Modal/Daytona/Blaxel edge), or the
|
|
117
127
|
// provider-supplied path (the selfhosted relay's `/stream` route, M8b). Always
|
|
118
128
|
// leading-slash-normalized.
|
|
119
|
-
const rawPath =
|
|
129
|
+
const rawPath =
|
|
130
|
+
typeof endpoint.path === "string" && endpoint.path.length > 0 ? endpoint.path : "/";
|
|
120
131
|
const path = rawPath.startsWith("/") ? rawPath : `/${rawPath}`;
|
|
121
|
-
const origin =
|
|
132
|
+
const origin =
|
|
133
|
+
endpoint.port === defaultPort ? `${scheme}://${host}` : `${scheme}://${host}:${endpoint.port}`;
|
|
122
134
|
const authority = `${origin}${path}`;
|
|
123
135
|
const query = endpoint.query ?? "";
|
|
124
136
|
return query ? `${authority}?${query}` : authority;
|
|
@@ -54,7 +54,10 @@ export type MintStreamTokenInput = {
|
|
|
54
54
|
* envelope (`ogs_` prefix). The token is RECORDED against the holder row by the
|
|
55
55
|
* caller and is NEVER appended to the data-plane URL as a query param.
|
|
56
56
|
*/
|
|
57
|
-
export async function mintStreamToken(
|
|
57
|
+
export async function mintStreamToken(
|
|
58
|
+
secret: string,
|
|
59
|
+
input: MintStreamTokenInput,
|
|
60
|
+
): Promise<string> {
|
|
58
61
|
const nowSeconds = input.nowSeconds ?? Math.floor(Date.now() / 1000);
|
|
59
62
|
const ttlSeconds = input.ttlSeconds ?? STREAM_TOKEN_DEFAULT_TTL_SECONDS;
|
|
60
63
|
const payload: StreamTokenPayloadType = StreamTokenPayload.parse({
|
|
@@ -87,4 +90,7 @@ export async function verifyStreamToken(
|
|
|
87
90
|
return verifyStreamTokenEnvelope(secret, token, nowSeconds);
|
|
88
91
|
}
|
|
89
92
|
|
|
90
|
-
export {
|
|
93
|
+
export {
|
|
94
|
+
StreamTokenPayload,
|
|
95
|
+
type StreamTokenPayload as StreamTokenPayloadType,
|
|
96
|
+
} from "@opengeni/contracts";
|
|
@@ -43,7 +43,9 @@ export class TerminalServerError extends Error {
|
|
|
43
43
|
|
|
44
44
|
constructor(exitCode: number, output: string) {
|
|
45
45
|
const stage = exitCode === 14 ? "ttyd" : "unknown";
|
|
46
|
-
super(
|
|
46
|
+
super(
|
|
47
|
+
`terminal server failed at stage "${stage}" (exit ${exitCode})${output ? `:\n${output}` : ""}`,
|
|
48
|
+
);
|
|
47
49
|
this.name = "TerminalServerError";
|
|
48
50
|
this.exitCode = exitCode;
|
|
49
51
|
this.stage = stage;
|
|
@@ -70,8 +72,16 @@ type ExecResultLike = {
|
|
|
70
72
|
exitCode?: number | null;
|
|
71
73
|
};
|
|
72
74
|
type ExecCapableSession = {
|
|
73
|
-
exec?: (args: {
|
|
74
|
-
|
|
75
|
+
exec?: (args: {
|
|
76
|
+
cmd: string;
|
|
77
|
+
yieldTimeMs?: number;
|
|
78
|
+
maxOutputTokens?: number;
|
|
79
|
+
}) => Promise<ExecResultLike>;
|
|
80
|
+
execCommand?: (args: {
|
|
81
|
+
cmd: string;
|
|
82
|
+
yieldTimeMs?: number;
|
|
83
|
+
maxOutputTokens?: number;
|
|
84
|
+
}) => Promise<string>;
|
|
75
85
|
};
|
|
76
86
|
|
|
77
87
|
export type EnsureTerminalServerOptions = {
|
|
@@ -198,6 +208,10 @@ export async function tearDownTerminalServer(session: unknown): Promise<void> {
|
|
|
198
208
|
return;
|
|
199
209
|
}
|
|
200
210
|
if (typeof s?.execCommand === "function") {
|
|
201
|
-
await s.execCommand({
|
|
211
|
+
await s.execCommand({
|
|
212
|
+
cmd: "opengeni-terminal-down",
|
|
213
|
+
yieldTimeMs: 10_000,
|
|
214
|
+
maxOutputTokens: 4_000,
|
|
215
|
+
});
|
|
202
216
|
}
|
|
203
217
|
}
|