Commit Graph
10 Commits
Author SHA1 Message Date
uma-prasadandCursor 77960229b8 fix(doctor): detect missing native binary during runtime checks (#450)
* fix(doctor): report missing npm native binary in wrapper

Co-authored-by: Cursor <[email protected]>

* fix(doctor): preserve JSON output for missing native binary

* fix(doctor): preserve doctor CLI behavior in wrapper fallback

Co-authored-by: Cursor <[email protected]>

* style: gofmt npmwrapper doctor fallback tests

Co-authored-by: Cursor <[email protected]>

* fix(doctor): reuse shared Bun recovery helpers in wrapper fallback

Extract bunRecoveryParagraph and related helpers so the doctor missing-binary text path and the generic wrapper bail share the same copy. Add regression tests with ZERO_WRAPPER_SIMULATE_BUN to guard against future drift.

Co-authored-by: Cursor <[email protected]>

---------

Co-authored-by: Cursor <[email protected]>
2026-07-18 22:49:19 +08:00
Kevin Codex 5e1405d0b7 feat(npm): ship the native binary as platform optionalDependencies (#626)
* feat(npm): ship the native binary as platform optionalDependencies

Replace the postinstall downloader with Codex-style platform packages:
suffixed versions of @gitlawb/zero (X.Y.Z-<platform>-<arch>) carrying the
binary, sandbox helpers, and the vendored agent-browser/tuistory tree,
referenced from the wrapper via npm: aliases in optionalDependencies.

The published wrapper has no lifecycle scripts and no dependencies, so
npm/Bun/pnpm installs are silent: no EBADENGINE from agent-browser's
node>=24 engines pin, no allow-scripts warnings, no network outside the
npm registry. The old downloader remains as a checksum-verified first-run
fallback for --omit=optional installs and unsupported platforms.

scripts/npm/build-platform-packages.mjs assembles the payloads from the
release archives at publish time: it materializes npm's symlink .bin
shims into regular sh shims (npm pack silently drops symlinks) and prunes
agent-browser's other-platform binaries (~65 MB per package). The publish
job uploads platform versions first under --tag platform, asserts the
latest dist-tag survived (platform versions are semver prereleases of the
wrapper version), then publishes the wrapper.

See docs/NPM_PACKAGING.md for the architecture and publishing rules.

* fix(npm): address PR review — LICENSE in payloads, dist-tag lookup guard, accurate fallback and platform docs

- Ship the repo LICENSE in both the wrapper and platform payloads (npm only
  includes a license file physically present in the packed directory).
- Guard the platform-publish dist-tag lookup with || true so a transient
  npm view failure (or a bootstrap publish with no latest tag) cannot abort
  the job between the platform and wrapper publishes.
- Gate the wrapper's fallback downloader on the release matrix so platforms
  with no asset (windows-arm64 and unknown combos) skip the spawn instead of
  attempting it every run; keep deliberate retry-on-failure for supported
  platforms and say so honestly in the notice and docs.
- Correct Windows support wording everywhere: x64 only, ARM runs under
  emulation (README, README_ZH, INSTALL.md incl. the release-target list).
2026-07-11 23:25:41 +08:00
Vasanth T 1dd884d6e9 chore(launch): MIT license, windows-arm64 install guard, TUI TTY guard (#340)
* chore(launch): MIT license field, windows-arm64 install guard, TUI TTY guard

Three launch-readiness fixes the docs cleanup doesn't cover:

- package.json: set "license": "MIT" (was the placeholder "SEE LICENSE IN
  LICENSE"), matching the LICENSE file and the README badge.
- scripts/postinstall.mjs: skip gracefully on windows-arm64. (win32, arm64)
  resolves to a valid platform/arch but the release matrix ships no
  windows-arm64 artifact, so the installer would download a 404 and hard-fail
  npm install. Treat it like any unsupported target (warnSkip, exit 0) and point
  to the x64 build (which runs under emulation on Windows on ARM) or source.
- internal/tui/run.go: fail fast when stdin is not a terminal. The interactive
  shell otherwise blocks forever in the Bubble Tea event loop on piped/redirected
  input (e.g. `echo "" | zero`); now it prints guidance toward `zero exec` and
  exits non-zero. Dependency-free ModeCharDevice check (no x/term added).

Tests: windows-arm64 skips with exit 0; the interactive shell returns non-zero
(not a hang) on non-TTY stdin.

* fix(tui): use term.IsTerminal for the stdin guard; pin exit code in test

Address review on the no-TTY guard:
- Replace the os.Stdin.Stat()/ModeCharDevice heuristic with term.IsTerminal
  (github.com/charmbracelet/x/term, already in the module graph via bubbletea).
  It is a true TTY check — it rejects pipes, regular files, and non-terminal
  char devices like /dev/null — and fails closed: anything not a verified
  terminal blocks the interactive shell.
- The test now asserts the documented exit code 2 from the guard, not just a
  non-zero result, so it proves the stdin guard fired.
2026-06-28 17:13:34 +05:30
Anandan f1502f0bfe Add local control browser and terminal tools (#319)
* Add local control browser and terminal tools

Add configurable local browser, desktop, and terminal control helpers with packaged helper discovery for install and release flows.

Improve sandbox scope classification for local control commands and shared temporary paths.

Add structured browser actions, terminal session line input/key normalization/read support, and artifact capture for local-control outputs.

Tested: GOCACHE=/tmp/zero-go-cache go test ./internal/localcontrol ./internal/tools ./internal/cli ./internal/config -run 'Test.*LocalControl|TestTerminal|TestBrowser|TestDesktop|TestCapture'

Tested: GOCACHE=/tmp/zero-go-cache go test ./internal/localcontrol ./internal/tools ./internal/cli ./internal/config ./internal/agent

Tested: tuistory smoke launched bash, sent interactive input, waited for expected output, and captured a no-cursor snapshot

Tested: git diff --cached --check

* Fix npm wrapper helper manifest test on macOS

Canonicalize expected helper paths in the npm wrapper manifest test so macOS /var and /private/var temp path aliases compare correctly.

Tested: GOCACHE=/tmp/zero-go-cache go test ./internal/npmwrapper

Tested: git diff --check

* Harden local control review fixes

Respect resolved local-control config before tool listing and filter validation while preserving stream-json usage error ordering.

Harden browser, desktop, and artifact tool validation; guard Linux-only app launch behavior; clear stale helper manifests from the npm wrapper.

Make helper packaging lockfile-based with npm ci and add installer/package tests for helper staging.

Tested: focused local-control/tool/CLI/release/install/sandbox suite

Tested: isolated full go test ./...

Tested: macOS and Windows compile-only checks for touched packages
2026-06-26 08:22:56 +05:30
Vasanth T 33da6b6454 feat(npm): publish @gitlawb/zero with a postinstall binary installer (#284)
Make `npm install -g @gitlawb/zero` deliver a working CLI. bin/zero.js already
execs an adjacent binary, but nothing fetched it and the name `zero` is taken
on npm. This wires up delivery:

- package.json: rename to @gitlawb/zero; add a postinstall hook, files, engines,
  os/cpu, repository, and a license placeholder (the LICENSE file is still
  pending and tracked separately).
- scripts/postinstall.mjs: detect platform/arch (mapping mirrors
  internal/release/release.go — darwin->macos, amd64->x64), download
  zero-v{version}-{os}-{arch}.{tar.gz|zip} from the matching GitHub Release,
  verify its SHA-256, and extract into place. Extraction goes to a temp dir and
  copies only known binary basenames, so a crafted archive cannot escape the
  package (no zip-slip). Idempotent via a version marker; ZERO_SKIP_DOWNLOAD and
  unsupported platforms are non-fatal skips.

Security hardening (from an adversarial review):
- require https for the download origin (the same-origin .sha256 makes TLS the
  load-bearing integrity control); ZERO_ALLOW_INSECURE_DOWNLOAD=1 opts out for
  local testing, and an https->http redirect is refused.
- the checksum parser is anchored per line and validates the filename field.
- cap the download size; warn when an optional sandbox helper is absent.

release-artifacts.yml: on a tag push, assert the git tag equals
v{package.json version} before packaging — zero-release names assets from
package.json while the release publishes under the tag, so any drift would 404
every install (install.sh, install.ps1, and npm alike).

Tests (internal/npmwrapper): package.json now requires exactly a postinstall
hook (no repo build scripts) plus name/license/repository/files; new
node-driven tests cover the per-platform asset plan, the skip env, and the
unsupported-platform path. Verified end-to-end against a local mirror:
download -> verify -> extract places the binary, a tampered checksum is
rejected, and http is refused without the opt-in.

Going live still needs two maintainer steps: create the `gitlawb` npm org +
NPM_TOKEN, and publish the first v0.1.0 release so there is an asset to fetch.
2026-06-21 15:56:48 +05:30
KRATOSandKRATOS f34588e725 Test: raise the npm-wrapper Windows spawn timeout to de-flake CI (#140)
TestNodeWrapperReportsMissingNativeBinary intermittently hit its 30s Windows deadline with empty output on loaded CI runners (cold node spawn), then passed in ~2.5s on the next run — a runner flake, not a wrapper bug. Raise the Windows timeout to 90s so a slow cold start has headroom; non-Windows stays at 10s.

Co-authored-by: KRATOS <[email protected]>
2026-06-08 11:39:11 +05:30
Anandan be8100e526 Add specialist lifecycle accounting (#120)
* Add specialist lifecycle accounting

Record specialist start and stop events in parent sessions and roll child stream-json usage into one parent usage event.

Background TaskOutput performs the same roll-up idempotently for persisted completed tasks.

Tested: GOCACHE=/tmp/zero-go-cache go test ./internal/specialist

Tested: GOCACHE=/tmp/zero-go-cache go test ./...

Tested: git diff --check

* Harden npm wrapper smoke test on Windows

Clear inherited NODE_OPTIONS when executing the Node wrapper fixture and allow a longer Windows timeout for hosted-runner cold starts.

Tested: GOCACHE=/tmp/zero-go-cache go test ./internal/npmwrapper ./internal/specialist

Tested: GOCACHE=/tmp/zero-go-cache go test ./...

Tested: git diff --check

* Address specialist accounting review feedback

Normalize mixed usage event totals before roll-up, preserve started child exit codes on execution errors, and make accounting payload assertions fail loudly on missing or mistyped fields.

Tested: GOCACHE=/tmp/zero-go-cache go test ./internal/specialist ./internal/npmwrapper

Tested: GOCACHE=/tmp/zero-go-cache go test ./...

Tested: git diff --check
2026-06-07 16:27:23 +05:30
0c77dad190 feat(zerocommands): add typed sandbox policy, risk, violation, and decision snapshots (#92)
* feat(zerocommands): add typed sandbox policy, risk, violation, and decision snapshots

The merged permission and verification event contracts (#82) added
typed snapshots for config, providers, models, and sessions, and
PR #89 added the typed snapshot for the persistent sandbox grant
store, but the live sandbox policy, the live risk classification,
the live violation, and the live decision were still exposed as
the raw backend structs. The TUI render path, the headless
'zero sandbox policy --json' and 'zero sandbox decide' commands,
the audit log, and PR/CI automation all need typed snapshots so
they do not hand-roll map[string]any payloads and so they do not
have to re-implement the runtime default-mode translation
themselves.

This commit adds five typed snapshot constructors and a bundled
plan snapshot to internal/zerocommands.

SandboxPolicySnapshot
  - mode, network, enforceWorkspace, denyDestructiveShell, and
    allowPolicyOnlyRunner are copied verbatim from the underlying
    sandbox.Policy.
  - effectiveMode is the resolved policy mode. An empty Mode in
    the input falls back to ModeEnforce, which is the runtime
    default. Consumers can read EffectiveMode to render the 'what
    is actually in effect' line without re-implementing the
    default.
  - effectiveMode is JSON-omitted when empty so the snapshot
    shape stays stable for downstream consumers.

SandboxRiskSnapshot
  - level, categories, reason.
  - categories is copied and sorted alphabetically so the JSON
    output is deterministic.
  - reason is trimmed.

SandboxViolationSnapshot
  - code, toolName, action, path, reason, recoverable.
  - The pointer-returning helper returns nil for a nil input so
    callers can chain it through SandboxDecisionSnapshot without
    nil checks at the call site.

SandboxBackendSnapshot
  - name, available, platform, fallback, commandWrapping,
    nativeIsolation, executable, message.
  - executable and message are trimmed.

SandboxPlanSnapshot
  - bundles policy, backend, restrictions, and workspaceRoot into
    one payload so 'zero sandbox policy --json' can return one
    typed result describing the full sandbox posture.
  - each entry of restrictions is trimmed, whitespace-only
    entries are dropped, and the result is sorted alphabetically.
  - workspaceRoot is trimmed and JSON-omitted when empty.

SandboxDecisionSnapshot
  - action, reason, risk, grantMatched, grant, violation.
  - The optional grant pointer is converted with
    SandboxGrantSnapshotFromGrant (from PR #89) so the JSON shape
    matches the persistent-grant snapshot exactly.
  - The optional violation pointer is converted with
    SandboxViolationSnapshotFromViolation so the snapshot always
    carries the same shape regardless of the decision outcome.
  - When grant or violation is nil, the field is JSON-omitted.

Tests
  - TestSandboxPolicySnapshotFromPolicyFillsEffectiveMode:
    verifies EffectiveMode is enforced for an empty Mode, set
    to ModeDisabled for an explicit disabled mode, and
    preserved for an explicit enforced mode.
  - TestSandboxRiskSnapshotFromRiskSortsCategoriesAndTrimsReason:
    verifies alphabetical sort, trimmed reason, and that a nil
    Categories input produces a nil output (no spurious empty
    slice).
  - TestSandboxViolationSnapshotFromViolationHandlesNilAndTrims:
    verifies the nil-input contract and the trim behaviour for
    every string field.
  - TestSandboxBackendSnapshotFromBackendCopiesAllFields:
    verifies the boolean and string fields round-trip and the
    trim behaviour for the executable and message.
  - TestSandboxPlanSnapshotFromPlanSortsRestrictionsAndTrimsRoot:
    verifies alphabetical sort, per-entry trim, and workspaceRoot
    trim.
  - TestSandboxDecisionSnapshotFromDecisionAllowBranchHasNoViolation:
    verifies the allow path has no grant and no violation.
  - TestSandboxDecisionSnapshotFromDecisionPersistentDenyCarriesGrantAndViolation:
    verifies the deny path carries both the resolved grant and
    the violation, and that the grant snapshot matches the
    persistent-grant snapshot shape from PR #89.
  - TestSandboxDecisionSnapshotJSONShapeIsStable: verifies the
    stable keys and the nil-pointer omission.
  - TestSandboxPolicySnapshotJSONOmitsEmptyEffectiveMode:
    verifies the omitempty tag.

Verification
  - go build ./...
  - go vet ./...
  - go test -count=1 -p 1 ./internal/zerocommands/... \
           ./internal/sandbox/... ./internal/redaction/...

DRI: Gnanam (runtime core owner per WORK_SPLIT_PRD.md §4). Closes
the typed-snapshot gap for the live sandbox surface that the TUI
render path, the 'zero sandbox' headless commands, the audit log,
and PR/CI automation consume.

* fix(npmwrapper): make copyWrapperFixture create package.json with type:module

This ensures the isolated wrapper .js fixture is always treated as ESM (matching real package installs).

Previously, without it, Node treated the .js as CJS on some platforms/runners (especially Windows), causing SyntaxError before the missing-native console.error path.

This was causing TestNodeWrapperReportsMissingNativeBinary to get empty output + timeout on windows-latest smoke jobs (e.g. in PR 92).

The test now reliably hits the error path and captures the expected message on all platforms.

Also improves robustness of the npm wrapper smoke test added in #88.

---------

Co-authored-by: Gnanam <gnanam@local>
Co-authored-by: KRATOS <[email protected]>
2026-06-06 14:50:01 +05:30
Anandan 442f24c7de Remove Bun installer test dependency (#91)
* Remove Bun installer test dependency

* Harden installer smoke tests

* Stabilize npm wrapper subprocess tests

* Make npm wrapper fixture explicit ESM
2026-06-06 13:58:08 +05:30
Anandan ce8dec087d Replace npm wrapper with Node entrypoint (#88)
* Replace npm wrapper with Node entrypoint

* Fix npm wrapper test portability
2026-06-06 13:21:57 +05:30