Skip to content

Commit 3093c57

Browse files
feat: add file upload to session working directory (#113)
## Summary Upload files from your phone (or any browser) directly into a terminal session's working directory. ### Features - **Upload endpoint**: `POST /api/sessions/:id/upload` — buffers raw file body and writes to the session's cwd with optional `X-Target-Dir` override - **Command Palette action**: "Upload files" under Session category (Ctrl+K) - **Confirmation modal**: shows file list with sizes, destination directory with folder browser - **Client-side validation**: files exceeding 10 MB shown in red with Upload button disabled - **Filename sanitization**: strips path traversal sequences, control characters, collapses whitespace; atomic dedup via exclusive-create (`wx` flag) with retry on collision - **Path security**: rejects relative `X-Target-Dir`, validates absolute paths, containment check ensures writes stay inside target directory - **Toast notification**: "3 files uploaded to /path/to/dir" on completion ### Testing - 14 new tests covering happy path, dedup, path traversal, invalid filenames, empty body, oversized files, custom target dir, non-existent dir, relative dir, non-directory target, read-only dir (skipped on Windows) - All tests pass ### Docs - Updated `docs/api.md`, `docs/usage-guide.md`, `docs/index.md`, `README.md`, `landing/index.html` Closes #110 --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 5281e28 commit 3093c57

10 files changed

Lines changed: 816 additions & 5 deletions

File tree

.github/copilot-instructions.md

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55
```bash
66
npm test # run all tests
77
node --test test/auth.test.js # run a single test file
8-
npm run test:coverage # tests + coverage (c8, 80% threshold)
8+
npm run test:coverage # tests + coverage (c8, 92% threshold)
99
npm run lint # syntax-check with node --check
1010
npm run format # format with Prettier
11+
npm run dev # start with auto-generated password
12+
npm start # start with defaults
1113
```
1214

1315
Pre-commit hooks (Husky + lint-staged) auto-format and syntax-check staged files.
@@ -32,6 +34,14 @@ Pre-commit hooks (Husky + lint-staged) auto-format and syntax-check staged files
3234

3335
**Port isolation:** Integration tests use port `0` (OS-assigned random port) to avoid conflicts. Never hardcode ports in tests.
3436

37+
### Playwright E2E
38+
39+
```bash
40+
npx playwright test # run e2e tests (chromium, sequential)
41+
```
42+
43+
E2E tests live in `test/e2e-*.test.js` and are excluded from `npm test`. See `playwright.config.js` for retries, reporters, and timeouts.
44+
3545
## Architecture
3646

3747
TermBeam is a Node.js CLI tool that exposes a local PTY (pseudo-terminal) over HTTP + WebSocket, with a mobile-optimized browser UI.
@@ -44,9 +54,14 @@ TermBeam is a Node.js CLI tool that exposes a local PTY (pseudo-terminal) over H
4454
- `routes.js` — Express routes for API (`/api/sessions`, `/api/auth`) and pages (`/terminal`, `/login`)
4555
- `websocket.js` — handles WebSocket messages (`attach`, `input`, `resize`, `output`, `exit`)
4656
- `tunnel.js` — optional DevTunnel integration for public URLs
57+
- `devtunnel-install.js` — DevTunnel CLI installer (cross-platform helper)
58+
- `preview.js` — local preview proxy for forwarding requests to a port
4759
- `logger.js` — structured logger with levels (error/warn/info/debug)
4860
- `shells.js` — cross-platform shell detection
4961
- `version.js` — detects version from package.json
62+
- `git.js` — git metadata and status parsing
63+
- `interactive.js` — interactive CLI setup wizard
64+
- `prompts.js` — reusable CLI prompt utilities
5065

5166
**CLI subcommands** dispatched in `bin/termbeam.js` before loading the server:
5267

@@ -75,9 +90,17 @@ TermBeam is a Node.js CLI tool that exposes a local PTY (pseudo-terminal) over H
7590
- **Prettier formatting** — single quotes, trailing commas, 100 char width, semicolons (`.prettierrc`)
7691
- **Cross-platform support** — must work on Windows, macOS, and Linux; CI tests on Ubuntu + Windows with Node 18, 20, 22
7792
- **PTY session cleanup**`pty.kill()` is async; the `onExit` callback removes the session from the Map
78-
- **Coverage exclusion**`src/tunnel.js` is excluded from coverage (requires external DevTunnel CLI)
93+
- **Coverage exclusion**`src/tunnel.js` and `src/devtunnel-install.js` are excluded from coverage (requires external DevTunnel CLI)
7994
- **Connection config** — server writes `~/.termbeam/connection.json` on start (port, host, password) for `termbeam resume` auto-discovery; removed on shutdown
8095

96+
## Environment Variables
97+
98+
- `PORT` — server port (default: 3456)
99+
- `TERMBEAM_PASSWORD` / `PTY_PASSWORD` — access password
100+
- `TERMBEAM_CWD` / `PTY_CWD` — working directory
101+
- `TERMBEAM_LOG_LEVEL` — log level (default: info)
102+
- `TERMBEAM_CONFIG_DIR` — location for `connection.json` (default: `~/.termbeam/`)
103+
81104
## Documentation
82105

83106
TermBeam has two layers of documentation that must stay in sync with code changes:
@@ -95,6 +118,16 @@ Preview docs locally: `pip install mkdocs-material && mkdocs serve`
95118

96119
Changes to `docs/` or `mkdocs.yml` pushed to `main` auto-deploy to GitHub Pages.
97120

121+
## CI and Publishing
122+
123+
- Release workflow: `.github/workflows/release.yml` bumps version, updates `CHANGELOG.md`, tags, and publishes to npm.
124+
- `prepublishOnly` runs `npm test` before publish.
125+
- `postinstall` fixes `node-pty` prebuild permissions (spawn-helper).
126+
- Landing site (`landing/`) deploys via `.github/workflows/landing.yml`.
127+
- Docs deploy via `.github/workflows/pages.yml`.
128+
129+
**IMPORTANT:** When asked to create a PR, open a PR, push to main, publish, release, or submit changes, **always use the `publish` skill**. It orchestrates the full workflow: local tests, lint, coverage, docs check, commit, push (or PR flow with proper branch naming), CI verification, and release. Do not manually run `gh pr create` or `git push origin main` — the skill handles all of this with the correct conventions.
130+
98131
## Demo Video
99132

100133
The demo video lives in `demo-video/` and is built with Remotion 4 + TypeScript + React, using the remotion agent skill "npx skills add remotion-dev/skills".

.github/skills/publish/SKILL.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ Must exit cleanly. If it fails, stop and report.
3636
npm run test:coverage
3737
```
3838

39-
Coverage must meet the 80% threshold. If it drops below 80%, stop and report
39+
Coverage must meet the 92% threshold. If it drops below 80%, stop and report
4040
which files/areas lost coverage. The coverage summary is written to
4141
`coverage/coverage-summary.json` — you can inspect it for details.
4242

@@ -58,6 +58,12 @@ Compare the changes against the documentation files. Check:
5858
- **`docs/getting-started.md`** — if installation or first-run steps changed.
5959
and any other docs files relevant to the changes.
6060

61+
Use read-only subagents to verify documentation accuracy. Assign each subagent
62+
to a doc area (e.g., CLI/config, API/WebSocket, security, architecture).
63+
Each subagent should compare the code changes to the relevant docs and report
64+
any mismatches or missing updates. If any subagent flags an issue, update the
65+
docs before proceeding.
66+
6167
If docs are outdated, update them before proceeding. Show the user what you updated.
6268

6369
If the changes are purely UI/cosmetic (e.g., CSS, HTML template changes in `public/`),
@@ -88,9 +94,11 @@ If there are multiple types of changes, use the most significant one.
8894

8995
- **PR flow:** If the user explicitly asked to create a PR:
9096
1. **Ensure an issue exists.** Every PR must reference an issue. If the
91-
user mentioned an issue number, use it. If not, create one:
97+
user mentioned an issue number, use it. Otherwise, verify that a matching
98+
issue already exists or create one:
9299

93100
```bash
101+
gh issue list --state open --search "<keywords>" --limit 5
94102
gh issue create --title "<short description>" --body "<brief context>"
95103
```
96104

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ termbeam -i # interactive setup wizard
7272

7373
- **Terminal search** with regex, match count, and prev/next navigation
7474
- **Command palette** (Ctrl+K / Cmd+K) for quick access to all actions
75+
- **File upload** — send files from your phone to the session's working directory
7576
- **Completion notifications** — browser alerts when background commands finish
7677
- **12 color themes** with adjustable font size
7778
- **Port preview** — reverse-proxy a local web server through TermBeam

docs/api.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,48 @@ Returned when the file's magic bytes don't match the declared `Content-Type` hea
328328

329329
Maximum file size is 10 MB.
330330

331+
#### `POST /api/sessions/:id/upload`
332+
333+
Upload a file to a session's working directory. The request body is the raw file content. The filename is provided via the `X-Filename` header and sanitized server-side (path traversal sequences are stripped). Duplicate filenames are auto-renamed (e.g., `file (1).txt`).
334+
335+
**Request headers:**
336+
337+
- `Content-Type`: The file's MIME type (e.g., `application/octet-stream`)
338+
- `X-Filename`: Original filename (required)
339+
- `X-Target-Dir`: Override destination directory (optional, defaults to session cwd)
340+
341+
**Response (200):**
342+
343+
```json
344+
{ "name": "script.sh", "path": "/home/user/project/script.sh", "size": 1024 }
345+
```
346+
347+
**Response (400):**
348+
349+
```json
350+
{ "error": "Missing X-Filename header" }
351+
```
352+
353+
```json
354+
{ "error": "Invalid filename" }
355+
```
356+
357+
```json
358+
{ "error": "Empty file" }
359+
```
360+
361+
**Response (404):**
362+
363+
```json
364+
{ "error": "Session not found" }
365+
```
366+
367+
**Response (413):**
368+
369+
```json
370+
{ "error": "File too large (max 10 MB)" }
371+
```
372+
331373
#### `GET /uploads/:id`
332374

333375
Serve a previously uploaded file by its opaque ID. Requires authentication.

docs/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ Built for developers who need quick remote terminal access without the hassle of
3636

3737
- **Terminal search** — <kbd>Ctrl+F</kbd> / <kbd>Cmd+F</kbd> with regex support
3838
- **Command palette** — <kbd>Ctrl+K</kbd> / <kbd>Cmd+K</kbd> for quick access to all actions
39+
- **File upload** — send files from your phone to the session's working directory via the command palette
3940
- **Command completion notifications** — browser alerts when commands finish in background tabs
4041
- **12 color themes** (dark, light, monokai, nord, dracula…) with adjustable font size
4142
- **Port preview** — reverse-proxy a local web server through TermBeam

docs/usage-guide.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,14 @@ Once you've [started TermBeam](getting-started.md) and connected from your devic
3939
- Browse categorized actions: **Session**, **Search**, **View**, **Share**, **Notifications**, **System**
4040
- A quick way to discover all available features and shortcuts
4141

42+
### File Upload
43+
44+
- Open the **command palette** and select **Upload files** to send files from your phone (or any browser) to the active session's working directory
45+
- Select one or more files — a confirmation modal shows the file list with sizes and the destination directory
46+
- Use the **folder browser** to choose a different target directory
47+
- Files exceeding 10 MB are flagged and cannot be uploaded
48+
- After upload, a toast notification confirms the count and destination
49+
4250
### Notifications
4351

4452
- Open the **command palette** (<kbd>Ctrl+K</kbd> / <kbd>Cmd+K</kbd>) and select **Toggle notifications** to enable **command completion notifications**

landing/index.html

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,15 @@ <h3>Folder browser</h3>
288288
commands.
289289
</p>
290290
</div>
291+
292+
<div class="feature-card">
293+
<div class="feature-icon-wrap">📤</div>
294+
<h3>File upload</h3>
295+
<p>
296+
Upload files from your phone directly into the session's working directory. Multi-file
297+
support with a confirmation dialog.
298+
</p>
299+
</div>
291300
</div>
292301
</div>
293302
</section>

0 commit comments

Comments
 (0)