@buda-ai/connector 0.1.0 → 0.1.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/package.json CHANGED
@@ -1,11 +1,15 @@
1
1
  {
2
2
  "name": "@buda-ai/connector",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "publishConfig": {
7
7
  "access": "public"
8
8
  },
9
+ "files": [
10
+ "dist",
11
+ "README.md"
12
+ ],
9
13
  "bin": {
10
14
  "buda-connector": "./dist/cli.js",
11
15
  "connector": "./dist/cli.js"
@@ -22,12 +26,12 @@
22
26
  },
23
27
  "dependencies": {
24
28
  "@bunny-agent/daemon": "0.9.43",
25
- "relaylib": "workspace:*",
26
29
  "ws": "^8.21.0"
27
30
  },
28
31
  "devDependencies": {
29
32
  "@types/node": "^24.10.0",
30
33
  "@types/ws": "^8.18.1",
34
+ "relaylib": "workspace:*",
31
35
  "tsup": "^8.5.0",
32
36
  "tsx": "^4.20.5",
33
37
  "typescript": "^5.9.3"
@@ -1,52 +0,0 @@
1
- import { mkdir } from "node:fs/promises";
2
- import { createDaemon } from "@bunny-agent/daemon";
3
- import { getBunnyDaemonHost, getBunnyDaemonPort } from "./config.js";
4
- import { writeLog } from "./logger.js";
5
- import type { ConnectorLaunchConfig, DeviceConfig, EmbeddedBunnyDaemon } from "./types.js";
6
-
7
- export async function startEmbeddedBunnyDaemon(
8
- config: DeviceConfig,
9
- launchConfig?: ConnectorLaunchConfig,
10
- ): Promise<EmbeddedBunnyDaemon> {
11
- const host = getBunnyDaemonHost(launchConfig);
12
- const port = getBunnyDaemonPort(launchConfig);
13
- const root = config.workdirRoot;
14
- await mkdir(root, { recursive: true });
15
-
16
- const server = createDaemon({ host, port, root });
17
- await new Promise<void>((resolve, reject) => {
18
- const onError = (error: Error) => {
19
- server.off("listening", onListening);
20
- reject(error);
21
- };
22
- const onListening = () => {
23
- server.off("error", onError);
24
- resolve();
25
- };
26
- server.once("error", onError);
27
- server.once("listening", onListening);
28
- server.listen(port, host);
29
- });
30
-
31
- const address = server.address();
32
- const actualPort = typeof address === "object" && address ? address.port : port;
33
- const baseUrl = `http://${host}:${actualPort}`;
34
- await writeLog("info", `bunny daemon listening ${baseUrl}`, {
35
- root,
36
- });
37
-
38
- return {
39
- server,
40
- host,
41
- port: actualPort,
42
- baseUrl,
43
- root,
44
- };
45
- }
46
-
47
- export async function stopEmbeddedBunnyDaemon(daemon: EmbeddedBunnyDaemon) {
48
- await new Promise<void>((resolve) => {
49
- daemon.server.close(() => resolve());
50
- });
51
- await writeLog("info", `bunny daemon stopped ${daemon.baseUrl}`);
52
- }
package/src/cli.ts DELETED
@@ -1,97 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { LAUNCH_CONFIG_STDIN_FLAG, parseLaunchConfig } from "./config.js";
4
- import { runConnectorDaemon, runNewConnector } from "./runtime.js";
5
- import type { ConnectorCliCommand, ConnectorLaunchConfig } from "./types.js";
6
-
7
- function printUsage() {
8
- console.log(
9
- "Usage: buda-connector new | dev | daemon [--space-id <space>] [--server-url <url>] [--token <token>] [--workdir <path>] [--launch-config-stdin]\n" +
10
- " pnpm --filter @buda-ai/connector dev\n\n" +
11
- "Commands:\n" +
12
- " new Create/register a new connector identity\n" +
13
- " dev Run the connector daemon in development\n" +
14
- " daemon Run the connector daemon for sidecar/service usage",
15
- );
16
- }
17
-
18
- function readFlagValue(name: string): string | undefined {
19
- const index = process.argv.indexOf(name);
20
- if (index < 0) return undefined;
21
- const value = process.argv[index + 1];
22
- return value && !value.startsWith("--") ? value : undefined;
23
- }
24
-
25
- function readCliLaunchConfig(): ConnectorLaunchConfig {
26
- const bunnyDaemonPort = readFlagValue("--bunny-daemon-port");
27
- return {
28
- serverUrl: readFlagValue("--server-url"),
29
- workdirRoot: readFlagValue("--workdir"),
30
- oauthToken: readFlagValue("--token"),
31
- spaceId: readFlagValue("--space-id"),
32
- bunnyDaemonHost: readFlagValue("--bunny-daemon-host"),
33
- bunnyDaemonPort:
34
- bunnyDaemonPort && Number.isInteger(Number(bunnyDaemonPort))
35
- ? Number(bunnyDaemonPort)
36
- : undefined,
37
- };
38
- }
39
-
40
- async function readLaunchConfig(): Promise<ConnectorLaunchConfig> {
41
- const cliConfig = readCliLaunchConfig();
42
- if (!process.argv.includes(LAUNCH_CONFIG_STDIN_FLAG)) return cliConfig;
43
-
44
- process.stdin.setEncoding("utf8");
45
- return new Promise((resolve, reject) => {
46
- let raw = "";
47
- const cleanup = () => {
48
- process.stdin.off("data", onData);
49
- process.stdin.off("error", onError);
50
- process.stdin.off("end", onEnd);
51
- };
52
- const onError = (error: Error) => {
53
- cleanup();
54
- reject(error);
55
- };
56
- const onData = (chunk: string) => {
57
- raw += chunk;
58
- if (!raw.includes("\n")) return;
59
- cleanup();
60
- resolve({ ...cliConfig, ...parseLaunchConfig(raw.split("\n")[0] ?? "") });
61
- };
62
- const onEnd = () => {
63
- cleanup();
64
- resolve({ ...cliConfig, ...parseLaunchConfig(raw) });
65
- };
66
- process.stdin.on("data", onData);
67
- process.stdin.on("error", onError);
68
- process.stdin.on("end", onEnd);
69
- process.stdin.resume();
70
- });
71
- }
72
-
73
- async function main() {
74
- const command = process.argv[2] as ConnectorCliCommand | undefined;
75
- if (!command || process.argv.includes("--help") || process.argv.includes("-h")) {
76
- printUsage();
77
- return;
78
- }
79
-
80
- const launchConfig = await readLaunchConfig();
81
- if (command === "new") {
82
- await runNewConnector(launchConfig);
83
- return;
84
- }
85
- if (command === "dev" || command === "daemon") {
86
- await runConnectorDaemon(launchConfig);
87
- return;
88
- }
89
-
90
- printUsage();
91
- process.exit(1);
92
- }
93
-
94
- main().catch((error) => {
95
- console.error("[buda-connector] failed:", error);
96
- process.exit(1);
97
- });
@@ -1,134 +0,0 @@
1
- import { platform, release } from "node:os";
2
- import { getSpaceId } from "./config.js";
3
- import { restorePendingLogs, takePendingLogs, writeLog } from "./logger.js";
4
- import type {
5
- ConnectorLaunchConfig,
6
- ConnectorLogEntry,
7
- DeviceConfig,
8
- EmbeddedBunnyDaemon,
9
- } from "./types.js";
10
-
11
- function buildPayload(
12
- config: DeviceConfig,
13
- extra?: {
14
- bunnyDaemon?: EmbeddedBunnyDaemon;
15
- logs?: ConnectorLogEntry[];
16
- },
17
- ) {
18
- return {
19
- connectorId: config.connectorId,
20
- deviceId: config.deviceId,
21
- deviceName: config.deviceName,
22
- hostLabel: config.hostLabel,
23
- platform: `${platform()} ${release()}`,
24
- version: "dev",
25
- pid: process.pid,
26
- workdirRoot: config.workdirRoot,
27
- baseUrl: extra?.bunnyDaemon?.baseUrl,
28
- bunnyDaemon: extra?.bunnyDaemon
29
- ? {
30
- baseUrl: extra.bunnyDaemon.baseUrl,
31
- host: extra.bunnyDaemon.host,
32
- port: extra.bunnyDaemon.port,
33
- root: extra.bunnyDaemon.root,
34
- status: "online",
35
- }
36
- : undefined,
37
- capabilities: ["connector", "bunny-daemon", "coding", "filesystem", "shell", "git"],
38
- ...(extra?.logs?.length ? { logs: extra.logs } : {}),
39
- };
40
- }
41
-
42
- async function postJson<T>(
43
- serverUrl: string,
44
- pathname: string,
45
- body: unknown,
46
- launchConfig?: ConnectorLaunchConfig,
47
- ): Promise<T> {
48
- const token = launchConfig?.oauthToken?.trim();
49
- const spaceId = getSpaceId(launchConfig)?.trim();
50
- const response = await fetch(`${serverUrl}${pathname}`, {
51
- method: "POST",
52
- headers: {
53
- "Content-Type": "application/json",
54
- ...(token ? { Authorization: `Bearer ${token}` } : {}),
55
- ...(!token && spaceId ? { "X-Buda-Space-Id": spaceId } : {}),
56
- },
57
- body: JSON.stringify(body),
58
- });
59
- if (!response.ok) {
60
- const text = await response.text().catch(() => "");
61
- throw new Error(`${pathname} failed: ${response.status} ${text}`);
62
- }
63
- return (await response.json()) as T;
64
- }
65
-
66
- interface RegisteredConnectorResponse {
67
- connector: {
68
- connectorId: string;
69
- status: string;
70
- userId?: string | null;
71
- userEmail?: string | null;
72
- };
73
- }
74
-
75
- function formatConnectorOwner(connector: RegisteredConnectorResponse["connector"]): string {
76
- return connector.userEmail ?? connector.userId ?? "anonymous";
77
- }
78
-
79
- export async function registerConnector(
80
- config: DeviceConfig,
81
- launchConfig?: ConnectorLaunchConfig,
82
- bunnyDaemon?: EmbeddedBunnyDaemon,
83
- ) {
84
- const result = await postJson<RegisteredConnectorResponse>(
85
- config.serverUrl,
86
- "/api/connectors/register",
87
- buildPayload(config, { bunnyDaemon }),
88
- launchConfig,
89
- );
90
- await writeLog(
91
- "info",
92
- `registered connector=${result.connector.connectorId} status=${result.connector.status} owner=${formatConnectorOwner(result.connector)}`,
93
- );
94
- }
95
-
96
- export async function heartbeatConnector(
97
- config: DeviceConfig,
98
- launchConfig?: ConnectorLaunchConfig,
99
- bunnyDaemon?: EmbeddedBunnyDaemon,
100
- ) {
101
- const logs = takePendingLogs();
102
- const result = await postJson<RegisteredConnectorResponse>(
103
- config.serverUrl,
104
- "/api/connectors/heartbeat",
105
- buildPayload(config, {
106
- bunnyDaemon,
107
- logs,
108
- }),
109
- launchConfig,
110
- ).catch((error) => {
111
- restorePendingLogs(logs);
112
- throw error;
113
- });
114
- await writeLog(
115
- "info",
116
- `heartbeat connector=${result.connector.connectorId} status=${result.connector.status} owner=${formatConnectorOwner(result.connector)}`,
117
- );
118
- }
119
-
120
- export async function unregisterConnector(
121
- config: DeviceConfig,
122
- launchConfig?: ConnectorLaunchConfig,
123
- ) {
124
- await postJson(
125
- config.serverUrl,
126
- "/api/connectors/unregister",
127
- {
128
- connectorId: config.connectorId,
129
- },
130
- launchConfig,
131
- ).catch((error) => {
132
- void writeLog("warn", `unregister failed: ${error instanceof Error ? error.message : error}`);
133
- });
134
- }
package/src/config.ts DELETED
@@ -1,122 +0,0 @@
1
- import { randomUUID } from "node:crypto";
2
- import { mkdir, readFile, writeFile } from "node:fs/promises";
3
- import { homedir, hostname, platform } from "node:os";
4
- import path from "node:path";
5
- import type { ConnectorLaunchConfig, DeviceConfig } from "./types.js";
6
-
7
- export const CONFIG_DIR = path.join(homedir(), ".buda");
8
- export const CONFIG_PATH = path.join(CONFIG_DIR, "device.json");
9
- export const LOG_PATH = path.join(CONFIG_DIR, "connector.log");
10
- export const DEFAULT_SERVER_URL = "https://buda.im";
11
- export const DEFAULT_WORKDIR_ROOT = path.join(CONFIG_DIR, "agents");
12
- export const DEFAULT_BUNNY_DAEMON_HOST = "127.0.0.1";
13
- export const HEARTBEAT_INTERVAL_MS = 5_000;
14
- export const RELAY_RECONNECT_INTERVAL_MS = 3_000;
15
- export const LAUNCH_CONFIG_STDIN_FLAG = "--launch-config-stdin";
16
-
17
- export function getServerUrl(launchConfig?: ConnectorLaunchConfig) {
18
- return (
19
- launchConfig?.serverUrl ||
20
- process.env.BUDA_CONNECTOR_SERVER_URL ||
21
- process.env.BUDA_SERVER_URL ||
22
- process.env.NEXT_PUBLIC_APP_URL ||
23
- DEFAULT_SERVER_URL
24
- ).replace(/\/$/, "");
25
- }
26
-
27
- export function getConnectorRelayUrl(serverUrl: string, connectorId: string) {
28
- const url = new URL("/api/connectors/ws", serverUrl);
29
- url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
30
- url.searchParams.set("connectorId", connectorId);
31
- return url.toString();
32
- }
33
-
34
- export function getWorkdirRoot(launchConfig?: ConnectorLaunchConfig) {
35
- return launchConfig?.workdirRoot || process.env.BUDA_CONNECTOR_WORKDIR || DEFAULT_WORKDIR_ROOT;
36
- }
37
-
38
- export function getBunnyDaemonHost(launchConfig?: ConnectorLaunchConfig) {
39
- return (
40
- launchConfig?.bunnyDaemonHost ||
41
- process.env.BUDA_CONNECTOR_BUNNY_DAEMON_HOST ||
42
- DEFAULT_BUNNY_DAEMON_HOST
43
- );
44
- }
45
-
46
- export function getBunnyDaemonPort(launchConfig?: ConnectorLaunchConfig) {
47
- if (launchConfig?.bunnyDaemonPort !== undefined) return launchConfig.bunnyDaemonPort;
48
- const raw = process.env.BUDA_CONNECTOR_BUNNY_DAEMON_PORT;
49
- if (!raw) return 0;
50
- const parsed = Number(raw);
51
- return Number.isInteger(parsed) && parsed >= 0 ? parsed : 0;
52
- }
53
-
54
- export function getSpaceId(launchConfig?: ConnectorLaunchConfig) {
55
- return launchConfig?.spaceId || process.env.BUDA_CONNECTOR_SPACE_ID;
56
- }
57
-
58
- export function buildDeviceConfig(launchConfig?: ConnectorLaunchConfig): DeviceConfig {
59
- const host = hostname();
60
- return {
61
- connectorId: `cnr_${randomUUID()}`,
62
- deviceId: `dev_${randomUUID()}`,
63
- deviceName: host,
64
- hostLabel: `${host} (${platform()})`,
65
- serverUrl: getServerUrl(launchConfig),
66
- workdirRoot: getWorkdirRoot(launchConfig),
67
- createdAt: new Date().toISOString(),
68
- };
69
- }
70
-
71
- export async function readDeviceConfig(): Promise<DeviceConfig | null> {
72
- try {
73
- const raw = await readFile(CONFIG_PATH, "utf8");
74
- return JSON.parse(raw) as DeviceConfig;
75
- } catch {
76
- return null;
77
- }
78
- }
79
-
80
- export async function writeDeviceConfig(config: DeviceConfig) {
81
- await mkdir(CONFIG_DIR, { recursive: true });
82
- await mkdir(config.workdirRoot, { recursive: true });
83
- await writeFile(CONFIG_PATH, `${JSON.stringify(config, null, 2)}\n`, { mode: 0o600 });
84
- }
85
-
86
- export async function ensureDeviceConfig(
87
- launchConfig?: ConnectorLaunchConfig,
88
- ): Promise<DeviceConfig> {
89
- const existing = await readDeviceConfig();
90
- if (existing) {
91
- return {
92
- ...existing,
93
- serverUrl: getServerUrl(launchConfig),
94
- workdirRoot:
95
- launchConfig?.workdirRoot ||
96
- process.env.BUDA_CONNECTOR_WORKDIR ||
97
- existing.workdirRoot ||
98
- getWorkdirRoot(launchConfig),
99
- };
100
- }
101
- const config = buildDeviceConfig(launchConfig);
102
- await writeDeviceConfig(config);
103
- return config;
104
- }
105
-
106
- export function parseLaunchConfig(raw: string): ConnectorLaunchConfig {
107
- if (!raw.trim()) return {};
108
- const parsed = JSON.parse(raw) as Partial<ConnectorLaunchConfig>;
109
- const bunnyDaemonPort =
110
- typeof parsed.bunnyDaemonPort === "number" && Number.isInteger(parsed.bunnyDaemonPort)
111
- ? parsed.bunnyDaemonPort
112
- : undefined;
113
- return {
114
- serverUrl: typeof parsed.serverUrl === "string" ? parsed.serverUrl : undefined,
115
- workdirRoot: typeof parsed.workdirRoot === "string" ? parsed.workdirRoot : undefined,
116
- oauthToken: typeof parsed.oauthToken === "string" ? parsed.oauthToken : undefined,
117
- spaceId: typeof parsed.spaceId === "string" ? parsed.spaceId : undefined,
118
- bunnyDaemonHost:
119
- typeof parsed.bunnyDaemonHost === "string" ? parsed.bunnyDaemonHost : undefined,
120
- bunnyDaemonPort,
121
- };
122
- }
package/src/logger.ts DELETED
@@ -1,36 +0,0 @@
1
- import { appendFile, mkdir } from "node:fs/promises";
2
- import { CONFIG_DIR, LOG_PATH } from "./config.js";
3
- import type { ConnectorLogEntry } from "./types.js";
4
-
5
- const pendingLogs: ConnectorLogEntry[] = [];
6
-
7
- export async function writeLog(
8
- level: ConnectorLogEntry["level"],
9
- message: string,
10
- metadata?: Record<string, unknown>,
11
- ) {
12
- const entry: ConnectorLogEntry = {
13
- level,
14
- message,
15
- metadata,
16
- createdAt: new Date().toISOString(),
17
- };
18
- pendingLogs.push(entry);
19
- if (pendingLogs.length > 100) pendingLogs.splice(0, pendingLogs.length - 100);
20
-
21
- await mkdir(CONFIG_DIR, { recursive: true }).catch(() => undefined);
22
- await appendFile(LOG_PATH, `${JSON.stringify(entry)}\n`).catch(() => undefined);
23
-
24
- const line = `[buda-connector] ${message}`;
25
- if (level === "error") console.error(line);
26
- else if (level === "warn") console.warn(line);
27
- else console.log(line);
28
- }
29
-
30
- export function takePendingLogs(): ConnectorLogEntry[] {
31
- return pendingLogs.splice(0, pendingLogs.length);
32
- }
33
-
34
- export function restorePendingLogs(logs: ConnectorLogEntry[]) {
35
- pendingLogs.unshift(...logs);
36
- }
package/src/relay.ts DELETED
@@ -1,96 +0,0 @@
1
- import { attachRelayClient, type RelayRequestHandler } from "relaylib";
2
- import WebSocket from "ws";
3
- import { getConnectorRelayUrl, RELAY_RECONNECT_INTERVAL_MS } from "./config.js";
4
- import { writeLog } from "./logger.js";
5
- import type { ConnectorLaunchConfig, DeviceConfig, EmbeddedBunnyDaemon } from "./types.js";
6
-
7
- export function startConnectorRelay(
8
- config: DeviceConfig,
9
- bunnyDaemon: EmbeddedBunnyDaemon,
10
- launchConfig?: ConnectorLaunchConfig,
11
- ) {
12
- let socket: WebSocket | null = null;
13
- let reconnectTimer: NodeJS.Timeout | null = null;
14
- let stopped = false;
15
- const handler = createRelayHandler(bunnyDaemon);
16
-
17
- const connect = () => {
18
- if (stopped) return;
19
- const relayUrl = getConnectorRelayUrl(config.serverUrl, config.connectorId);
20
- const token = launchConfig?.oauthToken?.trim();
21
- socket = new WebSocket(relayUrl, {
22
- headers: token ? { Authorization: `Bearer ${token}` } : undefined,
23
- });
24
-
25
- socket.on("open", () => {
26
- void writeLog("info", `relay connected ${relayUrl}`);
27
- });
28
-
29
- // The protocol loop (request -> handler -> streamed start/chunk/end) lives in
30
- // relaylib. `chunkEncoding: "utf8"` keeps the legacy text wire format, since a
31
- // base64-aware hub is not guaranteed to be deployed yet (the connector ships
32
- // independently of the cloud). Switch to base64 once that hub is rolled out.
33
- attachRelayClient(socket, handler, { chunkEncoding: "utf8" });
34
-
35
- socket.on("close", () => {
36
- void writeLog("warn", "relay disconnected");
37
- if (stopped) return;
38
- reconnectTimer = setTimeout(connect, RELAY_RECONNECT_INTERVAL_MS);
39
- });
40
-
41
- socket.on("error", (error) => {
42
- void writeLog("warn", `relay error: ${error instanceof Error ? error.message : error}`);
43
- });
44
- };
45
-
46
- connect();
47
-
48
- return {
49
- stop() {
50
- stopped = true;
51
- if (reconnectTimer) clearTimeout(reconnectTimer);
52
- socket?.close();
53
- },
54
- };
55
- }
56
-
57
- /**
58
- * App-specific relay handler: forwards each request to the embedded bunny daemon
59
- * only (origin guard), records the relay audit headers, then proxies via fetch.
60
- */
61
- function createRelayHandler(bunnyDaemon: EmbeddedBunnyDaemon): RelayRequestHandler {
62
- const allowedOrigin = new URL(bunnyDaemon.baseUrl).origin;
63
-
64
- return async (request) => {
65
- const requestUrl = new URL(request.path, bunnyDaemon.baseUrl);
66
- if (requestUrl.origin !== allowedOrigin) {
67
- throw new Error("Relay request must target the embedded bunny daemon.");
68
- }
69
-
70
- await writeLog("info", `relay request ${request.method} ${requestUrl.pathname}`, {
71
- actorEmail: getHeaderValue(request.headers, "x-buda-relay-actor-email") || null,
72
- actorId: getHeaderValue(request.headers, "x-buda-relay-actor-id") || null,
73
- source: getHeaderValue(request.headers, "x-buda-relay-source") || "unknown",
74
- action: getHeaderValue(request.headers, "x-buda-relay-action") || "unknown",
75
- target: requestUrl.toString(),
76
- });
77
-
78
- return fetch(requestUrl, {
79
- method: request.method,
80
- headers: {
81
- "Content-Type": "application/json",
82
- ...(request.headers ?? {}),
83
- },
84
- body: request.body === undefined ? undefined : JSON.stringify(request.body),
85
- });
86
- };
87
- }
88
-
89
- function getHeaderValue(headers: Record<string, string> | undefined, name: string) {
90
- if (!headers) return undefined;
91
- const normalized = name.toLowerCase();
92
- for (const [key, value] of Object.entries(headers)) {
93
- if (key.toLowerCase() === normalized) return value;
94
- }
95
- return undefined;
96
- }
package/src/runtime.ts DELETED
@@ -1,59 +0,0 @@
1
- import { startEmbeddedBunnyDaemon, stopEmbeddedBunnyDaemon } from "./bunny-daemon.js";
2
- import { heartbeatConnector, registerConnector, unregisterConnector } from "./cloud-client.js";
3
- import {
4
- buildDeviceConfig,
5
- CONFIG_PATH,
6
- ensureDeviceConfig,
7
- HEARTBEAT_INTERVAL_MS,
8
- LOG_PATH,
9
- writeDeviceConfig,
10
- } from "./config.js";
11
- import { writeLog } from "./logger.js";
12
- import { startConnectorRelay } from "./relay.js";
13
- import type { ConnectorLaunchConfig } from "./types.js";
14
-
15
- export async function runNewConnector(launchConfig?: ConnectorLaunchConfig) {
16
- const config = buildDeviceConfig(launchConfig);
17
- await writeDeviceConfig(config);
18
- await registerConnector(config, launchConfig);
19
- await writeLog("info", `wrote ${CONFIG_PATH}`);
20
- await writeLog("info", `workdir root ${config.workdirRoot}`);
21
- await writeLog("info", `log file ${LOG_PATH}`);
22
- }
23
-
24
- export async function runConnectorDaemon(launchConfig?: ConnectorLaunchConfig) {
25
- const config = await ensureDeviceConfig(launchConfig);
26
- const bunnyDaemon = await startEmbeddedBunnyDaemon(config, launchConfig);
27
- try {
28
- await registerConnector(config, launchConfig, bunnyDaemon);
29
- } catch (error) {
30
- await stopEmbeddedBunnyDaemon(bunnyDaemon);
31
- throw error;
32
- }
33
- const relay = startConnectorRelay(config, bunnyDaemon, launchConfig);
34
- await writeLog("info", `server ${config.serverUrl}`);
35
- await writeLog("info", `config ${CONFIG_PATH}`);
36
- await writeLog("info", `workdir root ${config.workdirRoot}`);
37
- await writeLog("info", `log file ${LOG_PATH}`);
38
- await writeLog("info", `bunny daemon ${bunnyDaemon.baseUrl}`);
39
-
40
- const interval = setInterval(() => {
41
- heartbeatConnector(config, launchConfig, bunnyDaemon).catch((error) => {
42
- void writeLog(
43
- "warn",
44
- `heartbeat failed: ${error instanceof Error ? error.message : String(error)}`,
45
- );
46
- });
47
- }, HEARTBEAT_INTERVAL_MS);
48
-
49
- const shutdown = async () => {
50
- clearInterval(interval);
51
- relay.stop();
52
- await unregisterConnector(config, launchConfig);
53
- await stopEmbeddedBunnyDaemon(bunnyDaemon);
54
- process.exit(0);
55
- };
56
-
57
- process.on("SIGINT", shutdown);
58
- process.on("SIGTERM", shutdown);
59
- }
package/src/types.ts DELETED
@@ -1,37 +0,0 @@
1
- import type { Server } from "node:http";
2
-
3
- export type ConnectorCliCommand = "new" | "dev" | "daemon";
4
-
5
- export interface DeviceConfig {
6
- connectorId: string;
7
- deviceId: string;
8
- deviceName: string;
9
- hostLabel: string;
10
- serverUrl: string;
11
- workdirRoot: string;
12
- createdAt: string;
13
- }
14
-
15
- export interface ConnectorLaunchConfig {
16
- serverUrl?: string;
17
- workdirRoot?: string;
18
- oauthToken?: string;
19
- spaceId?: string;
20
- bunnyDaemonHost?: string;
21
- bunnyDaemonPort?: number;
22
- }
23
-
24
- export interface EmbeddedBunnyDaemon {
25
- server: Server;
26
- host: string;
27
- port: number;
28
- baseUrl: string;
29
- root: string;
30
- }
31
-
32
- export interface ConnectorLogEntry {
33
- level: "info" | "warn" | "error";
34
- message: string;
35
- metadata?: Record<string, unknown>;
36
- createdAt: string;
37
- }
package/tsconfig.json DELETED
@@ -1,44 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "lib": ["ES2022", "DOM"],
5
- "module": "NodeNext",
6
- "moduleResolution": "NodeNext",
7
- "strict": true,
8
- "esModuleInterop": true,
9
- "skipLibCheck": true,
10
- "forceConsistentCasingInFileNames": true,
11
- "noEmit": true,
12
- "types": ["node"],
13
- "paths": {
14
- "kui/ai-elements/*": ["../../packages/kui/src/components/ai-elements/*"],
15
- "kui/hooks/*": ["../../packages/kui/src/hooks/*"],
16
- "kui/tour": ["../../packages/kui/src/components/tour.tsx"],
17
- "kui/utils": ["../../packages/kui/src/lib/utils.ts"],
18
- "kui/*": ["../../packages/kui/src/components/ui/*"],
19
- "sharelib": ["../../packages/sharelib/index.ts"],
20
- "sharelib/onboarding-types": ["../../packages/sharelib/src/onboarding-types.ts"],
21
- "sharelib/scheduler/cache": ["../../packages/sharelib/scheduler/scheduler-cache.ts"],
22
- "sharelib/scheduler/cron": ["../../packages/sharelib/scheduler/cron-parser.ts"],
23
- "sharelib/ui/ProductScreenshot": ["../../packages/sharelib/ui/common/ProductScreenshot.tsx"],
24
- "sharelib/ui/ProductShowcase": ["../../packages/sharelib/ui/common/ProductShowcase.tsx"],
25
- "sharelib/ui/LanguageSwitcher": ["../../packages/sharelib/ui/common/LanguageSwitcher.tsx"],
26
- "sharelib/ui/dashboard": ["../../packages/sharelib/ui/dashboard/index.ts"],
27
- "sharelib/*": ["../../packages/sharelib/*"],
28
- "share-domains/*": ["../../packages/share-domains/*"],
29
- "billing": ["../../packages/billing/index.ts"],
30
- "billing/*": ["../../packages/billing/*"],
31
- "emaillib": ["../../packages/emaillib/index.ts"],
32
- "emaillib/types": ["../../packages/emaillib/types/index.ts"],
33
- "transactional/components": ["../../packages/transactional/emails/_components/index.ts"],
34
- "transactional/emails/*": ["../../packages/transactional/emails/*.tsx"],
35
- "transactional/*": ["../../packages/transactional/emails/*.tsx"],
36
- "@mcpsdk/sdk-ts": ["../../packages/@mcpsdk/sdk-ts/src/index.ts"],
37
- "@mcpsdk/sdk-ts/*": ["../../packages/@mcpsdk/sdk-ts/src/*"],
38
- "@mcpsdk/plugin-core": ["../../packages/@mcpsdk/plugin-core/src/index.ts"],
39
- "@mcpsdk/plugin-core/*": ["../../packages/@mcpsdk/plugin-core/src/*"],
40
- "@mcpsdk/mcp-server": ["../../packages/@mcpsdk/mcp-server/index.js"]
41
- }
42
- },
43
- "include": ["src/**/*.ts"]
44
- }
package/tsup.config.ts DELETED
@@ -1,20 +0,0 @@
1
- import { resolve } from "node:path";
2
- import { defineConfig } from "tsup";
3
-
4
- // relaylib ships TypeScript source (its package exports point at `./src/*.ts`),
5
- // so it cannot be a runtime dependency of this published npm CLI. Bundle it
6
- // straight into dist instead — the connector stays standalone. `ws` and
7
- // @bunny-agent/* stay external (real runtime deps).
8
- export default defineConfig({
9
- entry: { cli: "src/cli.ts" },
10
- format: ["esm"],
11
- target: "node20",
12
- platform: "node",
13
- outDir: "dist",
14
- clean: true,
15
- dts: false,
16
- noExternal: [/^relaylib/],
17
- esbuildOptions(options) {
18
- options.nodePaths = [resolve(process.cwd(), "node_modules")];
19
- },
20
- });