gstack/scripts
Garry Tan 8f3701b761
v1.16.0.0 feat: tunnel allowlist 17→26 + canDispatchOverTunnel pure function (#1253)
* feat: extend tunnel allowlist to 26 commands + extract canDispatchOverTunnel

Adds newtab, tabs, back, forward, reload, snapshot, fill, url, closetab to
TUNNEL_COMMANDS (matching what cli.ts and REMOTE_BROWSER_ACCESS.md already
documented). Each new command is bounded by the existing per-tab ownership
check at server.ts:613-624 — scoped tokens default to tabPolicy: 'own-only'
so paired agents still can't operate on tabs they don't own.

Refactors the inline gate check at server.ts:1771-1783 into a pure exported
function canDispatchOverTunnel(command). Same behavior as the inline check;
the difference is unit-testability without HTTP.

Adds BROWSE_TUNNEL_LOCAL_ONLY=1 test-mode flag that binds the second Bun.serve
listener with makeFetchHandler('tunnel') on 127.0.0.1 — no ngrok needed.
Production tunnel still requires BROWSE_TUNNEL=1 + valid NGROK_AUTHTOKEN.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* test: source-level guards + pure-function unit test + dual-listener behavioral eval

Three layers of regression coverage for the tunnel allowlist:

1. dual-listener.test.ts: replaces must-include/must-exclude with exact-set
   equality on the 26-command literal (the prior intersection-only style let
   new commands sneak into the source without test updates). Adds a regex
   assertion that the `command !== 'newtab'` ownership exemption at
   server.ts:613 still exists — catches refactors that re-introduce the
   catch-22 from the other side. Updates the /command handler test to look
   for canDispatchOverTunnel(body?.command) instead of the inline check.

2. tunnel-gate-unit.test.ts (new): 53 expects covering all 26 allowed,
   20 blocked, null/undefined/empty/non-string defensive handling, and alias
   canonicalization (e.g. 'set-content' resolves to 'load-html' which is
   correctly rejected since 'load-html' isn't tunnel-allowed).

3. pair-agent-tunnel-eval.test.ts (new): 4 behavioral tests that spawn the
   daemon under BROWSE_HEADLESS_SKIP=1 BROWSE_TUNNEL_LOCAL_ONLY=1, bind both
   listeners on 127.0.0.1, mint a scoped token via /pair → /connect, and
   assert: (a) newtab over tunnel passes the gate; (b) pair over tunnel
   403s with disallowed_command:pair AND writes a denial-log entry;
   (c) pair over local does NOT trigger the tunnel gate (proves the gate
   is surface-scoped); (d) regression for the catch-22 — newtab + goto on
   the resulting tab does not 403 with "Tab not owned by your agent".

All four tests run free under bun test (no API spend, no ngrok).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs: bump tunnel allowlist count 17 -> 26 in CLAUDE.md and REMOTE_BROWSER_ACCESS.md

Both docs already named the 9 new commands as remote-accessible (the operator
guide's per-command sections at lines 86-119 and 168, plus cli.ts:546-586's
instruction blocks). The allowlist count was the only place the drift was
visible. Also corrected REMOTE_BROWSER_ACCESS.md's denied-commands list:
'eval' is in the allowlist, not the denied list — prior doc was wrong.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore: bump version and changelog (v1.21.0.0)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore: re-version v1.21.0.0 -> v1.16.0.0 (lowest unclaimed slot)

The previous bump landed at v1.21.0.0 because gstack-next-version
advances past the highest claimed slot (v1.20.0.0 from #1252) rather
than picking the lowest unclaimed. v1.16-v1.18 are unclaimed and
v1.16.0.0 preserves monotonic version ordering on main once #1234
(v1.17), #1233 (v1.19), and #1252 (v1.20) merge after us.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(ci): version-gate enforces collisions, allows lower-but-unclaimed slots

The gate was rejecting any PR VERSION below the util's next-slot
recommendation, even when the lower slot was unclaimed. This blocked
PRs that legitimately want to land at an unclaimed slot below the queue
max — which is what /ship should pick when the goal is monotonic version
ordering on main (lower-numbered PRs landing first preserves order; the
util's "advance past max claimed" semantics only optimizes for fresh
runs picking unique slots, not for queue ordering on merge).

New gate logic:

1. Hard-fail if PR VERSION <= base VERSION (no actual bump).
2. Hard-fail if PR VERSION exactly matches another open PR's VERSION
   (real collision).
3. Pass otherwise. If the PR is below the util's suggestion, emit an
   informational ::notice:: explaining the slot is unclaimed.

The util's output stays informational — it tells fresh /ship runs what
the next-up slot should be, but the gate only blocks actual conflicts.
This is a strict relaxation: every PR that passed the old gate also
passes the new one.

Confirmed by dry-run against the current queue (4 open PRs claiming
1.17.0.0, 1.19.0.0, 1.21.1.0, 1.22.0.0):
  - v1.16.0.0  → pass with informational notice (unclaimed)
  - v1.17.0.0  → fail (collision with #1234)
  - v1.15.0.0  → fail (no bump from base)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 00:57:28 -07:00
..
app feat: GStack Browser — double-click AI browser with anti-bot stealth (#695) 2026-04-04 10:17:05 -07:00
host-adapters feat: declarative multi-host platform + OpenCode, Slate, Cursor, OpenClaw (v0.15.5.0) (#793) 2026-04-04 15:32:20 -07:00
resolvers v1.15.0.0 feat: slim preamble + real-PTY plan-mode E2E harness (#1215) 2026-04-26 13:55:13 -07:00
analytics.ts feat: safety hook skills + skill usage telemetry (v0.7.1) (#189) 2026-03-18 23:57:59 -05:00
archetypes.ts feat: gstack v1 — simpler prompts + real LOC receipts (v1.0.0.0) (#1039) 2026-04-18 15:05:42 +08:00
build-app.sh feat: GStack Browser — double-click AI browser with anti-bot stealth (#695) 2026-04-04 10:17:05 -07:00
compare-pr-version.ts v1.16.0.0 feat: tunnel allowlist 17→26 + canDispatchOverTunnel pure function (#1253) 2026-04-28 00:57:28 -07:00
detect-bump.ts v1.11.0.0 feat(ship): workspace-aware version allocation (#1168) 2026-04-23 23:03:27 -07:00
dev-skill.ts feat: Wave 3 — community bug fixes & platform support (v0.11.6.0) (#359) 2026-03-23 22:15:23 -07:00
discover-skills.ts fix: community PRs + security hardening + E2E stability (v0.12.7.0) (#552) 2026-03-26 23:21:27 -06:00
eval-compare.ts feat: worktree isolation for E2E tests + infrastructure elegance (v0.11.12.0) (#425) 2026-03-23 23:05:22 -07:00
eval-list.ts feat: worktree isolation for E2E tests + infrastructure elegance (v0.11.12.0) (#425) 2026-03-23 23:05:22 -07:00
eval-select.ts feat: diff-based test selection for E2E and LLM-judge evals (v0.6.1.0) (#139) 2026-03-17 18:45:41 -05:00
eval-summary.ts feat: worktree isolation for E2E tests + infrastructure elegance (v0.11.12.0) (#425) 2026-03-23 23:05:22 -07:00
eval-watch.ts feat: /land-and-deploy, /canary, /benchmark + perf review (v0.7.0) (#183) 2026-03-21 14:31:36 -07:00
garry-output-comparison.ts fix: remove hardcoded author emails from throughput script 2026-04-18 15:36:50 +08:00
gen-skill-docs.ts v1.11.1.0 fix: plan-mode handshake + canUseTool test harness (#1182) 2026-04-24 00:04:53 -07:00
host-config-export.ts feat: declarative multi-host platform + OpenCode, Slate, Cursor, OpenClaw (v0.15.5.0) (#793) 2026-04-04 15:32:20 -07:00
host-config.ts feat: OpenClaw integration v2 — prompt is the bridge (v0.15.9.0) (#816) 2026-04-05 02:23:59 -07:00
jargon-list.json feat: gstack v1 — simpler prompts + real LOC receipts (v1.0.0.0) (#1039) 2026-04-18 15:05:42 +08:00
models.ts feat(v1.5.2.0): Opus 4.7 migration — model overlay, voice, routing (#1117) 2026-04-22 01:06:22 -07:00
one-way-doors.ts feat: gstack v1 — simpler prompts + real LOC receipts (v1.0.0.0) (#1039) 2026-04-18 15:05:42 +08:00
preflight-agent-sdk.ts v1.13.0.0 feat: add Claude outside-voice skill (#1212) 2026-04-25 11:52:48 -07:00
psychographic-signals.ts feat: gstack v1 — simpler prompts + real LOC receipts (v1.0.0.0) (#1039) 2026-04-18 15:05:42 +08:00
question-registry.ts v1.12.1.0 fix: remove vestigial plan-mode handshake (#1185) 2026-04-24 02:11:24 -07:00
setup-scc.sh feat: gstack v1 — simpler prompts + real LOC receipts (v1.0.0.0) (#1039) 2026-04-18 15:05:42 +08:00
skill-check.ts v1.15.0.0 feat: slim preamble + real-PTY plan-mode E2E harness (#1215) 2026-04-26 13:55:13 -07:00
slop-diff.ts security: tunnel dual-listener + SSRF + envelope + path wave (v1.6.0.0) (#1137) 2026-04-21 21:58:27 -07:00
update-readme-throughput.ts feat: gstack v1 — simpler prompts + real LOC receipts (v1.0.0.0) (#1039) 2026-04-18 15:05:42 +08:00