Console API
client.console opens a real shell on the connected host and streams its bytes. It is the same capability the Terminal app uses, not a "run one command" helper: you write input, you read output, and you own the session until you close it.
Availability
Requires the system.console permission and a connected workspace whose provider offers the terminal capability. Without both, open fails with denied or unavailable.
const env = await client.environment.get();
const canOpen = env.connection === "connected" && env.capabilities.includes("terminal");Interface
interface RemoteConsole {
readonly binding: string;
readonly resizable: boolean;
read(signal?: AbortSignal): Promise<Uint8Array | null>; // null means end of stream
write(bytes: Uint8Array | string, signal?: AbortSignal): Promise<void>; // strings are sent as UTF-8
resize(cols: number, rows: number, signal?: AbortSignal): Promise<void>;
close(): Promise<void>;
}
interface AppConsoleAPI {
open(options: { binding: string; cols?: number; rows?: number }, signal?: AbortSignal): Promise<RemoteConsole>;
}Defaults are 80 columns by 24 rows. Dimensions must be 2–500 columns and 2–300 rows. resizable reports whether the provider accepts resize at all — a fixed-size console rejects it with unavailable.
Examples
Open a shell, echo its output and send a command:
const console_ = await client.console.open({ binding, cols: 100, rows: 30 });
const decoder = new TextDecoder();
const pump = (async () => {
for (;;) {
const chunk = await console_.read();
if (chunk === null) break; // the shell ended
append(decoder.decode(chunk, { stream: true }));
}
})();
await console_.write("uptime\n");Always release it, and stop reading when your window goes away:
try {
await pump;
} finally {
await console_.close();
}Follow the size of your own view:
if (console_.resizable) await console_.resize(cols, rows);Output arrives as raw bytes, including escape sequences. Decode with TextDecoder and { stream: true } so multi-byte characters split across chunks survive, and render with a terminal emulator if you need cursor control.
Errors
| Code | Message | Meaning |
|---|---|---|
denied | The extension does not hold the required permission. | system.console is not granted. |
unavailable | This console has fixed dimensions. | resize on a non-resizable console. |
invalid | Console dimensions must be 2–500 columns and 2–300 rows. | Out-of-range size. |
invalid | Supply at most 64 KiB of bytes. | A single write was too large; the SDK already chunks strings for you. |
busy | Close an existing console before opening another. | More than 16 consoles in one window. |
busy | Only one read may wait on a console. | Two concurrent read() calls. |
busy | Wait for the current console write. | Two concurrent writes. |
closed | Console is closed. | Used after close(). |
closed | Console is closed or belongs to a previous connection. | The connection changed. |
closed | Console retired during writing; some bytes may have been delivered. | Genuinely uncertain: do not blindly resend. |
aborted | Console read canceled; the console was closed. | A read's signal fired, which also closes the console. |
failed | Console provider violated output flow control or returned an invalid byte chunk. | A provider bug; the console is retired. |
Lifecycle and permissions
A console is owned by the window that opened it, up to 16 per window including pending opens. Cancelling any single operation closes the whole console, because a half-cancelled shell has no useful state — pass a signal only when you mean to end the session.
Output is backpressured: the host holds chunks of at most 64 KiB until your read() consumes them, so a chatty process cannot flood your window. Writes above 64 KiB are rejected, and the SDK splits long strings into chunks for you.
Losing the connection, or the terminal capability, retires every console: pending reads and writes reject with closed. Open a new one after the workspace reconnects — shells do not resume. A console is a real shell with the permissions of the account the workspace signed in as.
Related guides
- Terminal sessions — the built-in Terminal, and what a shell can do.
- Events and environment API — noticing that
terminalcame back. - Custom services — device operations that are not a shell.
- App lifecycle and services — cleanup rules.