Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
7ba6732
feat: add daemon mode with IPC, service installation, and CLI subcomm…
Niladri2003 May 3, 2026
ae8ccd9
feat: enhance help message and improve error handling in IPC server a…
Niladri2003 May 4, 2026
9b17138
WIP: implementing daemon tunnel management in cli
Niladri2003 May 6, 2026
2972782
feat: enhance daemon management and CLI commands, improve error handl…
Niladri2003 May 11, 2026
983ade0
fix:improve Windows daemon spawning
Niladri2003 May 13, 2026
8617db0
feat: Add md files for project overview, common commands, architectur…
Niladri2003 May 14, 2026
cde9ea6
fix: xff by setting reverseProxy value to true.
Niladri2003 May 14, 2026
9b74f07
feat: Electron daemon spawn, origin tagging, per-tunnel log toggle
Niladri2003 May 18, 2026
391152b
feat(daemon): enhance daemon management and IPC communication
Niladri2003 May 22, 2026
29dc11c
feat: Simplified TunnelClient, Ts types introduced in IPCSERVER and I…
Niladri2003 May 22, 2026
be150b4
refactor: subcommands, handler and ipc codes are refactored into sub …
Niladri2003 May 23, 2026
6958862
refactor: update import paths for IPC and logger modules across daemo…
Niladri2003 May 25, 2026
9d891b0
feat(daemon): implement persistent daemon configuration and log rotation
Niladri2003 May 25, 2026
9d67049
refactor: some enhancements to handle multi terminal env
Niladri2003 May 25, 2026
bef0661
feat(daemon): better start UX, clean external stop, dedup tunnel guard
Niladri2003 May 25, 2026
6cb8d76
feat: update @pinggy/pinggy dependency to version 0.4.8
Niladri2003 May 26, 2026
db1b9ff
feat(daemon): implement shutdown handlers
Niladri2003 May 26, 2026
989d6f3
feat: imports modified
Niladri2003 May 26, 2026
23a6779
Add end-to-end tests for daemon lifecycle, config management, and IPC…
Niladri2003 May 27, 2026
7f2f686
fix: Readme updated
Niladri2003 May 27, 2026
306a91e
fix: Fixed CI/CD failing tests by properly identifying foreground and…
Niladri2003 May 27, 2026
3e85a1c
feat(docs): add architecture and tunnel lifecycle documentation
Niladri2003 May 27, 2026
f38d5f6
feat: update tunnel configuration options and enhance help messages. …
Niladri2003 May 27, 2026
2201f75
Minor issue related to full-config-save test fixed
Niladri2003 May 27, 2026
dee2b1a
refactor: Updated TypeScript configuration for stricter type checkin…
Niladri2003 May 28, 2026
6a819f7
feat: implement session mode handling for tunnels and enhance lifecyc…
Niladri2003 May 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ logs/
src/
node_modules/
test/
docs/


index-test.js
Expand Down
1 change: 1 addition & 0 deletions AGENTS.md
112 changes: 112 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
## Project Overview

Pinggy CLI (`@pinggy/cli`) is a Node.js CLI tool for creating and managing Pinggy tunnels. It runs as **two processes**: a short-lived foreground CLI that the user invokes, and a long-running daemon that owns every tunnel. The CLI talks to the daemon over HTTP + WebSocket on `127.0.0.1`. Built with TypeScript, wraps the `@pinggy/pinggy` SDK in the daemon, and ships a blessed-based TUI plus remote control via WebSocket.

## Common Commands

```bash
# Build (produces CJS + ESM in dist/)
npm run build

# TypeScript type-check only (output to dist_tsc/)
npm run build:tsc

# Dev workflow (link SDK, build, link locally)
npm run dev

# Build platform binaries (via pkg)
npm run pack:all

# E2E suite against a packaged binary (see test/e2e/README.md)
node test/e2e/run.cjs out/pinggy-<platform>
```

## Architecture

### Two-process model

The CLI binary has 3 entry modes, dispatched in `src/main.ts`:

1. **Daemon child** (`--_daemon-child` flag). Calls `runDaemonChild()` in `src/daemon/daemonChild.ts`. The CLI re-execs itself with this flag when it needs to spawn a daemon; users never invoke it directly.
2. **Subcommand** (`config`, `start`, `stop`, `ps`, `attach`, `daemon`, `d`). Routes to `handleSubcommand()` in `src/cli/subcommands.ts`.
3. **Legacy single-tunnel** (no subcommand). Routes to `buildAndStartTunnel()` for flags like `-l 3000` or `-R0:localhost:3000`.

The daemon owns the `TunnelManager` singleton, the `@pinggy/pinggy` SDK, the web debugger server, and any `--serve` file servers. The CLI owns argument parsing, the TUI, the remote-management WebSocket client, and the IPC client to the daemon.

### IPC: HTTP + WebSocket on localhost

The daemon listens on `127.0.0.1` with an OS-assigned port (recorded in `daemon.json`):

- **HTTP** for request/response (`src/daemon/ipcServer.ts`): `GET /ping`, `GET /tunnels`, `POST /tunnels/start`, `POST /tunnels/start-config`, `POST /tunnels/stop`, `POST /tunnels/restart`, `POST /shutdown`, plus v1 compatibility routes used by remote management.
- **WebSocket** for streaming tunnel events (schema in `src/daemon/wsProtocol.ts`). Client subscribes by `tunnelId`; daemon emits `tunnel_event` frames keyed by event name.

CLI code calls `TunnelClient` (`src/daemon/tunnelClient.ts`), the public facade that combines HTTP RPC with WebSocket event dispatch. `IPCClient` (`src/daemon/ipcClient.ts`) is the raw HTTP wrapper underneath; nothing outside `tunnelClient.ts` should touch it.

### Daemon discovery and lifecycle

`getDaemonInfo()` in `src/daemon/daemonManager.ts` reads `daemon.json`, validates the PID with `process.kill(pid, 0)`, deletes the file if stale, and returns `null` if no live daemon is found. `startDaemon()` spawns a detached daemon child and polls `daemon.json` for up to 8s.

Single daemon per user. State lives under `~/.config/pinggy/` on Linux/macOS or `%APPDATA%/pinggy/` on Windows (helper: `src/utils/configDir.ts`):

- `daemon.json`: `{pid, port, startedAt}`.
- `daemon-state.json`: detached tunnel configs for crash recovery (`src/daemon/stateStore.ts`). Deleted on clean shutdown; replayed on next start.
- `daemon.log`: SDK + daemon logs. CLI logs stay separate.
- `tunnels/<name>_<configId>.json`: saved tunnel configs from `pinggy config save`.

### Foreground vs detached tunnels

`SessionTracker` (`src/daemon/sessionTracker.ts`) maps each `tunnelId` to a `sessionId` plus a mode:

- **Foreground**: CLI holds an open WebSocket subscription. If the subscription closes, a 5-second grace period starts; if no other CLI re-attaches, the daemon stops the tunnel.
- **Detached** (`-b` flag, or remote-management tunnels): tunnel persists in the daemon regardless of CLI presence and is recorded in `daemon-state.json`.

`pinggy attach <name|id>` reopens a foreground subscription and renders the TUI.

**Core types** are in `src/types.ts`: `TunnelStatus`, `Status`, `TunnelStateType` enum (`idle/starting/running/live/closed/exited`), `FinalConfig` (extends SDK's `TunnelConfigurationV1`). Browse `src/daemon/` and `src/cli/` for the rest of the module layout.

## Subcommands (user-facing)

| Command | Purpose |
|---|---|
| `pinggy config list \| show \| save \| update \| delete \| auto \| noauto` | CRUD on saved tunnel configs |
| `pinggy start <name...> [-b] [--all]` | Start saved tunnel(s). `-b` detaches; `--all` starts every auto-start config |
| `pinggy stop <name\|id>` | Stop running tunnel(s) by name or ID prefix |
| `pinggy ps` | Table of running tunnels (ID, name, status, local, URL) |
| `pinggy attach <name\|id>` | Re-attach TUI to a running tunnel |
| `pinggy daemon start \| stop \| status \| install-service \| uninstall-service` (alias `d`) | Daemon lifecycle and system-service installation |

## Build System

tsup bundles to `dist/` (CJS + ESM). tsc type-checks only (`npm run build:tsc`). pkg builds standalone platform binaries (`out/pinggy-<platform>`). ts-jest with the ESM preset for unit tests (`jest.config.cjs`).

## Testing

**Unit tests** live in `src/_tests_/`. Use `@jest/globals` imports (no global `jest`). `TunnelManager` is a singleton, so reset it between tests with `TunnelManager.instance = undefined`, and call `jest.clearAllMocks()` in `beforeEach`.

**End-to-end tests** live in `test/e2e/`. They run against a packaged `pkg` binary, spawn real Pinggy free-tier tunnels, and assert HTTP/TCP/UDP behavior over the live edge. See `test/e2e/README.md` for layout, framework helpers, and how to add a case. CI runs them across 6 platform binaries via `.github/workflows/e2e-test.yml`.

## Key Patterns

- **Singletons** inside the daemon: `TunnelManager`, Winston `logger`, `CLIPrinter`. Access via `getInstance()` or static methods.
- **Listener/observer maps** for SDK callbacks: `Map<tunnelId, Map<listenerId, fn>>`. Register/unregister by `listenerId` to avoid leaks.
- **Zod validation** on remote-management payloads (`src/remote_management/remote_schema.ts`), V1 and V2.
- **Worker threads** for file serving (`src/workers/file_serve_worker.ts`).
- **DaemonTunnelHandler**. Remote management lives in the CLI, but every tunnel operation it receives is forwarded to the daemon via this adapter in `src/daemon/tunnelClient.ts`. Same code path as user-typed subcommands.

## English Style

- **No em-dashes.** Use a period, colon, or restructure the sentence. No obvious characters or constructions used by LLMs and AI.
- **Short phrases.** Enough to convey technical meaning. Nothing more.
- **No filler words.** Cut: "in order to", "it is important to note", "please note that", "essentially", "basically", "simply".
- **No passive voice** unless the subject is unknown or irrelevant.
- **No nominalizations.** Prefer "detect" over "perform detection"; "configure" over "apply configuration".
- **One idea per sentence.** Split compound sentences.
- **No throat-clearing openers.** Never start with "This document describes...", "The purpose of this is...", "As mentioned above...".
- **Prefer concrete over abstract.** Name the thing: `navigator.webdriver`, not "the relevant browser property".
- **Present tense.** "The system routes requests." Not "The system will route requests."
- **No redundant qualifiers.** "Persistent storage" not "persistent, durable, long-lived storage".
- **Numbers.** Use digits for all quantities: "3 retries", "50 sessions", "300ms".

## Code Style
- **No dead code.** Remove unused imports, functions, and variables immediately. `ruff` enforces this.
- **Small functions.** If a function needs a comment to explain its sections, split it.
1 change: 1 addition & 0 deletions GEMINI.md
Loading
Loading