Skip to content

memhtml session exec

1. Synopsis

Run a script over the session’s view (head plus overlay) in a writable sandbox and harvest its writes into the overlay.

Terminal window
memhtml session exec [options]

2. Arguments

This command takes no positional arguments.

3. Flags

Flag Type Default Values Description
--id string no default no fixed set The session whose view the script sees. Defaults to $MEMHTML_SESSION; with neither, the call is ERR_MISSING_ARGUMENT. With the id taken from $MEMHTML_SESSION and no log yet, the session is started on refs/heads/main’s tip first, as session put does.
--file string no default no fixed set The script, as a path on the HOST. Omit it, pass --file -, or a positional - to read stdin, which is the usual door: a heredoc (memhtml session exec --id s1 <<'SH' … SH) delivers the script with no quoting to get wrong. Mutually exclusive with --script. A new file the script writes is held to the write bar session put states; one below it blocks the whole harvest. An existing file whose article is unchanged may gain or lose <link rel="memhtml-..."> lines and <meta name="memhtml-entity"> lines in its head: each becomes a link, unlink, label, or unlabel op, and any other head edit is rejected with a reason. A file copied unchanged to archive/<YYYY>/<its path> and removed from its old one becomes an archive, and one copied unchanged to exactly one new live path a move, which keeps its bytes and its content hash; either way, every edge a live record carries to the old path is repointed to where the file went in the same harvest (an unlink of the edge and a link of the same rel), so the script need not edit the records that cite it. New files the script writes are not repointed: one that supersedes a record the same script archives names it at /archive/<YYYY>/<its path>, and one that names the old path blocks the harvest. An edge on a record the session may not write (an arc or a people record, outside a curate/ ref) is left for the curator after an archive, and blocks a move. A file removed with no copy is rejected: deletion is not an operation.
--script string no default no fixed set The script source, inline. Mutually exclusive with --file and with stdin.
--lang string bash bash, js The script’s language, echoed as lang in the report. bash runs it as a shell program: cat, grep -r, sed -i, find (with -exec), xargs, jq, awk, pipes, redirection, and nested heredocs all work on the corpus at /mnt/memhtml, so cat > /mnt/memhtml/projects/<slug>/<name>.html <<'HTML' … HTML writes a record and a sed -i that splices a <meta name="memhtml-entity"> line into a head labels one. Use absolute paths. js-exec <file> runs a JavaScript module from inside a bash script. js runs the whole script as a JavaScript module through js-exec with /workspace/lib/corpus.mjs preloaded, as memhtml exec does. The mount, the timeout, and the harvest are the same for both. There is no network in either: curl is not a command.
--timeout-ms integer 30000 no fixed set Wall-clock bound on the script. Exceeding it is exitCode 124 with timedOut: true, for a bash loop and a js-exec module alike. Other sandbox bounds end a runaway sooner with exit 126 and the bound named on stderr: call depth 100, and 100,000 iterations inside one awk, sed, or jq. A value above 600000 is refused. Under the head server a run still going 5 s past the bound (a single command the shell cannot interrupt) has its worker stopped, and it reports exit 124 with nothing harvested.
--server flag true no fixed set Ask the head server on .memhtml/head.sock to run the script (memhtml head serve); --no-server always runs it here. The server reads the session’s log, takes the version at the session’s base when that is its ref’s tip or an ancestor of it, runs the script in a worker, harvests, validates, and appends exactly as a local run does, one exec per session at a time; the payload’s head.source is server and server.base says tip or ancestor. The script runs here instead when the connection to a server never completes (no socket, nobody listening, any connect error, no accept within 100 ms, or a socket path past the platform’s limit, which is never dialed), when the server answers anything but 200, or when it declines (server.reason base-unservable or busy); server.reason names why (disabled under --no-server, no-server, refused). When the server took the script and its answer was lost, the call fails with ERR_STORAGE rather than run it twice. Either way the request body (the script as JSON) is capped at 1,048,576 bytes: a larger script is ERR_INVALID_FLAG at exit 2 before anything runs.

Every command also accepts the global flags.

4. Response

On success the command writes one JSON envelope to stdout, and its type is session.exec.report.

On failure it writes the failure envelope, whose code comes from error codes.

The JSON envelope gives the fields both shapes carry. Response types lists every value type takes across the binary.

5. Further reading

The guide blocks that name this command:

6. Provenance

A loader generates this page from apps/cli/src/commands.ts while the site builds, so no file in the repository holds it: the COMMANDS array there is the one source of argument parsing, of memhtml manifest, and of AGENTS.md. Change the registry and this page changes with it.