@quantiya/codevibe-claude-plugin 1.0.9 โ 1.0.11
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/.claude-plugin/plugin.json +1 -1
- package/README.md +75 -237
- package/hooks/session-start.sh +186 -0
- package/package.json +6 -9
- package/dist/appsync-client.d.ts +0 -67
- package/dist/appsync-client.d.ts.map +0 -1
- package/dist/appsync-client.js.map +0 -1
- package/dist/auth-cli.d.ts +0 -18
- package/dist/auth-cli.d.ts.map +0 -1
- package/dist/auth-cli.js.map +0 -1
- package/dist/command-executor.d.ts +0 -20
- package/dist/command-executor.d.ts.map +0 -1
- package/dist/command-executor.js.map +0 -1
- package/dist/config.d.ts +0 -25
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js.map +0 -1
- package/dist/crypto-service.d.ts +0 -115
- package/dist/crypto-service.d.ts.map +0 -1
- package/dist/crypto-service.js.map +0 -1
- package/dist/http-api.d.ts +0 -35
- package/dist/http-api.d.ts.map +0 -1
- package/dist/http-api.js.map +0 -1
- package/dist/key-manager.d.ts +0 -87
- package/dist/key-manager.d.ts.map +0 -1
- package/dist/key-manager.js.map +0 -1
- package/dist/logger.d.ts +0 -2
- package/dist/logger.d.ts.map +0 -1
- package/dist/logger.js.map +0 -1
- package/dist/prompt-responder.d.ts +0 -22
- package/dist/prompt-responder.d.ts.map +0 -1
- package/dist/prompt-responder.js.map +0 -1
- package/dist/server.d.ts +0 -9
- package/dist/server.d.ts.map +0 -1
- package/dist/server.js.map +0 -1
- package/dist/token-storage.d.ts +0 -39
- package/dist/token-storage.d.ts.map +0 -1
- package/dist/token-storage.js.map +0 -1
- package/dist/types.d.ts +0 -110
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codevibe-claude",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.11",
|
|
4
4
|
"description": "Sync Claude Code sessions with iOS mobile app via AWS backend. Control Claude Code from your phone with real-time bidirectional synchronization.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "CodeVibe Team"
|
package/README.md
CHANGED
|
@@ -1,295 +1,133 @@
|
|
|
1
|
-
# CodeVibe Claude
|
|
1
|
+
# CodeVibe for Claude Code
|
|
2
2
|
|
|
3
|
-
Control Claude Code from your
|
|
3
|
+
**Control Claude Code from your iPhone and Android.** See your agent's work, approve file edits, dictate prompts by voice โ all from your phone, anywhere you are.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
๐ **[quantiya.ai/codevibe](https://quantiya.ai/codevibe)** โ landing page, demo video, and one-liner installer
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
- **Mobile Control** - Send prompts from your phone that execute in the correct desktop session
|
|
9
|
-
- **Locked Screen Support** - Works even when your Mac screen is locked (via tmux)
|
|
10
|
-
- **Interactive Prompts** - Answer permission dialogs with numbered options (1=Yes, 2=Yes for project, 3=Reject)
|
|
11
|
-
- **Voice Input** - Dictate prompts using iOS speech-to-text
|
|
12
|
-
- **Image Attachments** - Send screenshots and photos with your messages
|
|
13
|
-
- **Markdown Rendering** - Code blocks and formatting displayed beautifully
|
|
14
|
-
- **Secure** - Uses AWS AppSync with Cognito authentication (Sign in with Apple/Google)
|
|
15
|
-
- **Complete Capture** - Syncs user prompts, assistant responses, tool usage, and file changes
|
|
16
|
-
- **Session-Aware** - Multiple Claude Code sessions work independently
|
|
17
|
-
- **Auto-execution** - Mobile prompts execute immediately (same trust model as desktop)
|
|
7
|
+
๐ฑ **[Download on the App Store](https://apps.apple.com/app/id6756500217)** ยท **[Get it on Google Play](https://play.google.com/store/apps/details?id=ai.quantiya.app.codevibe)**
|
|
18
8
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
- **macOS** (darwin platform)
|
|
22
|
-
- **Node.js** 18.0.0 or higher
|
|
23
|
-
- **tmux** (for locked screen support): `brew install tmux`
|
|
24
|
-
- **Claude Code** with plugin system enabled
|
|
25
|
-
|
|
26
|
-
## Installation
|
|
27
|
-
|
|
28
|
-
### Step 1: Install Wrapper Command (One-time)
|
|
29
|
-
|
|
30
|
-
Install the CodeVibe wrapper command via npm:
|
|
31
|
-
|
|
32
|
-
```bash
|
|
33
|
-
npm install -g @quantiya/codevibe
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
This puts `codevibe-claude` (and `codevibe-gemini`, `codevibe-codex`) in your PATH.
|
|
37
|
-
|
|
38
|
-
### Step 2: Register MCP Plugin (One-time)
|
|
39
|
-
|
|
40
|
-
Claude Code also needs its MCP plugin registered. Inside a Claude Code session, run:
|
|
41
|
-
|
|
42
|
-
```
|
|
43
|
-
/plugin marketplace add https://github.com/hendryyeh/quantiya-codevibe-marketplace
|
|
44
|
-
```
|
|
9
|
+
---
|
|
45
10
|
|
|
46
|
-
|
|
11
|
+
## Why CodeVibe for Claude Code
|
|
47
12
|
|
|
48
|
-
|
|
49
|
-
/
|
|
50
|
-
|
|
13
|
+
- **๐ Real-time sync** โ every prompt, response, and file change shows up on your phone in 100โ500ms
|
|
14
|
+
- **โ
Approve from anywhere** โ review full file diffs and approve or reject Edit/Write operations from your phone, with the same numbered options Claude Code shows in the terminal
|
|
15
|
+
- **๐๏ธ Voice prompts** โ dictate your next prompt with speech-to-text, no keyboard required
|
|
16
|
+
- **๐ท Image attachments** โ send screenshots and photos directly to the agent
|
|
17
|
+
- **๐ Push notifications** โ get notified when your agent needs input so you never block on approvals
|
|
18
|
+
- **๐ End-to-end encrypted** โ AES-256-GCM with ECDH key exchange; your prompts never pass through a server that can read them
|
|
19
|
+
- **๐ Locked screen support** โ works even when your computer screen is locked (via tmux)
|
|
20
|
+
- **๐ง Session-aware** โ runs multiple concurrent Claude Code sessions independently
|
|
51
21
|
|
|
52
|
-
|
|
22
|
+
## Install in 30 seconds
|
|
53
23
|
|
|
54
|
-
|
|
24
|
+
The fastest path installs everything (Node, tmux, Claude Code, CodeVibe) in one command:
|
|
55
25
|
|
|
56
26
|
```bash
|
|
57
|
-
|
|
27
|
+
curl -fsSL https://quantiya.ai/codevibe/install.sh | bash
|
|
58
28
|
```
|
|
59
29
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
Other auth commands:
|
|
63
|
-
```bash
|
|
64
|
-
codevibe-claude status # Check authentication status
|
|
65
|
-
codevibe-claude logout # Sign out and clear tokens
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
### Step 5: Start Claude Code with Mobile Support
|
|
69
|
-
|
|
70
|
-
**Important:** You must use the `codevibe-claude` wrapper instead of `claude` directly:
|
|
30
|
+
Then download the **[iOS app](https://apps.apple.com/app/id6756500217)** or **[Android app](https://play.google.com/store/apps/details?id=ai.quantiya.app.codevibe)**, sign in with the same Apple or Google account, and start a session:
|
|
71
31
|
|
|
72
32
|
```bash
|
|
73
33
|
codevibe-claude
|
|
74
34
|
```
|
|
75
35
|
|
|
76
|
-
|
|
77
|
-
- Creates a tmux session for the Claude Code instance
|
|
78
|
-
- Enables mobile prompts to work even when your Mac screen is locked
|
|
79
|
-
- Supports multiple concurrent Claude Code sessions
|
|
80
|
-
|
|
81
|
-
**Note:** Running `claude` directly without the wrapper will prevent mobile prompts from working when your screen is locked.
|
|
82
|
-
|
|
83
|
-
### Step 6: Download the iOS App
|
|
84
|
-
|
|
85
|
-
Download "CodeVibe" from the App Store and sign in with the same account you used in Step 4.
|
|
86
|
-
|
|
87
|
-
## Usage
|
|
36
|
+
Your session appears on your phone automatically. That's it.
|
|
88
37
|
|
|
89
|
-
###
|
|
38
|
+
### Manual install (if you already have Claude Code)
|
|
90
39
|
|
|
91
40
|
```bash
|
|
92
|
-
|
|
41
|
+
npm install -g @quantiya/codevibe
|
|
42
|
+
claude plugin marketplace add https://github.com/hendryyeh/quantiya-codevibe-marketplace
|
|
43
|
+
claude plugin install codevibe-claude@codevibe-marketplace
|
|
44
|
+
codevibe login
|
|
93
45
|
codevibe-claude
|
|
94
|
-
|
|
95
|
-
# Resume the last session
|
|
96
|
-
codevibe-claude --resume
|
|
97
|
-
|
|
98
|
-
# Start with a prompt
|
|
99
|
-
codevibe-claude -p "fix the bug"
|
|
100
|
-
|
|
101
|
-
# All other claude arguments work too
|
|
102
|
-
codevibe-claude --model sonnet
|
|
103
46
|
```
|
|
104
47
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
| Feature | `codevibe-claude` | Regular `claude` |
|
|
108
|
-
|---------|-------------------|------------------|
|
|
109
|
-
| Mobile prompts | Works always | Only when screen unlocked |
|
|
110
|
-
| Screen locked support | Yes (via tmux) | No |
|
|
111
|
-
| Mouse scrolling | Yes | Yes |
|
|
112
|
-
| Same experience | Yes | Yes |
|
|
48
|
+
## Requirements
|
|
113
49
|
|
|
114
|
-
|
|
50
|
+
- **macOS, Linux, or WSL Ubuntu** โ Windows without WSL is not supported
|
|
51
|
+
- **Node.js** 18.0.0+
|
|
52
|
+
- **tmux** โ `brew install tmux` on macOS, `apt install tmux` on Linux/WSL
|
|
53
|
+
- **Claude Code** with the plugin system enabled
|
|
115
54
|
|
|
116
|
-
|
|
55
|
+
## How it works
|
|
117
56
|
|
|
118
|
-
- **User Prompts** - Your questions and commands
|
|
119
|
-
- **Assistant Responses** - Claude's complete responses
|
|
120
|
-
- **Tool Usage** - File edits, reads, writes
|
|
121
|
-
- **Interactive Prompts** - Y/n permission requests
|
|
122
|
-
- **Notifications** - System messages
|
|
123
|
-
|
|
124
|
-
### Mobile to Desktop Control
|
|
125
|
-
|
|
126
|
-
From the iOS app:
|
|
127
|
-
|
|
128
|
-
1. Open the CodeVibe app
|
|
129
|
-
2. Select your active session
|
|
130
|
-
3. Type a message or response
|
|
131
|
-
4. It executes immediately in your desktop Claude Code session
|
|
132
|
-
5. See the response on both desktop and mobile
|
|
133
|
-
|
|
134
|
-
### Responding to Permission Prompts from Mobile
|
|
135
|
-
|
|
136
|
-
When Claude needs permission to edit or write files, you can respond directly from your phone.
|
|
137
|
-
|
|
138
|
-
**Available Options:**
|
|
139
|
-
|
|
140
|
-
| Reply | Description |
|
|
141
|
-
|-------|-------------|
|
|
142
|
-
| `1` | **Approve** - Allow this specific file operation |
|
|
143
|
-
| `2` | **Always Allow** - Approve and skip future prompts for this project |
|
|
144
|
-
| `3` | **Reject** - Decline the operation |
|
|
145
|
-
| `3 <message>` | **Reject with Instructions** - Decline and tell Claude what to do instead |
|
|
146
|
-
|
|
147
|
-
**Examples:**
|
|
148
57
|
```
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
58
|
+
โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ
|
|
59
|
+
โ Claude Code โ โโ โ CodeVibe โ โโ โ Your phone โ
|
|
60
|
+
โ on desktop โ โ E2E encrypt โ โ (iOS/Android)โ
|
|
61
|
+
โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ
|
|
62
|
+
โ โ
|
|
63
|
+
โโโโโโโโโโโ mobile prompts โโโโโโโโโโโโโโ
|
|
153
64
|
```
|
|
154
65
|
|
|
155
|
-
|
|
156
|
-
- `Y` / `N` - For yes/no confirmation prompts
|
|
157
|
-
- `exit`, `quit` - To cancel running operations
|
|
158
|
-
- Any other text - Sent as a new prompt to Claude
|
|
66
|
+
Every prompt, response, tool call, and file edit in Claude Code gets encrypted and relayed to your phone in real time via AWS AppSync. Messages from your phone execute immediately in the correct desktop session via tmux โ no polling, no manual refresh.
|
|
159
67
|
|
|
160
|
-
##
|
|
68
|
+
## Responding to permission prompts from your phone
|
|
161
69
|
|
|
162
|
-
|
|
70
|
+
When Claude asks for permission to edit a file, you see the **same numbered options on your phone** as in the terminal. Reply by typing or voice-inputting the option number:
|
|
163
71
|
|
|
164
|
-
|
|
72
|
+
| Reply | What it does |
|
|
73
|
+
|---|---|
|
|
74
|
+
| `1` | Approve this specific file operation |
|
|
75
|
+
| `2` | Always allow for this project |
|
|
76
|
+
| `3` | Reject the operation |
|
|
77
|
+
| `3 use a different approach` | Reject and redirect Claude with new instructions |
|
|
165
78
|
|
|
166
|
-
|
|
167
|
-
```bash
|
|
168
|
-
source ~/.zshrc # or ~/.bashrc
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
Or check if PATH was added:
|
|
172
|
-
```bash
|
|
173
|
-
grep "codevibe-claude" ~/.zshrc
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
### "Not authenticated" error
|
|
177
|
-
|
|
178
|
-
**Cause:** User hasn't logged in or tokens expired.
|
|
179
|
-
|
|
180
|
-
**Solution:**
|
|
181
|
-
```bash
|
|
182
|
-
codevibe-claude login
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
### Mobile messages not executing
|
|
186
|
-
|
|
187
|
-
**Cause:** Not using the codevibe-claude wrapper.
|
|
188
|
-
|
|
189
|
-
**Solution:** Start Claude Code with:
|
|
190
|
-
```bash
|
|
191
|
-
codevibe-claude
|
|
192
|
-
```
|
|
193
|
-
Not just `claude`.
|
|
194
|
-
|
|
195
|
-
### Plugin hooks not firing
|
|
196
|
-
|
|
197
|
-
**Cause:** Plugin not properly installed.
|
|
198
|
-
|
|
199
|
-
**Solution:**
|
|
200
|
-
```
|
|
201
|
-
# In Claude Code
|
|
202
|
-
/plugin list # Check if installed
|
|
203
|
-
/plugin uninstall codevibe-claude
|
|
204
|
-
/plugin install codevibe-claude
|
|
205
|
-
```
|
|
79
|
+
You can also reply with any text to send a new prompt.
|
|
206
80
|
|
|
207
|
-
|
|
81
|
+
## Benefits of the `codevibe-claude` wrapper
|
|
208
82
|
|
|
209
|
-
|
|
83
|
+
| | `codevibe-claude` | plain `claude` |
|
|
84
|
+
|---|---|---|
|
|
85
|
+
| Mobile prompts | โ
Always | โ ๏ธ Only when screen unlocked |
|
|
86
|
+
| Screen-locked support | โ
Yes (via tmux) | โ No |
|
|
87
|
+
| Multiple concurrent sessions | โ
Yes | โ No |
|
|
210
88
|
|
|
211
|
-
|
|
89
|
+
Use `codevibe-claude` anywhere you'd use `claude` โ all the same flags (`--resume`, `-p`, `--model`, etc.) work identically.
|
|
212
90
|
|
|
213
|
-
|
|
214
|
-
pkill -f "node dist/server.js"
|
|
215
|
-
```
|
|
216
|
-
|
|
217
|
-
## Mac Power Settings (for Locked Screen Support)
|
|
218
|
-
|
|
219
|
-
To ensure mobile prompts work when your Mac screen is locked:
|
|
220
|
-
|
|
221
|
-
1. **System Settings** -> **Battery** (or **Energy**) -> Set "Computer sleep" to **Never** (when on power)
|
|
222
|
-
2. In Terminal:
|
|
223
|
-
```bash
|
|
224
|
-
sudo pmset -a tcpkeepalive 1
|
|
225
|
-
sudo pmset -a womp 1
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
This keeps network connections alive during sleep.
|
|
229
|
-
|
|
230
|
-
## Architecture
|
|
231
|
-
|
|
232
|
-
```
|
|
233
|
-
Desktop -> Mobile:
|
|
234
|
-
+-------------+ +----------+ +---------+ +----------+ +--------+
|
|
235
|
-
| Claude Code | -> | Hook | -> | MCP | -> | AppSync | -> | Mobile |
|
|
236
|
-
| | | Scripts | | Server | | GraphQL | | App |
|
|
237
|
-
+-------------+ +----------+ +---------+ +----------+ +--------+
|
|
238
|
-
|
|
239
|
-
Mobile -> Desktop:
|
|
240
|
-
+--------+ +----------+ +---------+ +-------------+
|
|
241
|
-
| Mobile | -> | AppSync | -> | MCP | -> | Claude Code |
|
|
242
|
-
| App | | GraphQL | | Server | | (tmux keys) |
|
|
243
|
-
+--------+ +----------+ +---------+ +-------------+
|
|
244
|
-
```
|
|
91
|
+
## Power settings for reliable locked-screen mode
|
|
245
92
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
If you want to contribute or run from source:
|
|
249
|
-
|
|
250
|
-
### Clone and Build
|
|
93
|
+
For mobile prompts to keep working when your screen is locked, keep your computer awake on AC power:
|
|
251
94
|
|
|
95
|
+
**macOS:**
|
|
252
96
|
```bash
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
npm install
|
|
256
|
-
npm run build
|
|
97
|
+
sudo pmset -a tcpkeepalive 1
|
|
98
|
+
sudo pmset -a womp 1
|
|
257
99
|
```
|
|
100
|
+
Then set **System Settings โ Battery โ Computer sleep: Never** (when on power).
|
|
258
101
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
For local development and testing:
|
|
102
|
+
**Linux / WSL Ubuntu:** disable sleep in your desktop environment's power settings, or use `systemd-inhibit --what=sleep:idle sleep infinity &`. On WSL, configure Windows power settings on the host.
|
|
262
103
|
|
|
263
|
-
|
|
264
|
-
./install-local.sh
|
|
104
|
+
## Troubleshooting
|
|
265
105
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
106
|
+
- **"command not found: codevibe-claude"** โ reload your shell: `source ~/.zshrc` (or `~/.bashrc`)
|
|
107
|
+
- **"not authenticated"** โ run `codevibe login`
|
|
108
|
+
- **Mobile messages not executing** โ make sure you launched via `codevibe-claude`, not plain `claude`
|
|
109
|
+
- **Plugin hooks not firing** โ `codevibe update`, then start a fresh session
|
|
270
110
|
|
|
271
|
-
### View
|
|
111
|
+
### View logs
|
|
272
112
|
|
|
273
113
|
```bash
|
|
274
|
-
# MCP Server logs
|
|
275
114
|
tail -f /tmp/codevibe-claude-mcp.log
|
|
276
|
-
|
|
277
|
-
# Hook scripts logs
|
|
278
115
|
tail -f /tmp/codevibe-claude-hooks.log
|
|
279
116
|
```
|
|
280
117
|
|
|
281
118
|
## Support
|
|
282
119
|
|
|
283
|
-
- **
|
|
120
|
+
- **Email:** support@quantiya.ai
|
|
121
|
+
- **Landing page:** [quantiya.ai/codevibe](https://quantiya.ai/codevibe)
|
|
122
|
+
- **Privacy policy:** [quantiya.ai/privacy](https://quantiya.ai/privacy)
|
|
284
123
|
|
|
285
|
-
##
|
|
124
|
+
## Part of the CodeVibe family
|
|
286
125
|
|
|
287
|
-
- **
|
|
126
|
+
- **[@quantiya/codevibe](https://www.npmjs.com/package/@quantiya/codevibe)** โ meta-package with the `codevibe` CLI (install this, not this plugin directly)
|
|
127
|
+
- **[@quantiya/codevibe-core](https://www.npmjs.com/package/@quantiya/codevibe-core)** โ shared library used by all plugins
|
|
128
|
+
- **[@quantiya/codevibe-gemini-plugin](https://www.npmjs.com/package/@quantiya/codevibe-gemini-plugin)** โ Gemini CLI support
|
|
129
|
+
- **[@quantiya/codevibe-codex-plugin](https://www.npmjs.com/package/@quantiya/codevibe-codex-plugin)** โ OpenAI Codex CLI support
|
|
288
130
|
|
|
289
131
|
## License
|
|
290
132
|
|
|
291
|
-
MIT
|
|
292
|
-
|
|
293
|
-
---
|
|
294
|
-
|
|
295
|
-
**Made with care by the CodeVibe Team**
|
|
133
|
+
MIT
|
package/hooks/session-start.sh
CHANGED
|
@@ -35,6 +35,192 @@ else
|
|
|
35
35
|
MAPPING_FILE=""
|
|
36
36
|
fi
|
|
37
37
|
|
|
38
|
+
# โโโ Orphan daemon sweep โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
39
|
+
#
|
|
40
|
+
# When Claude Code terminates abnormally (crash, force-kill, window closed
|
|
41
|
+
# without /exit, network drop), the SessionEnd hook doesn't fire and the
|
|
42
|
+
# background daemon is never sent SIGTERM. Because the daemon is a PPID=1
|
|
43
|
+
# process (detached from the hook that launched it), nothing in the OS
|
|
44
|
+
# cleans it up โ it keeps holding its AppSync subscription, sending
|
|
45
|
+
# heartbeats, and showing the session as "desktop connected" on iOS forever.
|
|
46
|
+
#
|
|
47
|
+
# This sweep runs on every SessionStart and cleans up orphan daemons whose
|
|
48
|
+
# tmux session no longer exists. Daemons belonging to live tmux sessions
|
|
49
|
+
# (including our own) are left alone.
|
|
50
|
+
#
|
|
51
|
+
# Scope: the sweep only considers daemons that have an instance mapping file
|
|
52
|
+
# (i.e. were launched under a tmux session). Daemons launched without tmux
|
|
53
|
+
# have no mapping and cannot be safely identified as orphans, so we leave
|
|
54
|
+
# them alone.
|
|
55
|
+
#
|
|
56
|
+
# Safety guards:
|
|
57
|
+
#
|
|
58
|
+
# (A) PID reuse: before killing a PID, verify via `ps -o command=` that the
|
|
59
|
+
# process is actually a node + codevibe-claude + server.js invocation.
|
|
60
|
+
# PIDs are recyclable (macOS cycles through ~32K), so a stale mapping
|
|
61
|
+
# from weeks ago could end up pointing at an unrelated user process.
|
|
62
|
+
# Without identity verification, we'd kill random things.
|
|
63
|
+
#
|
|
64
|
+
# (B) tmux unavailable: if the `tmux` binary is missing or fails for a reason
|
|
65
|
+
# other than "session doesn't exist", we conservatively treat the session
|
|
66
|
+
# as alive and leave the daemon alone. This prevents a degraded-tmux
|
|
67
|
+
# environment from causing a sweep-induced mass kill.
|
|
68
|
+
|
|
69
|
+
# Check whether a PID is still identifiable as a codevibe-claude daemon.
|
|
70
|
+
# Returns 0 if yes, non-zero otherwise.
|
|
71
|
+
is_codevibe_daemon() {
|
|
72
|
+
local pid="$1"
|
|
73
|
+
local cmd
|
|
74
|
+
cmd=$(ps -o command= -p "$pid" 2>/dev/null)
|
|
75
|
+
# Pattern: "node" + "codevibe-claude" + "server.js" somewhere in the command line.
|
|
76
|
+
# Matches both the cache path (...codevibe-marketplace/codevibe-claude/X.Y.Z/dist/server.js)
|
|
77
|
+
# and the dev path (...codevibe-claude-plugin/dist/server.js).
|
|
78
|
+
case "$cmd" in
|
|
79
|
+
*node*codevibe-claude*server.js*) return 0 ;;
|
|
80
|
+
*) return 1 ;;
|
|
81
|
+
esac
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
# Return 0 if the tmux session is alive OR we can't tell.
|
|
85
|
+
# Return non-zero ONLY when we can prove the session is gone: tmux binary exists,
|
|
86
|
+
# tmux server is reachable, and the server says this specific session doesn't exist.
|
|
87
|
+
# Every other failure mode (no binary, server down, socket error, permission issue)
|
|
88
|
+
# returns 0 โ conservative, we'd rather leak a daemon than kill the wrong thing.
|
|
89
|
+
tmux_session_alive() {
|
|
90
|
+
local tmux_name="$1"
|
|
91
|
+
if ! command -v tmux >/dev/null 2>&1; then
|
|
92
|
+
return 0 # no tmux binary โ can't verify โ assume alive
|
|
93
|
+
fi
|
|
94
|
+
# Verify the tmux server itself is reachable before trusting has-session.
|
|
95
|
+
# `tmux ls` exits 0 when the server is running and has at least one session.
|
|
96
|
+
# If it fails (server not running, socket error, no sessions at all), we can't
|
|
97
|
+
# reliably distinguish "this session is gone" from "tmux is broken." Conservative
|
|
98
|
+
# choice: assume the session might still be alive (or recoverable when the server
|
|
99
|
+
# comes back).
|
|
100
|
+
if ! tmux ls >/dev/null 2>&1; then
|
|
101
|
+
return 0 # server unreachable or empty โ can't verify โ assume alive
|
|
102
|
+
fi
|
|
103
|
+
# Server is confirmed reachable and has sessions. has-session non-zero now
|
|
104
|
+
# reliably means "this specific session does not exist."
|
|
105
|
+
tmux has-session -t "$tmux_name" 2>/dev/null
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
sweep_orphan_daemons() {
|
|
109
|
+
local swept=0
|
|
110
|
+
local kept=0
|
|
111
|
+
local cleaned_stale=0
|
|
112
|
+
|
|
113
|
+
# Pass 1: walk mapping files, which are the source of truth for (tmux_session, pid) pairs
|
|
114
|
+
shopt -s nullglob
|
|
115
|
+
local map_file
|
|
116
|
+
for map_file in "${CODEVIBE_TMPDIR}"/codevibe-claude-instance-*.json; do
|
|
117
|
+
# Extract tmux session name from filename
|
|
118
|
+
local orphan_tmux
|
|
119
|
+
orphan_tmux=$(basename "$map_file" .json | sed 's/^codevibe-claude-instance-//')
|
|
120
|
+
|
|
121
|
+
# Skip our own tmux session โ we handle it in the existing reuse path below
|
|
122
|
+
if [ -n "$TMUX_SESSION" ] && [ "$orphan_tmux" = "$TMUX_SESSION" ]; then
|
|
123
|
+
continue
|
|
124
|
+
fi
|
|
125
|
+
|
|
126
|
+
local orphan_pid
|
|
127
|
+
orphan_pid=$(jq -r '.pid // empty' "$map_file" 2>/dev/null)
|
|
128
|
+
|
|
129
|
+
# Empty or missing PID in mapping โ clean up the mapping file
|
|
130
|
+
if [ -z "$orphan_pid" ]; then
|
|
131
|
+
log "INFO" "Orphan sweep: removing mapping with no PID: $map_file"
|
|
132
|
+
rm -f "$map_file"
|
|
133
|
+
cleaned_stale=$((cleaned_stale + 1))
|
|
134
|
+
continue
|
|
135
|
+
fi
|
|
136
|
+
|
|
137
|
+
# PID is no longer running โ clean up the stale mapping.
|
|
138
|
+
# NOTE: do NOT touch the pidfile or portfile by session UUID here. If /resume
|
|
139
|
+
# or a UUID collision caused a stale mapping to reference the same session
|
|
140
|
+
# UUID as a currently-live pidfile, we'd delete the live one. Pass 2 below
|
|
141
|
+
# handles pidfile cleanup by verifying each pidfile's content independently.
|
|
142
|
+
if ! ps -p "$orphan_pid" > /dev/null 2>&1; then
|
|
143
|
+
log "INFO" "Orphan sweep: removing stale mapping for dead PID $orphan_pid (tmux $orphan_tmux)"
|
|
144
|
+
rm -f "$map_file"
|
|
145
|
+
cleaned_stale=$((cleaned_stale + 1))
|
|
146
|
+
continue
|
|
147
|
+
fi
|
|
148
|
+
|
|
149
|
+
# PID is alive. Is its tmux session still alive (or unknowable)?
|
|
150
|
+
if tmux_session_alive "$orphan_tmux"; then
|
|
151
|
+
# Live other session โ or tmux is unavailable and we can't tell. Either
|
|
152
|
+
# way, the conservative choice is to leave the daemon alone.
|
|
153
|
+
log "DEBUG" "Orphan sweep: tmux $orphan_tmux is alive (or tmux unavailable), leaving daemon $orphan_pid"
|
|
154
|
+
kept=$((kept + 1))
|
|
155
|
+
continue
|
|
156
|
+
fi
|
|
157
|
+
|
|
158
|
+
# tmux is gone but the PID is alive โ looks like an orphan. But before
|
|
159
|
+
# killing, verify the PID actually belongs to a codevibe-claude daemon.
|
|
160
|
+
# If PID reuse has handed this PID to an unrelated process, we must not
|
|
161
|
+
# touch it. Just clean up the stale mapping and move on.
|
|
162
|
+
if ! is_codevibe_daemon "$orphan_pid"; then
|
|
163
|
+
log "INFO" "Orphan sweep: PID $orphan_pid is alive but is not a CodeVibe daemon (likely PID reused), removing stale mapping only"
|
|
164
|
+
rm -f "$map_file"
|
|
165
|
+
cleaned_stale=$((cleaned_stale + 1))
|
|
166
|
+
continue
|
|
167
|
+
fi
|
|
168
|
+
|
|
169
|
+
# Verified: this is our daemon and its tmux is gone. Kill it.
|
|
170
|
+
log "WARN" "Orphan sweep: killing orphan daemon PID $orphan_pid (tmux $orphan_tmux is gone)"
|
|
171
|
+
|
|
172
|
+
# SIGTERM first โ lets the daemon run its graceful shutdown (mark session INACTIVE, etc.)
|
|
173
|
+
kill "$orphan_pid" 2>/dev/null || true
|
|
174
|
+
|
|
175
|
+
# Give it a moment to exit cleanly, then SIGKILL if it's still alive
|
|
176
|
+
sleep 0.5
|
|
177
|
+
if ps -p "$orphan_pid" > /dev/null 2>&1; then
|
|
178
|
+
log "WARN" "Orphan sweep: daemon $orphan_pid did not exit from SIGTERM, sending SIGKILL"
|
|
179
|
+
kill -9 "$orphan_pid" 2>/dev/null || true
|
|
180
|
+
fi
|
|
181
|
+
|
|
182
|
+
# Remove the mapping file. Do NOT touch pidfile/portfile by session UUID โ
|
|
183
|
+
# Pass 2 below will pick up the now-dead PID and clean the pidfile (and its
|
|
184
|
+
# matching portfile) safely, based on PID content, not mapping contents.
|
|
185
|
+
rm -f "$map_file"
|
|
186
|
+
swept=$((swept + 1))
|
|
187
|
+
done
|
|
188
|
+
|
|
189
|
+
# Pass 2: defensive cleanup of stale pidfiles that have no mapping and are dead
|
|
190
|
+
# (e.g. daemons that died before writing a mapping file, or legacy non-tmux launches)
|
|
191
|
+
local pid_file
|
|
192
|
+
for pid_file in "${CODEVIBE_TMPDIR}"/codevibe-claude-*.pid; do
|
|
193
|
+
# Skip the instance-mapping files' PID files (they have 'instance' in the name and are .json not .pid,
|
|
194
|
+
# but be explicit in case the glob matches something unexpected)
|
|
195
|
+
local base
|
|
196
|
+
base=$(basename "$pid_file")
|
|
197
|
+
case "$base" in
|
|
198
|
+
codevibe-claude-instance-*) continue ;;
|
|
199
|
+
esac
|
|
200
|
+
|
|
201
|
+
local pid
|
|
202
|
+
pid=$(cat "$pid_file" 2>/dev/null)
|
|
203
|
+
if [ -z "$pid" ] || ! ps -p "$pid" > /dev/null 2>&1; then
|
|
204
|
+
log "INFO" "Orphan sweep: removing stale pidfile $pid_file (PID ${pid:-empty} not alive)"
|
|
205
|
+
rm -f "$pid_file"
|
|
206
|
+
# Also clean up the corresponding portfile
|
|
207
|
+
local port_file="${pid_file%.pid}.port"
|
|
208
|
+
rm -f "$port_file"
|
|
209
|
+
cleaned_stale=$((cleaned_stale + 1))
|
|
210
|
+
fi
|
|
211
|
+
done
|
|
212
|
+
shopt -u nullglob
|
|
213
|
+
|
|
214
|
+
if [ $swept -gt 0 ] || [ $cleaned_stale -gt 0 ]; then
|
|
215
|
+
log "INFO" "Orphan sweep: killed $swept orphan daemon(s), cleaned $cleaned_stale stale file(s), kept $kept live session(s)"
|
|
216
|
+
else
|
|
217
|
+
log "DEBUG" "Orphan sweep: nothing to clean (kept $kept live session(s))"
|
|
218
|
+
fi
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
# Run the sweep before touching the mapping file for our own session
|
|
222
|
+
sweep_orphan_daemons || log "WARN" "Orphan sweep failed (non-fatal, continuing)"
|
|
223
|
+
|
|
38
224
|
# Check if there's an MCP server running for this Claude Code instance
|
|
39
225
|
EXISTING_SERVER_PORT=""
|
|
40
226
|
EXISTING_SERVER_PID=""
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quantiya/codevibe-claude-plugin",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.0.11",
|
|
4
|
+
"description": "Control Claude Code from your iPhone and Android โ real-time sync, approve file edits, send prompts by voice. Part of CodeVibe.",
|
|
5
5
|
"main": "dist/server.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"codevibe-claude": "./bin/codevibe-claude"
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
|
-
"dist",
|
|
10
|
+
"dist/**/*.js",
|
|
11
11
|
"bin",
|
|
12
12
|
"hooks",
|
|
13
13
|
".claude-plugin",
|
|
@@ -24,10 +24,6 @@
|
|
|
24
24
|
"watch": "tsc --watch",
|
|
25
25
|
"test": "jest --config jest.config.js --forceExit"
|
|
26
26
|
},
|
|
27
|
-
"repository": {
|
|
28
|
-
"type": "git",
|
|
29
|
-
"url": "git+https://github.com/hendryyeh/quantiya-codevibe-claude-plugin.git"
|
|
30
|
-
},
|
|
31
27
|
"keywords": [
|
|
32
28
|
"claude",
|
|
33
29
|
"claude-code",
|
|
@@ -35,6 +31,7 @@
|
|
|
35
31
|
"plugin",
|
|
36
32
|
"mobile",
|
|
37
33
|
"ios",
|
|
34
|
+
"android",
|
|
38
35
|
"codevibe",
|
|
39
36
|
"remote-control",
|
|
40
37
|
"ai",
|
|
@@ -43,9 +40,9 @@
|
|
|
43
40
|
"author": "Quantiya <support@quantiya.ai>",
|
|
44
41
|
"license": "MIT",
|
|
45
42
|
"bugs": {
|
|
46
|
-
"
|
|
43
|
+
"email": "support@quantiya.ai"
|
|
47
44
|
},
|
|
48
|
-
"homepage": "https://
|
|
45
|
+
"homepage": "https://quantiya.ai/codevibe",
|
|
49
46
|
"engines": {
|
|
50
47
|
"node": ">=18.0.0"
|
|
51
48
|
},
|