@kcisoul/remotecode 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +358 -0
- package/dist/__tests__/config.test.d.ts +2 -0
- package/dist/__tests__/config.test.d.ts.map +1 -0
- package/dist/__tests__/config.test.js +84 -0
- package/dist/__tests__/config.test.js.map +1 -0
- package/dist/__tests__/format.test.d.ts +2 -0
- package/dist/__tests__/format.test.d.ts.map +1 -0
- package/dist/__tests__/format.test.js +62 -0
- package/dist/__tests__/format.test.js.map +1 -0
- package/dist/__tests__/sessions.test.d.ts +2 -0
- package/dist/__tests__/sessions.test.d.ts.map +1 -0
- package/dist/__tests__/sessions.test.js +51 -0
- package/dist/__tests__/sessions.test.js.map +1 -0
- package/dist/banner.d.ts +4 -0
- package/dist/banner.d.ts.map +1 -0
- package/dist/banner.js +154 -0
- package/dist/banner.js.map +1 -0
- package/dist/callbacks.d.ts +10 -0
- package/dist/callbacks.d.ts.map +1 -0
- package/dist/callbacks.js +184 -0
- package/dist/callbacks.js.map +1 -0
- package/dist/claude.d.ts +9 -0
- package/dist/claude.d.ts.map +1 -0
- package/dist/claude.js +117 -0
- package/dist/claude.js.map +1 -0
- package/dist/cli.d.ts +14 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +296 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands.d.ts +3 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/commands.js +107 -0
- package/dist/commands.js.map +1 -0
- package/dist/config.d.ts +24 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +153 -0
- package/dist/config.js.map +1 -0
- package/dist/context.d.ts +12 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +22 -0
- package/dist/context.js.map +1 -0
- package/dist/daemon.d.ts +11 -0
- package/dist/daemon.d.ts.map +1 -0
- package/dist/daemon.js +316 -0
- package/dist/daemon.js.map +1 -0
- package/dist/format.d.ts +8 -0
- package/dist/format.d.ts.map +1 -0
- package/dist/format.js +57 -0
- package/dist/format.js.map +1 -0
- package/dist/handler.d.ts +4 -0
- package/dist/handler.d.ts.map +1 -0
- package/dist/handler.js +292 -0
- package/dist/handler.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +142 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +10 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +42 -0
- package/dist/logger.js.map +1 -0
- package/dist/session-ui.d.ts +17 -0
- package/dist/session-ui.d.ts.map +1 -0
- package/dist/session-ui.js +237 -0
- package/dist/session-ui.js.map +1 -0
- package/dist/sessions.d.ts +33 -0
- package/dist/sessions.d.ts.map +1 -0
- package/dist/sessions.js +341 -0
- package/dist/sessions.js.map +1 -0
- package/dist/setup.d.ts +3 -0
- package/dist/setup.d.ts.map +1 -0
- package/dist/setup.js +144 -0
- package/dist/setup.js.map +1 -0
- package/dist/stt.d.ts +11 -0
- package/dist/stt.d.ts.map +1 -0
- package/dist/stt.js +192 -0
- package/dist/stt.js.map +1 -0
- package/dist/telegram.d.ts +85 -0
- package/dist/telegram.d.ts.map +1 -0
- package/dist/telegram.js +97 -0
- package/dist/telegram.js.map +1 -0
- package/dist/watcher.d.ts +6 -0
- package/dist/watcher.d.ts.map +1 -0
- package/dist/watcher.js +230 -0
- package/dist/watcher.js.map +1 -0
- package/package.json +45 -0
package/README.md
ADDED
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
# RemoteCode
|
|
2
|
+
|
|
3
|
+
Control [Claude Code](https://docs.anthropic.com/en/docs/claude-code) remotely through Telegram. Built specifically for Claude Code.
|
|
4
|
+
|
|
5
|
+
RemoteCode works directly with your local Claude Code -- same sessions, same project context, same history. Pick up where you left off in the terminal, switch projects, or start a new session, all from Telegram.
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
You (Anywhere) <--> RemoteCode (Host) <--> Claude Code (Host)
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- **Text chat** -- Send any message, get Claude Code responses with Markdown formatting
|
|
14
|
+
- **Image analysis** -- Send photos or image documents, optionally with a caption prompt
|
|
15
|
+
- **Voice messages** -- Transcribed locally via whisper-cli (offline, free), then sent to Claude Code
|
|
16
|
+
- **Session management** -- Multiple sessions, switch between them, browse by project
|
|
17
|
+
- **Auto-sync** -- Watch active session files and forward new messages in real-time
|
|
18
|
+
- **Background daemon** -- Runs as a detached process with log rotation
|
|
19
|
+
- **User access control** -- Restrict access by Telegram user ID or username
|
|
20
|
+
|
|
21
|
+
## How It Works
|
|
22
|
+
|
|
23
|
+
```mermaid
|
|
24
|
+
sequenceDiagram
|
|
25
|
+
participant U as You (Telegram)
|
|
26
|
+
participant T as Telegram API
|
|
27
|
+
participant D as RemoteCode Daemon
|
|
28
|
+
participant C as Claude Code CLI
|
|
29
|
+
|
|
30
|
+
U->>T: Send message
|
|
31
|
+
T->>D: Long polling (getUpdates)
|
|
32
|
+
D->>D: Auth check & route
|
|
33
|
+
|
|
34
|
+
alt Text message
|
|
35
|
+
D->>C: claude --resume <session> --print "prompt"
|
|
36
|
+
else Image
|
|
37
|
+
D->>T: Download file
|
|
38
|
+
D->>C: claude --resume <session> --print "prompt + image path"
|
|
39
|
+
else Voice
|
|
40
|
+
D->>T: Download audio
|
|
41
|
+
D->>D: ffmpeg + whisper-cli transcription
|
|
42
|
+
D->>C: claude --resume <session> --print "transcription"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
C-->>D: stdout response
|
|
46
|
+
D->>D: Markdown → Telegram HTML
|
|
47
|
+
D->>T: sendMessage (HTML)
|
|
48
|
+
T-->>U: Formatted response
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Architecture
|
|
52
|
+
|
|
53
|
+
```mermaid
|
|
54
|
+
graph TB
|
|
55
|
+
subgraph CLI["CLI (Terminal)"]
|
|
56
|
+
IDX[index.ts<br/>arg parser]
|
|
57
|
+
CL[cli.ts<br/>subcommands]
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
subgraph Daemon["Daemon Process"]
|
|
61
|
+
DAE[daemon.ts<br/>poll loop + PID]
|
|
62
|
+
HND[handler.ts<br/>message router]
|
|
63
|
+
CMD[commands.ts<br/>slash commands]
|
|
64
|
+
CB[callbacks.ts<br/>inline buttons]
|
|
65
|
+
WAT[watcher.ts<br/>auto-sync]
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
subgraph Core["Core"]
|
|
69
|
+
TEL[telegram.ts<br/>API client]
|
|
70
|
+
CLA[claude.ts<br/>CLI spawner]
|
|
71
|
+
SES[sessions.ts<br/>discovery + state]
|
|
72
|
+
SUI[session-ui.ts<br/>UI formatting]
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
subgraph Shared["Shared"]
|
|
76
|
+
CFG[config.ts<br/>paths + KV I/O]
|
|
77
|
+
LOG[logger.ts<br/>logging]
|
|
78
|
+
BAN[banner.ts<br/>terminal UI]
|
|
79
|
+
CTX[context.ts<br/>auth + locks]
|
|
80
|
+
FMT[format.ts<br/>MD → HTML]
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
IDX --> DAE
|
|
84
|
+
IDX --> CL
|
|
85
|
+
CL --> DAE
|
|
86
|
+
DAE --> HND
|
|
87
|
+
DAE --> CB
|
|
88
|
+
HND --> CMD
|
|
89
|
+
HND --> CLA
|
|
90
|
+
HND --> CTX
|
|
91
|
+
CB --> SES
|
|
92
|
+
CB --> SUI
|
|
93
|
+
CMD --> SUI
|
|
94
|
+
WAT --> SES
|
|
95
|
+
WAT --> CTX
|
|
96
|
+
CLA --> LOG
|
|
97
|
+
TEL --> LOG
|
|
98
|
+
SUI --> FMT
|
|
99
|
+
CL --> BAN
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Quick Start
|
|
103
|
+
|
|
104
|
+
### Prerequisites
|
|
105
|
+
|
|
106
|
+
- **Node.js** >= 18
|
|
107
|
+
- **Claude Code CLI** installed and authenticated (`claude` command available)
|
|
108
|
+
- **Telegram Bot Token** -- create a bot via [@BotFather](https://t.me/BotFather) on Telegram (send `/newbot`, follow the prompts, copy the token). See [Telegram's official guide](https://core.telegram.org/bots/tutorial#obtain-your-bot-token) for details
|
|
109
|
+
|
|
110
|
+
### Install
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
npm install -g @kcisoul/remotecode
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Or from source:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
git clone https://github.com/kcisoul/remotecode.git
|
|
120
|
+
cd remotecode
|
|
121
|
+
npm install && npm run build
|
|
122
|
+
npm link
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### First Run
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
remotecode
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
The interactive setup wizard will prompt for:
|
|
132
|
+
|
|
133
|
+
1. **TELEGRAM_BOT_TOKEN** -- validated against Telegram API
|
|
134
|
+
2. **REMOTECODE_ALLOWED_USERS** -- comma-separated user IDs or @usernames
|
|
135
|
+
3. **REMOTECODE_YOLO** -- `Y` enables autonomous mode (Claude Code runs without permission prompts, required for full remote control). Set `N` if you prefer read-only / monitoring use, but note that any action requiring approval will block since there's no terminal to confirm
|
|
136
|
+
4. **STT setup** -- optional offline voice transcription. Installs `whisper-cli` and `ffmpeg` via Homebrew, and downloads a local Whisper model (~466 MB). Runs entirely on your machine -- no API calls, completely free
|
|
137
|
+
|
|
138
|
+
Config is saved to `~/.remotecode/config`.
|
|
139
|
+
|
|
140
|
+
## CLI Commands
|
|
141
|
+
|
|
142
|
+
| Command | Description |
|
|
143
|
+
|---|---|
|
|
144
|
+
| `remotecode` | Start daemon (or show status if already running) |
|
|
145
|
+
| `remotecode start` | Start the background daemon |
|
|
146
|
+
| `remotecode stop` | Stop the daemon |
|
|
147
|
+
| `remotecode restart` | Restart the daemon |
|
|
148
|
+
| `remotecode status` | Show daemon status, active session, uptime |
|
|
149
|
+
| `remotecode logs` | Follow logs in real-time (default) |
|
|
150
|
+
| `remotecode logs -n 50` | Show last 50 log lines (static) |
|
|
151
|
+
| `remotecode logs --level ERROR` | Filter by log level (DEBUG/INFO/WARN/ERROR) |
|
|
152
|
+
| `remotecode logs --tag claude` | Filter by component tag |
|
|
153
|
+
| `remotecode config` | Edit configuration (auto-restarts daemon) |
|
|
154
|
+
| `remotecode setup-stt` | Install whisper-cli, ffmpeg, and download model |
|
|
155
|
+
|
|
156
|
+
### Flags
|
|
157
|
+
|
|
158
|
+
| Flag | Description |
|
|
159
|
+
|---|---|
|
|
160
|
+
| `-v`, `--verbose` | Enable verbose (DEBUG) logging |
|
|
161
|
+
|
|
162
|
+
## Telegram Commands
|
|
163
|
+
|
|
164
|
+
Send these as messages in your Telegram chat with the bot:
|
|
165
|
+
|
|
166
|
+
| Command | Description |
|
|
167
|
+
|---|---|
|
|
168
|
+
| `/start`, `/help` | Welcome message with quick action buttons |
|
|
169
|
+
| `/sessions` | Browse and switch between recent sessions |
|
|
170
|
+
| `/projects` | Browse sessions grouped by project directory |
|
|
171
|
+
| `/new` | Start a new Claude Code session |
|
|
172
|
+
| `/history` | Show conversation history of current session |
|
|
173
|
+
| `/sync` | Toggle auto-sync notifications on/off |
|
|
174
|
+
|
|
175
|
+
### Inline Buttons
|
|
176
|
+
|
|
177
|
+
After `/sessions` or `/projects`, interactive inline keyboards let you:
|
|
178
|
+
|
|
179
|
+
- **Switch** to any session with one tap
|
|
180
|
+
- **Create** new sessions (globally or per-project)
|
|
181
|
+
- **Delete** sessions
|
|
182
|
+
- **Navigate** between project views
|
|
183
|
+
|
|
184
|
+
## Message Types
|
|
185
|
+
|
|
186
|
+
### Text
|
|
187
|
+
|
|
188
|
+
Send any text message. If it's not a `/command`, it's forwarded to Claude Code as a prompt. Responses are rendered as Telegram HTML with code blocks, bold, italic, and more.
|
|
189
|
+
|
|
190
|
+
### Images
|
|
191
|
+
|
|
192
|
+
Send a photo or image document (PNG, JPG, etc.). The bot downloads the image, saves it to a temp directory, and includes the file path in the Claude Code prompt. Add a caption to provide context.
|
|
193
|
+
|
|
194
|
+
### Voice / Audio
|
|
195
|
+
|
|
196
|
+
Send a voice message or audio file. The bot:
|
|
197
|
+
|
|
198
|
+
1. Downloads the audio file
|
|
199
|
+
2. Converts to WAV via `ffmpeg`
|
|
200
|
+
3. Transcribes via `whisper-cli` (local, offline)
|
|
201
|
+
4. Sends the transcription as a prompt to Claude Code
|
|
202
|
+
5. Returns both your transcription and Claude's response in a blockquote
|
|
203
|
+
|
|
204
|
+
> Requires STT setup: `remotecode setup-stt`
|
|
205
|
+
|
|
206
|
+
## Session Management
|
|
207
|
+
|
|
208
|
+
```mermaid
|
|
209
|
+
stateDiagram-v2
|
|
210
|
+
[*] --> NoSession: First message
|
|
211
|
+
NoSession --> NewSession: auto-create UUID
|
|
212
|
+
NewSession --> Active: claude --session-id
|
|
213
|
+
Active --> Active: claude --resume
|
|
214
|
+
Active --> Switched: /sessions or inline button
|
|
215
|
+
Switched --> Active: select different session
|
|
216
|
+
Active --> NewSession: /new
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
RemoteCode discovers sessions from `~/.claude/projects/*/` by scanning `.jsonl` files. Each session maps to a Claude Code conversation.
|
|
220
|
+
|
|
221
|
+
- **Active session** is stored in `~/.remotecode/local`
|
|
222
|
+
- **Session CWD** determines which directory Claude Code runs in
|
|
223
|
+
- Sessions are auto-created on first message if none exists
|
|
224
|
+
- The `--resume` flag is used to continue existing sessions; falls back to `--session-id` for new ones
|
|
225
|
+
|
|
226
|
+
### Auto-Sync
|
|
227
|
+
|
|
228
|
+
When enabled (`/sync`), RemoteCode watches the active session's `.jsonl` file and forwards new messages from Claude Code in real-time. This means if you use Claude Code on your host machine, you'll see the conversation in Telegram too.
|
|
229
|
+
|
|
230
|
+
The watcher polls for session changes every 3 seconds and uses `fs.watch` for file-level changes with 500ms debouncing.
|
|
231
|
+
|
|
232
|
+
## Configuration
|
|
233
|
+
|
|
234
|
+
### Config File
|
|
235
|
+
|
|
236
|
+
`~/.remotecode/config` -- simple key=value format:
|
|
237
|
+
|
|
238
|
+
```ini
|
|
239
|
+
TELEGRAM_BOT_TOKEN=123456:ABC-DEF
|
|
240
|
+
REMOTECODE_ALLOWED_USERS=12345678,@username
|
|
241
|
+
REMOTECODE_YOLO=true
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### Environment Variables
|
|
245
|
+
|
|
246
|
+
| Variable | Required | Description |
|
|
247
|
+
|---|---|---|
|
|
248
|
+
| `TELEGRAM_BOT_TOKEN` | Yes | Bot token from @BotFather |
|
|
249
|
+
| `REMOTECODE_ALLOWED_USERS` | Yes | Comma/space-separated user IDs or @usernames |
|
|
250
|
+
| `REMOTECODE_YOLO` | No | `true` for full remote control (skips Claude Code permission prompts). Set `false` for read-only / monitoring use |
|
|
251
|
+
| `REMOTECODE_VERBOSE` | No | `true` to enable DEBUG-level logging |
|
|
252
|
+
|
|
253
|
+
## File Structure
|
|
254
|
+
|
|
255
|
+
```
|
|
256
|
+
~/.remotecode/
|
|
257
|
+
config # Global configuration (KV file)
|
|
258
|
+
local # Active session state (session ID, CWD, chat ID)
|
|
259
|
+
remotecode.pid # Daemon process ID
|
|
260
|
+
remotecode.log # Log file (5MB rotation, keeps .old)
|
|
261
|
+
remotecode.log.old # Previous rotated log
|
|
262
|
+
whisper/
|
|
263
|
+
ggml-small.bin # Whisper model (if STT enabled)
|
|
264
|
+
RemoteCodeSessions/ # Default CWD for new sessions
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
## Message Flow
|
|
268
|
+
|
|
269
|
+
```mermaid
|
|
270
|
+
flowchart TD
|
|
271
|
+
A[Telegram Update] --> B{Message type?}
|
|
272
|
+
B -->|callback_query| C[callbacks.ts]
|
|
273
|
+
B -->|message| D{Content type?}
|
|
274
|
+
|
|
275
|
+
D -->|text starts with /| E[commands.ts]
|
|
276
|
+
D -->|text| F[handlePrompt]
|
|
277
|
+
D -->|photo / image doc| G[Download & save image]
|
|
278
|
+
D -->|voice / audio| H{STT ready?}
|
|
279
|
+
|
|
280
|
+
H -->|No| I[Send setup instructions]
|
|
281
|
+
H -->|Yes| J[Download → ffmpeg → whisper-cli]
|
|
282
|
+
J --> K{Blank audio?}
|
|
283
|
+
K -->|Yes| L[No speech detected]
|
|
284
|
+
K -->|No| F
|
|
285
|
+
|
|
286
|
+
G --> F
|
|
287
|
+
|
|
288
|
+
F --> M[withSessionLock]
|
|
289
|
+
M --> N[askClaude via CLI]
|
|
290
|
+
N --> O{Session exists?}
|
|
291
|
+
O -->|Yes| P[--resume session]
|
|
292
|
+
O -->|No| Q[--session-id new]
|
|
293
|
+
O -->|Busy| R[Retry up to 5x]
|
|
294
|
+
|
|
295
|
+
P --> S[Format response]
|
|
296
|
+
Q --> S
|
|
297
|
+
R --> S
|
|
298
|
+
S --> T[sendMessage HTML]
|
|
299
|
+
|
|
300
|
+
C --> U{Action}
|
|
301
|
+
U -->|sess:list| V[Show session grid]
|
|
302
|
+
U -->|sess:ID| W[Switch session]
|
|
303
|
+
U -->|sess:new| X[Create new session]
|
|
304
|
+
U -->|proj:list| Y[Show project list]
|
|
305
|
+
U -->|proj:DIR| Z[Show project sessions]
|
|
306
|
+
U -->|sessdel:ID| AA[Delete session]
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
## Speech-to-Text (STT)
|
|
310
|
+
|
|
311
|
+
RemoteCode uses [whisper.cpp](https://github.com/ggerganov/whisper.cpp) for local, offline speech-to-text.
|
|
312
|
+
|
|
313
|
+
### Setup
|
|
314
|
+
|
|
315
|
+
```bash
|
|
316
|
+
remotecode setup-stt
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
This installs (via Homebrew on macOS):
|
|
320
|
+
- **whisper-cpp** -- C++ inference engine for Whisper
|
|
321
|
+
- **ffmpeg** -- audio format conversion
|
|
322
|
+
- **ggml-small.bin** -- Whisper small model (~466 MB, downloaded from HuggingFace)
|
|
323
|
+
|
|
324
|
+
### How it works
|
|
325
|
+
|
|
326
|
+
1. Audio downloaded from Telegram (`.oga` format)
|
|
327
|
+
2. Converted to 16kHz mono WAV via `ffmpeg`
|
|
328
|
+
3. Transcribed via `whisper-cli -m ggml-small.bin -l auto`
|
|
329
|
+
4. Blank audio detection filters out silence/noise
|
|
330
|
+
5. Transcription sent to Claude Code as a regular prompt
|
|
331
|
+
|
|
332
|
+
## Security
|
|
333
|
+
|
|
334
|
+
- **User allowlist** -- Only configured user IDs and usernames can interact
|
|
335
|
+
- **Repeat block** -- Unauthorized users are warned once, then silently blocked
|
|
336
|
+
- **No webhook** -- Uses long polling, no public endpoints needed
|
|
337
|
+
- **Local STT** -- Voice transcription runs entirely offline via whisper.cpp
|
|
338
|
+
- **YOLO mode** -- Required for full remote control. Without it, any Claude Code action needing approval will hang since there's no terminal to confirm. If security is a concern, set YOLO to `false` and use RemoteCode for text conversations and monitoring only
|
|
339
|
+
|
|
340
|
+
## Development
|
|
341
|
+
|
|
342
|
+
```bash
|
|
343
|
+
# Run in development mode
|
|
344
|
+
npm run dev
|
|
345
|
+
|
|
346
|
+
# Build TypeScript
|
|
347
|
+
npm run build
|
|
348
|
+
|
|
349
|
+
# Run tests
|
|
350
|
+
npm test
|
|
351
|
+
|
|
352
|
+
# Watch tests
|
|
353
|
+
npm run test:watch
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
## License
|
|
357
|
+
|
|
358
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/config.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
const vitest_1 = require("vitest");
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const os = __importStar(require("os"));
|
|
40
|
+
const config_1 = require("../config");
|
|
41
|
+
(0, vitest_1.describe)("readKvFile", () => {
|
|
42
|
+
const tmpFile = path.join(os.tmpdir(), `remotecode_test_kv_${Date.now()}.txt`);
|
|
43
|
+
(0, vitest_1.afterEach)(() => {
|
|
44
|
+
try {
|
|
45
|
+
fs.unlinkSync(tmpFile);
|
|
46
|
+
}
|
|
47
|
+
catch { /* ignore */ }
|
|
48
|
+
});
|
|
49
|
+
(0, vitest_1.it)("returns empty object for non-existent file", () => {
|
|
50
|
+
(0, vitest_1.expect)((0, config_1.readKvFile)("/tmp/does-not-exist-xyz.txt")).toEqual({});
|
|
51
|
+
});
|
|
52
|
+
(0, vitest_1.it)("parses key=value pairs", () => {
|
|
53
|
+
fs.writeFileSync(tmpFile, "FOO=bar\nBAZ=qux\n");
|
|
54
|
+
(0, vitest_1.expect)((0, config_1.readKvFile)(tmpFile)).toEqual({ FOO: "bar", BAZ: "qux" });
|
|
55
|
+
});
|
|
56
|
+
(0, vitest_1.it)("skips comments and empty lines", () => {
|
|
57
|
+
fs.writeFileSync(tmpFile, "# comment\n\nKEY=val\n");
|
|
58
|
+
(0, vitest_1.expect)((0, config_1.readKvFile)(tmpFile)).toEqual({ KEY: "val" });
|
|
59
|
+
});
|
|
60
|
+
(0, vitest_1.it)("handles values with = sign", () => {
|
|
61
|
+
fs.writeFileSync(tmpFile, "TOKEN=abc=def=ghi\n");
|
|
62
|
+
(0, vitest_1.expect)((0, config_1.readKvFile)(tmpFile)).toEqual({ TOKEN: "abc=def=ghi" });
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
(0, vitest_1.describe)("readEnvLines / writeEnvLines", () => {
|
|
66
|
+
const tmpFile = path.join(os.tmpdir(), `remotecode_test_env_${Date.now()}.txt`);
|
|
67
|
+
(0, vitest_1.afterEach)(() => {
|
|
68
|
+
try {
|
|
69
|
+
fs.unlinkSync(tmpFile);
|
|
70
|
+
}
|
|
71
|
+
catch { /* ignore */ }
|
|
72
|
+
});
|
|
73
|
+
(0, vitest_1.it)("returns empty array for non-existent file", () => {
|
|
74
|
+
(0, vitest_1.expect)((0, config_1.readEnvLines)("/tmp/does-not-exist-xyz.txt")).toEqual([]);
|
|
75
|
+
});
|
|
76
|
+
(0, vitest_1.it)("round-trips lines", () => {
|
|
77
|
+
const lines = ["FOO=bar", "BAZ=qux"];
|
|
78
|
+
(0, config_1.writeEnvLines)(tmpFile, lines);
|
|
79
|
+
const read = (0, config_1.readEnvLines)(tmpFile);
|
|
80
|
+
(0, vitest_1.expect)(read).toContain("FOO=bar");
|
|
81
|
+
(0, vitest_1.expect)(read).toContain("BAZ=qux");
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
//# sourceMappingURL=config.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.test.js","sourceRoot":"","sources":["../../src/__tests__/config.test.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,mCAAqE;AACrE,uCAAyB;AACzB,2CAA6B;AAC7B,uCAAyB;AACzB,sCAAoE;AAEpE,IAAA,iBAAQ,EAAC,YAAY,EAAE,GAAG,EAAE;IAC1B,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,sBAAsB,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAE/E,IAAA,kBAAS,EAAC,GAAG,EAAE;QACb,IAAI,CAAC;YAAC,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,4CAA4C,EAAE,GAAG,EAAE;QACpD,IAAA,eAAM,EAAC,IAAA,mBAAU,EAAC,6BAA6B,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAChE,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,wBAAwB,EAAE,GAAG,EAAE;QAChC,EAAE,CAAC,aAAa,CAAC,OAAO,EAAE,oBAAoB,CAAC,CAAC;QAChD,IAAA,eAAM,EAAC,IAAA,mBAAU,EAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,gCAAgC,EAAE,GAAG,EAAE;QACxC,EAAE,CAAC,aAAa,CAAC,OAAO,EAAE,wBAAwB,CAAC,CAAC;QACpD,IAAA,eAAM,EAAC,IAAA,mBAAU,EAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,4BAA4B,EAAE,GAAG,EAAE;QACpC,EAAE,CAAC,aAAa,CAAC,OAAO,EAAE,qBAAqB,CAAC,CAAC;QACjD,IAAA,eAAM,EAAC,IAAA,mBAAU,EAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC,CAAC;IAChE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,IAAA,iBAAQ,EAAC,8BAA8B,EAAE,GAAG,EAAE;IAC5C,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,uBAAuB,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAEhF,IAAA,kBAAS,EAAC,GAAG,EAAE;QACb,IAAI,CAAC;YAAC,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,2CAA2C,EAAE,GAAG,EAAE;QACnD,IAAA,eAAM,EAAC,IAAA,qBAAY,EAAC,6BAA6B,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,mBAAmB,EAAE,GAAG,EAAE;QAC3B,MAAM,KAAK,GAAG,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;QACrC,IAAA,sBAAa,EAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QAC9B,MAAM,IAAI,GAAG,IAAA,qBAAY,EAAC,OAAO,CAAC,CAAC;QACnC,IAAA,eAAM,EAAC,IAAI,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QAClC,IAAA,eAAM,EAAC,IAAI,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/format.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const vitest_1 = require("vitest");
|
|
4
|
+
const format_1 = require("../format");
|
|
5
|
+
(0, vitest_1.describe)("escapeHtml", () => {
|
|
6
|
+
(0, vitest_1.it)("escapes &, <, >", () => {
|
|
7
|
+
(0, vitest_1.expect)((0, format_1.escapeHtml)("a & b < c > d")).toBe("a & b < c > d");
|
|
8
|
+
});
|
|
9
|
+
(0, vitest_1.it)("returns empty string unchanged", () => {
|
|
10
|
+
(0, vitest_1.expect)((0, format_1.escapeHtml)("")).toBe("");
|
|
11
|
+
});
|
|
12
|
+
});
|
|
13
|
+
(0, vitest_1.describe)("mdToTelegramHtml", () => {
|
|
14
|
+
(0, vitest_1.it)("converts bold **text**", () => {
|
|
15
|
+
(0, vitest_1.expect)((0, format_1.mdToTelegramHtml)("**hello**")).toBe("<b>hello</b>");
|
|
16
|
+
});
|
|
17
|
+
(0, vitest_1.it)("converts bold __text__", () => {
|
|
18
|
+
(0, vitest_1.expect)((0, format_1.mdToTelegramHtml)("__hello__")).toBe("<b>hello</b>");
|
|
19
|
+
});
|
|
20
|
+
(0, vitest_1.it)("converts italic *text*", () => {
|
|
21
|
+
(0, vitest_1.expect)((0, format_1.mdToTelegramHtml)("*hello*")).toBe("<i>hello</i>");
|
|
22
|
+
});
|
|
23
|
+
(0, vitest_1.it)("converts inline code", () => {
|
|
24
|
+
(0, vitest_1.expect)((0, format_1.mdToTelegramHtml)("`code`")).toBe("<code>code</code>");
|
|
25
|
+
});
|
|
26
|
+
(0, vitest_1.it)("converts code blocks", () => {
|
|
27
|
+
const result = (0, format_1.mdToTelegramHtml)("```js\nconsole.log(1)\n```");
|
|
28
|
+
(0, vitest_1.expect)(result).toBe("<pre>console.log(1)</pre>");
|
|
29
|
+
});
|
|
30
|
+
(0, vitest_1.it)("converts headings to bold", () => {
|
|
31
|
+
(0, vitest_1.expect)((0, format_1.mdToTelegramHtml)("## Title")).toBe("<b>Title</b>");
|
|
32
|
+
});
|
|
33
|
+
(0, vitest_1.it)("converts strikethrough", () => {
|
|
34
|
+
(0, vitest_1.expect)((0, format_1.mdToTelegramHtml)("~~removed~~")).toBe("<s>removed</s>");
|
|
35
|
+
});
|
|
36
|
+
(0, vitest_1.it)("escapes HTML in plain text", () => {
|
|
37
|
+
(0, vitest_1.expect)((0, format_1.mdToTelegramHtml)("<script>alert(1)</script>")).toBe("<script>alert(1)</script>");
|
|
38
|
+
});
|
|
39
|
+
(0, vitest_1.it)("preserves HTML inside code blocks", () => {
|
|
40
|
+
const result = (0, format_1.mdToTelegramHtml)("```\n<div>hi</div>\n```");
|
|
41
|
+
(0, vitest_1.expect)(result).toContain("<div>hi</div>");
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
(0, vitest_1.describe)("tryMdToHtml", () => {
|
|
45
|
+
(0, vitest_1.it)("returns HTML with parseMode", () => {
|
|
46
|
+
const result = (0, format_1.tryMdToHtml)("**bold**");
|
|
47
|
+
(0, vitest_1.expect)(result.text).toBe("<b>bold</b>");
|
|
48
|
+
(0, vitest_1.expect)(result.parseMode).toBe("HTML");
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
(0, vitest_1.describe)("truncateMessage", () => {
|
|
52
|
+
(0, vitest_1.it)("returns short text unchanged", () => {
|
|
53
|
+
(0, vitest_1.expect)((0, format_1.truncateMessage)("hello", 10)).toBe("hello");
|
|
54
|
+
});
|
|
55
|
+
(0, vitest_1.it)("truncates long text", () => {
|
|
56
|
+
const long = "a".repeat(100);
|
|
57
|
+
const result = (0, format_1.truncateMessage)(long, 50);
|
|
58
|
+
(0, vitest_1.expect)(result.length).toBeLessThanOrEqual(70); // 50 + truncation marker
|
|
59
|
+
(0, vitest_1.expect)(result).toContain("[truncated]");
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
//# sourceMappingURL=format.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.test.js","sourceRoot":"","sources":["../../src/__tests__/format.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessions.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/sessions.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const vitest_1 = require("vitest");
|
|
4
|
+
const sessions_1 = require("../sessions");
|
|
5
|
+
function makeSession(overrides = {}) {
|
|
6
|
+
return {
|
|
7
|
+
sessionId: "00000000-0000-0000-0000-000000000000",
|
|
8
|
+
slug: null,
|
|
9
|
+
project: "/home/user/project",
|
|
10
|
+
projectName: "project",
|
|
11
|
+
lastModified: Date.now() / 1000,
|
|
12
|
+
firstMessage: null,
|
|
13
|
+
lastMessage: null,
|
|
14
|
+
...overrides,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
(0, vitest_1.describe)("formatTimeAgo", () => {
|
|
18
|
+
(0, vitest_1.it)("returns 'just now' for < 60s", () => {
|
|
19
|
+
(0, vitest_1.expect)((0, sessions_1.formatTimeAgo)(Date.now() / 1000 - 10)).toBe("just now");
|
|
20
|
+
});
|
|
21
|
+
(0, vitest_1.it)("returns minutes ago", () => {
|
|
22
|
+
(0, vitest_1.expect)((0, sessions_1.formatTimeAgo)(Date.now() / 1000 - 300)).toBe("5m ago");
|
|
23
|
+
});
|
|
24
|
+
(0, vitest_1.it)("returns hours ago", () => {
|
|
25
|
+
(0, vitest_1.expect)((0, sessions_1.formatTimeAgo)(Date.now() / 1000 - 7200)).toBe("2h ago");
|
|
26
|
+
});
|
|
27
|
+
(0, vitest_1.it)("returns days ago", () => {
|
|
28
|
+
(0, vitest_1.expect)((0, sessions_1.formatTimeAgo)(Date.now() / 1000 - 172800)).toBe("2d ago");
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
(0, vitest_1.describe)("formatSessionLabel", () => {
|
|
32
|
+
(0, vitest_1.it)("shows project + firstMessage", () => {
|
|
33
|
+
const s = makeSession({ firstMessage: "hello world" });
|
|
34
|
+
(0, vitest_1.expect)((0, sessions_1.formatSessionLabel)(s)).toBe("project - hello world");
|
|
35
|
+
});
|
|
36
|
+
(0, vitest_1.it)("truncates long firstMessage", () => {
|
|
37
|
+
const s = makeSession({ firstMessage: "a".repeat(50) });
|
|
38
|
+
const label = (0, sessions_1.formatSessionLabel)(s);
|
|
39
|
+
(0, vitest_1.expect)(label.length).toBeLessThanOrEqual(60);
|
|
40
|
+
(0, vitest_1.expect)(label).toContain("...");
|
|
41
|
+
});
|
|
42
|
+
(0, vitest_1.it)("falls back to slug", () => {
|
|
43
|
+
const s = makeSession({ slug: "my-slug" });
|
|
44
|
+
(0, vitest_1.expect)((0, sessions_1.formatSessionLabel)(s)).toBe("project (my-slug)");
|
|
45
|
+
});
|
|
46
|
+
(0, vitest_1.it)("falls back to project name only", () => {
|
|
47
|
+
const s = makeSession();
|
|
48
|
+
(0, vitest_1.expect)((0, sessions_1.formatSessionLabel)(s)).toBe("project");
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
//# sourceMappingURL=sessions.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessions.test.js","sourceRoot":"","sources":["../../src/__tests__/sessions.test.ts"],"names":[],"mappings":"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"}
|
package/dist/banner.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"banner.d.ts","sourceRoot":"","sources":["../src/banner.ts"],"names":[],"mappings":"AAcA,iFAAiF;AACjF,wBAAgB,gBAAgB,IAAI,IAAI,CAKvC;AAED,wBAAgB,WAAW,CAAC,YAAY,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CA0GzD"}
|