mirror of
https://github.com/samsonjs/vibetunnel.git
synced 2026-04-23 14:15:54 +00:00
- Remove registration retry from HQClient, let caller handle retries - Make HQClient destroy method async and await unregister - Remove session ID namespacing - UUIDs are unique enough - Add /api/health endpoint for cheaper health checks - Remove unnecessary RemoteServer.status field - Track sessions by remote using sessionIds Set - Fix remote session creation to use remote's token (not HQ's auth) - Update session proxy to lookup remote by session ID - Make cleanup-exited work across all remotes - Remove tty_fwd_path code - always use node-pty - Fix duplicate HQ endpoints - Improve health check to try /api/health first, fall back to /api/sessions - Remove offline remotes automatically on failed health check BREAKING CHANGES: - HQClient.destroy() is now async - RemoteServer no longer has status field - Session IDs are no longer namespaced with remoteId prefix 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
72 lines
2.2 KiB
TypeScript
72 lines
2.2 KiB
TypeScript
import { v4 as uuidv4 } from 'uuid';
|
|
|
|
export class HQClient {
|
|
private readonly hqUrl: string;
|
|
private readonly remoteId: string;
|
|
private readonly remoteName: string;
|
|
private readonly token: string;
|
|
private readonly hqUsername: string;
|
|
private readonly hqPassword: string;
|
|
|
|
constructor(hqUrl: string, hqUsername: string, hqPassword: string, remoteName: string) {
|
|
this.hqUrl = hqUrl;
|
|
this.remoteId = uuidv4();
|
|
this.remoteName = remoteName;
|
|
this.token = uuidv4();
|
|
this.hqUsername = hqUsername;
|
|
this.hqPassword = hqPassword;
|
|
}
|
|
|
|
async register(): Promise<void> {
|
|
try {
|
|
const response = await fetch(`${this.hqUrl}/api/remotes/register`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
Authorization: `Basic ${Buffer.from(`${this.hqUsername}:${this.hqPassword}`).toString('base64')}`,
|
|
},
|
|
body: JSON.stringify({
|
|
id: this.remoteId,
|
|
name: this.remoteName,
|
|
url: `http://localhost:${process.env.PORT || 4020}`,
|
|
token: this.token, // Token for HQ to authenticate with this remote
|
|
}),
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const errorBody = await response.json().catch(() => ({ error: response.statusText }));
|
|
throw new Error(`Registration failed: ${errorBody.error || response.statusText}`);
|
|
}
|
|
|
|
console.log(`Successfully registered with HQ at ${this.hqUrl}`);
|
|
console.log(`Remote ID: ${this.remoteId}`);
|
|
console.log(`Remote name: ${this.remoteName}`);
|
|
console.log(`Token: ${this.token}`);
|
|
} catch (error) {
|
|
console.error('Failed to register with HQ:', error);
|
|
throw error; // Let the caller handle retries if needed
|
|
}
|
|
}
|
|
|
|
async destroy(): Promise<void> {
|
|
try {
|
|
// Try to unregister
|
|
await fetch(`${this.hqUrl}/api/remotes/${this.remoteId}`, {
|
|
method: 'DELETE',
|
|
headers: {
|
|
Authorization: `Basic ${Buffer.from(`${this.hqUsername}:${this.hqPassword}`).toString('base64')}`,
|
|
},
|
|
});
|
|
} catch {
|
|
// Ignore errors during shutdown
|
|
}
|
|
}
|
|
|
|
getRemoteId(): string {
|
|
return this.remoteId;
|
|
}
|
|
|
|
getToken(): string {
|
|
return this.token;
|
|
}
|
|
}
|