vibora 4.5.2 → 4.7.0

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 CHANGED
@@ -135,8 +135,9 @@ claude plugin install vibora@vibora --scope user
135
135
 
136
136
  ### MCP Tools
137
137
 
138
- The plugin includes an MCP server that exposes task management tools:
138
+ The plugin includes an MCP server that exposes task management and remote execution tools:
139
139
 
140
+ **Task Management:**
140
141
  - `list_tasks` — List all tasks with optional status/repo filter
141
142
  - `get_task` — Get task details by ID
142
143
  - `create_task` — Create a new task with git worktree
@@ -146,6 +147,14 @@ The plugin includes an MCP server that exposes task management tools:
146
147
  - `list_repositories` — List configured repositories
147
148
  - `send_notification` — Send notification to enabled channels
148
149
 
150
+ **Remote Command Execution:**
151
+ - `execute_command` — Execute shell commands on the Vibora server with persistent session support
152
+ - `list_exec_sessions` — List active command execution sessions
153
+ - `update_exec_session` — Rename a session
154
+ - `destroy_exec_session` — Clean up a session
155
+
156
+ The `execute_command` tool supports persistent sessions where environment variables, working directory, and shell state are preserved between commands. Sessions can be given optional names for identification and persist until manually destroyed.
157
+
149
158
  For Claude Desktop, add to your `claude_desktop_config.json`:
150
159
 
151
160
  ```json
@@ -163,18 +172,40 @@ For Claude Desktop, add to your `claude_desktop_config.json`:
163
172
 
164
173
  Run the backend on a remote server and connect from anywhere. Launch tasks, close your laptop, and your agents keep working. As AI becomes more capable of autonomous work, this becomes essential.
165
174
 
166
- 1. **Set up a secure tunnel** — Use Tailscale (recommended) or Cloudflare Tunnels to securely expose your server
167
- - **Tailscale** — Works with both web and desktop app. No CORS configuration needed.
168
- - **Cloudflare Tunnels** Alternative for web-only usage. Note: Desktop app has CORS limitations with Cloudflare Tunnels.
175
+ ### Desktop App: SSH Port Forwarding (Recommended)
176
+
177
+ The desktop app connects to `localhost:7777`. Use SSH port forwarding to tunnel to your remote server:
178
+
179
+ ```bash
180
+ # Forward local port 7777 to remote server's port 7777
181
+ ssh -L 7777:localhost:7777 your-server
182
+
183
+ # Or run in background with keep-alive
184
+ ssh -fN -o ServerAliveInterval=30 -o ServerAliveCountMax=3 -L 7777:localhost:7777 your-server
185
+ ```
186
+
187
+ On the remote server, start Vibora:
188
+ ```bash
189
+ npx vibora@latest up
190
+ ```
191
+
192
+ The desktop app will connect through the tunnel automatically. This approach is:
193
+ - **Secure** — Backend stays bound to localhost, no exposed ports
194
+ - **Performant** — Direct SSH connection, lower latency than overlay networks
195
+ - **Simple** — No additional configuration needed
169
196
 
170
- 2. **On the remote server:**
197
+ For persistent tunnels on macOS, use a launchd agent. See [this guide](https://gist.github.com/knowsuchagency/60656087903cd56d3a9b5d1d5c803186).
198
+
199
+ ### Browser: Tailscale or Cloudflare Tunnels
200
+
201
+ For browser-only access, you can use Tailscale or Cloudflare Tunnels to expose your server:
202
+
203
+ 1. **On the remote server:**
171
204
  ```bash
172
205
  npx vibora@latest up
173
206
  ```
174
207
 
175
- 3. **Connect from desktop app** — Click "Connect to Remote" and enter the server URL (e.g., `http://your-server.tailnet.ts.net:7777`)
176
-
177
- 4. **Or access via browser** — Open the tunnel URL in your browser
208
+ 2. **Access via browser** — Open the tunnel URL (e.g., `http://your-server.tailnet.ts.net:7777`)
178
209
 
179
210
  <details>
180
211
  <summary><strong>Configuration</strong></summary>
@@ -187,13 +218,12 @@ Settings are stored in `.vibora/settings.json`. The vibora directory is resolved
187
218
 
188
219
  | Setting | Env Var | Default |
189
220
  |---------|---------|---------|
190
- | port | `PORT` | 7777 |
191
- | defaultGitReposDir | `VIBORA_GIT_REPOS_DIR` | ~ |
192
- | remoteHost | `VIBORA_REMOTE_HOST` | (empty) |
193
- | sshPort | `VIBORA_SSH_PORT` | 22 |
194
- | linearApiKey | `LINEAR_API_KEY` | null |
195
- | githubPat | `GITHUB_PAT` | null |
196
- | language | — | null (auto-detect) |
221
+ | server.port | `PORT` | 7777 |
222
+ | paths.defaultGitReposDir | `VIBORA_GIT_REPOS_DIR` | ~ |
223
+ | editor.sshPort | `VIBORA_SSH_PORT` | 22 |
224
+ | integrations.linearApiKey | `LINEAR_API_KEY` | null |
225
+ | integrations.githubPat | `GITHUB_PAT` | null |
226
+ | appearance.language | | null (auto-detect) |
197
227
 
198
228
  Notification settings (sound, Slack, Discord, Pushover) are configured via the Settings UI or CLI.
199
229
 
package/bin/vibora.js CHANGED
@@ -274,6 +274,30 @@ class ViboraClient {
274
274
  method: "POST"
275
275
  });
276
276
  }
277
+ async executeCommand(command, options) {
278
+ const body = {
279
+ command,
280
+ ...options
281
+ };
282
+ return this.fetch("/api/exec", {
283
+ method: "POST",
284
+ body: JSON.stringify(body)
285
+ });
286
+ }
287
+ async listExecSessions() {
288
+ return this.fetch("/api/exec/sessions");
289
+ }
290
+ async updateExecSession(sessionId, updates) {
291
+ return this.fetch(`/api/exec/sessions/${sessionId}`, {
292
+ method: "PATCH",
293
+ body: JSON.stringify(updates)
294
+ });
295
+ }
296
+ async destroyExecSession(sessionId) {
297
+ return this.fetch(`/api/exec/sessions/${sessionId}`, {
298
+ method: "DELETE"
299
+ });
300
+ }
277
301
  }
278
302
  var init_client = __esm(() => {
279
303
  init_server();
@@ -29776,6 +29800,49 @@ function registerTools(server, client) {
29776
29800
  return handleToolError(err);
29777
29801
  }
29778
29802
  });
29803
+ server.tool("execute_command", "Execute a CLI command on the remote Vibora server. Supports persistent sessions for stateful workflows where environment variables, working directory, and shell state are preserved between commands.", {
29804
+ command: exports_external.string().describe("The shell command to execute"),
29805
+ sessionId: exports_external.optional(exports_external.string()).describe("Session ID for stateful workflows. Omit to create a new session. Reuse to maintain shell state."),
29806
+ cwd: exports_external.optional(exports_external.string()).describe("Initial working directory (only used when creating a new session)"),
29807
+ timeout: exports_external.optional(exports_external.number()).describe("Timeout in milliseconds (default: 30000). Use longer timeouts for slow commands."),
29808
+ name: exports_external.optional(exports_external.string()).describe("Optional session name for identification (only used when creating a new session)")
29809
+ }, async ({ command, sessionId, cwd, timeout, name }) => {
29810
+ try {
29811
+ const result = await client.executeCommand(command, { sessionId, cwd, timeout, name });
29812
+ return formatSuccess(result);
29813
+ } catch (err) {
29814
+ return handleToolError(err);
29815
+ }
29816
+ });
29817
+ server.tool("list_exec_sessions", "List all active command execution sessions on the Vibora server", {}, async () => {
29818
+ try {
29819
+ const sessions = await client.listExecSessions();
29820
+ return formatSuccess(sessions);
29821
+ } catch (err) {
29822
+ return handleToolError(err);
29823
+ }
29824
+ });
29825
+ server.tool("update_exec_session", "Update an existing command execution session (e.g., rename it)", {
29826
+ sessionId: exports_external.string().describe("The session ID to update"),
29827
+ name: exports_external.optional(exports_external.string()).describe("New name for the session")
29828
+ }, async ({ sessionId, name }) => {
29829
+ try {
29830
+ const result = await client.updateExecSession(sessionId, { name });
29831
+ return formatSuccess(result);
29832
+ } catch (err) {
29833
+ return handleToolError(err);
29834
+ }
29835
+ });
29836
+ server.tool("destroy_exec_session", "Destroy an active command execution session to free resources", {
29837
+ sessionId: exports_external.string().describe("The session ID to destroy")
29838
+ }, async ({ sessionId }) => {
29839
+ try {
29840
+ const result = await client.destroyExecSession(sessionId);
29841
+ return formatSuccess(result);
29842
+ } catch (err) {
29843
+ return handleToolError(err);
29844
+ }
29845
+ });
29779
29846
  }
29780
29847
  var TaskStatusSchema2;
29781
29848
  var init_tools = __esm(() => {
@@ -30567,8 +30634,9 @@ async function main() {
30567
30634
  Usage: vibora <command> [options]
30568
30635
 
30569
30636
  Commands:
30570
- current-task Get task for current worktree
30637
+ current-task Get task for current worktree (use inside a Vibora task worktree)
30571
30638
  current-task pr <url> Associate a PR with current task
30639
+ current-task linear <url> Link a Linear ticket to current task
30572
30640
  current-task in-progress Mark current task as IN_PROGRESS
30573
30641
  current-task review Mark current task as IN_REVIEW
30574
30642
  current-task done Mark current task as DONE