MCPcopy Create free account
hub / github.com/cloudflare/computer / WorkspaceShell

Class WorkspaceShell

packages/computer/src/shell.ts:145–231  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

143}
144
145export class WorkspaceShell {
146 readonly #shell: ShellRPC;
147 readonly #sync: Sync;
148 readonly #observer: WorkspaceObserver;
149
150 constructor(shell: ShellRPC, sync: Sync, observer: WorkspaceObserver = noopObserver) {
151 this.#shell = shell;
152 this.#sync = sync;
153 this.#observer = observer;
154 }
155
156 exec(command: string): Promise<ExecHandle<undefined>>;
157 exec(command: string, options: ExecOptions<undefined>): Promise<ExecHandle<undefined>>;
158 exec(command: string, options: ExecOptions<"utf8">): Promise<ExecHandle<"utf8">>;
159 async exec<E extends ExecEncoding>(
160 command: string,
161 options: ExecOptions<E> = {},
162 ): Promise<ExecHandle<E>> {
163 assertNotTemplate(command);
164 // Pre-exec push: ship anything the host wrote since the last
165 // push so the spawned command sees it. Failures non-fatal per
166 // docs/05 — the command still runs; pushed reports 0.
167 let pushed = 0;
168 try {
169 pushed = await this.#sync.push();
170 } catch {
171 // pushed stays 0
172 }
173 const envelope = await withSpan(
174 this.#observer,
175 "workspace.runtime.exec.spawn",
176 {
177 "workspace.runtime.cwd": options.cwd,
178 "workspace.runtime.timeout_ms": options.timeoutMs,
179 "workspace.runtime.id": options.id,
180 },
181 () =>
182 this.#shell.exec({
183 command,
184 id: options.id,
185 cwd: options.cwd,
186 timeoutMs: options.timeoutMs,
187 env: options.env,
188 stdin:
189 typeof options.stdin === "string"
190 ? new TextEncoder().encode(options.stdin)
191 : options.stdin,
192 }),
193 (span, outcome) => {
194 if (outcome.ok) span.setAttribute("workspace.runtime.id", outcome.value.id);
195 },
196 );
197 // Dispose the result envelope when the event stream finishes
198 // draining. Without this, capnweb's exports table holds onto
199 // the envelope for the life of the session — one entry per
200 // exec call — because we hand the inner stream off to the
201 // caller and can't `using` the envelope ourselves.
202 const events = disposeOnDone(envelope.events, () => maybeDispose(envelope));

Callers

nothing calls this directly

Calls

no outgoing calls

Tested by

no test coverage detected