@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.
Files changed (65) hide show
  1. package/dist/chunk-2PO56VAL.js +3478 -0
  2. package/dist/chunk-2PO56VAL.js.map +1 -0
  3. package/dist/index.d.ts +912 -0
  4. package/dist/index.js +3663 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/sandbox/index.d.ts +1738 -0
  7. package/dist/sandbox/index.js +187 -0
  8. package/dist/sandbox/index.js.map +1 -0
  9. package/package.json +49 -0
  10. package/src/bundled_hashicorp_terraform_skills/LICENSE +373 -0
  11. package/src/bundled_hashicorp_terraform_skills/README.md +18 -0
  12. package/src/bundled_hashicorp_terraform_skills/UPSTREAM_GIT_SHA +1 -0
  13. package/src/bundled_hashicorp_terraform_skills/azure-verified-modules/SKILL.md +613 -0
  14. package/src/bundled_hashicorp_terraform_skills/checkov/SKILL.md +43 -0
  15. package/src/bundled_hashicorp_terraform_skills/refactor-module/SKILL.md +538 -0
  16. package/src/bundled_hashicorp_terraform_skills/social-media-marketing/SKILL.md +35 -0
  17. package/src/bundled_hashicorp_terraform_skills/terraform-search-import/SKILL.md +372 -0
  18. package/src/bundled_hashicorp_terraform_skills/terraform-search-import/references/MANUAL-IMPORT.md +113 -0
  19. package/src/bundled_hashicorp_terraform_skills/terraform-search-import/scripts/list_resources.sh +38 -0
  20. package/src/bundled_hashicorp_terraform_skills/terraform-stacks/SKILL.md +480 -0
  21. package/src/bundled_hashicorp_terraform_skills/terraform-stacks/references/api-monitoring.md +543 -0
  22. package/src/bundled_hashicorp_terraform_skills/terraform-stacks/references/component-blocks.md +476 -0
  23. package/src/bundled_hashicorp_terraform_skills/terraform-stacks/references/deployment-blocks.md +391 -0
  24. package/src/bundled_hashicorp_terraform_skills/terraform-stacks/references/examples.md +1529 -0
  25. package/src/bundled_hashicorp_terraform_skills/terraform-stacks/references/linked-stacks.md +187 -0
  26. package/src/bundled_hashicorp_terraform_skills/terraform-stacks/references/troubleshooting.md +671 -0
  27. package/src/bundled_hashicorp_terraform_skills/terraform-style-guide/SKILL.md +353 -0
  28. package/src/bundled_hashicorp_terraform_skills/terraform-test/SKILL.md +451 -0
  29. package/src/bundled_hashicorp_terraform_skills/terraform-test/references/CI_CD.md +80 -0
  30. package/src/bundled_hashicorp_terraform_skills/terraform-test/references/EXAMPLES.md +314 -0
  31. package/src/bundled_hashicorp_terraform_skills/terraform-test/references/MOCK_PROVIDERS.md +171 -0
  32. package/src/codex-tool-search.ts +267 -0
  33. package/src/context-compaction.ts +538 -0
  34. package/src/history-sanitizer.ts +719 -0
  35. package/src/index.ts +3299 -0
  36. package/src/sandbox/capabilities.ts +69 -0
  37. package/src/sandbox/channel-a.ts +1031 -0
  38. package/src/sandbox/display-stack.ts +231 -0
  39. package/src/sandbox/errors.ts +34 -0
  40. package/src/sandbox/index.ts +832 -0
  41. package/src/sandbox/providers/blaxel.ts +35 -0
  42. package/src/sandbox/providers/cloudflare.ts +24 -0
  43. package/src/sandbox/providers/daytona.ts +34 -0
  44. package/src/sandbox/providers/docker.ts +17 -0
  45. package/src/sandbox/providers/e2b.ts +36 -0
  46. package/src/sandbox/providers/index.ts +107 -0
  47. package/src/sandbox/providers/local.ts +13 -0
  48. package/src/sandbox/providers/modal.ts +55 -0
  49. package/src/sandbox/providers/none.ts +13 -0
  50. package/src/sandbox/providers/runloop.ts +32 -0
  51. package/src/sandbox/providers/selfhosted.ts +96 -0
  52. package/src/sandbox/providers/types.ts +38 -0
  53. package/src/sandbox/providers/vercel.ts +29 -0
  54. package/src/sandbox/recording.ts +286 -0
  55. package/src/sandbox/routing/backend-resolver.ts +189 -0
  56. package/src/sandbox/routing/routing-session.ts +455 -0
  57. package/src/sandbox/select.ts +371 -0
  58. package/src/sandbox/selfhosted/capabilities.ts +255 -0
  59. package/src/sandbox/selfhosted/control-rpc.ts +351 -0
  60. package/src/sandbox/selfhosted/session.ts +930 -0
  61. package/src/sandbox/selfhosted/testing.ts +230 -0
  62. package/src/sandbox/stream-port.ts +185 -0
  63. package/src/sandbox/stream-token.ts +90 -0
  64. package/src/sandbox/terminal-server.ts +203 -0
  65. 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
+ }