@burdenoff/vibe-agent 2.4.0 → 2.6.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.
@@ -0,0 +1,601 @@
1
+ // @bun
2
+ import {
3
+ Elysia,
4
+ t
5
+ } from "./index-wr0mkm57.js";
6
+ import {
7
+ apiGet,
8
+ apiPost,
9
+ fail,
10
+ formatTable,
11
+ getAgentUrl,
12
+ header,
13
+ info,
14
+ kv,
15
+ success,
16
+ warn
17
+ } from "./index-xmeskdnb.js";
18
+ import {
19
+ __require,
20
+ __toESM
21
+ } from "./index-g8dczzvv.js";
22
+
23
+ // src/plugins/plugin-mgr/routes.ts
24
+ var AVAILABLE_PLUGINS = [
25
+ {
26
+ packageName: "@burdenoff/vibe-plugin-tunnel-cloudflare",
27
+ name: "tunnel-cloudflare",
28
+ description: "Cloudflare Tunnel provider for remote access",
29
+ cliCommand: "tunnel",
30
+ apiPrefix: "/api/tunnel-cloudflare",
31
+ installed: false,
32
+ category: "tunnel"
33
+ },
34
+ {
35
+ packageName: "@burdenoff/vibe-plugin-session-tmux",
36
+ name: "session-tmux",
37
+ description: "tmux session provider for terminal management",
38
+ cliCommand: "session",
39
+ apiPrefix: "/api/session-tmux",
40
+ installed: false,
41
+ category: "session"
42
+ },
43
+ {
44
+ packageName: "@burdenoff/vibe-plugin-ssh",
45
+ name: "ssh",
46
+ description: "SSH connection management and port forwarding",
47
+ cliCommand: "ssh",
48
+ apiPrefix: "/api/ssh",
49
+ installed: false,
50
+ category: "tool"
51
+ },
52
+ {
53
+ packageName: "@burdenoff/vibe-plugin-ai",
54
+ name: "ai",
55
+ description: "AI tool management and integration",
56
+ cliCommand: "ai",
57
+ apiPrefix: undefined,
58
+ installed: false,
59
+ category: "integration"
60
+ },
61
+ {
62
+ packageName: "@burdenoff/vibe-plugin-ui-ssh",
63
+ name: "ui-ssh",
64
+ description: "Web UI for SSH connection management and port forwarding",
65
+ cliCommand: undefined,
66
+ apiPrefix: undefined,
67
+ installed: false,
68
+ category: "frontend"
69
+ },
70
+ {
71
+ packageName: "@burdenoff/vibe-plugin-ui-ai",
72
+ name: "ui-ai",
73
+ description: "Web UI for AI tool catalog and detection status",
74
+ cliCommand: undefined,
75
+ apiPrefix: undefined,
76
+ installed: false,
77
+ category: "frontend"
78
+ }
79
+ ];
80
+ function createRoutes(deps) {
81
+ const { pluginManager } = deps;
82
+ return new Elysia().get("/", () => {
83
+ const plugins = pluginManager.getPluginDetails();
84
+ return { plugins, count: plugins.length };
85
+ }).get("/available", () => {
86
+ const installed = pluginManager.getPluginDetails();
87
+ const installedNames = new Set(installed.map((p) => p.packageName));
88
+ return {
89
+ plugins: AVAILABLE_PLUGINS.map((p) => ({
90
+ ...p,
91
+ installed: installedNames.has(p.packageName)
92
+ }))
93
+ };
94
+ }).post("/install", async ({ body, set }) => {
95
+ if (!body.packageName || typeof body.packageName !== "string") {
96
+ set.status = 400;
97
+ return { error: "Missing required field: packageName" };
98
+ }
99
+ try {
100
+ await pluginManager.install(body.packageName);
101
+ return {
102
+ success: true,
103
+ message: `Plugin ${body.packageName} installed and loaded successfully`
104
+ };
105
+ } catch (err) {
106
+ set.status = 500;
107
+ return { error: "Failed to install plugin", details: String(err) };
108
+ }
109
+ }, {
110
+ body: t.Object({
111
+ packageName: t.String()
112
+ })
113
+ }).post("/remove", async ({ body, set }) => {
114
+ if (!body.packageName || typeof body.packageName !== "string") {
115
+ set.status = 400;
116
+ return { error: "Missing required field: packageName" };
117
+ }
118
+ try {
119
+ await pluginManager.remove(body.packageName);
120
+ return {
121
+ success: true,
122
+ message: `Plugin ${body.packageName} removed successfully`
123
+ };
124
+ } catch (err) {
125
+ set.status = 500;
126
+ return { error: "Failed to remove plugin", details: String(err) };
127
+ }
128
+ }, {
129
+ body: t.Object({
130
+ packageName: t.String()
131
+ })
132
+ }).post("/reload", async ({ set }) => {
133
+ try {
134
+ await pluginManager.dispatchServerStop();
135
+ await pluginManager.loadAll();
136
+ const plugins = pluginManager.getPluginDetails();
137
+ return {
138
+ success: true,
139
+ plugins,
140
+ message: `Reloaded ${plugins.length} plugin(s)`
141
+ };
142
+ } catch (err) {
143
+ set.status = 500;
144
+ return { error: "Failed to reload plugins", details: String(err) };
145
+ }
146
+ });
147
+ }
148
+
149
+ // src/cli/commands/plugin.cmd.ts
150
+ import { mkdirSync, writeFileSync, existsSync } from "fs";
151
+ import { join, resolve } from "path";
152
+ var DEFAULT_AGENT_URL = "http://localhost:3005";
153
+ function register(program) {
154
+ const cmd = program.command("plugin").description("Manage plugins");
155
+ cmd.command("list").description("List installed plugins").option("--agent-url <url>", "Agent URL", DEFAULT_AGENT_URL).action(async (options) => {
156
+ try {
157
+ const url = getAgentUrl(options);
158
+ let plugins;
159
+ try {
160
+ const data = await apiGet(url, "/api/plugins");
161
+ plugins = data.plugins || [];
162
+ } catch {
163
+ warn("Agent not reachable. Attempting local plugin registry...");
164
+ try {
165
+ const { PluginManager } = await import("./plugin-system-x4tbwzyq.js");
166
+ const pm = new PluginManager;
167
+ plugins = pm.getPluginDetails() || [];
168
+ } catch {
169
+ fail("Could not list plugins from agent or local registry.");
170
+ return;
171
+ }
172
+ }
173
+ if (!plugins || plugins.length === 0) {
174
+ info("No plugins installed.");
175
+ return;
176
+ }
177
+ header("Installed Plugins");
178
+ formatTable(plugins.map((p) => ({
179
+ Name: p.name || p.package || "-",
180
+ Version: p.version || "-",
181
+ Status: p.status || p.enabled ? "enabled" : "disabled",
182
+ Description: p.description || "-"
183
+ })));
184
+ } catch (err) {
185
+ fail(err.message);
186
+ }
187
+ });
188
+ cmd.command("install").description("Install a plugin").argument("<package>", "NPM package name to install").option("--agent-url <url>", "Agent URL", DEFAULT_AGENT_URL).action(async (packageName, options) => {
189
+ try {
190
+ const url = getAgentUrl(options);
191
+ try {
192
+ const result = await apiPost(url, "/api/plugins/install", {
193
+ package: packageName
194
+ });
195
+ success(`Plugin "${packageName}" installed.`);
196
+ if (result?.version)
197
+ kv("Version", result.version);
198
+ } catch {
199
+ warn("Agent not reachable. Attempting local install...");
200
+ try {
201
+ const { PluginManager } = await import("./plugin-system-x4tbwzyq.js");
202
+ const pm = new PluginManager;
203
+ await pm.install(packageName);
204
+ success(`Plugin "${packageName}" installed locally.`);
205
+ } catch (localErr) {
206
+ fail(`Could not install plugin via agent or locally: ${localErr.message}`);
207
+ }
208
+ }
209
+ } catch (err) {
210
+ fail(err.message);
211
+ }
212
+ });
213
+ cmd.command("remove").description("Remove a plugin").argument("<package>", "NPM package name to remove").option("--agent-url <url>", "Agent URL", DEFAULT_AGENT_URL).action(async (packageName, options) => {
214
+ try {
215
+ const url = getAgentUrl(options);
216
+ try {
217
+ await apiPost(url, "/api/plugins/remove", {
218
+ package: packageName
219
+ });
220
+ success(`Plugin "${packageName}" removed.`);
221
+ } catch {
222
+ warn("Agent not reachable. Attempting local removal...");
223
+ try {
224
+ const { PluginManager } = await import("./plugin-system-x4tbwzyq.js");
225
+ const pm = new PluginManager;
226
+ await pm.remove(packageName);
227
+ success(`Plugin "${packageName}" removed locally.`);
228
+ } catch (localErr) {
229
+ fail(`Could not remove plugin via agent or locally: ${localErr.message}`);
230
+ }
231
+ }
232
+ } catch (err) {
233
+ fail(err.message);
234
+ }
235
+ });
236
+ cmd.command("reload").description("Reload all plugins").option("--agent-url <url>", "Agent URL", DEFAULT_AGENT_URL).action(async (options) => {
237
+ try {
238
+ const url = getAgentUrl(options);
239
+ await apiPost(url, "/api/plugins/reload", {});
240
+ success("Plugins reloaded.");
241
+ } catch (err) {
242
+ fail(err.message);
243
+ }
244
+ });
245
+ cmd.command("available").description("Show available plugins").option("--agent-url <url>", "Agent URL", DEFAULT_AGENT_URL).action(async (options) => {
246
+ try {
247
+ const url = getAgentUrl(options);
248
+ const availData = await apiGet(url, "/api/plugins/available");
249
+ const plugins = availData.plugins || [];
250
+ if (!plugins || plugins.length === 0) {
251
+ info("No available plugins found.");
252
+ return;
253
+ }
254
+ header("Available Plugins");
255
+ formatTable(plugins.map((p) => ({
256
+ Name: p.name || p.package || "-",
257
+ Version: p.version || p.latestVersion || "-",
258
+ Description: p.description || "-"
259
+ })));
260
+ } catch (err) {
261
+ fail(err.message);
262
+ }
263
+ });
264
+ cmd.command("create").description("Scaffold a new plugin project").argument("<name>", 'Plugin name (e.g. "docker" \u2192 vibe-plugin-docker)').option("-d, --dir <directory>", "Parent directory", ".").option("--tag <tag>", "Plugin tag (backend, frontend, cli, provider, adapter, integration)", "backend").option("--with-ui", "Include a companion UI plugin scaffold", false).action(async (name, options) => {
265
+ try {
266
+ const pluginName = name.startsWith("vibe-plugin-") ? name : `vibe-plugin-${name}`;
267
+ const dir = resolve(options.dir, pluginName);
268
+ if (existsSync(dir)) {
269
+ fail(`Directory "${dir}" already exists.`);
270
+ return;
271
+ }
272
+ info(`Scaffolding ${pluginName}...`);
273
+ mkdirSync(join(dir, "src"), { recursive: true });
274
+ mkdirSync(join(dir, "dist"), { recursive: true });
275
+ const tag = options.tag;
276
+ const description = `VibeControls plugin: ${name}`;
277
+ const packageJson = {
278
+ name: `@burdenoff/${pluginName}`,
279
+ version: "0.1.0",
280
+ main: "./dist/index.js",
281
+ type: "module",
282
+ engines: { bun: ">=1.3.0" },
283
+ scripts: {
284
+ build: "bun build ./src/index.ts --outdir ./dist --target bun",
285
+ lint: "eslint ./src",
286
+ format: "bunx prettier . --write",
287
+ "format:check": "bunx prettier . --check",
288
+ "type:check": "tsc --noEmit",
289
+ clean: "rimraf dist",
290
+ prebuild: "bun run clean",
291
+ prepublishOnly: "bun run build",
292
+ sanity: "bun run format:check && bun run lint && bun run type:check && bun run build"
293
+ },
294
+ keywords: ["vibecontrols", "vibe", "vibe-plugin", name, "bun"],
295
+ author: {
296
+ name: "Your Name",
297
+ email: "you@example.com"
298
+ },
299
+ license: "SEE LICENSE IN LICENSE",
300
+ description,
301
+ devDependencies: {
302
+ "@types/bun": "^1.2.16",
303
+ "bun-types": "^1.3.9",
304
+ commander: "^14.0.3",
305
+ eslint: "^9.30.1",
306
+ prettier: "^3.6.2",
307
+ rimraf: "^6.0.1",
308
+ typescript: "^5.8.3",
309
+ "typescript-eslint": "^8.56.0"
310
+ },
311
+ peerDependencies: {
312
+ "@burdenoff/vibe-agent": ">=2.0.0"
313
+ },
314
+ peerDependenciesMeta: {
315
+ "@burdenoff/vibe-agent": { optional: true }
316
+ },
317
+ publishConfig: {
318
+ access: "public",
319
+ registry: "https://registry.npmjs.org/"
320
+ },
321
+ files: ["dist/**/*", "README.md", "LICENSE"]
322
+ };
323
+ writeFileSync(join(dir, "package.json"), JSON.stringify(packageJson, null, 2) + `
324
+ `);
325
+ const tsconfig = {
326
+ compilerOptions: {
327
+ target: "ES2022",
328
+ module: "ES2022",
329
+ moduleResolution: "bundler",
330
+ types: ["bun-types"],
331
+ strict: true,
332
+ esModuleInterop: true,
333
+ skipLibCheck: true,
334
+ outDir: "./dist",
335
+ rootDir: "./src",
336
+ declaration: true,
337
+ sourceMap: true
338
+ },
339
+ include: ["src/**/*.ts"],
340
+ exclude: ["node_modules", "dist"]
341
+ };
342
+ writeFileSync(join(dir, "tsconfig.json"), JSON.stringify(tsconfig, null, 2) + `
343
+ `);
344
+ writeFileSync(join(dir, ".gitignore"), [
345
+ "node_modules/",
346
+ "dist/",
347
+ "bun.lock",
348
+ ".env",
349
+ ".env.local",
350
+ "*.tgz"
351
+ ].join(`
352
+ `) + `
353
+ `);
354
+ const cliCmd = name.replace(/[^a-z0-9-]/gi, "-").toLowerCase();
355
+ const indexTs = `import type { Command } from "commander";
356
+
357
+ /**
358
+ * @burdenoff/${pluginName}
359
+ *
360
+ * ${description}
361
+ */
362
+
363
+ // \u2500\u2500 Plugin Interfaces \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
364
+
365
+ export interface HostServices {
366
+ logger?: {
367
+ info: (msg: string) => void;
368
+ warn: (msg: string) => void;
369
+ error: (msg: string) => void;
370
+ debug: (msg: string) => void;
371
+ };
372
+ config?: Record<string, unknown>;
373
+ }
374
+
375
+ export interface VibePlugin {
376
+ name: string;
377
+ version: string;
378
+ description: string;
379
+ tags?: Array<
380
+ "backend" | "frontend" | "cli" | "provider" | "adapter" | "integration"
381
+ >;
382
+ cliCommand: string;
383
+ onCliSetup: (program: Command, hostServices?: HostServices) => void;
384
+ }
385
+
386
+ // \u2500\u2500 Plugin Implementation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
387
+
388
+ const plugin: VibePlugin = {
389
+ name: "${pluginName}",
390
+ version: "0.1.0",
391
+ description: "${description}",
392
+ tags: ["${tag}"],
393
+ cliCommand: "${cliCmd}",
394
+
395
+ onCliSetup(program: Command, _hostServices?: HostServices): void {
396
+ const cmd = program
397
+ .command("${cliCmd}")
398
+ .description("${description}");
399
+
400
+ cmd
401
+ .command("status")
402
+ .description("Show ${name} status")
403
+ .action(() => {
404
+ console.log("${pluginName} is ready.");
405
+ });
406
+
407
+ cmd
408
+ .command("info")
409
+ .description("Show plugin info")
410
+ .action(() => {
411
+ console.log(JSON.stringify({
412
+ name: plugin.name,
413
+ version: plugin.version,
414
+ description: plugin.description,
415
+ tags: plugin.tags,
416
+ }, null, 2));
417
+ });
418
+ },
419
+ };
420
+
421
+ export default plugin;
422
+ `;
423
+ writeFileSync(join(dir, "src", "index.ts"), indexTs);
424
+ const readme = `# ${pluginName}
425
+
426
+ ${description}
427
+
428
+ ## Installation
429
+
430
+ \`\`\`bash
431
+ vibe plugin install @burdenoff/${pluginName}
432
+ \`\`\`
433
+
434
+ ## Usage
435
+
436
+ \`\`\`bash
437
+ vibe ${cliCmd} status
438
+ vibe ${cliCmd} info
439
+ \`\`\`
440
+
441
+ ## Development
442
+
443
+ \`\`\`bash
444
+ bun install
445
+ bun run build
446
+ bun run sanity
447
+ \`\`\`
448
+
449
+ ## Publishing
450
+
451
+ \`\`\`bash
452
+ bun run build
453
+ npm publish
454
+ \`\`\`
455
+ `;
456
+ writeFileSync(join(dir, "README.md"), readme);
457
+ success(`Created ${pluginName} at ${dir}`);
458
+ kv("Plugin Name", `@burdenoff/${pluginName}`);
459
+ kv("CLI Command", `vibe ${cliCmd}`);
460
+ kv("Tag", tag);
461
+ info(`
462
+ Next steps:`);
463
+ info(` cd ${pluginName}`);
464
+ info(" bun install");
465
+ info(" bun run build");
466
+ info(` vibe plugin install . # install locally from this directory`);
467
+ if (options.withUi) {
468
+ const uiName = `${pluginName.replace("vibe-plugin-", "vibe-plugin-ui-")}`;
469
+ const uiDir = resolve(options.dir, uiName);
470
+ if (existsSync(uiDir)) {
471
+ warn(`UI directory "${uiDir}" already exists, skipping.`);
472
+ } else {
473
+ mkdirSync(join(uiDir, "src"), { recursive: true });
474
+ mkdirSync(join(uiDir, "public"), { recursive: true });
475
+ const uiPackageJson = {
476
+ name: `@burdenoff/${uiName}`,
477
+ version: "0.1.0",
478
+ private: false,
479
+ type: "module",
480
+ scripts: {
481
+ dev: "vite --port 5180",
482
+ build: "tsc && vite build",
483
+ preview: "vite preview",
484
+ lint: "eslint ./src",
485
+ format: "bunx prettier . --write",
486
+ "format:check": "bunx prettier . --check",
487
+ "type:check": "tsc --noEmit",
488
+ sanity: "bun run format:check && bun run lint && bun run type:check && bun run build"
489
+ },
490
+ keywords: ["vibecontrols", "vibe-plugin", "ui", name],
491
+ author: { name: "Your Name", email: "you@example.com" },
492
+ license: "SEE LICENSE IN LICENSE",
493
+ description: `UI for ${pluginName}`,
494
+ dependencies: {
495
+ react: "^19.0.0",
496
+ "react-dom": "^19.0.0"
497
+ },
498
+ devDependencies: {
499
+ "@types/react": "^19.0.0",
500
+ "@types/react-dom": "^19.0.0",
501
+ "@vitejs/plugin-react": "^4.5.2",
502
+ typescript: "^5.8.3",
503
+ vite: "^7.0.0"
504
+ },
505
+ publishConfig: {
506
+ access: "public",
507
+ registry: "https://registry.npmjs.org/"
508
+ },
509
+ files: ["dist/**/*", "README.md"]
510
+ };
511
+ writeFileSync(join(uiDir, "package.json"), JSON.stringify(uiPackageJson, null, 2) + `
512
+ `);
513
+ const uiIndex = `import React from "react";
514
+ import ReactDOM from "react-dom/client";
515
+ import App from "./App";
516
+
517
+ ReactDOM.createRoot(document.getElementById("root")!).render(
518
+ <React.StrictMode>
519
+ <App />
520
+ </React.StrictMode>
521
+ );
522
+ `;
523
+ writeFileSync(join(uiDir, "src", "main.tsx"), uiIndex);
524
+ const uiApp = `import { useState, useEffect } from "react";
525
+
526
+ const DARK_BG = "#0d1117";
527
+ const DARK_TEXT = "#e6edf3";
528
+
529
+ export default function App() {
530
+ const [apiKey, setApiKey] = useState<string | null>(null);
531
+ const [status, setStatus] = useState("loading...");
532
+
533
+ useEffect(() => {
534
+ // Auth via postMessage (iframe) or URL param (new tab)
535
+ const params = new URLSearchParams(window.location.search);
536
+ const key = params.get("apiKey");
537
+ if (key) {
538
+ setApiKey(key);
539
+ setStatus("connected");
540
+ return;
541
+ }
542
+
543
+ const handler = (e: MessageEvent) => {
544
+ if (e.data?.type === "vibe-auth" && e.data.apiKey) {
545
+ setApiKey(e.data.apiKey);
546
+ setStatus("connected");
547
+ }
548
+ };
549
+ window.addEventListener("message", handler);
550
+ return () => window.removeEventListener("message", handler);
551
+ }, []);
552
+
553
+ return (
554
+ <div style={{ background: DARK_BG, color: DARK_TEXT, minHeight: "100vh", padding: 24, fontFamily: "system-ui" }}>
555
+ <h1>${name} UI</h1>
556
+ <p>Status: {status}</p>
557
+ {apiKey && <p style={{ color: "#3fb950" }}>Authenticated</p>}
558
+ </div>
559
+ );
560
+ }
561
+ `;
562
+ writeFileSync(join(uiDir, "src", "App.tsx"), uiApp);
563
+ const indexHtml = `<!DOCTYPE html>
564
+ <html lang="en">
565
+ <head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>${name} UI</title></head>
566
+ <body><div id="root"></div><script type="module" src="/src/main.tsx"></script></body>
567
+ </html>`;
568
+ writeFileSync(join(uiDir, "index.html"), indexHtml);
569
+ success(`Created UI plugin ${uiName} at ${uiDir}`);
570
+ }
571
+ }
572
+ } catch (err) {
573
+ fail(err.message);
574
+ }
575
+ });
576
+ }
577
+
578
+ // src/plugins/plugin-mgr/commands.ts
579
+ function registerCommands(program, _hostServices) {
580
+ register(program);
581
+ }
582
+
583
+ // src/plugins/plugin-mgr/index.ts
584
+ var vibePlugin = {
585
+ name: "plugin-mgr",
586
+ version: "2.2.0",
587
+ description: "Plugin lifecycle management \u2014 install, remove, reload, catalog",
588
+ tags: ["backend", "cli"],
589
+ cliCommand: "plugin",
590
+ apiPrefix: "/api/plugins",
591
+ createRoutes: (deps) => createRoutes(deps),
592
+ onCliSetup: async (program, hostServices) => {
593
+ registerCommands(program, hostServices);
594
+ }
595
+ };
596
+ export {
597
+ vibePlugin
598
+ };
599
+
600
+ //# debugId=77470ADF30C64CDD64756E2164756E21
601
+ //# sourceMappingURL=index-wxxv9rec.js.map