@tjamescouch/agentchat 0.22.1 → 0.23.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.
Files changed (153) hide show
  1. package/Dockerfile +1 -1
  2. package/dist/bin/agentchat.d.ts +7 -0
  3. package/dist/bin/agentchat.d.ts.map +1 -0
  4. package/dist/bin/agentchat.js +1511 -0
  5. package/dist/bin/agentchat.js.map +1 -0
  6. package/dist/lib/allowlist.d.ts +77 -0
  7. package/dist/lib/allowlist.d.ts.map +1 -0
  8. package/dist/lib/allowlist.js +151 -0
  9. package/dist/lib/allowlist.js.map +1 -0
  10. package/dist/lib/client.d.ts +147 -0
  11. package/dist/lib/client.d.ts.map +1 -0
  12. package/dist/lib/client.js +704 -0
  13. package/dist/lib/client.js.map +1 -0
  14. package/dist/lib/daemon.d.ts +122 -0
  15. package/dist/lib/daemon.d.ts.map +1 -0
  16. package/dist/lib/daemon.js +523 -0
  17. package/dist/lib/daemon.js.map +1 -0
  18. package/dist/lib/deploy/akash.d.ts +271 -0
  19. package/dist/lib/deploy/akash.d.ts.map +1 -0
  20. package/dist/lib/deploy/akash.js +671 -0
  21. package/dist/lib/deploy/akash.js.map +1 -0
  22. package/dist/lib/deploy/config.d.ts +62 -0
  23. package/dist/lib/deploy/config.d.ts.map +1 -0
  24. package/dist/lib/deploy/config.js +116 -0
  25. package/dist/lib/deploy/config.js.map +1 -0
  26. package/dist/lib/deploy/docker.d.ts +37 -0
  27. package/dist/lib/deploy/docker.d.ts.map +1 -0
  28. package/dist/lib/deploy/docker.js +122 -0
  29. package/dist/lib/deploy/docker.js.map +1 -0
  30. package/dist/lib/deploy/index.d.ts +11 -0
  31. package/dist/lib/deploy/index.d.ts.map +1 -0
  32. package/dist/lib/deploy/index.js +11 -0
  33. package/dist/lib/deploy/index.js.map +1 -0
  34. package/dist/lib/escrow-hooks.d.ts +199 -0
  35. package/dist/lib/escrow-hooks.d.ts.map +1 -0
  36. package/dist/lib/escrow-hooks.js +221 -0
  37. package/dist/lib/escrow-hooks.js.map +1 -0
  38. package/dist/lib/identity.d.ts +134 -0
  39. package/dist/lib/identity.d.ts.map +1 -0
  40. package/dist/lib/identity.js +334 -0
  41. package/dist/lib/identity.js.map +1 -0
  42. package/dist/lib/jitter.d.ts +42 -0
  43. package/dist/lib/jitter.d.ts.map +1 -0
  44. package/{lib/jitter.ts → dist/lib/jitter.js} +10 -18
  45. package/dist/lib/jitter.js.map +1 -0
  46. package/dist/lib/proposals.d.ts +223 -0
  47. package/dist/lib/proposals.d.ts.map +1 -0
  48. package/dist/lib/proposals.js +379 -0
  49. package/dist/lib/proposals.js.map +1 -0
  50. package/dist/lib/protocol.d.ts +220 -0
  51. package/dist/lib/protocol.d.ts.map +1 -0
  52. package/dist/lib/protocol.js +507 -0
  53. package/dist/lib/protocol.js.map +1 -0
  54. package/dist/lib/receipts.d.ts +134 -0
  55. package/dist/lib/receipts.d.ts.map +1 -0
  56. package/dist/lib/receipts.js +270 -0
  57. package/dist/lib/receipts.js.map +1 -0
  58. package/dist/lib/reputation.d.ts +250 -0
  59. package/dist/lib/reputation.d.ts.map +1 -0
  60. package/dist/lib/reputation.js +586 -0
  61. package/dist/lib/reputation.js.map +1 -0
  62. package/dist/lib/security.d.ts +27 -0
  63. package/dist/lib/security.d.ts.map +1 -0
  64. package/dist/lib/security.js +150 -0
  65. package/dist/lib/security.js.map +1 -0
  66. package/dist/lib/server/handlers/admin.d.ts +26 -0
  67. package/dist/lib/server/handlers/admin.d.ts.map +1 -0
  68. package/dist/lib/server/handlers/admin.js +76 -0
  69. package/dist/lib/server/handlers/admin.js.map +1 -0
  70. package/dist/lib/server/handlers/identity.d.ts +36 -0
  71. package/dist/lib/server/handlers/identity.d.ts.map +1 -0
  72. package/dist/lib/server/handlers/identity.js +330 -0
  73. package/dist/lib/server/handlers/identity.js.map +1 -0
  74. package/dist/lib/server/handlers/index.d.ts +10 -0
  75. package/dist/lib/server/handlers/index.d.ts.map +1 -0
  76. package/dist/lib/server/handlers/index.js +15 -0
  77. package/dist/lib/server/handlers/index.js.map +1 -0
  78. package/dist/lib/server/handlers/message.d.ts +47 -0
  79. package/dist/lib/server/handlers/message.d.ts.map +1 -0
  80. package/dist/lib/server/handlers/message.js +265 -0
  81. package/dist/lib/server/handlers/message.js.map +1 -0
  82. package/dist/lib/server/handlers/presence.d.ts +18 -0
  83. package/dist/lib/server/handlers/presence.d.ts.map +1 -0
  84. package/dist/lib/server/handlers/presence.js +35 -0
  85. package/dist/lib/server/handlers/presence.js.map +1 -0
  86. package/dist/lib/server/handlers/proposal.d.ts +38 -0
  87. package/dist/lib/server/handlers/proposal.d.ts.map +1 -0
  88. package/dist/lib/server/handlers/proposal.js +273 -0
  89. package/dist/lib/server/handlers/proposal.js.map +1 -0
  90. package/dist/lib/server/handlers/skills.d.ts +22 -0
  91. package/dist/lib/server/handlers/skills.d.ts.map +1 -0
  92. package/dist/lib/server/handlers/skills.js +119 -0
  93. package/dist/lib/server/handlers/skills.js.map +1 -0
  94. package/dist/lib/server-directory.d.ts +85 -0
  95. package/dist/lib/server-directory.d.ts.map +1 -0
  96. package/dist/lib/server-directory.js +177 -0
  97. package/dist/lib/server-directory.js.map +1 -0
  98. package/dist/lib/server.d.ts +162 -0
  99. package/dist/lib/server.d.ts.map +1 -0
  100. package/dist/lib/server.js +602 -0
  101. package/dist/lib/server.js.map +1 -0
  102. package/dist/lib/types.d.ts +461 -0
  103. package/dist/lib/types.d.ts.map +1 -0
  104. package/dist/lib/types.js +98 -0
  105. package/dist/lib/types.js.map +1 -0
  106. package/package.json +22 -13
  107. package/bin/agentchat.js +0 -1617
  108. package/bin/agentchat.ts +0 -1812
  109. package/lib/allowlist.js +0 -162
  110. package/lib/chat.py +0 -241
  111. package/lib/client.js +0 -821
  112. package/lib/client.ts +0 -877
  113. package/lib/daemon.js +0 -562
  114. package/lib/daemon.ts +0 -662
  115. package/lib/deploy/akash.js +0 -811
  116. package/lib/deploy/config.js +0 -128
  117. package/lib/deploy/docker.js +0 -132
  118. package/lib/deploy/index.js +0 -24
  119. package/lib/elo_swarm.py +0 -569
  120. package/lib/escrow-hooks.js +0 -237
  121. package/lib/escrow-hooks.ts +0 -391
  122. package/lib/identity.js +0 -376
  123. package/lib/identity.ts +0 -412
  124. package/lib/jitter.js +0 -54
  125. package/lib/proposals.js +0 -426
  126. package/lib/proposals.ts +0 -612
  127. package/lib/protocol.js +0 -516
  128. package/lib/receipts.js +0 -294
  129. package/lib/receipts.ts +0 -359
  130. package/lib/reputation.js +0 -664
  131. package/lib/reputation.ts +0 -790
  132. package/lib/security.js +0 -183
  133. package/lib/server/handlers/admin.js +0 -94
  134. package/lib/server/handlers/identity.js +0 -258
  135. package/lib/server/handlers/index.js +0 -42
  136. package/lib/server/handlers/message.js +0 -319
  137. package/lib/server/handlers/presence.js +0 -45
  138. package/lib/server/handlers/proposal.js +0 -358
  139. package/lib/server/handlers/skills.js +0 -141
  140. package/lib/server-directory.js +0 -190
  141. package/lib/server-directory.ts +0 -232
  142. package/lib/server.js +0 -633
  143. package/lib/server.ts +0 -698
  144. package/lib/supervisor/USAGE.md +0 -110
  145. package/lib/supervisor/agent-health.sh +0 -107
  146. package/lib/supervisor/agent-monitor.sh +0 -123
  147. package/lib/supervisor/agent-supervisor.sh +0 -135
  148. package/lib/supervisor/agentctl.sh +0 -266
  149. package/lib/supervisor/god-backup.sh +0 -126
  150. package/lib/supervisor/god-watchdog.sh +0 -107
  151. package/lib/supervisor/killswitch.sh +0 -43
  152. package/lib/supervisor/notify.sh +0 -19
  153. package/lib/types.ts +0 -433
@@ -1,141 +0,0 @@
1
- /**
2
- * Skills Handlers
3
- * Handles skill registration and search
4
- */
5
-
6
- import {
7
- ServerMessageType,
8
- ErrorCode,
9
- createMessage,
10
- createError,
11
- } from '../../protocol.js';
12
-
13
- /**
14
- * Handle REGISTER_SKILLS command
15
- */
16
- export function handleRegisterSkills(server, ws, msg) {
17
- const agent = server.agents.get(ws);
18
- if (!agent) {
19
- server._send(ws, createError(ErrorCode.AUTH_REQUIRED, 'Must IDENTIFY first'));
20
- return;
21
- }
22
-
23
- if (!agent.pubkey) {
24
- server._send(ws, createError(ErrorCode.SIGNATURE_REQUIRED, 'Skill registration requires persistent identity'));
25
- return;
26
- }
27
-
28
- // Store skills for this agent
29
- const registration = {
30
- agent_id: `@${agent.id}`,
31
- skills: msg.skills,
32
- registered_at: Date.now(),
33
- sig: msg.sig
34
- };
35
-
36
- server.skillsRegistry.set(agent.id, registration);
37
-
38
- server._log('skills_registered', { agent: agent.id, count: msg.skills.length });
39
-
40
- // Notify the registering agent
41
- server._send(ws, createMessage(ServerMessageType.SKILLS_REGISTERED, {
42
- agent_id: `@${agent.id}`,
43
- skills_count: msg.skills.length,
44
- registered_at: registration.registered_at
45
- }));
46
-
47
- // Optionally broadcast to #discovery channel if it exists
48
- if (server.channels.has('#discovery')) {
49
- server._broadcast('#discovery', createMessage(ServerMessageType.MSG, {
50
- from: '@server',
51
- to: '#discovery',
52
- content: `Agent @${agent.id} registered ${msg.skills.length} skill(s): ${msg.skills.map(s => s.capability).join(', ')}`
53
- }));
54
- }
55
- }
56
-
57
- /**
58
- * Handle SEARCH_SKILLS command
59
- */
60
- export async function handleSearchSkills(server, ws, msg) {
61
- const agent = server.agents.get(ws);
62
- if (!agent) {
63
- server._send(ws, createError(ErrorCode.AUTH_REQUIRED, 'Must IDENTIFY first'));
64
- return;
65
- }
66
-
67
- const query = msg.query || {};
68
- const results = [];
69
-
70
- // Search through all registered skills
71
- for (const [agentId, registration] of server.skillsRegistry) {
72
- for (const skill of registration.skills) {
73
- let matches = true;
74
-
75
- // Filter by capability (substring match, case-insensitive)
76
- if (query.capability) {
77
- const cap = skill.capability.toLowerCase();
78
- const search = query.capability.toLowerCase();
79
- if (!cap.includes(search)) {
80
- matches = false;
81
- }
82
- }
83
-
84
- // Filter by max_rate
85
- if (query.max_rate !== undefined && skill.rate !== undefined) {
86
- if (skill.rate > query.max_rate) {
87
- matches = false;
88
- }
89
- }
90
-
91
- // Filter by currency
92
- if (query.currency && skill.currency) {
93
- if (skill.currency.toLowerCase() !== query.currency.toLowerCase()) {
94
- matches = false;
95
- }
96
- }
97
-
98
- if (matches) {
99
- results.push({
100
- agent_id: registration.agent_id,
101
- ...skill,
102
- registered_at: registration.registered_at
103
- });
104
- }
105
- }
106
- }
107
-
108
- // Enrich results with reputation data
109
- const uniqueAgentIds = [...new Set(results.map(r => r.agent_id))];
110
- const ratingCache = new Map();
111
- for (const agentId of uniqueAgentIds) {
112
- const ratingInfo = await server.reputationStore.getRating(agentId);
113
- ratingCache.set(agentId, ratingInfo);
114
- }
115
-
116
- // Add rating info to each result
117
- for (const result of results) {
118
- const ratingInfo = ratingCache.get(result.agent_id);
119
- result.rating = ratingInfo.rating;
120
- result.transactions = ratingInfo.transactions;
121
- }
122
-
123
- // Sort by rating (highest first), then by registration time
124
- results.sort((a, b) => {
125
- if (b.rating !== a.rating) return b.rating - a.rating;
126
- return b.registered_at - a.registered_at;
127
- });
128
-
129
- // Limit results
130
- const limit = query.limit || 50;
131
- const limitedResults = results.slice(0, limit);
132
-
133
- server._log('skills_search', { agent: agent.id, query, results_count: limitedResults.length });
134
-
135
- server._send(ws, createMessage(ServerMessageType.SEARCH_RESULTS, {
136
- query_id: msg.query_id || null,
137
- query,
138
- results: limitedResults,
139
- total: results.length
140
- }));
141
- }
@@ -1,190 +0,0 @@
1
- /**
2
- * Server Directory
3
- * Registry of known AgentChat servers for discovery
4
- */
5
-
6
- import http from 'http';
7
- import https from 'https';
8
- import fs from 'fs/promises';
9
- import path from 'path';
10
-
11
- // Default public servers (can be extended)
12
- export const DEFAULT_SERVERS = process.env.AGENTCHAT_PUBLIC === 'true'
13
- ? [
14
- {
15
- name: 'AgentChat Public',
16
- url: 'wss://agentchat-server.fly.dev',
17
- description: 'Official public AgentChat server',
18
- region: 'global'
19
- }
20
- ]
21
- : [
22
- {
23
- name: 'AgentChat Local',
24
- url: 'ws://localhost:6667',
25
- description: 'Local AgentChat server',
26
- region: 'local'
27
- }
28
- ];
29
-
30
- // Default directory file path
31
- export const DEFAULT_DIRECTORY_PATH = path.join(
32
- process.env.HOME || process.env.USERPROFILE || '.',
33
- '.agentchat',
34
- 'servers.json'
35
- );
36
-
37
- /**
38
- * Server Directory for discovering AgentChat servers
39
- */
40
- export class ServerDirectory {
41
- constructor(options = {}) {
42
- this.directoryPath = options.directoryPath || DEFAULT_DIRECTORY_PATH;
43
- this.servers = [...DEFAULT_SERVERS];
44
- this.timeout = options.timeout || 5000;
45
- }
46
-
47
- /**
48
- * Load servers from directory file
49
- */
50
- async load() {
51
- try {
52
- const data = await fs.readFile(this.directoryPath, 'utf8');
53
- const loaded = JSON.parse(data);
54
- if (Array.isArray(loaded.servers)) {
55
- // Merge with defaults, avoiding duplicates by URL
56
- const urls = new Set(this.servers.map(s => s.url));
57
- for (const server of loaded.servers) {
58
- if (!urls.has(server.url)) {
59
- this.servers.push(server);
60
- urls.add(server.url);
61
- }
62
- }
63
- }
64
- } catch (err) {
65
- // File doesn't exist or is invalid, use defaults
66
- }
67
- return this;
68
- }
69
-
70
- /**
71
- * Save servers to directory file
72
- */
73
- async save() {
74
- const dir = path.dirname(this.directoryPath);
75
- await fs.mkdir(dir, { recursive: true });
76
- await fs.writeFile(this.directoryPath, JSON.stringify({
77
- version: 1,
78
- updated_at: new Date().toISOString(),
79
- servers: this.servers
80
- }, null, 2));
81
- }
82
-
83
- /**
84
- * Add a server to the directory
85
- */
86
- async addServer(server) {
87
- const existing = this.servers.find(s => s.url === server.url);
88
- if (existing) {
89
- Object.assign(existing, server);
90
- } else {
91
- this.servers.push(server);
92
- }
93
- await this.save();
94
- }
95
-
96
- /**
97
- * Remove a server from the directory
98
- */
99
- async removeServer(url) {
100
- this.servers = this.servers.filter(s => s.url !== url);
101
- await this.save();
102
- }
103
-
104
- /**
105
- * Check health of a single server
106
- * @param {Object} server - Server object with url
107
- * @returns {Object} Server with health status
108
- */
109
- async checkHealth(server) {
110
- const wsUrl = server.url;
111
- // Convert ws:// or wss:// to http:// or https://
112
- const httpUrl = wsUrl
113
- .replace('wss://', 'https://')
114
- .replace('ws://', 'http://');
115
-
116
- try {
117
- const health = await this._fetchHealth(httpUrl + '/health');
118
- return {
119
- ...server,
120
- status: 'online',
121
- health,
122
- checked_at: new Date().toISOString()
123
- };
124
- } catch (err) {
125
- return {
126
- ...server,
127
- status: 'offline',
128
- error: err.message || err.code || 'Unknown error',
129
- checked_at: new Date().toISOString()
130
- };
131
- }
132
- }
133
-
134
- /**
135
- * Fetch health endpoint
136
- */
137
- _fetchHealth(url) {
138
- return new Promise((resolve, reject) => {
139
- const protocol = url.startsWith('https') ? https : http;
140
- const req = protocol.get(url, { timeout: this.timeout }, (res) => {
141
- let data = '';
142
- res.on('data', chunk => data += chunk);
143
- res.on('end', () => {
144
- if (res.statusCode === 200) {
145
- try {
146
- resolve(JSON.parse(data));
147
- } catch {
148
- reject(new Error('Invalid health response'));
149
- }
150
- } else {
151
- reject(new Error(`HTTP ${res.statusCode}`));
152
- }
153
- });
154
- });
155
-
156
- req.on('error', reject);
157
- req.on('timeout', () => {
158
- req.destroy();
159
- reject(new Error('Timeout'));
160
- });
161
- });
162
- }
163
-
164
- /**
165
- * Discover available servers (check health of all known servers)
166
- * @param {Object} options
167
- * @param {boolean} options.onlineOnly - Only return online servers
168
- * @returns {Array} List of servers with status
169
- */
170
- async discover(options = {}) {
171
- const results = await Promise.all(
172
- this.servers.map(server => this.checkHealth(server))
173
- );
174
-
175
- if (options.onlineOnly) {
176
- return results.filter(s => s.status === 'online');
177
- }
178
-
179
- return results;
180
- }
181
-
182
- /**
183
- * Get list of known servers without health check
184
- */
185
- list() {
186
- return [...this.servers];
187
- }
188
- }
189
-
190
- export default ServerDirectory;
@@ -1,232 +0,0 @@
1
- /**
2
- * Server Directory
3
- * Registry of known AgentChat servers for discovery
4
- */
5
-
6
- import http from 'http';
7
- import https from 'https';
8
- import fs from 'fs/promises';
9
- import path from 'path';
10
-
11
- export interface ServerEntry {
12
- name: string;
13
- url: string;
14
- description?: string;
15
- region?: string;
16
- }
17
-
18
- export interface HealthData {
19
- agents?: {
20
- connected?: number;
21
- };
22
- uptime_seconds?: number;
23
- [key: string]: unknown;
24
- }
25
-
26
- export interface ServerWithStatus extends ServerEntry {
27
- status: 'online' | 'offline' | 'unknown';
28
- health?: HealthData;
29
- error?: string;
30
- checked_at?: string;
31
- }
32
-
33
- export interface ServerDirectoryFile {
34
- version: number;
35
- updated_at: string;
36
- servers: ServerEntry[];
37
- }
38
-
39
- export interface ServerDirectoryOptions {
40
- directoryPath?: string;
41
- timeout?: number;
42
- }
43
-
44
- export interface DiscoverOptions {
45
- onlineOnly?: boolean;
46
- }
47
-
48
- // Default public servers (can be extended)
49
- export const DEFAULT_SERVERS: ServerEntry[] = process.env.AGENTCHAT_PUBLIC === 'true'
50
- ? [
51
- {
52
- name: 'AgentChat Public',
53
- url: 'wss://agentchat-server.fly.dev',
54
- description: 'Official public AgentChat server',
55
- region: 'global'
56
- }
57
- ]
58
- : [
59
- {
60
- name: 'AgentChat Local',
61
- url: 'ws://localhost:6667',
62
- description: 'Local AgentChat server',
63
- region: 'local'
64
- }
65
- ];
66
-
67
- // Default directory file path
68
- export const DEFAULT_DIRECTORY_PATH: string = path.join(
69
- process.env.HOME || process.env.USERPROFILE || '.',
70
- '.agentchat',
71
- 'servers.json'
72
- );
73
-
74
- /**
75
- * Server Directory for discovering AgentChat servers
76
- */
77
- export class ServerDirectory {
78
- private directoryPath: string;
79
- private servers: ServerEntry[];
80
- private timeout: number;
81
-
82
- constructor(options: ServerDirectoryOptions = {}) {
83
- this.directoryPath = options.directoryPath || DEFAULT_DIRECTORY_PATH;
84
- this.servers = [...DEFAULT_SERVERS];
85
- this.timeout = options.timeout || 5000;
86
- }
87
-
88
- /**
89
- * Load servers from directory file
90
- */
91
- async load(): Promise<this> {
92
- try {
93
- const data = await fs.readFile(this.directoryPath, 'utf8');
94
- const loaded = JSON.parse(data) as ServerDirectoryFile;
95
- if (Array.isArray(loaded.servers)) {
96
- // Merge with defaults, avoiding duplicates by URL
97
- const urls = new Set(this.servers.map(s => s.url));
98
- for (const server of loaded.servers) {
99
- if (!urls.has(server.url)) {
100
- this.servers.push(server);
101
- urls.add(server.url);
102
- }
103
- }
104
- }
105
- } catch {
106
- // File doesn't exist or is invalid, use defaults
107
- }
108
- return this;
109
- }
110
-
111
- /**
112
- * Save servers to directory file
113
- */
114
- async save(): Promise<void> {
115
- const dir = path.dirname(this.directoryPath);
116
- await fs.mkdir(dir, { recursive: true });
117
- await fs.writeFile(this.directoryPath, JSON.stringify({
118
- version: 1,
119
- updated_at: new Date().toISOString(),
120
- servers: this.servers
121
- } as ServerDirectoryFile, null, 2));
122
- }
123
-
124
- /**
125
- * Add a server to the directory
126
- */
127
- async addServer(server: ServerEntry): Promise<void> {
128
- const existing = this.servers.find(s => s.url === server.url);
129
- if (existing) {
130
- Object.assign(existing, server);
131
- } else {
132
- this.servers.push(server);
133
- }
134
- await this.save();
135
- }
136
-
137
- /**
138
- * Remove a server from the directory
139
- */
140
- async removeServer(url: string): Promise<void> {
141
- this.servers = this.servers.filter(s => s.url !== url);
142
- await this.save();
143
- }
144
-
145
- /**
146
- * Check health of a single server
147
- * @param server - Server object with url
148
- * @returns Server with health status
149
- */
150
- async checkHealth(server: ServerEntry): Promise<ServerWithStatus> {
151
- const wsUrl = server.url;
152
- // Convert ws:// or wss:// to http:// or https://
153
- const httpUrl = wsUrl
154
- .replace('wss://', 'https://')
155
- .replace('ws://', 'http://');
156
-
157
- try {
158
- const health = await this._fetchHealth(httpUrl + '/health');
159
- return {
160
- ...server,
161
- status: 'online',
162
- health,
163
- checked_at: new Date().toISOString()
164
- };
165
- } catch (err) {
166
- const error = err as Error & { code?: string };
167
- return {
168
- ...server,
169
- status: 'offline',
170
- error: error.message || error.code || 'Unknown error',
171
- checked_at: new Date().toISOString()
172
- };
173
- }
174
- }
175
-
176
- /**
177
- * Fetch health endpoint
178
- */
179
- private _fetchHealth(url: string): Promise<HealthData> {
180
- return new Promise((resolve, reject) => {
181
- const protocol = url.startsWith('https') ? https : http;
182
- const req = protocol.get(url, { timeout: this.timeout }, (res) => {
183
- let data = '';
184
- res.on('data', (chunk: Buffer) => data += chunk);
185
- res.on('end', () => {
186
- if (res.statusCode === 200) {
187
- try {
188
- resolve(JSON.parse(data) as HealthData);
189
- } catch {
190
- reject(new Error('Invalid health response'));
191
- }
192
- } else {
193
- reject(new Error(`HTTP ${res.statusCode}`));
194
- }
195
- });
196
- });
197
-
198
- req.on('error', reject);
199
- req.on('timeout', () => {
200
- req.destroy();
201
- reject(new Error('Timeout'));
202
- });
203
- });
204
- }
205
-
206
- /**
207
- * Discover available servers (check health of all known servers)
208
- * @param options
209
- * @param options.onlineOnly - Only return online servers
210
- * @returns List of servers with status
211
- */
212
- async discover(options: DiscoverOptions = {}): Promise<ServerWithStatus[]> {
213
- const results = await Promise.all(
214
- this.servers.map(server => this.checkHealth(server))
215
- );
216
-
217
- if (options.onlineOnly) {
218
- return results.filter(s => s.status === 'online');
219
- }
220
-
221
- return results;
222
- }
223
-
224
- /**
225
- * Get list of known servers without health check
226
- */
227
- list(): ServerEntry[] {
228
- return [...this.servers];
229
- }
230
- }
231
-
232
- export default ServerDirectory;