Docs
Dashboard
Examples / Browser + shellBeta

Results after stop

A session's files outlive its machine. This example writes a folder of files in a shell session, stops the session, and then lists the folder, reads two of its files and downloads the whole folder as one .tar.gz, all without resuming the session: files.list, files.readText and files.archive answer from the workspace the session saved when it stopped, so no machine runs and no machine time is billed.

Usessessions.create with browser: false, shell: true, session.files.write, session.exec, session.stop, then session.files.list, readText and archive on the stopped session, BrowserDisabledError
Needsa plan with shell sessions
Sitenone
Outputoutput/results.tar.gz and output/result.json (the listing, what was read, the status of the session before and after)
LanguagesNode and Python

Also shown, because the session has no browser: its liveUrl and connectUrl are null, and a browser call (session.goto) fails with BrowserDisabledError (409 browser_disabled).

The folder results/ gets summary.txt and data.csv through the API, and squares.json and logs/run.log from commands run in the machine (one of them runs Python). A notes.txt outside the folder shows that the archive of a folder holds only that folder. Writes and commands on a stopped session are still refused (409 session_not_running): only reads work there.

The code

The example as it is in its folder. Both versions do the same job and write the same output/, so one result check covers both.

/** * Results after stop: a shell session writes a folder of files and stops. The files are then listed, read and downloaded * as one .tar.gz WITHOUT resuming the session: `files.list`, `files.readText` and `files.archive` work on a stopped session * (no machine runs, no machine time is billed). Also shown: a shell-only session has no `liveUrl`, and a browser call on * it fails with BrowserDisabledError. * *   npx tsx node/index.ts            (BOXLINE_API_KEY; a plan with shell sessions) * * Writes output/results.tar.gz and output/result.json. */import { mkdirSync, writeFileSync } from "node:fs";import { join } from "node:path";import { Boxline, BrowserDisabledError } from "@boxline/sdk";const out = process.env.OUTPUT_DIR ?? "output";const bx = new Boxline();const session = await bx.sessions.create({ browser: false, shell: true, timeout: 600, userMetadata: { example: "results-after-stop" } });console.log(`Session: ${session.id}`);try {  // 1. A shell-only session: no browser, so no live view, and a browser call is refused.  const shellOnly = { browser: session.data.browser, shell: session.data.shell, liveUrl: session.liveUrl, connectUrl: session.connectUrl };  const refused = await session.goto("https://example.com").then(    () => null,    (e: unknown) => e,  );  if (!(refused instanceof BrowserDisabledError)) throw new Error(`a browser call on a shell-only session should be refused, got ${String(refused)}`);  console.log(`Shell-only session: liveUrl ${shellOnly.liveUrl}; a browser call fails with ${refused.name} (${refused.code}, status ${refused.status})`);  // 2. Several files go into one folder: some written through the API, some by commands in the machine.  await session.files.write("results/summary.txt", "Results of the example results-after-stop\n");  await session.files.write("results/data.csv", ["n,square", ...Array.from({ length: 10 }, (_, k) => `${k + 1},${(k + 1) ** 2}`)].join("\n") + "\n");  const made = await session.exec(    'mkdir -p results/logs && python3 -c "import json; print(json.dumps([i * i for i in range(1, 11)]))" > results/squares.json && ' +      'for i in 1 2 3; do echo "line $i" >> results/logs/run.log; done && echo "outside the folder" > notes.txt',    { cwd: "/workspace", timeoutMs: 60_000 },  );  if (made.exitCode !== 0) throw new Error(`the commands failed: ${made.stderr.trim()}`);  // 3. The session stops: its workspace is saved and the machine is freed.  await session.stop();  console.log(`The session is ${session.status}`);  // 4. The results, read from the stopped session: no resume.  const listing = await session.files.list("results");  console.log(`results/: ${listing.entries.map((e) => `${e.name} (${e.type}, ${e.size} bytes)`).join(", ")}`);  const summary = await session.files.readText("results/summary.txt");  const squares = await session.files.readText("results/squares.json");  const archive = await session.files.archive("results");  console.log(`results.tar.gz: ${archive.length} bytes`);  const after = (await bx.sessions.get(session.id)).data.status;  console.log(`After reading and downloading, the session is still ${after}`);  mkdirSync(out, { recursive: true });  writeFileSync(join(out, "results.tar.gz"), archive);  writeFileSync(    join(out, "result.json"),    JSON.stringify(      {        shellOnly: { ...shellOnly, browserCall: { error: refused.name, code: refused.code, status: refused.status } },        stoppedStatus: session.status,        listing: listing.entries.map((e) => ({ name: e.name, type: e.type, size: e.size })),        read: { "results/summary.txt": summary, "results/squares.json": squares },        archive: { file: "results.tar.gz", bytes: archive.length },        statusAfterReads: after,      },      null,      2,    ),  );} finally {  if (session.status === "RUNNING") await session.stop().catch(() => undefined);}

Run it

Get the examples with git clone https://github.com/Boxline-dev/examples.git and open this one: cd examples/results-after-stop. Put your API key in BOXLINE_API_KEY (and BOXLINE_API_URL for another API), then run either version from the example’s folder. Both write to output/ and stop their sessions when they finish.

# Node 18 or newer, in the example's folderexport BOXLINE_API_KEY=…   # your API keynpm install @boxline/sdk tsxnpx tsx node/index.ts

Then check the result, from the same folder: npx tsx check.ts output. It exits with 0 only when the result is right.

What its check proves

check.ts reads what the example wrote and passes only when the result is right, not merely when the example ran.

npx tsx check.ts output exits with 0 only when the session was shell-only (liveUrl and connectUrl null, the browser call refused with BrowserDisabledError), the files were read while the session was STOPPED and it was still STOPPED after them, the listing and the two files read are what was written, and results.tar.gz really is a gzip tar holding exactly the folder's four files with their contents (and nothing from outside it).

Source

github.com/Boxline-dev/examples/results-after-stop on GitHub, with its README.md, check.ts and .env.example. The examples are open source. See all examples.

These docs describe a beta API. Endpoints, fields and SDK methods may change before general availability; breaking changes will be listed in the changelog.