Daniel Kim f1df93d4a3 Codex Auto: sandbox escalations become permission cards (PR2 re-land, v0.1.52) (#63)
* Codex auto mode: grant the orx data dir as a sandbox writable root

workspace-write denies writes outside the workspace, and the orx SQLite
store lives under ~/.local/share (WAL + read-write open), so every
store-touching orx command died with 'unable to open database file'
under Auto mode. Add store::data_dir() to the turn's writable_roots
alongside the shared git dir.

* Review fixes: pin ORX_DATA_DIR to the granted root, ensure_ naming

- Pin ORX_DATA_DIR on the codex child (after prepare_env) so the store
  the sandboxed orx resolves is the exact path granted as a writable
  root — synced env or a relative override could otherwise diverge.
- Rename orx_data_dir -> ensure_orx_data_dir (codebase ensure_ prefix
  for create-if-missing helpers); note the grant is dir-wide.
- Dedent the '-c replaces user roots' note: it covers both roots.

* Review round 2: match-expression pin, stale options() doc, asymmetry note

* Codex chat: migrate to app-server protocol with exec fallback

One long-lived 'codex app-server' child per session (CodexHost, the
AgentHost pattern), JSON-RPC over stdio. Threads persist as the
session's native id and resume across orx up restarts; the playbook
rides developerInstructions; the sandbox policy travels per turn as a
first-class sandboxPolicy param (writable roots + network) instead of
-c config overrides that clobbered user config.

approvalPolicy stays 'never' on every mode — this is a pure transport
swap; the follow-up flips Auto to on-request and surfaces approval
requests as permission cards (validated live against codex-cli 0.144:
sandbox escalations DO arrive as item/commandExecution/requestApproval,
upstream #21982 does not reproduce).

codex < 0.144 keeps the legacy exec path for one release
(ORX_CODEX_EXEC=1 forces it); detect() notes the upgrade.

* Review round 1 fixes: turn-id filter, drop race, bounded interrupt

Majors:
- Filter turn events by the turn/start response's turn id so an aborted
  predecessor's tail (fatally, its turn/completed) can't leak into a
  successor turn's transcript.
- TurnRoute::drop only detaches its own channel (same_channel guard) —
  an aborted task's late drop can no longer clobber a successor's
  registration.
- interrupt_active_turn bounded to 5s so a wedged child can't hold the
  user-facing interrupt/delete paths hostage; reader EOF now also kills
  a half-dead child instead of leaving a zombie in the registry.
- thread/resume falls back to a fresh thread only on a server-reported
  error (try_request); transport failures propagate so a resumable
  thread is never discarded over a timeout.
- delete_project now kills codex children too (was opencode-only).

Nits: options()/sandbox_policy_json doc rewrites, tool_part/tool_status/
part_exists/find_codex_required/respond_decline helpers, HashSet for
unanswered ids, REQUEST_TIMEOUT const, outputDelta creates its part,
turn/completed(inProgress) guard, double-error dedup, truthy
ORX_CODEX_EXEC, softer agent_note, import cleanups.

* Review round 2 fixes: abort-proof child bring-up, shared-helpers section

Major: spawn + register + handshake now run in a detached task, so an
aborted turn task (interrupt/delete during the first message) can no
longer orphan an unregistered app-server child; a no-displace insert
keeps a raced successor's live client intact, and handshake failure
kills + deregisters only its own entry.

Nits: shared helpers moved out of the legacy section under their own
banner; set_active_turn armed from the turn/start response (deletes the
turn/started special case and closes the early-interrupt no-op window);
parse_codex_version takes the first parseable token; resume-rejection
logged; HANDSHAKE_TIMEOUT const; TurnEnd/apply_notification docs;
has_error_part test; fold-test guard tightened; comment accuracy.

* Round 3 polish: doc word order, eprintln prefix, mid-handshake reuse note

* fmt

* Codex Auto: on-request approvals surfaced as permission cards

Auto now runs approvalPolicy on-request: the writable roots keep
routine orx traffic prompt-free, and anything escalating past the
sandbox arrives as item/*/requestApproval, surfaced as a permission
card (native_id = JSON-RPC request id) and answered inline via
resume_from_prompt -> {"decision": accept|decline}.

Invariant: no unresolved codex card outlives its turn — every turn
exit sweeps open cards (resolve + decline with codex); interrupt paths
cancel pending approvals natively before turn/interrupt;
serverRequest/resolved reconciles codex-side settlement (its ~5min
approval deadline). Stale-turn requests are declined, never surfaced;
Bypass auto-accepts; unknown request types get a JSON-RPC error
instead of blocking codex. A 10-minute no-event watchdog (suspended
while a card is pending) interrupts wedged turns.

* PR2 review round 1 fixes: stale-card defense, Bypass reply schema, card content

- Consent integrity: codex request ids restart per child, so a stale
  unresolved card could deliver a click to a different live request.
  Turn-entry resolve_stale_prompts sweep (store-side, codex-only —
  end-turn harnesses keep their cross-turn cards) + turn-scoped part
  ids (appr-{turn}-{id}) close it from both ends.
- Bypass now auto-accepts only card-able approval methods; unknown
  request types get the JSON-RPC error reply in every mode (a
  {decision} reply to item/tool/requestUserInput would fail to parse).
- Permission cards show the request's human-readable reason (fileChange
  cards carry nothing else); UI dist rebuilt.
- serverRequest/resolved also clears the id from the client's pending
  set (stale-answer guard stays truthful after codex self-settles).
- Reconciliation computes the part id directly (approval_part_id)
  instead of a reverse scan; resolve_card returns (); sweep declines
  unconditionally (respond's pending set is the single arbiter);
  watchdog bumped to 30min with an honest false-positive note;
  resume_from_prompt gains a kind guard.

* PR2 round 2 polish: schema-aware fallback replies, immediate card flush

Non-approval request methods now get the JSON-RPC error reply in every
fallback path (read_loop unrouted, stale-turn arm) via a shared
is_approval_request; codex-settled cards flush immediately instead of
waiting for the next event; message_json reuse; Handled doc drift;
watchdog comment dedup.

* Bump version to 0.1.52
2026-07-13 15:27:04 -07:00
2026-06-07 00:35:02 -07:00
2026-07-06 20:05:41 -07:00

OpenResearch CLI (orx)

A small command-line interface for OpenResearch. Log in via the browser, then query your projects from the terminal.

Requirements

  • Rust (stable) with Cargo. Install via rustup:

    curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
    

Build & run

# Build (debug):
cargo build

# Run during development:
cargo run -- login
cargo run -- projects

# Build an optimized release binary at target/release/orx:
cargo build --release
./target/release/orx login

# Or install `orx` onto your PATH (~/.cargo/bin):
cargo install --path .
orx login
orx projects

Run the tests with cargo test.

Commands

Command Description
orx login [--api-url <url>] Opens your browser, authenticates, and stores a personal access token.
orx logout Removes the stored token.
orx install-skills [--agent claude|codex|opencode|cursor|all] Installs the orx skill shim into local coding agents (Claude Code, Codex, OpenCode, Cursor) so they auto-discover the CLI (orx login also offers this).
orx projects [--all] Lists your projects, grouped by organization. --all includes archived.
orx compute [--gpu <id>] [--count <n>] [--provider <name>] | --cpu] Lists the GPU compute catalog across all providers and regions, or CPU-only offers with --cpu, sorted by price.
orx instance create <orgId> (--gpu <id> [--count <n>] [--disk <gb>] [--provider <name>] | --cpu <flavor> [--vcpus <n>]) Spins up a standalone instance in an organization (not tied to an experiment), like the dashboard's "Spin up".
orx exp status <expId> Shows an experiment's status, branch, parent, run command, and latest run, plus a local git diff recipe for what the run changed.
orx exp cmd <expId> [--set <command>] Views or sets the experiment's run command.
orx exp run <expId> (--gpu <id> [--count <n>] [--disk <gb>] [--provider <name>] | --cpu <flavor> [--vcpus <n>] | --sandbox <id>) Launches a run on new GPU, new CPU-only, or existing compute.
orx exp cancel <expId> Cancels the in-flight run.
orx lit "<query>" [--limit <n>] [--json] Full-text search alphaXiv's paper corpus (no login required).
orx paper <id|url> [--full] Fetch a paper's machine-readable report, or its full text with --full.
orx version [--check] [--json] Prints the CLI version; --check compares it to the latest release.
orx update [--dry-run] [--force] Updates orx by re-running the release installer (installer installs only).

Updating

orx update only manages binaries installed by the release installer script (it checks the cargo-dist install receipt at ${XDG_CONFIG_HOME:-~/.config}/openresearch-cli/). If you installed with cargo install, update the same way instead. In interactive terminals orx also checks for new releases in the background at most once a day and prints a one-line notice to stderr; set ORX_NO_UPDATE_CHECK=1 (or OPENRESEARCH_CLI_DISABLE_UPDATE=1) to turn that off. Agents, scripts, pipes, and CI never see the notice.

Configuration

  • API URL resolves from --api-url → OPENRESEARCH_API_URL → the built-in default (http://localhost:4000). Point it at the production API host for real use:

    export OPENRESEARCH_API_URL=https://api.openresearch.sh
    
  • Credentials are stored at ${XDG_CONFIG_HOME:-~/.config}/openresearch/credentials.json (mode 0600).

How login works

orx login starts a temporary HTTP listener on a random 127.0.0.1 port, opens {api}/auth/cli/login in your browser, and waits for the API to redirect back with a freshly minted personal access token after you authenticate. The token is sent as Authorization: Bearer … on every subsequent request.

S
Description
GitHub Trending: alphaXiv/OpenResearch
Readme MIT
935 MiB
Languages
Rust 70.9%
TypeScript 20.9%
JavaScript 6.4%
Python 0.8%
HTML 0.3%
Other 0.6%