6of 9
Tools
By default a panel cannot reach the network, so live data comes through tools: commands declared in
panel.json.
Approve the commands
Before a panel with tools loads, Offsider shows a native permission window with its folder, working directory, and every tool’s command and resolved executable. Choose Allow All Capabilities or Cancel. There are no per-tool switches in this version. Panels without tools open directly.
Approval covers all declared tools and the JSON arguments the panel sends them. Commands run with your user permissions and terminal credentials, so approve only tools you trust.
Signed, provisioned builds remember approvals in Offsider’s app-scoped macOS Keychain group.
Development builds show Allow All for This Session and ask again after Offsider quits.
Changing the tool definitions, panel folder, working directory, resolved program, or an executable
file requires approval again. Explicit file arguments (such as python3 tools/query.py) are also
fingerprinted. The CLI checks these before each call and refuses changed tools until you reopen
the panel. Moving a panel or updating a command-line program can therefore trigger another prompt.
Use Forget Panel Approvals… in Offsider’s menu bar to revoke all approvals and close current panels. Running jobs are stopped; ordinary tool calls already in progress may finish.
Run a tool
-
Where they run. In Offsider.app, with the directory and environment captured from
offsider open. They continue working after the opening CLI detaches. -
The command. An array, never passed through a shell. A first element starting
./is relative to the panel’s folder. Absolute paths run directly; other paths containing/are relative to the opening terminal’s directory. Bare command names are resolved onPATHwhen opening and pinned to the reviewed executable. -
Input.
offsider.call(name, args)sendsargsas JSON on stdin.OFFSIDER_PANEL_DIRis set to the panel’s folder. -
Output. A JSON object on stdout is what
callresolves with. Other JSON comes back as{ "value": … }, and anything that isn’t JSON comes back as the text. -
Failures. A non-zero exit, or running past
timeout(60 seconds by default), rejects the call with the command’s stderr.
#!/usr/bin/env ruby
# tools/prs: {"numbers": [377, 380]} in, {"prs": [...]} out
numbers = JSON.parse($stdin.read).fetch("numbers")
prs = numbers.map { JSON.parse(`gh pr view --json number,state,title`) }
puts JSON.generate(prs:)
For long-running commands, use offsider.run(name, args) instead. It starts a declared tool and
returns a job whose events async iterable emits labelled stdout and stderr chunks followed by
an exit event. job.cancel() terminates it. Jobs stop when the panel closes. Tools run with your
user permissions, so inspect the folder before opening it.
const job = await offsider.
await
}
The job receives its arguments as one JSON document on stdin. A job with no declared timeout may
keep running until it exits, the panel closes, or the panel calls job.cancel().
Keep job.id if the page may reload; offsider.attachJob(id) replays recent output and then
continues with live events. Offsider keeps the most recent 256 events for each of up to 20 jobs
per panel.