@burdenoff/vibe-agent 2.1.1 → 2.3.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/dist/app-sm6n9xst.js +1165 -0
- package/dist/app-sm6n9xst.js.map +19 -0
- package/dist/cli.js +167 -2050
- package/dist/cli.js.map +8 -30
- package/dist/index-05qfwz8r.js +122 -0
- package/dist/index-05qfwz8r.js.map +10 -0
- package/dist/index-30p492yv.js +294 -0
- package/dist/index-30p492yv.js.map +13 -0
- package/dist/index-3rjnbp97.js +268 -0
- package/dist/index-3rjnbp97.js.map +13 -0
- package/dist/index-6vry08rz.js +285 -0
- package/dist/index-6vry08rz.js.map +13 -0
- package/dist/index-88ym10cs.js +194 -0
- package/dist/index-88ym10cs.js.map +10 -0
- package/dist/index-a9g7hbj9.js +229 -0
- package/dist/index-a9g7hbj9.js.map +13 -0
- package/dist/index-atjhkm74.js +149 -0
- package/dist/index-atjhkm74.js.map +10 -0
- package/dist/index-b1eq3qvs.js +515 -0
- package/dist/index-b1eq3qvs.js.map +19 -0
- package/dist/index-c7zy3n33.js +167 -0
- package/dist/index-c7zy3n33.js.map +13 -0
- package/dist/index-fm6gqenc.js +338 -0
- package/dist/index-fm6gqenc.js.map +13 -0
- package/dist/index-hefqxwht.js +270 -0
- package/dist/index-hefqxwht.js.map +13 -0
- package/dist/index-k9hb0b93.js +280 -0
- package/dist/index-k9hb0b93.js.map +13 -0
- package/dist/index-npmvh1x9.js +385 -0
- package/dist/index-npmvh1x9.js.map +13 -0
- package/dist/index-rdm6e3rr.js +587 -0
- package/dist/index-rdm6e3rr.js.map +13 -0
- package/dist/index-s7ff1fj1.js +415 -0
- package/dist/index-s7ff1fj1.js.map +11 -0
- package/dist/index-t4qgjy5w.js +287 -0
- package/dist/index-t4qgjy5w.js.map +13 -0
- package/dist/index-wmvkjcjj.js +301 -0
- package/dist/index-wmvkjcjj.js.map +13 -0
- package/dist/{app-31chs2a1.js → index-wr0mkm57.js} +8 -3201
- package/dist/{app-31chs2a1.js.map → index-wr0mkm57.js.map} +4 -25
- package/dist/index-xmeskdnb.js +292 -0
- package/dist/index-xmeskdnb.js.map +11 -0
- package/dist/index.js +9 -6
- package/dist/index.js.map +2 -2
- package/dist/{package-hb6db316.js → package-04nkt49b.js} +5 -3
- package/dist/{package-hb6db316.js.map → package-04nkt49b.js.map} +1 -1
- package/dist/plugin-system-7r9mb1tb.js +479 -0
- package/dist/plugin-system-7r9mb1tb.js.map +10 -0
- package/package.json +3 -1
- package/dist/index-t06ktmx9.js +0 -216
- package/dist/index-t06ktmx9.js.map +0 -11
- package/dist/plugin-system-bg1pzjj9.js +0 -450
- package/dist/plugin-system-bg1pzjj9.js.map +0 -11
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/core/service-registry.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"/**\n * Service Registry\n *\n * Central registry for providers and named services.\n * Supports multiple providers per type with configurable defaults.\n *\n * Default provider resolution order:\n * 1. DB config key `provider:default:<type>` (if db is available)\n * 2. Last registered provider (fallback)\n */\n\nimport { logger } from \"../services/logger.js\";\nimport type { AgentDatabase } from \"../db/database.js\";\n\n// Provider types\nexport type ProviderType = \"tunnel\" | \"session\";\n\ninterface RegisteredProvider {\n provider: unknown;\n pluginName: string;\n registeredAt: Date;\n}\n\ninterface RegisteredService {\n service: unknown;\n pluginName: string;\n}\n\nexport class ServiceRegistry {\n // Multi-provider: type → Map<pluginName, RegisteredProvider>\n private providers = new Map<ProviderType, Map<string, RegisteredProvider>>();\n // Track registration order for fallback default\n private providerOrder = new Map<ProviderType, string[]>();\n // Named services\n private services = new Map<string, RegisteredService>();\n // Optional DB for default provider lookups\n private db?: AgentDatabase;\n\n constructor(db?: AgentDatabase) {\n this.db = db;\n }\n\n // ── Provider Registration ──────────────────────────────────────────\n\n /**\n * Register a provider for a given type.\n * Multiple providers per type are supported, keyed by plugin name.\n */\n registerProvider(\n type: ProviderType,\n provider: unknown,\n pluginName: string,\n ): void {\n if (!this.providers.has(type)) {\n this.providers.set(type, new Map());\n this.providerOrder.set(type, []);\n }\n\n const typeMap = this.providers.get(type)!;\n const order = this.providerOrder.get(type)!;\n\n if (typeMap.has(pluginName)) {\n logger.info(\n \"service-registry\",\n `Updating ${type} provider from plugin '${pluginName}'`,\n );\n } else {\n logger.info(\n \"service-registry\",\n `Registered ${type} provider from plugin '${pluginName}'`,\n );\n }\n\n typeMap.set(pluginName, {\n provider,\n pluginName,\n registeredAt: new Date(),\n });\n\n // Update order: move to end if already present, otherwise append\n const idx = order.indexOf(pluginName);\n if (idx !== -1) order.splice(idx, 1);\n order.push(pluginName);\n }\n\n /**\n * Get the default provider for a type.\n *\n * Resolution:\n * 1. DB config `provider:default:<type>` → matching provider\n * 2. Last registered provider (fallback)\n * 3. undefined\n */\n getProvider<T>(type: ProviderType): T | undefined {\n const typeMap = this.providers.get(type);\n if (!typeMap || typeMap.size === 0) return undefined;\n\n // Check DB for configured default\n if (this.db) {\n try {\n const defaultName = this.db.getConfig(`provider:default:${type}`);\n if (defaultName && typeMap.has(defaultName)) {\n return typeMap.get(defaultName)!.provider as T;\n }\n } catch {\n // DB not available or errored, fall through to fallback\n }\n }\n\n // Fallback: last registered\n const order = this.providerOrder.get(type);\n if (order && order.length > 0) {\n const lastPluginName = order[order.length - 1];\n return typeMap.get(lastPluginName)?.provider as T;\n }\n\n return undefined;\n }\n\n /**\n * Get a specific provider by plugin name.\n */\n getProviderByName<T>(type: ProviderType, pluginName: string): T | undefined {\n const typeMap = this.providers.get(type);\n if (!typeMap) return undefined;\n return typeMap.get(pluginName)?.provider as T;\n }\n\n /**\n * Check if at least one provider is registered for a type.\n */\n hasProvider(type: ProviderType): boolean {\n const typeMap = this.providers.get(type);\n return !!typeMap && typeMap.size > 0;\n }\n\n /**\n * Unregister a specific provider by type and plugin name.\n */\n unregisterProvider(type: ProviderType, pluginName: string): boolean {\n const typeMap = this.providers.get(type);\n if (!typeMap) return false;\n\n const removed = typeMap.delete(pluginName);\n\n if (removed) {\n const order = this.providerOrder.get(type);\n if (order) {\n const idx = order.indexOf(pluginName);\n if (idx !== -1) order.splice(idx, 1);\n }\n logger.info(\n \"service-registry\",\n `Unregistered ${type} provider from plugin '${pluginName}'`,\n );\n }\n\n return removed;\n }\n\n /**\n * List all registered providers for a given type, with default indicator.\n */\n listProvidersForType(\n type: ProviderType,\n ): Array<{ pluginName: string; isDefault: boolean }> {\n const typeMap = this.providers.get(type);\n if (!typeMap) return [];\n\n let defaultName: string | undefined;\n\n // Resolve default from DB config\n if (this.db) {\n try {\n defaultName =\n this.db.getConfig(`provider:default:${type}`) ?? undefined;\n } catch {\n // Ignore DB errors\n }\n }\n\n // If no DB default (or DB default doesn't match any registered provider),\n // fall back to last registered\n if (!defaultName || !typeMap.has(defaultName)) {\n const order = this.providerOrder.get(type);\n defaultName =\n order && order.length > 0 ? order[order.length - 1] : undefined;\n }\n\n return Array.from(typeMap.keys()).map((name) => ({\n pluginName: name,\n isDefault: name === defaultName,\n }));\n }\n\n // ── Named Service Registration ──────────────────────────────────────\n\n /**\n * Register a named service from a plugin.\n */\n registerService(\n pluginName: string,\n serviceName: string,\n service: unknown,\n ): void {\n const key = `${pluginName}:${serviceName}`;\n this.services.set(key, { service, pluginName });\n logger.debug(\"service-registry\", `Registered service '${key}'`);\n }\n\n /**\n * Get a named service.\n */\n getService<T>(pluginName: string, serviceName: string): T | undefined {\n const key = `${pluginName}:${serviceName}`;\n return this.services.get(key)?.service as T;\n }\n\n /**\n * Unregister all services from a plugin.\n */\n unregisterServices(pluginName: string): void {\n const keysToDelete: string[] = [];\n for (const [key, entry] of this.services) {\n if (entry.pluginName === pluginName) {\n keysToDelete.push(key);\n }\n }\n for (const key of keysToDelete) {\n this.services.delete(key);\n }\n if (keysToDelete.length > 0) {\n logger.info(\n \"service-registry\",\n `Unregistered ${keysToDelete.length} services from ${pluginName}`,\n );\n }\n }\n\n // ── Introspection ──────────────────────────────────────────────────\n\n /**\n * List all registered providers across all types.\n */\n listProviders(): Array<{\n type: ProviderType;\n name: string;\n pluginName: string;\n }> {\n const result: Array<{\n type: ProviderType;\n name: string;\n pluginName: string;\n }> = [];\n for (const [type, typeMap] of this.providers) {\n for (const [pluginName, entry] of typeMap) {\n result.push({ type, name: pluginName, pluginName: entry.pluginName });\n }\n }\n return result;\n }\n\n /**\n * List all registered services.\n */\n listServices(): Array<{\n pluginName: string;\n serviceName: string;\n }> {\n return Array.from(this.services.entries()).map(([key, entry]) => ({\n pluginName: entry.pluginName,\n serviceName: key.split(\":\").slice(1).join(\":\"),\n }));\n }\n\n /**\n * Clear all registrations (providers, order tracking, and services).\n */\n clear(): void {\n this.providers.clear();\n this.providerOrder.clear();\n this.services.clear();\n }\n}\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "7B6692C0FD509A9264756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -0,0 +1,515 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import {
|
|
3
|
+
gatewayClient
|
|
4
|
+
} from "./index-05qfwz8r.js";
|
|
5
|
+
import {
|
|
6
|
+
ServiceManager,
|
|
7
|
+
checkDependencies,
|
|
8
|
+
installDependencies
|
|
9
|
+
} from "./index-s7ff1fj1.js";
|
|
10
|
+
import"./index-88ym10cs.js";
|
|
11
|
+
import {
|
|
12
|
+
Elysia,
|
|
13
|
+
t
|
|
14
|
+
} from "./index-wr0mkm57.js";
|
|
15
|
+
import {
|
|
16
|
+
apiGet,
|
|
17
|
+
apiPost,
|
|
18
|
+
blank,
|
|
19
|
+
colors,
|
|
20
|
+
fail,
|
|
21
|
+
formatBytes,
|
|
22
|
+
formatDuration,
|
|
23
|
+
formatStatus,
|
|
24
|
+
formatTable,
|
|
25
|
+
getAgentUrl,
|
|
26
|
+
header,
|
|
27
|
+
icons,
|
|
28
|
+
info,
|
|
29
|
+
kv,
|
|
30
|
+
success,
|
|
31
|
+
timeAgo
|
|
32
|
+
} from "./index-xmeskdnb.js";
|
|
33
|
+
import"./index-g8dczzvv.js";
|
|
34
|
+
|
|
35
|
+
// src/plugins/agent/routes.ts
|
|
36
|
+
import os from "os";
|
|
37
|
+
import { join } from "path";
|
|
38
|
+
import { readFileSync } from "fs";
|
|
39
|
+
function getMachineId() {
|
|
40
|
+
const hostname = os.hostname();
|
|
41
|
+
const platform = os.platform();
|
|
42
|
+
const arch = os.arch();
|
|
43
|
+
const hasher = new Bun.CryptoHasher("sha256");
|
|
44
|
+
hasher.update(`${hostname}-${platform}-${arch}`);
|
|
45
|
+
return hasher.digest("hex").substring(0, 16);
|
|
46
|
+
}
|
|
47
|
+
function getAgentVersion() {
|
|
48
|
+
try {
|
|
49
|
+
const pkgPath = join(import.meta.dir, "..", "..", "package.json");
|
|
50
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
|
|
51
|
+
return pkg.version || "0.0.0";
|
|
52
|
+
} catch {
|
|
53
|
+
return "0.0.0";
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function createRoutes(deps) {
|
|
57
|
+
const { db, serviceRegistry } = deps;
|
|
58
|
+
const version = getAgentVersion();
|
|
59
|
+
const machineId = getMachineId();
|
|
60
|
+
return new Elysia().get("/identity", () => ({
|
|
61
|
+
machineId,
|
|
62
|
+
hostname: os.hostname(),
|
|
63
|
+
platform: os.platform(),
|
|
64
|
+
arch: os.arch(),
|
|
65
|
+
version,
|
|
66
|
+
uptime: process.uptime()
|
|
67
|
+
})).get("/api-key", ({ store }) => ({
|
|
68
|
+
apiKey: store.apiKey ?? "unknown"
|
|
69
|
+
})).get("/tunnel", async () => {
|
|
70
|
+
const tunnelProvider = serviceRegistry.getProvider("tunnel");
|
|
71
|
+
if (!tunnelProvider) {
|
|
72
|
+
return { tunnelUrl: null, message: "No tunnel provider registered" };
|
|
73
|
+
}
|
|
74
|
+
const url = await tunnelProvider.getActiveTunnelUrl();
|
|
75
|
+
return { tunnelUrl: url };
|
|
76
|
+
}).post("/tunnel/start", async ({ set }) => {
|
|
77
|
+
const tunnelProvider = serviceRegistry.getProvider("tunnel");
|
|
78
|
+
if (!tunnelProvider) {
|
|
79
|
+
set.status = 400;
|
|
80
|
+
return { error: "No tunnel provider registered" };
|
|
81
|
+
}
|
|
82
|
+
const port = parseInt(process.env.PORT || "3005");
|
|
83
|
+
const tunnel = await tunnelProvider.start({ port, name: "agent" });
|
|
84
|
+
return { success: true, tunnel };
|
|
85
|
+
}).post("/tunnel/stop", async ({ set }) => {
|
|
86
|
+
const tunnelProvider = serviceRegistry.getProvider("tunnel");
|
|
87
|
+
if (!tunnelProvider) {
|
|
88
|
+
set.status = 400;
|
|
89
|
+
return { error: "No tunnel provider registered" };
|
|
90
|
+
}
|
|
91
|
+
const tunnels = await tunnelProvider.list();
|
|
92
|
+
const agentTunnel = tunnels.find((t2) => t2.metadata?.isAgent);
|
|
93
|
+
if (agentTunnel) {
|
|
94
|
+
await tunnelProvider.stop(agentTunnel.id);
|
|
95
|
+
}
|
|
96
|
+
return { success: true };
|
|
97
|
+
}).get("/setup/check", () => {
|
|
98
|
+
const deps2 = checkDependencies();
|
|
99
|
+
const allInstalled = deps2.filter((d) => d.required).every((d) => d.installed);
|
|
100
|
+
return { ready: allInstalled, dependencies: deps2 };
|
|
101
|
+
}).post("/setup/install", async () => {
|
|
102
|
+
const result = await installDependencies();
|
|
103
|
+
return result;
|
|
104
|
+
}).post("/gateway-auth", async ({ body, set }) => {
|
|
105
|
+
try {
|
|
106
|
+
gatewayClient.configure({
|
|
107
|
+
globalGatewayUrl: body.tenantApiUrl || body.globalGatewayUrl || "",
|
|
108
|
+
workspaceGatewayUrl: body.workspacesApiUrl || body.workspaceGatewayUrl,
|
|
109
|
+
clientId: body.appClientId || body.clientId || "",
|
|
110
|
+
clientSecret: body.appClientSecret || body.clientSecret || ""
|
|
111
|
+
});
|
|
112
|
+
if (body.appClientId || body.clientId) {
|
|
113
|
+
db.setConfig("gateway-auth:clientId", body.appClientId || body.clientId || "");
|
|
114
|
+
}
|
|
115
|
+
if (body.appClientSecret || body.clientSecret) {
|
|
116
|
+
db.setConfig("gateway-auth:clientSecret", body.appClientSecret || body.clientSecret || "");
|
|
117
|
+
}
|
|
118
|
+
if (body.workspacesApiUrl || body.workspaceGatewayUrl) {
|
|
119
|
+
db.setConfig("gateway-auth:workspaceGatewayUrl", body.workspacesApiUrl || body.workspaceGatewayUrl || "");
|
|
120
|
+
}
|
|
121
|
+
if (body.tenantApiUrl || body.globalGatewayUrl) {
|
|
122
|
+
db.setConfig("gateway-auth:globalGatewayUrl", body.tenantApiUrl || body.globalGatewayUrl || "");
|
|
123
|
+
}
|
|
124
|
+
if (body.workspaceId) {
|
|
125
|
+
db.setConfig("gateway-auth:workspaceId", body.workspaceId);
|
|
126
|
+
}
|
|
127
|
+
return { success: true, message: "Gateway auth configured" };
|
|
128
|
+
} catch (err) {
|
|
129
|
+
set.status = 500;
|
|
130
|
+
return {
|
|
131
|
+
error: "Failed to configure gateway auth",
|
|
132
|
+
message: String(err)
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
}, {
|
|
136
|
+
body: t.Object({
|
|
137
|
+
workspacesApiUrl: t.Optional(t.String()),
|
|
138
|
+
tenantApiUrl: t.Optional(t.String()),
|
|
139
|
+
globalGatewayUrl: t.Optional(t.String()),
|
|
140
|
+
workspaceGatewayUrl: t.Optional(t.String()),
|
|
141
|
+
appClientId: t.Optional(t.String()),
|
|
142
|
+
appClientSecret: t.Optional(t.String()),
|
|
143
|
+
clientId: t.Optional(t.String()),
|
|
144
|
+
clientSecret: t.Optional(t.String()),
|
|
145
|
+
workspaceId: t.Optional(t.String())
|
|
146
|
+
})
|
|
147
|
+
}).get("/gateway-auth", () => {
|
|
148
|
+
const config = gatewayClient.getConfig();
|
|
149
|
+
return { configured: gatewayClient.isConfigured(), config };
|
|
150
|
+
}).get("/version", () => ({
|
|
151
|
+
version,
|
|
152
|
+
runtime: "bun",
|
|
153
|
+
runtimeVersion: typeof Bun !== "undefined" ? Bun.version : "unknown"
|
|
154
|
+
})).get("/system", () => ({
|
|
155
|
+
hostname: os.hostname(),
|
|
156
|
+
platform: os.platform(),
|
|
157
|
+
arch: os.arch(),
|
|
158
|
+
release: os.release(),
|
|
159
|
+
cpus: os.cpus().length,
|
|
160
|
+
totalMemory: os.totalmem(),
|
|
161
|
+
freeMemory: os.freemem(),
|
|
162
|
+
uptime: os.uptime(),
|
|
163
|
+
bunVersion: typeof Bun !== "undefined" ? Bun.version : undefined,
|
|
164
|
+
agentVersion: version,
|
|
165
|
+
homeDir: os.homedir(),
|
|
166
|
+
cwd: process.cwd(),
|
|
167
|
+
environment: "development"
|
|
168
|
+
})).get("/status", () => ({
|
|
169
|
+
status: "online",
|
|
170
|
+
timestamp: new Date().toISOString(),
|
|
171
|
+
version,
|
|
172
|
+
providers: serviceRegistry.listProviders(),
|
|
173
|
+
services: serviceRegistry.listServices()
|
|
174
|
+
})).get("/url", async () => {
|
|
175
|
+
const tunnelProvider = serviceRegistry.getProvider("tunnel");
|
|
176
|
+
const tunnelUrl = tunnelProvider ? await tunnelProvider.getActiveTunnelUrl() : null;
|
|
177
|
+
const localUrl = `http://localhost:${process.env.PORT || 3005}`;
|
|
178
|
+
return {
|
|
179
|
+
url: tunnelUrl || localUrl,
|
|
180
|
+
tunnelUrl,
|
|
181
|
+
localUrl,
|
|
182
|
+
source: tunnelUrl ? "tunnel" : "local"
|
|
183
|
+
};
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// src/cli/commands/health.cmd.ts
|
|
188
|
+
var DEFAULT_AGENT_URL = "http://localhost:3005";
|
|
189
|
+
function register(program) {
|
|
190
|
+
program.command("health").description("Check health of a VibeControls agent instance").option("-n, --name <name>", "Agent instance name").option("--agent-url <url>", "Agent URL", DEFAULT_AGENT_URL).action(async (opts) => {
|
|
191
|
+
try {
|
|
192
|
+
header("Agent Health");
|
|
193
|
+
blank();
|
|
194
|
+
if (opts.name) {
|
|
195
|
+
const serviceManager = new ServiceManager;
|
|
196
|
+
const result = await serviceManager.checkHealth(opts.name);
|
|
197
|
+
if (!result.healthy) {
|
|
198
|
+
fail(`Agent instance ${colors.bold(opts.name)} is not responding.`);
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
const details = result.details;
|
|
202
|
+
kv("Instance", opts.name);
|
|
203
|
+
kv("Status", formatStatus(details.status ?? "healthy"));
|
|
204
|
+
if (details.uptime !== undefined) {
|
|
205
|
+
kv("Uptime", `${details.uptime}s`);
|
|
206
|
+
}
|
|
207
|
+
if (details.checks) {
|
|
208
|
+
blank();
|
|
209
|
+
info(`${icons.info} Health checks:`);
|
|
210
|
+
for (const [name, check] of Object.entries(details.checks)) {
|
|
211
|
+
const icon = check.status === "healthy" ? icons.success : icons.error;
|
|
212
|
+
kv(` ${icon} ${name}`, formatStatus(check.status));
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
} else {
|
|
216
|
+
const agentUrl = getAgentUrl(opts);
|
|
217
|
+
const data = await apiGet(agentUrl, "/health");
|
|
218
|
+
kv("URL", agentUrl);
|
|
219
|
+
kv("Status", formatStatus(data.status));
|
|
220
|
+
if (data.version) {
|
|
221
|
+
kv("Version", data.version);
|
|
222
|
+
}
|
|
223
|
+
if (data.uptime !== undefined) {
|
|
224
|
+
kv("Uptime", `${data.uptime}s`);
|
|
225
|
+
}
|
|
226
|
+
if (data.timestamp) {
|
|
227
|
+
kv("Timestamp", data.timestamp);
|
|
228
|
+
}
|
|
229
|
+
if (data.checks) {
|
|
230
|
+
blank();
|
|
231
|
+
info(`${icons.info} Health checks:`);
|
|
232
|
+
for (const [name, check] of Object.entries(data.checks)) {
|
|
233
|
+
const icon = check.status === "healthy" ? icons.success : icons.error;
|
|
234
|
+
kv(` ${icon} ${name}`, formatStatus(check.status));
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
blank();
|
|
239
|
+
success(`${icons.success} Agent is healthy.`);
|
|
240
|
+
} catch (err) {
|
|
241
|
+
fail(`Health check failed: ${err.message}`);
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// src/cli/commands/info.cmd.ts
|
|
247
|
+
import { platform, arch, release, hostname, cpus, totalmem } from "os";
|
|
248
|
+
var DEFAULT_AGENT_URL2 = "http://localhost:3005";
|
|
249
|
+
function register2(program) {
|
|
250
|
+
program.command("info").description("Show local and remote VibeControls agent information").option("--agent-url <url>", "Agent URL", DEFAULT_AGENT_URL2).action(async (opts) => {
|
|
251
|
+
try {
|
|
252
|
+
header("VibeControls Agent Info");
|
|
253
|
+
blank();
|
|
254
|
+
info(`${icons.info} Local System`);
|
|
255
|
+
kv("Hostname", hostname());
|
|
256
|
+
kv("Platform", `${platform()} ${arch()}`);
|
|
257
|
+
kv("OS Release", release());
|
|
258
|
+
kv("CPUs", String(cpus().length));
|
|
259
|
+
kv("Total Memory", formatBytes(totalmem()));
|
|
260
|
+
kv("Node.js", process.version);
|
|
261
|
+
if (process.versions.bun) {
|
|
262
|
+
kv("Bun", process.versions.bun);
|
|
263
|
+
}
|
|
264
|
+
blank();
|
|
265
|
+
const agentUrl = getAgentUrl(opts);
|
|
266
|
+
try {
|
|
267
|
+
const data = await apiGet(agentUrl, "/api/agent/version");
|
|
268
|
+
info(`${icons.info} Remote Agent`);
|
|
269
|
+
kv("URL", agentUrl);
|
|
270
|
+
kv("Version", colors.bold(data.version));
|
|
271
|
+
if (data.build) {
|
|
272
|
+
kv("Build", data.build);
|
|
273
|
+
}
|
|
274
|
+
if (data.commit) {
|
|
275
|
+
kv("Commit", data.commit);
|
|
276
|
+
}
|
|
277
|
+
if (data.nodeVersion) {
|
|
278
|
+
kv("Node.js (remote)", data.nodeVersion);
|
|
279
|
+
}
|
|
280
|
+
if (data.bunVersion) {
|
|
281
|
+
kv("Bun (remote)", data.bunVersion);
|
|
282
|
+
}
|
|
283
|
+
} catch {
|
|
284
|
+
info(`${icons.info} Remote Agent`);
|
|
285
|
+
kv("URL", agentUrl);
|
|
286
|
+
kv("Status", colors.dim("not reachable"));
|
|
287
|
+
}
|
|
288
|
+
blank();
|
|
289
|
+
} catch (err) {
|
|
290
|
+
fail(`Failed to get info: ${err.message}`);
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// src/cli/commands/key.cmd.ts
|
|
296
|
+
var DEFAULT_AGENT_URL3 = "http://localhost:3005";
|
|
297
|
+
function register3(program) {
|
|
298
|
+
program.command("key").description("Get the API key for a running VibeControls agent").option("--agent-url <url>", "Agent URL", DEFAULT_AGENT_URL3).action(async (opts) => {
|
|
299
|
+
try {
|
|
300
|
+
const agentUrl = getAgentUrl(opts);
|
|
301
|
+
header("Agent API Key");
|
|
302
|
+
blank();
|
|
303
|
+
const data = await apiGet(agentUrl, "/api/agent/api-key");
|
|
304
|
+
kv("API Key", colors.bold(data.apiKey));
|
|
305
|
+
blank();
|
|
306
|
+
} catch (err) {
|
|
307
|
+
fail(`Failed to get API key: ${err.message}`);
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// src/cli/commands/system.cmd.ts
|
|
313
|
+
var DEFAULT_AGENT_URL4 = "http://localhost:3005";
|
|
314
|
+
function register4(program) {
|
|
315
|
+
program.command("system").description("Show system information for a running VibeControls agent").option("--agent-url <url>", "Agent URL", DEFAULT_AGENT_URL4).action(async (opts) => {
|
|
316
|
+
try {
|
|
317
|
+
const agentUrl = getAgentUrl(opts);
|
|
318
|
+
header("Agent System Information");
|
|
319
|
+
blank();
|
|
320
|
+
const data = await apiGet(agentUrl, "/api/agent/system");
|
|
321
|
+
info(`${icons.info} Agent`);
|
|
322
|
+
kv("Version", colors.bold(data.agentVersion));
|
|
323
|
+
if (data.bunVersion) {
|
|
324
|
+
kv("Bun", data.bunVersion);
|
|
325
|
+
}
|
|
326
|
+
if (data.environment) {
|
|
327
|
+
kv("Environment", data.environment);
|
|
328
|
+
}
|
|
329
|
+
kv("Uptime", formatDuration(data.uptime));
|
|
330
|
+
blank();
|
|
331
|
+
info(`${icons.info} Platform`);
|
|
332
|
+
kv("OS", `${data.platform} ${data.arch}`);
|
|
333
|
+
kv("Hostname", data.hostname);
|
|
334
|
+
kv("Release", data.release);
|
|
335
|
+
blank();
|
|
336
|
+
info(`${icons.info} Resources`);
|
|
337
|
+
kv("CPUs", String(data.cpus));
|
|
338
|
+
kv("Total Memory", formatBytes(data.totalMemory));
|
|
339
|
+
kv("Free Memory", formatBytes(data.freeMemory));
|
|
340
|
+
blank();
|
|
341
|
+
info(`${icons.info} Paths`);
|
|
342
|
+
kv("Home", data.homeDir);
|
|
343
|
+
kv("CWD", data.cwd);
|
|
344
|
+
blank();
|
|
345
|
+
} catch (err) {
|
|
346
|
+
fail(`Failed to get system info: ${err.message}`);
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// src/cli/commands/status.cmd.ts
|
|
352
|
+
function register5(program) {
|
|
353
|
+
program.command("status").description("Show status of VibeControls agent instance(s)").option("-n, --name <name>", "Agent instance name").action(async (opts) => {
|
|
354
|
+
try {
|
|
355
|
+
const serviceManager = new ServiceManager;
|
|
356
|
+
if (opts.name) {
|
|
357
|
+
header(`Agent Status: ${opts.name}`);
|
|
358
|
+
blank();
|
|
359
|
+
const instance = await serviceManager.getStatus(opts.name);
|
|
360
|
+
if (!instance) {
|
|
361
|
+
fail(`Agent instance ${colors.bold(opts.name)} not found.`);
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
kv("Name", instance.name);
|
|
365
|
+
kv("Status", formatStatus(instance.status));
|
|
366
|
+
kv("PID", instance.pid ? String(instance.pid) : colors.dim("n/a"));
|
|
367
|
+
kv("Port", instance.port ? String(instance.port) : colors.dim("n/a"));
|
|
368
|
+
kv("Host", instance.config?.host || colors.dim("n/a"));
|
|
369
|
+
kv("Database", instance.config?.dbPath || colors.dim("n/a"));
|
|
370
|
+
if (instance.startTime) {
|
|
371
|
+
kv("Started", timeAgo(instance.startTime));
|
|
372
|
+
}
|
|
373
|
+
blank();
|
|
374
|
+
} else {
|
|
375
|
+
header("Agent Instances");
|
|
376
|
+
blank();
|
|
377
|
+
const instances = await serviceManager.listInstances();
|
|
378
|
+
if (!instances || instances.length === 0) {
|
|
379
|
+
info(`${icons.info} No agent instances found.`);
|
|
380
|
+
info(`Run ${colors.bold("vibe start")} to start an agent.`);
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
const rows = instances.map((inst) => ({
|
|
384
|
+
Name: inst.name,
|
|
385
|
+
Status: formatStatus(inst.status),
|
|
386
|
+
PID: inst.pid || "-",
|
|
387
|
+
Port: inst.port || "-",
|
|
388
|
+
Started: inst.startTime ? timeAgo(inst.startTime) : "-"
|
|
389
|
+
}));
|
|
390
|
+
formatTable(rows);
|
|
391
|
+
blank();
|
|
392
|
+
info(`${icons.info} ${instances.length} instance(s) found. Use ${colors.bold("vibe status -n <name>")} for details.`);
|
|
393
|
+
}
|
|
394
|
+
} catch (err) {
|
|
395
|
+
fail(`Failed to get status: ${err.message}`);
|
|
396
|
+
}
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// src/cli/commands/url.cmd.ts
|
|
401
|
+
var DEFAULT_AGENT_URL5 = "http://localhost:3005";
|
|
402
|
+
function register6(program) {
|
|
403
|
+
program.command("url").description("Show the active URL for a running VibeControls agent").option("--agent-url <url>", "Agent URL", DEFAULT_AGENT_URL5).action(async (opts) => {
|
|
404
|
+
try {
|
|
405
|
+
const agentUrl = getAgentUrl(opts);
|
|
406
|
+
header("Agent URL");
|
|
407
|
+
blank();
|
|
408
|
+
const data = await apiGet(agentUrl, "/api/agent/url");
|
|
409
|
+
kv("Active URL", colors.bold(data.url));
|
|
410
|
+
kv("Local URL", data.localUrl);
|
|
411
|
+
if (data.tunnelUrl) {
|
|
412
|
+
kv("Tunnel URL", colors.cyan(data.tunnelUrl));
|
|
413
|
+
}
|
|
414
|
+
kv("Mode", data.isTunnel ? `${icons.running} Tunnel active` : `${icons.info} Local only`);
|
|
415
|
+
blank();
|
|
416
|
+
if (data.isTunnel) {
|
|
417
|
+
info(`${icons.info} Agent is accessible via tunnel at ${colors.bold(data.tunnelUrl)}`);
|
|
418
|
+
} else {
|
|
419
|
+
info(`${icons.info} Agent is running locally at ${colors.bold(data.localUrl)}`);
|
|
420
|
+
}
|
|
421
|
+
} catch (err) {
|
|
422
|
+
fail(`Failed to get URL: ${err.message}`);
|
|
423
|
+
}
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// src/cli/commands/gateway-auth.cmd.ts
|
|
428
|
+
var DEFAULT_AGENT_URL6 = "http://localhost:3005";
|
|
429
|
+
function register7(program) {
|
|
430
|
+
const gatewayAuth = program.command("gateway-auth").description("Manage gateway authentication for VibeControls agent");
|
|
431
|
+
gatewayAuth.command("configure").description("Configure gateway authentication credentials").requiredOption("--global-url <url>", "Global gateway URL").requiredOption("--workspace-url <url>", "Workspace gateway URL").requiredOption("--client-id <id>", "OAuth client ID").requiredOption("--client-secret <secret>", "OAuth client secret").option("--agent-url <url>", "Agent URL", DEFAULT_AGENT_URL6).action(async (opts) => {
|
|
432
|
+
try {
|
|
433
|
+
const agentUrl = getAgentUrl(opts);
|
|
434
|
+
header("Configure Gateway Authentication");
|
|
435
|
+
blank();
|
|
436
|
+
info(`${icons.info} Sending gateway auth configuration...`);
|
|
437
|
+
blank();
|
|
438
|
+
const data = await apiPost(agentUrl, "/api/agent/gateway-auth", {
|
|
439
|
+
globalUrl: opts.globalUrl,
|
|
440
|
+
workspaceUrl: opts.workspaceUrl,
|
|
441
|
+
clientId: opts.clientId,
|
|
442
|
+
clientSecret: opts.clientSecret
|
|
443
|
+
});
|
|
444
|
+
if (data.success) {
|
|
445
|
+
success(`${icons.success} Gateway authentication configured successfully.`);
|
|
446
|
+
blank();
|
|
447
|
+
kv("Global URL", opts.globalUrl);
|
|
448
|
+
kv("Workspace URL", opts.workspaceUrl);
|
|
449
|
+
kv("Client ID", opts.clientId);
|
|
450
|
+
kv("Client Secret", colors.dim("********"));
|
|
451
|
+
} else {
|
|
452
|
+
fail(data.message || "Failed to configure gateway authentication.");
|
|
453
|
+
}
|
|
454
|
+
blank();
|
|
455
|
+
} catch (err) {
|
|
456
|
+
fail(`Failed to configure gateway auth: ${err.message}`);
|
|
457
|
+
}
|
|
458
|
+
});
|
|
459
|
+
gatewayAuth.command("status").description("Check gateway authentication status").option("--agent-url <url>", "Agent URL", DEFAULT_AGENT_URL6).action(async (opts) => {
|
|
460
|
+
try {
|
|
461
|
+
const agentUrl = getAgentUrl(opts);
|
|
462
|
+
header("Gateway Authentication Status");
|
|
463
|
+
blank();
|
|
464
|
+
const data = await apiGet(agentUrl, "/api/agent/gateway-auth");
|
|
465
|
+
kv("Configured", data.configured ? `${icons.success} ${colors.green("Yes")}` : `${icons.error} ${colors.red("No")}`);
|
|
466
|
+
if (data.configured) {
|
|
467
|
+
kv("Global URL", data.globalUrl || colors.dim("n/a"));
|
|
468
|
+
kv("Workspace URL", data.workspaceUrl || colors.dim("n/a"));
|
|
469
|
+
kv("Client ID", data.clientId || colors.dim("n/a"));
|
|
470
|
+
kv("Authenticated", data.authenticated ? `${icons.success} ${colors.green("Yes")}` : `${icons.error} ${colors.red("No")}`);
|
|
471
|
+
if (data.tokenExpiresAt) {
|
|
472
|
+
kv("Token Expires", data.tokenExpiresAt);
|
|
473
|
+
}
|
|
474
|
+
} else {
|
|
475
|
+
blank();
|
|
476
|
+
info(`${icons.info} Run ${colors.bold("vibe gateway-auth configure")} to set up gateway authentication.`);
|
|
477
|
+
}
|
|
478
|
+
blank();
|
|
479
|
+
} catch (err) {
|
|
480
|
+
fail(`Failed to get gateway auth status: ${err.message}`);
|
|
481
|
+
}
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
// src/plugins/agent/commands.ts
|
|
486
|
+
function registerCommands(program, _hostServices) {
|
|
487
|
+
register(program);
|
|
488
|
+
register2(program);
|
|
489
|
+
register3(program);
|
|
490
|
+
register4(program);
|
|
491
|
+
register5(program);
|
|
492
|
+
register6(program);
|
|
493
|
+
register7(program);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// src/plugins/agent/index.ts
|
|
497
|
+
var vibePlugin = {
|
|
498
|
+
name: "agent",
|
|
499
|
+
version: "2.2.0",
|
|
500
|
+
description: "Core agent identity, system info, setup, and gateway authentication",
|
|
501
|
+
tags: ["backend", "cli"],
|
|
502
|
+
cliCommand: "agent",
|
|
503
|
+
apiPrefix: "/api/agent",
|
|
504
|
+
publicPaths: ["/identity", "/api-key", "/tunnel"],
|
|
505
|
+
createRoutes: (deps) => createRoutes(deps),
|
|
506
|
+
onCliSetup: async (program, hostServices) => {
|
|
507
|
+
registerCommands(program, hostServices);
|
|
508
|
+
}
|
|
509
|
+
};
|
|
510
|
+
export {
|
|
511
|
+
vibePlugin
|
|
512
|
+
};
|
|
513
|
+
|
|
514
|
+
//# debugId=7350B96B6F44788864756E2164756E21
|
|
515
|
+
//# sourceMappingURL=index-b1eq3qvs.js.map
|