0xkobold 0.2.0 → 0.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.
Files changed (60) hide show
  1. package/HEARTBEAT.md +40 -58
  2. package/README.md +387 -337
  3. package/dist/package.json +4 -2
  4. package/dist/src/auth/device-auth.js +202 -0
  5. package/dist/src/auth/device-auth.js.map +1 -0
  6. package/dist/src/auth/index.js +3 -0
  7. package/dist/src/auth/index.js.map +1 -0
  8. package/dist/src/channels/index.js +8 -0
  9. package/dist/src/channels/index.js.map +1 -0
  10. package/dist/src/channels/slack/webhook.js +128 -0
  11. package/dist/src/channels/slack/webhook.js.map +1 -0
  12. package/dist/src/channels/telegram/bot.js +223 -0
  13. package/dist/src/channels/telegram/bot.js.map +1 -0
  14. package/dist/src/channels/whatsapp/integration.js +325 -0
  15. package/dist/src/channels/whatsapp/integration.js.map +1 -0
  16. package/dist/src/cli/commands/check.js +69 -0
  17. package/dist/src/cli/commands/check.js.map +1 -0
  18. package/dist/src/cli/commands/migrate.js +24 -0
  19. package/dist/src/cli/commands/migrate.js.map +1 -0
  20. package/dist/src/cli/commands/tailscale.js +81 -0
  21. package/dist/src/cli/commands/tailscale.js.map +1 -0
  22. package/dist/src/cli/commands/telegram.js +111 -0
  23. package/dist/src/cli/commands/telegram.js.map +1 -0
  24. package/dist/src/cli/commands/tui.js +40 -22
  25. package/dist/src/cli/commands/tui.js.map +1 -1
  26. package/dist/src/cli/commands/whatsapp.js +116 -0
  27. package/dist/src/cli/commands/whatsapp.js.map +1 -0
  28. package/dist/src/cli/program.js +20 -0
  29. package/dist/src/cli/program.js.map +1 -1
  30. package/dist/src/config/index.js +2 -9
  31. package/dist/src/config/index.js.map +1 -1
  32. package/dist/src/config/manager.js +222 -0
  33. package/dist/src/config/manager.js.map +1 -0
  34. package/dist/src/documents/index.js +3 -0
  35. package/dist/src/documents/index.js.map +1 -0
  36. package/dist/src/documents/pdf.js +168 -0
  37. package/dist/src/documents/pdf.js.map +1 -0
  38. package/dist/src/gateway/client.js +318 -0
  39. package/dist/src/gateway/client.js.map +1 -0
  40. package/dist/src/infra/index.js +3 -0
  41. package/dist/src/infra/index.js.map +1 -0
  42. package/dist/src/infra/tailscale.js +163 -0
  43. package/dist/src/infra/tailscale.js.map +1 -0
  44. package/dist/src/media/audio.js +130 -0
  45. package/dist/src/media/audio.js.map +1 -0
  46. package/dist/src/media/index.js +4 -0
  47. package/dist/src/media/index.js.map +1 -0
  48. package/dist/src/media/vision.js +131 -0
  49. package/dist/src/media/vision.js.map +1 -0
  50. package/dist/src/migration/openclaw.js +498 -0
  51. package/dist/src/migration/openclaw.js.map +1 -0
  52. package/dist/src/sandbox/docker-runner.js +228 -0
  53. package/dist/src/sandbox/docker-runner.js.map +1 -0
  54. package/dist/src/sandbox/index.js +3 -0
  55. package/dist/src/sandbox/index.js.map +1 -0
  56. package/dist/src/skills/builtin/duplicate-detector.js +469 -0
  57. package/dist/src/skills/builtin/duplicate-detector.js.map +1 -0
  58. package/dist/src/skills/index.js +2 -0
  59. package/dist/src/skills/index.js.map +1 -1
  60. package/package.json +4 -2
@@ -0,0 +1,168 @@
1
+ /**
2
+ * PDF Document Support - v0.3.0
3
+ *
4
+ * Text extraction from PDF documents.
5
+ */
6
+ import * as fs from "node:fs/promises";
7
+ class PDFExtractor {
8
+ config;
9
+ constructor(config = {}) {
10
+ this.config = {
11
+ maxPages: 100,
12
+ extractText: true,
13
+ extractMetadata: true,
14
+ ...config,
15
+ };
16
+ }
17
+ /**
18
+ * Extract from file
19
+ */
20
+ async extractFromFile(filePath) {
21
+ const buffer = await fs.readFile(filePath);
22
+ return this.extractFromBuffer(buffer);
23
+ }
24
+ /**
25
+ * Extract from buffer
26
+ */
27
+ async extractFromBuffer(buffer) {
28
+ // PDF magic number check
29
+ if (buffer.slice(0, 5).toString() !== "%PDF-") {
30
+ throw new Error("Invalid PDF file");
31
+ }
32
+ // Parse basic PDF structure
33
+ const result = {
34
+ text: "",
35
+ metadata: {},
36
+ pages: [],
37
+ };
38
+ // Extract text using regex (basic implementation)
39
+ // In production, use pdf-parse or pdf-lib
40
+ const textContent = this.extractTextFromPDF(buffer);
41
+ result.text = textContent;
42
+ result.pages = [{
43
+ pageNumber: 1,
44
+ text: textContent.slice(0, 5000), // First page preview
45
+ }];
46
+ // Extract metadata
47
+ if (this.config.extractMetadata) {
48
+ result.metadata = this.extractMetadata(buffer);
49
+ }
50
+ return result;
51
+ }
52
+ /**
53
+ * Extract text using basic PDF parsing
54
+ */
55
+ extractTextFromPDF(buffer) {
56
+ const content = buffer.toString("latin1");
57
+ // Find text streams (BT ... ET)
58
+ const textRegex = /BT\s*((?:(?!ET).|\s)*)\s*ET/g;
59
+ const matches = content.match(textRegex) || [];
60
+ const extractedTexts = [];
61
+ for (const match of matches) {
62
+ // Extract Tj and TJ operands
63
+ const text = match
64
+ .replace(/BT|ET/g, "")
65
+ .replace(/\[[^\]]*\]TJ/g, (m) => {
66
+ // Handle TJ arrays
67
+ const inner = m.slice(1, -3);
68
+ return inner
69
+ .split(" ")
70
+ .filter((_, i) => i % 2 === 0)
71
+ .join("");
72
+ })
73
+ .replace(/\([^)]*\)Tj/g, (m) => {
74
+ // Handle Tj strings
75
+ return m.slice(1, -3);
76
+ })
77
+ .replace(/\d+\.?\d*\s+\d+\.?\d*\s+Td/g, " ") // Position operators
78
+ .replace(/\d+\.?\d*\s+TL/g, " ") // Line operators
79
+ .replace(/\s+/g, " ")
80
+ .trim();
81
+ if (text)
82
+ extractedTexts.push(text);
83
+ }
84
+ // Also try obj streams
85
+ const streamRegex = /stream\r?\n([\s\S]*?)endstream/g;
86
+ let streamMatch;
87
+ while ((streamMatch = streamRegex.exec(content)) !== null) {
88
+ const stream = streamMatch[1];
89
+ // Check if it looks like text
90
+ if (stream.includes("(") && stream.includes("Tj")) {
91
+ const text = stream
92
+ .replace(/\([^)]*\)Tj/g, (m) => m.slice(1, -3))
93
+ .replace(/\s+/g, " ")
94
+ .trim();
95
+ if (text.length > 3)
96
+ extractedTexts.push(text);
97
+ }
98
+ }
99
+ return extractedTexts.join("\n").slice(0, 50000); // Limit output
100
+ }
101
+ /**
102
+ * Extract metadata
103
+ */
104
+ extractMetadata(buffer) {
105
+ const content = buffer.toString("latin1");
106
+ const metadata = {};
107
+ // Title
108
+ const titleMatch = content.match(/\/Title\s*\(([^)]+)\)/);
109
+ if (titleMatch)
110
+ metadata.title = this.unescapePDFString(titleMatch[1]);
111
+ // Author
112
+ const authorMatch = content.match(/\/Author\s*\(([^)]+)\)/);
113
+ if (authorMatch)
114
+ metadata.author = this.unescapePDFString(authorMatch[1]);
115
+ // Subject
116
+ const subjectMatch = content.match(/\/Subject\s*\(([^)]+)\)/);
117
+ if (subjectMatch)
118
+ metadata.subject = this.unescapePDFString(subjectMatch[1]);
119
+ // Creator
120
+ const creatorMatch = content.match(/\/Creator\s*\(([^)]+)\)/);
121
+ if (creatorMatch)
122
+ metadata.creator = this.unescapePDFString(creatorMatch[1]);
123
+ // Producer
124
+ const producerMatch = content.match(/\/Producer\s*\(([^)]+)\)/);
125
+ if (producerMatch)
126
+ metadata.producer = this.unescapePDFString(producerMatch[1]);
127
+ // Page count
128
+ const pagesMatch = content.match(/\/Count\s+(\d+)/);
129
+ if (pagesMatch)
130
+ metadata.pageCount = parseInt(pagesMatch[1], 10);
131
+ // Keywords
132
+ const keywordsMatch = content.match(/\/Keywords\s*\(([^)]+)\)/);
133
+ if (keywordsMatch)
134
+ metadata.keywords = this.unescapePDFString(keywordsMatch[1]);
135
+ return metadata;
136
+ }
137
+ /**
138
+ * Unescape PDF string
139
+ */
140
+ unescapePDFString(str) {
141
+ return str
142
+ .replace(/\\n/g, "\n")
143
+ .replace(/\\r/g, "\r")
144
+ .replace(/\\t/g, "\t")
145
+ .replace(/\\\\/g, "\\")
146
+ .replace(/\\\(/g, "(")
147
+ .replace(/\\\)/g, ")");
148
+ }
149
+ /**
150
+ * Get PDF info
151
+ */
152
+ async getInfo(filePath) {
153
+ const buffer = await fs.readFile(filePath);
154
+ return this.extractMetadata(buffer);
155
+ }
156
+ }
157
+ // Helper functions
158
+ export async function extractPDF(filePath, config) {
159
+ const extractor = new PDFExtractor(config);
160
+ return extractor.extractFromFile(filePath);
161
+ }
162
+ export async function extractPDFText(filePath) {
163
+ const result = await extractPDF(filePath, { extractMetadata: false });
164
+ return result.text;
165
+ }
166
+ export { PDFExtractor };
167
+ export default PDFExtractor;
168
+ //# sourceMappingURL=pdf.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pdf.js","sourceRoot":"","sources":["../../../src/documents/pdf.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,318 @@
1
+ /**
2
+ * Gateway Client - Remote/VPS Support
3
+ *
4
+ * Connect to remote or local gateway server for distributed architecture.
5
+ * Based on kod/OpenClaw client architecture - simplified for 0xKobold.
6
+ */
7
+ import { EventEmitter } from "events";
8
+ import { homedir } from "os";
9
+ import { join } from "path";
10
+ import * as fs from "node:fs/promises";
11
+ export const GATEWAY_STATE = {
12
+ DISCONNECTED: "disconnected",
13
+ CONNECTING: "connecting",
14
+ CONNECTED: "connected",
15
+ };
16
+ /**
17
+ * GatewayClient - WebSocket client for connecting to local or remote gateway
18
+ *
19
+ * Architecture:
20
+ * - Local mode: Connects to ws://localhost:7777
21
+ * - Remote mode: Connects to wss://your-vps.com:7777
22
+ * - Handles auth tokens (local storage in ~/.0xkobold/)
23
+ * - Auto-reconnect with exponential backoff
24
+ * - Heartbeat/ping-pong for connection health
25
+ */
26
+ export class GatewayClient extends EventEmitter {
27
+ ws = null;
28
+ config;
29
+ state = GATEWAY_STATE.DISCONNECTED;
30
+ reconnectTimer;
31
+ pingTimer;
32
+ reconnectCount = 0;
33
+ pendingMessages = [];
34
+ messageId = 0;
35
+ constructor(config) {
36
+ super();
37
+ this.config = {
38
+ url: config.url,
39
+ autoReconnect: config.autoReconnect ?? true,
40
+ reconnectDelay: config.reconnectDelay ?? 1000,
41
+ reconnectAttempts: config.reconnectAttempts ?? 10,
42
+ token: config.token,
43
+ deviceToken: config.deviceToken,
44
+ password: config.password,
45
+ clientName: config.clientName ?? "0xKobold-Client",
46
+ clientVersion: config.clientVersion ?? "0.3.0",
47
+ deviceId: config.deviceId ?? "unknown",
48
+ capabilities: config.capabilities ?? ["chat", "files", "heartbeat"],
49
+ onConnect: config.onConnect,
50
+ onDisconnect: config.onDisconnect,
51
+ onMessage: config.onMessage,
52
+ onError: config.onError,
53
+ };
54
+ }
55
+ /**
56
+ * Connect to gateway
57
+ */
58
+ async connect() {
59
+ if (this.state === GATEWAY_STATE.CONNECTED) {
60
+ return true;
61
+ }
62
+ this.state = GATEWAY_STATE.CONNECTING;
63
+ this.emit("stateChange", this.state);
64
+ try {
65
+ // Determine protocol (ws:// vs wss://)
66
+ const isSecure = this.config.url.startsWith("wss://");
67
+ const wsUrl = this.config.url;
68
+ console.log(`[GatewayClient] Connecting to ${isSecure ? "secure" : "insecure"} WebSocket...`);
69
+ console.log(`[GatewayClient] URL: ${wsUrl}`);
70
+ // Create WebSocket
71
+ this.ws = new WebSocket(wsUrl);
72
+ // Setup handlers
73
+ this.ws.onopen = () => this.handleOpen();
74
+ this.ws.onclose = (event) => this.handleClose(event.code, event.reason);
75
+ this.ws.onerror = (err) => this.handleError(new Error(String(err)));
76
+ this.ws.onmessage = (msg) => this.handleMessage(msg.data);
77
+ // Wait for connection
78
+ await this.waitForConnection();
79
+ return true;
80
+ }
81
+ catch (err) {
82
+ this.handleError(err);
83
+ return false;
84
+ }
85
+ }
86
+ /**
87
+ * Disconnect from gateway
88
+ */
89
+ disconnect() {
90
+ this.clearTimers();
91
+ if (this.ws) {
92
+ this.ws.close(1000, "Client disconnect");
93
+ this.ws = null;
94
+ }
95
+ this.state = GATEWAY_STATE.DISCONNECTED;
96
+ this.emit("stateChange", this.state);
97
+ }
98
+ /**
99
+ * Send message to gateway
100
+ */
101
+ send(message) {
102
+ const fullMessage = {
103
+ ...message,
104
+ id: this.generateMessageId(),
105
+ timestamp: Date.now(),
106
+ };
107
+ if (this.state === GATEWAY_STATE.CONNECTED && this.ws) {
108
+ this.ws.send(JSON.stringify(fullMessage));
109
+ }
110
+ else {
111
+ // Queue for later
112
+ this.pendingMessages.push(fullMessage);
113
+ }
114
+ return fullMessage.id;
115
+ }
116
+ /**
117
+ * Send chat request
118
+ */
119
+ chat(message, context) {
120
+ return this.send({
121
+ type: "request",
122
+ channel: "chat",
123
+ payload: {
124
+ message,
125
+ context,
126
+ capabilities: this.config.capabilities,
127
+ },
128
+ });
129
+ }
130
+ /**
131
+ * Send ping to check connection
132
+ */
133
+ ping() {
134
+ this.send({
135
+ type: "ping",
136
+ payload: { timestamp: Date.now() },
137
+ });
138
+ }
139
+ /**
140
+ * Get current state
141
+ */
142
+ getState() {
143
+ return this.state;
144
+ }
145
+ /**
146
+ * Check if connected
147
+ */
148
+ isConnected() {
149
+ return this.state === GATEWAY_STATE.CONNECTED;
150
+ }
151
+ /**
152
+ * Load cached device token from disk
153
+ */
154
+ static async loadDeviceToken() {
155
+ const tokenPath = join(homedir(), ".0xkobold", ".device-token");
156
+ try {
157
+ const token = await fs.readFile(tokenPath, "utf-8");
158
+ return token.trim();
159
+ }
160
+ catch {
161
+ return null;
162
+ }
163
+ }
164
+ /**
165
+ * Save device token to disk
166
+ */
167
+ static async saveDeviceToken(token) {
168
+ const tokenPath = join(homedir(), ".0xkobold", ".device-token");
169
+ await fs.mkdir(join(homedir(), ".0xkobold"), { recursive: true });
170
+ await fs.writeFile(tokenPath, token, "utf-8");
171
+ await fs.chmod(tokenPath, 0o600); // Secure permissions
172
+ }
173
+ /**
174
+ * Clear saved device token
175
+ */
176
+ static async clearDeviceToken() {
177
+ const tokenPath = join(homedir(), ".0xkobold", ".device-token");
178
+ try {
179
+ await fs.unlink(tokenPath);
180
+ }
181
+ catch {
182
+ // Ignore if doesn't exist
183
+ }
184
+ }
185
+ // Private methods
186
+ handleOpen() {
187
+ console.log("[GatewayClient] Connected successfully");
188
+ this.state = GATEWAY_STATE.CONNECTED;
189
+ this.reconnectCount = 0;
190
+ // Send authentication
191
+ this.sendAuth();
192
+ // Process queued messages
193
+ this.processPendingMessages();
194
+ // Start heartbeat
195
+ this.startHeartbeat();
196
+ this.emit("connected");
197
+ this.config.onConnect?.();
198
+ }
199
+ handleClose(code, reason) {
200
+ console.log(`[GatewayClient] Disconnected: ${code} - ${reason}`);
201
+ this.state = GATEWAY_STATE.DISCONNECTED;
202
+ this.emit("disconnected", code, reason);
203
+ this.config.onDisconnect?.(code, reason);
204
+ this.clearTimers();
205
+ // Attempt reconnect if enabled
206
+ if (this.config.autoReconnect && this.reconnectCount < this.config.reconnectAttempts) {
207
+ this.scheduleReconnect();
208
+ }
209
+ }
210
+ handleError(err) {
211
+ console.error("[GatewayClient] Error:", err.message);
212
+ this.emit("error", err);
213
+ this.config.onError?.(err);
214
+ }
215
+ handleMessage(data) {
216
+ try {
217
+ const message = JSON.parse(data.toString());
218
+ // Handle ping/pong
219
+ if (message.type === "pong") {
220
+ this.emit("pong", message);
221
+ return;
222
+ }
223
+ this.emit("message", message);
224
+ this.config.onMessage?.(message);
225
+ }
226
+ catch (err) {
227
+ console.error("[GatewayClient] Failed to parse message:", err);
228
+ }
229
+ }
230
+ sendAuth() {
231
+ const authPayload = {
232
+ client: this.config.clientName,
233
+ version: this.config.clientVersion,
234
+ deviceId: this.config.deviceId,
235
+ capabilities: this.config.capabilities,
236
+ };
237
+ if (this.config.token) {
238
+ authPayload.token = this.config.token;
239
+ }
240
+ if (this.config.deviceToken) {
241
+ authPayload.deviceToken = this.config.deviceToken;
242
+ }
243
+ if (this.config.password) {
244
+ authPayload.password = this.config.password;
245
+ }
246
+ this.send({
247
+ type: "request",
248
+ channel: "auth",
249
+ payload: authPayload,
250
+ });
251
+ }
252
+ waitForConnection() {
253
+ return new Promise((resolve, reject) => {
254
+ const timeout = setTimeout(() => {
255
+ reject(new Error("Connection timeout"));
256
+ }, 10000);
257
+ const onConnect = () => {
258
+ clearTimeout(timeout);
259
+ this.off("error", onError);
260
+ resolve();
261
+ };
262
+ const onError = (err) => {
263
+ clearTimeout(timeout);
264
+ this.off("connected", onConnect);
265
+ reject(err);
266
+ };
267
+ this.once("connected", onConnect);
268
+ this.once("error", onError);
269
+ });
270
+ }
271
+ scheduleReconnect() {
272
+ this.reconnectCount++;
273
+ const delay = this.config.reconnectDelay * Math.min(this.reconnectCount, 5);
274
+ console.log(`[GatewayClient] Reconnecting in ${delay}ms (attempt ${this.reconnectCount})`);
275
+ this.reconnectTimer = setTimeout(() => {
276
+ this.connect();
277
+ }, delay);
278
+ }
279
+ startHeartbeat() {
280
+ this.pingTimer = setInterval(() => {
281
+ if (this.isConnected()) {
282
+ this.ping();
283
+ }
284
+ }, 30000); // Ping every 30s
285
+ }
286
+ processPendingMessages() {
287
+ while (this.pendingMessages.length > 0) {
288
+ const msg = this.pendingMessages.shift();
289
+ if (msg && this.ws) {
290
+ this.ws.send(JSON.stringify(msg));
291
+ }
292
+ }
293
+ }
294
+ clearTimers() {
295
+ if (this.reconnectTimer) {
296
+ clearTimeout(this.reconnectTimer);
297
+ this.reconnectTimer = undefined;
298
+ }
299
+ if (this.pingTimer) {
300
+ clearInterval(this.pingTimer);
301
+ this.pingTimer = undefined;
302
+ }
303
+ }
304
+ generateMessageId() {
305
+ return `msg-${Date.now()}-${++this.messageId}`;
306
+ }
307
+ }
308
+ // Factory for creating client with auto-loaded token
309
+ export async function createGatewayClient(url, options) {
310
+ const deviceToken = await GatewayClient.loadDeviceToken();
311
+ return new GatewayClient({
312
+ url,
313
+ deviceToken: deviceToken ?? undefined,
314
+ ...options,
315
+ });
316
+ }
317
+ export default GatewayClient;
318
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../../src/gateway/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ // Infrastructure Module - v0.3.0
2
+ export { TailscaleIntegration, getTailscaleIntegration, } from "./tailscale.js";
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/infra/index.ts"],"names":[],"mappings":"AAAA,iCAAiC;AACjC,OAAO,EACL,oBAAoB,EACpB,uBAAuB,GAGxB,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,163 @@
1
+ /**
2
+ * Tailscale Integration - v0.3.0
3
+ *
4
+ * Zero-config VPN for secure remote gateway access.
5
+ * No port forwarding required, works through NAT/firewalls.
6
+ */
7
+ import { spawn } from "node:child_process";
8
+ import { EventEmitter } from "events";
9
+ class TailscaleIntegration extends EventEmitter {
10
+ config;
11
+ connected = false;
12
+ constructor(config = {}) {
13
+ super();
14
+ this.config = {
15
+ enabled: false,
16
+ autoStart: true,
17
+ advertiseExitNode: false,
18
+ ...config,
19
+ };
20
+ }
21
+ /**
22
+ * Check if Tailscale is installed
23
+ */
24
+ async isInstalled() {
25
+ try {
26
+ const result = await this.execCommand("tailscale", ["version"]);
27
+ return result.success;
28
+ }
29
+ catch {
30
+ return false;
31
+ }
32
+ }
33
+ /**
34
+ * Check if Tailscale is running
35
+ */
36
+ async isRunning() {
37
+ try {
38
+ const result = await this.execCommand("tailscale", ["status", "--json"]);
39
+ if (!result.success)
40
+ return false;
41
+ const status = JSON.parse(result.stdout);
42
+ return status?.Self?.Online || false;
43
+ }
44
+ catch {
45
+ return false;
46
+ }
47
+ }
48
+ /**
49
+ * Get Tailscale status
50
+ */
51
+ async getStatus() {
52
+ const installed = await this.isInstalled();
53
+ const running = installed ? await this.isRunning() : false;
54
+ if (running) {
55
+ try {
56
+ const result = await this.execCommand("tailscale", ["status", "--json"]);
57
+ const status = JSON.parse(result.stdout);
58
+ return {
59
+ installed,
60
+ running,
61
+ connected: status?.Self?.Online || false,
62
+ myIP: status?.Self?.TailscaleIPs?.[0],
63
+ };
64
+ }
65
+ catch {
66
+ return { installed, running, connected: false };
67
+ }
68
+ }
69
+ return { installed, running, connected: false };
70
+ }
71
+ /**
72
+ * Start Tailscale daemon
73
+ */
74
+ async start() {
75
+ const status = await this.getStatus();
76
+ if (!status.installed) {
77
+ console.log("⚠️ Tailscale not installed");
78
+ console.log(" Install: https://tailscale.com/download");
79
+ return false;
80
+ }
81
+ if (status.running) {
82
+ console.log("✅ Tailscale already running");
83
+ this.emit("ready", { ip: status.myIP });
84
+ return true;
85
+ }
86
+ console.log("🚀 Starting Tailscale...");
87
+ let result;
88
+ if (process.platform === "darwin") {
89
+ result = await this.execCommand("sudo", ["tailscaled", "--daemon"]);
90
+ }
91
+ else if (process.platform === "linux") {
92
+ result = await this.execCommand("sudo", ["systemctl", "start", "tailscaled"]);
93
+ }
94
+ else {
95
+ console.log("⚠️ Windows not yet supported for auto-start");
96
+ return false;
97
+ }
98
+ if (result.success) {
99
+ await new Promise((resolve) => setTimeout(resolve, 2000));
100
+ const newStatus = await this.getStatus();
101
+ if (newStatus.running) {
102
+ console.log(`✅ Tailscale ready: ${newStatus.myIP}`);
103
+ this.emit("ready", { ip: newStatus.myIP });
104
+ return true;
105
+ }
106
+ }
107
+ return false;
108
+ }
109
+ /**
110
+ * Get my Tailscale IP
111
+ */
112
+ async getMyIP() {
113
+ const result = await this.execCommand("tailscale", ["ip", "-4"]);
114
+ if (result.success) {
115
+ return result.stdout.trim();
116
+ }
117
+ return undefined;
118
+ }
119
+ /**
120
+ * Generate gateway URL for this device
121
+ */
122
+ async getGatewayURL(port = 7777) {
123
+ const ip = await this.getMyIP();
124
+ if (!ip)
125
+ return undefined;
126
+ return `wss://${ip}:${port}`;
127
+ }
128
+ /**
129
+ * Execute command helper
130
+ */
131
+ execCommand(cmd, args) {
132
+ return new Promise((resolve) => {
133
+ const child = spawn(cmd, args, {
134
+ stdio: ["ignore", "pipe", "pipe"],
135
+ });
136
+ let stdout = "";
137
+ let stderr = "";
138
+ child.stdout?.on("data", (data) => {
139
+ stdout += data.toString();
140
+ });
141
+ child.stderr?.on("data", (data) => {
142
+ stderr += data.toString();
143
+ });
144
+ child.on("exit", (code) => {
145
+ resolve({ success: code === 0, stdout, stderr });
146
+ });
147
+ child.on("error", () => {
148
+ resolve({ success: false, stdout, stderr });
149
+ });
150
+ });
151
+ }
152
+ }
153
+ // Singleton
154
+ let instance = null;
155
+ export function getTailscaleIntegration(config) {
156
+ if (!instance) {
157
+ instance = new TailscaleIntegration(config);
158
+ }
159
+ return instance;
160
+ }
161
+ export { TailscaleIntegration };
162
+ export default TailscaleIntegration;
163
+ //# sourceMappingURL=tailscale.js.map