Skip to content

feat: implement program status protocol - #729

Open
jroddie wants to merge 1 commit into
box:mainfrom
jroddie:program-status-protocol
Open

jroddie wants to merge 1 commit into
box:mainfrom
jroddie:program-status-protocol

Conversation

@jroddie

@jroddie jroddie commented Oct 20, 2026

Copy link
Copy Markdown

Summary

Reports what the CLI is doing using the Program Status Protocol (OSC 7501), so terminals that support it can show command state outside the terminal (for example on tabs or in a session list). Terminals that don't support it ignore the sequences.

  • Reports working when a command starts, with progress for downloads, chunked uploads, and bulk input; blocked during confirmation prompts, questions, and browser sign-in; and done, error, or idle (cancelled or interrupted) when it ends.
  • Reports go only to stderr and only when stderr is a TTY, so stdout and redirected output are unchanged. Nothing is reported for --help.
  • Can be turned off with BOX_PROGRAM_STATUS=0 or "enableProgramStatus": false in ~/.box/settings.json, and forced on with BOX_PROGRAM_STATUS=1. Also off when TERM=dumb.
  • Messages are sanitized (ANSI, control characters, OSC sequences, and bidi overrides removed) and truncated on character boundaries to stay within protocol limits.
  • The reporter never throws if writing to stderr fails, and only listens for SIGINT/SIGTERM while a command is running.

Changes

  • src/program-status.js: new ProgramStatus reporter plus formatReport, sanitizeText, truncateUtf8, and isEnabled.
  • src/box-command.js: starts and finishes reporting around each command, and reports blocked around confirm() and prompt().
  • Commands with progress or user interaction (files/folders upload and download, files:versions, files:zip, login, logout, configure:environments:delete, configure:environments:set-current) and src/modules/upload.js report progress and prompts.

Testing

  • New test/program-status.test.js covers formatting, sanitizing, enablement, state transitions, signals, prompts, and full command runs, including bulk progress and failures.
  • New login tests cover blocked on auth while waiting for the browser, plus timeout and invalid-callback errors.
  • test/helpers/setup.js sets BOX_PROGRAM_STATUS=0 for the whole test run so existing output assertions aren't affected when tests run in a TTY.
  • Full suite: 1234 passing, 9 pending. ESLint clean.

Made with Cursor

Co-authored-by: Cursor <cursoragent@cursor.com>
@jroddie
jroddie requested a review from a team October 20, 2026 03:00
@CLAassistant

CLAassistant commented Oct 20, 2026 •

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

Comment thread src/program-status.js

/**
* Reports what the CLI is doing to the terminal with the Program Status
* Protocol (OSC 7501): https://www.superlogical.com/rex/docs/build/program-status

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think it's worth indicating against which revision of OSC 7501 this implementation supports? The newest revision is 0.4

Comment thread src/program-status.js
if (ENABLE_VALUES.has(override)) {
return true;
}
if (settings && settings.enableProgramStatus === false) {

@mwwoda mwwoda Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should consider adding an option to configure it from settings (e.g. --enable-program-status, --no-enable-program-status and document this setting)

@mwwoda mwwoda Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just sharing my thoughts on feature detection/enablement part

We could consider feature detection as per Program Status spec

To avoid waiting, send primary device attributes (CSI c) right after the query. Every terminal answers that, so if its reply arrives first, the protocol is unsupported.

But in general terminal emulators should ignore unknown OSC sequences

Detection is optional. Terminals MUST ignore unknown OSCs, so a program MAY send reports without querying first, for example when it cannot read replies.

Additionally, such detection adds overhead to every CLI command

Default opt-in should be a deliberate choice, but FYI it should be safe

Comment thread src/box-command.js
{ kind: 'permission' }
);

if (!answers.confirmation && !this.isBulk) {

@mwwoda mwwoda Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

output() calls confirm() at the very end, after the result has already been set. In bulk mode, isBulk has also been reset to false by then, so the bulk guard here doesn't apply. cancel() then overwrites whatever outcome was stored.

I could repro it with the following

printf 'fileID\n111\n222\n333\n' > bulk.csv; echo '{}' > out.json
BOX_PROGRAM_STATUS=1 ./bin/run files:get --bulk-file-path bulk.csv --token fake \
    --save-to-file-path out.json 2>status.log

Then answering NO to a prompt

Decoded reports:

  working   100  Bulk files:get: 3/3 entries
  blocked   permission  File .../out.json already exists — overwrite?
  working   100  Bulk files:get: 3/3 entries
  idle      Cancelled box files:get

Answering "Yes" gives

error  Bulk files:get: 3 of 3 entries failed

Which is what I'd expect in both cases, no matter the prompt answer. I think we may consider cancel to keep the outcome already set

Comment thread src/modules/upload.js
}

function runChunkedUpload(uploader, size) {
function runChunkedUpload(uploader, size, onProgress) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-blocking: the bar and PSP progress are calculated separately here and in files:download. That's fine for now, but a shared helper later would stop them from drifting apart.

@coveralls

Copy link
Copy Markdown

Coverage Report for CI Build 39361948155

Coverage increased (+0.3%) to 85.19%

Details

  • Coverage increased (+0.3%) from the base build.
  • Patch coverage: 22 uncovered changes across 9 files (179 of 201 lines covered, 89.05%).
  • 8 coverage regressions across 4 files.

Uncovered Changes

File Changed Covered %
src/commands/login.js 16 9 56.25%
src/commands/logout.js 4 0 0.0%
src/modules/upload.js 4 0 0.0%
src/program-status.js 116 114 98.28%
src/commands/configure/environments/delete.js 1 0 0.0%
src/commands/configure/environments/set-current.js 1 0 0.0%
src/commands/files/download.js 5 4 80.0%
src/commands/files/upload.js 5 4 80.0%
src/commands/files/versions/upload.js 4 3 75.0%
Total (14 files) 201 179 89.05%

Coverage Regressions

8 previously-covered lines in 4 files lost coverage.

File Lines Losing Coverage Coverage
src/commands/login.js 4 60.78%
src/commands/logout.js 2 9.24%
src/commands/configure/environments/delete.js 1 25.0%
src/commands/configure/environments/set-current.js 1 34.78%

Coverage Stats

Coverage Status
Relevant Lines: 6249
Covered Lines: 5571
Line Coverage: 89.15%
Relevant Branches: 2259
Covered Branches: 1677
Branch Coverage: 74.24%
Branches in Coverage %: Yes
Coverage Strength: 674.06 hits per line

💛 - Coveralls

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants