@opengeni/runtime 0.2.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-2PO56VAL.js +3478 -0
- package/dist/chunk-2PO56VAL.js.map +1 -0
- package/dist/index.d.ts +912 -0
- package/dist/index.js +3663 -0
- package/dist/index.js.map +1 -0
- package/dist/sandbox/index.d.ts +1738 -0
- package/dist/sandbox/index.js +187 -0
- package/dist/sandbox/index.js.map +1 -0
- package/package.json +49 -0
- package/src/bundled_hashicorp_terraform_skills/LICENSE +373 -0
- package/src/bundled_hashicorp_terraform_skills/README.md +18 -0
- package/src/bundled_hashicorp_terraform_skills/UPSTREAM_GIT_SHA +1 -0
- package/src/bundled_hashicorp_terraform_skills/azure-verified-modules/SKILL.md +613 -0
- package/src/bundled_hashicorp_terraform_skills/checkov/SKILL.md +43 -0
- package/src/bundled_hashicorp_terraform_skills/refactor-module/SKILL.md +538 -0
- package/src/bundled_hashicorp_terraform_skills/social-media-marketing/SKILL.md +35 -0
- package/src/bundled_hashicorp_terraform_skills/terraform-search-import/SKILL.md +372 -0
- package/src/bundled_hashicorp_terraform_skills/terraform-search-import/references/MANUAL-IMPORT.md +113 -0
- package/src/bundled_hashicorp_terraform_skills/terraform-search-import/scripts/list_resources.sh +38 -0
- package/src/bundled_hashicorp_terraform_skills/terraform-stacks/SKILL.md +480 -0
- package/src/bundled_hashicorp_terraform_skills/terraform-stacks/references/api-monitoring.md +543 -0
- package/src/bundled_hashicorp_terraform_skills/terraform-stacks/references/component-blocks.md +476 -0
- package/src/bundled_hashicorp_terraform_skills/terraform-stacks/references/deployment-blocks.md +391 -0
- package/src/bundled_hashicorp_terraform_skills/terraform-stacks/references/examples.md +1529 -0
- package/src/bundled_hashicorp_terraform_skills/terraform-stacks/references/linked-stacks.md +187 -0
- package/src/bundled_hashicorp_terraform_skills/terraform-stacks/references/troubleshooting.md +671 -0
- package/src/bundled_hashicorp_terraform_skills/terraform-style-guide/SKILL.md +353 -0
- package/src/bundled_hashicorp_terraform_skills/terraform-test/SKILL.md +451 -0
- package/src/bundled_hashicorp_terraform_skills/terraform-test/references/CI_CD.md +80 -0
- package/src/bundled_hashicorp_terraform_skills/terraform-test/references/EXAMPLES.md +314 -0
- package/src/bundled_hashicorp_terraform_skills/terraform-test/references/MOCK_PROVIDERS.md +171 -0
- package/src/codex-tool-search.ts +267 -0
- package/src/context-compaction.ts +538 -0
- package/src/history-sanitizer.ts +719 -0
- package/src/index.ts +3299 -0
- package/src/sandbox/capabilities.ts +69 -0
- package/src/sandbox/channel-a.ts +1031 -0
- package/src/sandbox/display-stack.ts +231 -0
- package/src/sandbox/errors.ts +34 -0
- package/src/sandbox/index.ts +832 -0
- package/src/sandbox/providers/blaxel.ts +35 -0
- package/src/sandbox/providers/cloudflare.ts +24 -0
- package/src/sandbox/providers/daytona.ts +34 -0
- package/src/sandbox/providers/docker.ts +17 -0
- package/src/sandbox/providers/e2b.ts +36 -0
- package/src/sandbox/providers/index.ts +107 -0
- package/src/sandbox/providers/local.ts +13 -0
- package/src/sandbox/providers/modal.ts +55 -0
- package/src/sandbox/providers/none.ts +13 -0
- package/src/sandbox/providers/runloop.ts +32 -0
- package/src/sandbox/providers/selfhosted.ts +96 -0
- package/src/sandbox/providers/types.ts +38 -0
- package/src/sandbox/providers/vercel.ts +29 -0
- package/src/sandbox/recording.ts +286 -0
- package/src/sandbox/routing/backend-resolver.ts +189 -0
- package/src/sandbox/routing/routing-session.ts +455 -0
- package/src/sandbox/select.ts +371 -0
- package/src/sandbox/selfhosted/capabilities.ts +255 -0
- package/src/sandbox/selfhosted/control-rpc.ts +351 -0
- package/src/sandbox/selfhosted/session.ts +930 -0
- package/src/sandbox/selfhosted/testing.ts +230 -0
- package/src/sandbox/stream-port.ts +185 -0
- package/src/sandbox/stream-token.ts +90 -0
- package/src/sandbox/terminal-server.ts +203 -0
- package/src/sandbox-computer.ts +835 -0
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
// @opengeni/runtime/sandbox — the REAL PTY terminal-server launcher (P5.t).
|
|
2
|
+
//
|
|
3
|
+
// The agent-loop-free home for `ensureTerminalServer`: the exec-launched,
|
|
4
|
+
// flock-idempotent procedure that brings up the ttyd PTY-over-websocket server
|
|
5
|
+
// (a live PTY-backed `bash -l` per ws client, listening on 7681) on a live,
|
|
6
|
+
// externally-owned box. It is the SYMMETRIC TWIN of ensureDisplayStack (the
|
|
7
|
+
// Channel-B pixel stack) — same exec/execCommand channel (NOT a container CMD)
|
|
8
|
+
// so it re-establishes after a snapshot rollover / box re-election, same flock
|
|
9
|
+
// idempotency so a second concurrent caller (the API viewer op + the agent turn,
|
|
10
|
+
// both racing after a rollover) serializes and no-ops when ttyd is already up.
|
|
11
|
+
//
|
|
12
|
+
// WHY A REAL PTY OVER THE TUNNEL (not the old ptyWrite-over-HTTP): the stateless
|
|
13
|
+
// HTTP plane builds a fresh provider session per call, so the in-memory live-PTY
|
|
14
|
+
// process handle (Modal's per-call activeProcesses Map) is gone by the next
|
|
15
|
+
// write ("session not found: 1"). ttyd holds the live PTY in-box and streams it
|
|
16
|
+
// over the SAME Modal raw-TLS tunnel the desktop noVNC already uses, gated by the
|
|
17
|
+
// SAME scoped stream-token mechanism (the server records the token; the boundary
|
|
18
|
+
// is the unguessable short-TTL tunnel URL + the recorded token — no in-box gate).
|
|
19
|
+
//
|
|
20
|
+
// It lives under @opengeni/runtime/sandbox so the API-direct control plane
|
|
21
|
+
// (apps/api) and the worker (apps/worker) both pull it from the same single
|
|
22
|
+
// agent-loop-free leaf.
|
|
23
|
+
|
|
24
|
+
import { TERMINAL_STREAM_PORT } from "@opengeni/contracts";
|
|
25
|
+
|
|
26
|
+
// Re-export the canonical terminal port so callers (exposeStreamPort, the API
|
|
27
|
+
// mint) pull the single source of truth (contracts) from this leaf.
|
|
28
|
+
export { TERMINAL_STREAM_PORT };
|
|
29
|
+
|
|
30
|
+
// The ttyd launch is bounded by the readiness gate inside the up-script (50 *
|
|
31
|
+
// 0.1s = ~5s) PLUS first-boot warm-up on a cold gVisor box. 60s gives headroom
|
|
32
|
+
// over the observed warm path (~1-2s; ttyd is a single static binary) without
|
|
33
|
+
// masking a genuine wedge. Symmetric with DISPLAY_STACK_TIMEOUT_MS.
|
|
34
|
+
export const TERMINAL_SERVER_TIMEOUT_MS = 60_000;
|
|
35
|
+
|
|
36
|
+
/** Thrown when the ttyd launch failed inside the box. exitCode 14 maps to the
|
|
37
|
+
* up-script's "ttyd failed to come up" stage; any other non-zero is unknown.
|
|
38
|
+
* Degradation is surfaced as a value to clients by the caller (Terminal
|
|
39
|
+
* transport falls back to sse-events / null); this error is for diagnostics. */
|
|
40
|
+
export class TerminalServerError extends Error {
|
|
41
|
+
readonly exitCode: number;
|
|
42
|
+
readonly stage: "ttyd" | "unknown";
|
|
43
|
+
|
|
44
|
+
constructor(exitCode: number, output: string) {
|
|
45
|
+
const stage = exitCode === 14 ? "ttyd" : "unknown";
|
|
46
|
+
super(`terminal server failed at stage "${stage}" (exit ${exitCode})${output ? `:\n${output}` : ""}`);
|
|
47
|
+
this.name = "TerminalServerError";
|
|
48
|
+
this.exitCode = exitCode;
|
|
49
|
+
this.stage = stage;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Thrown when the provider session cannot run commands (a headless-only backend
|
|
54
|
+
* with neither `exec` nor `execCommand`). The terminal tier degrades to the
|
|
55
|
+
* Channel-A sse-events firehose — the caller maps this to a `transport:null`
|
|
56
|
+
* pty-ws (the read-only firehose still works). */
|
|
57
|
+
export class TerminalServerUnsupportedError extends Error {
|
|
58
|
+
constructor(message: string) {
|
|
59
|
+
super(message);
|
|
60
|
+
this.name = "TerminalServerUnsupportedError";
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// The structural slice of a provider session we need: run a command (preferring
|
|
65
|
+
// `exec` for the structured exit code, falling back to `execCommand`).
|
|
66
|
+
type ExecResultLike = {
|
|
67
|
+
output?: string;
|
|
68
|
+
stdout?: string;
|
|
69
|
+
stderr?: string;
|
|
70
|
+
exitCode?: number | null;
|
|
71
|
+
};
|
|
72
|
+
type ExecCapableSession = {
|
|
73
|
+
exec?: (args: { cmd: string; yieldTimeMs?: number; maxOutputTokens?: number }) => Promise<ExecResultLike>;
|
|
74
|
+
execCommand?: (args: { cmd: string; yieldTimeMs?: number; maxOutputTokens?: number }) => Promise<string>;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export type EnsureTerminalServerOptions = {
|
|
78
|
+
/** The exposed terminal port; defaults to 7681 (ttyd default). */
|
|
79
|
+
port?: number;
|
|
80
|
+
/** Per-exec timeout; defaults to TERMINAL_SERVER_TIMEOUT_MS. */
|
|
81
|
+
timeoutMs?: number;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export type EnsureTerminalServerResult = {
|
|
85
|
+
/** The exposed port ttyd listens on (PTY-over-websocket). */
|
|
86
|
+
port: number;
|
|
87
|
+
/** The raw `OPENGENI_TERMINAL_UP …` marker line, for diagnostics. Never
|
|
88
|
+
* surfaced to clients. */
|
|
89
|
+
marker: string;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Build the shell command that runs the idempotent up-script under an in-box
|
|
94
|
+
* `flock`. The script is shipped in the image at /usr/local/bin/opengeni-terminal-up
|
|
95
|
+
* (the canonical desktop image, alongside opengeni-desktop-up); we set the port
|
|
96
|
+
* env and wrap the call in `flock` so two concurrent ensureTerminalServer callers
|
|
97
|
+
* (the API viewer op + the agent turn, both racing after a rollover) serialize
|
|
98
|
+
* without a double launch. The up-script's own curl readiness probe makes the
|
|
99
|
+
* second call a no-op.
|
|
100
|
+
*
|
|
101
|
+
* Exported (pure, side-effect-free) so the ensureTerminalServer unit test can
|
|
102
|
+
* assert the exact command sequence without a live box. Mirrors
|
|
103
|
+
* buildDisplayStackScript.
|
|
104
|
+
*/
|
|
105
|
+
export function buildTerminalServerScript(options: EnsureTerminalServerOptions = {}): string {
|
|
106
|
+
const port = options.port ?? TERMINAL_STREAM_PORT;
|
|
107
|
+
// flock -w bounds the wait so a wedged holder can't deadlock the caller; the
|
|
108
|
+
// up-script's curl probe ALSO makes the launch idempotent (belt + braces) so
|
|
109
|
+
// this works even against an older image that predates the wrapper.
|
|
110
|
+
return (
|
|
111
|
+
`mkdir -p /tmp/opengeni-terminal && ` +
|
|
112
|
+
`flock -w 30 /tmp/opengeni-terminal/up.outer.lock ` +
|
|
113
|
+
`env TERMINAL_PORT=${port} opengeni-terminal-up`
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function execResultOutput(result: ExecResultLike | string): string {
|
|
118
|
+
if (typeof result === "string") {
|
|
119
|
+
return result;
|
|
120
|
+
}
|
|
121
|
+
return [result.output, result.stderr, result.stdout]
|
|
122
|
+
.filter((v): v is string => typeof v === "string" && v.length > 0)
|
|
123
|
+
.join("\n");
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function execResultExitCode(result: ExecResultLike | string): number | null {
|
|
127
|
+
if (typeof result === "string") {
|
|
128
|
+
return null; // execCommand returns a bare string — no exit code available.
|
|
129
|
+
}
|
|
130
|
+
return typeof result.exitCode === "number" ? result.exitCode : null;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Parse the exit code the up-script signals via its trailing marker. When we ran
|
|
134
|
+
// through `exec` we have the real exitCode; when we only had `execCommand` (a
|
|
135
|
+
// bare string), we infer success from the OPENGENI_TERMINAL_UP marker and infer
|
|
136
|
+
// the failing stage from the stage-failure message the script prints to stderr.
|
|
137
|
+
// Mirrors inferExitFromOutput (display-stack).
|
|
138
|
+
function inferExitFromOutput(output: string): number {
|
|
139
|
+
if (/OPENGENI_TERMINAL_UP\b/.test(output)) {
|
|
140
|
+
return 0;
|
|
141
|
+
}
|
|
142
|
+
if (/ttyd failed to come up/.test(output)) {
|
|
143
|
+
return 14;
|
|
144
|
+
}
|
|
145
|
+
return -1;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Idempotently bring up the ttyd PTY-over-websocket server on the live box. Safe
|
|
150
|
+
* to call N times (the in-box flock + the up-script's curl readiness probe make a
|
|
151
|
+
* second call a no-op). Resolves with the exposed port on success; throws
|
|
152
|
+
* `TerminalServerError` on a launch failure and `TerminalServerUnsupportedError`
|
|
153
|
+
* when the session cannot run commands.
|
|
154
|
+
*
|
|
155
|
+
* `session` is the externally-owned provider session (the `established.session`
|
|
156
|
+
* from establishSandboxSessionFromEnvelope, or any SandboxSessionLike). We prefer
|
|
157
|
+
* `session.exec` (structured `{exitCode}`) and fall back to `session.execCommand`
|
|
158
|
+
* (bare string), inferring success from the up-script's marker line in the
|
|
159
|
+
* fallback case. Mirrors ensureDisplayStack exactly.
|
|
160
|
+
*/
|
|
161
|
+
export async function ensureTerminalServer(
|
|
162
|
+
session: unknown,
|
|
163
|
+
options: EnsureTerminalServerOptions = {},
|
|
164
|
+
): Promise<EnsureTerminalServerResult> {
|
|
165
|
+
const s = session as ExecCapableSession;
|
|
166
|
+
if (typeof s?.exec !== "function" && typeof s?.execCommand !== "function") {
|
|
167
|
+
throw new TerminalServerUnsupportedError(
|
|
168
|
+
"provider session cannot run commands (no exec/execCommand) — terminal pty-ws unavailable",
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const port = options.port ?? TERMINAL_STREAM_PORT;
|
|
173
|
+
const timeoutMs = options.timeoutMs ?? TERMINAL_SERVER_TIMEOUT_MS;
|
|
174
|
+
const cmd = buildTerminalServerScript({ port });
|
|
175
|
+
|
|
176
|
+
const result =
|
|
177
|
+
typeof s.exec === "function"
|
|
178
|
+
? await s.exec({ cmd, yieldTimeMs: timeoutMs, maxOutputTokens: 20_000 })
|
|
179
|
+
: await s.execCommand!({ cmd, yieldTimeMs: timeoutMs, maxOutputTokens: 20_000 });
|
|
180
|
+
|
|
181
|
+
const output = execResultOutput(result);
|
|
182
|
+
const exitCode = execResultExitCode(result) ?? inferExitFromOutput(output);
|
|
183
|
+
|
|
184
|
+
if (exitCode !== 0) {
|
|
185
|
+
throw new TerminalServerError(exitCode, output);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const marker = (output.match(/OPENGENI_TERMINAL_UP[^\n]*/) ?? [""])[0];
|
|
189
|
+
return { port, marker };
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/** Tear the terminal server down (down-script). Best-effort; never throws on a
|
|
193
|
+
* missing process. Mirrors tearDownDisplayStack. */
|
|
194
|
+
export async function tearDownTerminalServer(session: unknown): Promise<void> {
|
|
195
|
+
const s = session as ExecCapableSession;
|
|
196
|
+
if (typeof s?.exec === "function") {
|
|
197
|
+
await s.exec({ cmd: "opengeni-terminal-down", yieldTimeMs: 10_000, maxOutputTokens: 4_000 });
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
if (typeof s?.execCommand === "function") {
|
|
201
|
+
await s.execCommand({ cmd: "opengeni-terminal-down", yieldTimeMs: 10_000, maxOutputTokens: 4_000 });
|
|
202
|
+
}
|
|
203
|
+
}
|