You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Wraps InputPrompt in bordered Box (borderStyle=round, borderColor=cyan) for Copilot/Claude CLI style input zone. Includes NO_COLOR degradation and layout refinement.
Closesbradygaster#679
-**Context:** Implemented approved design spec from `docs/proposals/fixed-input-box-design.md` — wrap InputPrompt in bordered Box to match Copilot/Claude CLI UX.
254
+
-**Changes:**
255
+
-`App.tsx`: Wrapped InputPrompt in `<Box borderStyle="round" borderColor="cyan" paddingX={1}>` (line 398)
256
+
- Border degrades to `undefined` in NO_COLOR mode for graceful fallback
257
+
-`marginTop={1}` provides breathing room from live content region
258
+
-`InputPrompt.tsx`: Refactored return JSX to `flexDirection="column"` layout
259
+
- Prompt + value + cursor on first line
260
+
- Hint text (`Tab completes · ↑↓ history`) on second line (only when input empty)
261
+
- Processing state shows `[working...]` hint below spinner line (only when no buffer)
262
+
-**Design compliance:**
263
+
- ✅ Bordered box with `borderStyle="round"` (╔═╗ characters)
264
+
- ✅ NO_COLOR mode: border removed, plain text layout preserved
265
+
- ✅ Works at 40, 80, 120 column widths (inherits existing narrow/wide logic)
266
+
- ✅ Hint text inside box (not floating below)
267
+
- ✅ Standard buffer (no alt-screen) — preserves scrollback
268
+
-**Testing:** TypeScript compiles clean. Test suite: 24 failures vs 22 on main (2 additional, both unrelated to InputPrompt layout). 10+ pre-existing TerminalHarness timeouts known and acceptable.
269
+
-**Pattern:** Bordered Box is the canonical way to create visually distinct interaction zones in Ink TUIs. Border props (`borderStyle`, `borderColor`) automatically render box-drawing characters and degrade to plain layout when undefined.
-**Integration:** New `start` command in `cli-entry.ts` (lines 230-242)
769
+
770
+
**Dependencies Added:**
771
+
-`node-pty@1.1.0` — PTY for terminal mirroring (native addon, requires node-gyp)
772
+
-`ws@8.19.0` — WebSocket server (both CLI and SDK)
773
+
-`qrcode-terminal@0.12.0` — QR code display in terminal
774
+
-`@types/ws@8.18.1` (dev)
775
+
776
+
**Critical Issues — MUST FIX BEFORE MERGE:**
777
+
778
+
1.**Build broken (TypeScript errors):**
779
+
-`start.ts:117` — Cannot find module 'node-pty' (missing in tsconfig paths or needs `@types/node-pty`)
780
+
-`start.ts:177` — Binding element 'exitCode' implicitly has 'any' type (needs explicit type on `pty.onExit` callback)
781
+
-**All 18 tests fail** due to RemoteBridge/protocol functions not being exported properly from SDK
782
+
783
+
2.**Security — Command Injection Risk (HIGH):**
784
+
-`rc-tunnel.ts:47-49` — Uses `execFileSync` with string interpolation in `--labels` args. If `repo`, `branch`, or `machine` contain shell metacharacters, this is CWE-78. **MUST** pass label values as separate array elements without string interpolation.
785
+
-`rc-tunnel.ts:62-64` — Same issue in `port create` command.
786
+
-**Pattern violation:** Baer's decision (decisions.md) mandates `execFileSync` with array args, no string interpolation.
-`start.ts:119-122` and `rc.ts:184-188` — Hardcoded path `C:\ProgramData\global-npm\node_modules\@github\copilot\node_modules\@github\copilot-win32-x64\copilot.exe`.
796
+
- This breaks on macOS/Linux (no fallback logic shown).
797
+
- Cross-platform pattern should use `which copilot` or check `process.platform` and resolve from npm global dir programmatically.
798
+
799
+
5.**Rate Limiting — Weak HTTP Protection:**
800
+
-`bridge.ts:94-106` — HTTP rate limit is 30 req/min per IP. WebSocket has per-connection limit but no global connection limit per IP.
801
+
-**Attack vector:** Attacker can open 1000 WebSocket connections (each under rate limit) and DoS the bridge.
802
+
-**Fix:** Add global connection limit per IP (e.g., max 3 concurrent WS connections per IP).
803
+
804
+
6.**Session Token Exposure:**
805
+
-`start.ts:97-98` — Session token is appended to tunnel URL as query param and displayed in QR code + terminal output.
806
+
- This token is logged to terminal history, potentially visible in screenshots, and sent over tunnel URL (visible in proxy logs).
807
+
-**Better pattern:** Use the ticket exchange endpoint (`/api/auth/ticket`) instead — client POSTs token to get one-time ticket, uses ticket for WS connection.
808
+
-**Why it matters:** Token has 4-hour TTL, ticket has 1-minute TTL. Reduces window for replay attacks.
809
+
810
+
7.**Audit Log Location:**
811
+
-`bridge.ts:43` — Audit log goes to `~/.cli-tunnel/audit/`. This is not in `.squad/` directory.
812
+
-**Inconsistency:** All Squad state is in `.squad/` (decisions.md), but audit logs are elsewhere.
813
+
-**Recommendation:** Use `.squad/log/remote-audit-{timestamp}.jsonl` for consistency.
814
+
815
+
8.**Secret Redaction — Missing JWT Detection:**
816
+
-`bridge.ts:377-393` — `redactSecrets()` has patterns for GitHub tokens, AWS keys, Bearer tokens, JWTs.
- All 18 tests fail with "RemoteBridge is not a constructor" and "serializeEvent is not a function".
828
+
- Root cause: `packages/squad-sdk/src/remote/index.ts` exports `RemoteBridge` from `./bridge.js` but `bridge.ts` may not be built or exported correctly.
829
+
-**Build error** (from `npm run build`): TypeScript errors in `start.ts` block CLI build, so SDK may not have rebuilt.
- Consider renaming `start` command to `squad remote` or `squad tunnel` to avoid confusion with future `squad start` (which might mean "start the squad daemon").
873
+
874
+
**Decision File Needed:**
875
+
- This introduces a new CLI command paradigm (interactive terminal mirroring vs. agent orchestration). Needs a decision: "Remote access via devtunnel is Squad's mobile UX strategy" or "This is an experimental plugin".
# Decision: PR #547 Remote Control Feature — Architectural Review
2
+
3
+
**By:** Fenster
4
+
**Date:** 2026-03-01
5
+
**PR:**#547 "Squad Remote Control - PTY mirror + devtunnel for phone access" by tamirdresher (external)
6
+
7
+
## Context
8
+
9
+
External contributor Tamir Dresher submitted a PR adding `squad start --tunnel` command to run Copilot in a PTY and mirror terminal output to phone/browser via WebSocket + Microsoft Dev Tunnels.
10
+
11
+
## Architectural Question
12
+
13
+
Is remote terminal access via devtunnel + PTY mirroring in scope for Squad v1 core?
14
+
15
+
## Technical Assessment
16
+
17
+
**What works:**
18
+
- RemoteBridge WebSocket server architecture is sound
19
+
- PTY mirroring approach is technically correct
20
+
- Session management dashboard is useful
21
+
- Security headers and CSP are present
22
+
- Test coverage exists (18 tests, though failing due to build issues)
23
+
24
+
**Critical blockers:**
25
+
1.**Build broken** — TypeScript errors in `start.ts`, all tests failing
26
+
2.**Command injection vulnerability** — `execFileSync` with string interpolation in `rc-tunnel.ts`
27
+
3.**Native dependency** — `node-pty` requires C++ compiler (install friction)
**My take:** This belongs in a plugin, not core. External contributor did solid work on the WebSocket bridge, but Squad v1 needs to ship agent orchestration first. Remote access is a nice-to-have, not a v1 must-have.
68
+
69
+
If you want this in v1, we need a proposal (docs/proposals/) first.
0 commit comments