@xopcai/xopcbot 0.1.2 → 0.1.5

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 (101) hide show
  1. package/README.md +53 -48
  2. package/README.zh-CN.md +52 -45
  3. package/dist/__tests__/core.test.js +2 -2
  4. package/dist/__tests__/core.test.js.map +1 -1
  5. package/dist/agent/service.js +121 -35
  6. package/dist/agent/service.js.map +1 -1
  7. package/dist/agent/skills/test-framework.js +3 -3
  8. package/dist/agent/skills/test-framework.js.map +1 -1
  9. package/dist/channels/telegram/command-handler.d.ts +3 -0
  10. package/dist/channels/telegram/command-handler.js +69 -0
  11. package/dist/channels/telegram/command-handler.js.map +1 -1
  12. package/dist/channels/telegram/plugin.js +26 -1
  13. package/dist/channels/telegram/plugin.js.map +1 -1
  14. package/dist/cli/commands/config.js +59 -0
  15. package/dist/cli/commands/config.js.map +1 -1
  16. package/dist/cli/commands/configure.js +1 -3
  17. package/dist/cli/commands/configure.js.map +1 -1
  18. package/dist/cli/commands/gateway-daemon.d.ts +1 -0
  19. package/dist/cli/commands/gateway-daemon.js +141 -0
  20. package/dist/cli/commands/gateway-daemon.js.map +1 -0
  21. package/dist/cli/commands/gateway.js +387 -45
  22. package/dist/cli/commands/gateway.js.map +1 -1
  23. package/dist/cli/commands/onboard.js +284 -50
  24. package/dist/cli/commands/onboard.js.map +1 -1
  25. package/dist/cli/commands/setup.d.ts +1 -0
  26. package/dist/cli/commands/setup.js +123 -0
  27. package/dist/cli/commands/setup.js.map +1 -0
  28. package/dist/cli/index.d.ts +1 -1
  29. package/dist/cli/index.js +18 -6
  30. package/dist/cli/index.js.map +1 -1
  31. package/dist/config/schema.d.ts +22 -0
  32. package/dist/config/schema.js +22 -6
  33. package/dist/config/schema.js.map +1 -1
  34. package/dist/daemon/background-start.d.ts +33 -0
  35. package/dist/daemon/background-start.js +89 -0
  36. package/dist/daemon/background-start.js.map +1 -0
  37. package/dist/daemon/gateway-manager.d.ts +87 -0
  38. package/dist/daemon/gateway-manager.js +324 -0
  39. package/dist/daemon/gateway-manager.js.map +1 -0
  40. package/dist/daemon/index.d.ts +5 -0
  41. package/dist/daemon/index.js +6 -0
  42. package/dist/daemon/index.js.map +1 -0
  43. package/dist/gateway/__tests__/auth.test.d.ts +1 -0
  44. package/dist/gateway/__tests__/auth.test.js +128 -0
  45. package/dist/gateway/__tests__/auth.test.js.map +1 -0
  46. package/dist/gateway/__tests__/process-manager.test.d.ts +4 -0
  47. package/dist/gateway/__tests__/process-manager.test.js +140 -0
  48. package/dist/gateway/__tests__/process-manager.test.js.map +1 -0
  49. package/dist/gateway/auth.d.ts +33 -0
  50. package/dist/gateway/auth.js +94 -0
  51. package/dist/gateway/auth.js.map +1 -0
  52. package/dist/gateway/hono/app.js +61 -20
  53. package/dist/gateway/hono/app.js.map +1 -1
  54. package/dist/gateway/index.d.ts +3 -0
  55. package/dist/gateway/index.js +3 -0
  56. package/dist/gateway/index.js.map +1 -1
  57. package/dist/gateway/pid-file.d.ts +35 -0
  58. package/dist/gateway/pid-file.js +111 -0
  59. package/dist/gateway/pid-file.js.map +1 -0
  60. package/dist/gateway/port-checker.d.ts +24 -0
  61. package/dist/gateway/port-checker.js +132 -0
  62. package/dist/gateway/port-checker.js.map +1 -0
  63. package/dist/gateway/process-manager.d.ts +62 -0
  64. package/dist/gateway/process-manager.js +372 -0
  65. package/dist/gateway/process-manager.js.map +1 -0
  66. package/dist/gateway/process-manager.types.d.ts +64 -0
  67. package/dist/gateway/process-manager.types.js +5 -0
  68. package/dist/gateway/process-manager.types.js.map +1 -0
  69. package/dist/gateway/server.js +3 -1
  70. package/dist/gateway/server.js.map +1 -1
  71. package/dist/gateway/service.d.ts +20 -0
  72. package/dist/gateway/service.js +67 -0
  73. package/dist/gateway/service.js.map +1 -1
  74. package/dist/gateway/static/root/assets/{main-CfIxL-cL.js → main-DevbZW9K.js} +373 -251
  75. package/dist/gateway/static/root/assets/main-DevbZW9K.js.map +1 -0
  76. package/dist/gateway/static/root/assets/{main-DndcTCgX.css → main-DxZg1Nmz.css} +1 -1
  77. package/dist/gateway/static/root/index.html +2 -2
  78. package/dist/providers/auto-discovery.d.ts +0 -45
  79. package/dist/providers/auto-discovery.js +16 -63
  80. package/dist/providers/auto-discovery.js.map +1 -1
  81. package/dist/providers/model-catalog.d.ts +0 -113
  82. package/dist/providers/model-catalog.js +5 -78
  83. package/dist/providers/model-catalog.js.map +1 -1
  84. package/dist/providers/models-dev.d.ts +9 -5
  85. package/dist/providers/models-dev.js +14 -23
  86. package/dist/providers/models-dev.js.map +1 -1
  87. package/dist/providers/provider-catalog.d.ts +6 -88
  88. package/dist/providers/provider-catalog.js +2 -45
  89. package/dist/providers/provider-catalog.js.map +1 -1
  90. package/dist/providers/registry.d.ts +0 -20
  91. package/dist/providers/registry.js +7 -56
  92. package/dist/providers/registry.js.map +1 -1
  93. package/dist/session/store.js +11 -2
  94. package/dist/session/store.js.map +1 -1
  95. package/dist/utils/__tests__/frontmatter.test.d.ts +4 -0
  96. package/dist/utils/__tests__/frontmatter.test.js +149 -0
  97. package/dist/utils/__tests__/frontmatter.test.js.map +1 -0
  98. package/dist/utils/frontmatter.js +2 -2
  99. package/dist/utils/frontmatter.js.map +1 -1
  100. package/package.json +1 -1
  101. package/dist/gateway/static/root/assets/main-CfIxL-cL.js.map +0 -1
@@ -0,0 +1,324 @@
1
+ import { spawn } from 'child_process';
2
+ import { writeFile, mkdir, readFile, unlink } from 'fs/promises';
3
+ import { join } from 'path';
4
+ import { homedir } from 'os';
5
+ import { createLogger } from '../utils/logger.js';
6
+ const log = createLogger('GatewayManager');
7
+ /**
8
+ * Gateway process manager
9
+ * Handles starting, stopping, and monitoring the gateway process
10
+ */
11
+ export class GatewayManager {
12
+ process = null;
13
+ options;
14
+ startTime = 0;
15
+ constructor(options) {
16
+ this.options = options;
17
+ }
18
+ /**
19
+ * Get the PID file path
20
+ */
21
+ getPidFilePath() {
22
+ return join(homedir(), '.xopcbot', 'gateway.pid');
23
+ }
24
+ /**
25
+ * Get the log directory path
26
+ */
27
+ getLogDir() {
28
+ return join(homedir(), '.xopcbot', 'logs');
29
+ }
30
+ /**
31
+ * Resolve the correct script path (handles dist vs src)
32
+ */
33
+ resolveScriptPath() {
34
+ const scriptPath = process.argv[1];
35
+ // If running from dist, use dist path
36
+ if (scriptPath.includes('/dist/')) {
37
+ return scriptPath.replace('/dist/', '/dist/');
38
+ }
39
+ // If running from src, replace src with dist
40
+ if (scriptPath.includes('/src/')) {
41
+ return scriptPath.replace('/src/', '/dist/').replace(/\.ts$/, '.js');
42
+ }
43
+ // Fallback: try to use dist if it exists
44
+ const distPath = scriptPath.replace(/\.ts$/, '.js');
45
+ if (distPath.includes('dist') || scriptPath.endsWith('.js')) {
46
+ return distPath;
47
+ }
48
+ return scriptPath;
49
+ }
50
+ /**
51
+ * Start the gateway process
52
+ */
53
+ async start() {
54
+ if (await this.isRunning()) {
55
+ log.info('Gateway is already running');
56
+ return;
57
+ }
58
+ // Check if another instance is running (from PID file)
59
+ const existingPid = await this.readPidFile();
60
+ if (existingPid) {
61
+ try {
62
+ process.kill(existingPid, 0); // Check if process exists
63
+ log.info({ pid: existingPid }, 'Gateway is already running (found PID file)');
64
+ return;
65
+ }
66
+ catch {
67
+ // Process not running, clean up stale PID file
68
+ await this.removePidFile();
69
+ }
70
+ }
71
+ const logDir = this.getLogDir();
72
+ await mkdir(logDir, { recursive: true });
73
+ // Resolve correct script path (dist vs src)
74
+ const scriptPath = this.resolveScriptPath();
75
+ const args = [
76
+ scriptPath,
77
+ 'gateway',
78
+ '--port', String(this.options.port),
79
+ '--host', this.options.host,
80
+ '--token', this.options.token,
81
+ ];
82
+ log.info({ port: this.options.port, host: this.options.host }, 'Starting gateway process');
83
+ // Platform-specific background process spawning
84
+ if (process.platform === 'win32') {
85
+ this.process = this.spawnWindows(args, logDir);
86
+ }
87
+ else {
88
+ this.process = await this.spawnUnix(args, logDir);
89
+ }
90
+ this.startTime = Date.now();
91
+ // Save PID file
92
+ if (this.process.pid) {
93
+ await this.writePidFile(this.process.pid);
94
+ }
95
+ // Wait for health check
96
+ await this.waitForHealthy(15000);
97
+ log.info('Gateway started successfully');
98
+ }
99
+ /**
100
+ * Spawn process on Unix-like systems (macOS, Linux)
101
+ */
102
+ async spawnUnix(args, logDir) {
103
+ const stdoutLog = join(logDir, 'gateway.log');
104
+ const stderrLog = join(logDir, 'gateway.error.log');
105
+ const child = spawn(process.argv[0], args, {
106
+ detached: true,
107
+ stdio: ['ignore', 'pipe', 'pipe'],
108
+ env: {
109
+ ...process.env,
110
+ XOPCBOT_CONFIG: this.options.configPath,
111
+ XOPCBOT_WORKSPACE: this.options.workspacePath,
112
+ },
113
+ });
114
+ // Pipe output to log files using dynamic import for ES modules
115
+ const fs = await import('node:fs');
116
+ const stdout = fs.createWriteStream(stdoutLog, { flags: 'a' });
117
+ const stderr = fs.createWriteStream(stderrLog, { flags: 'a' });
118
+ child.stdout?.pipe(stdout);
119
+ child.stderr?.pipe(stderr);
120
+ // Handle process exit
121
+ child.on('exit', (code) => {
122
+ log.info({ code }, 'Gateway process exited');
123
+ stdout.end();
124
+ stderr.end();
125
+ this.cleanup();
126
+ });
127
+ // Unref so parent can exit
128
+ child.unref();
129
+ return child;
130
+ }
131
+ /**
132
+ * Spawn process on Windows
133
+ */
134
+ spawnWindows(args, _logDir) {
135
+ // Use start command to spawn detached process on Windows
136
+ const child = spawn('cmd', ['/c', 'start', '/min', process.argv[0], ...args], {
137
+ detached: true,
138
+ windowsHide: true,
139
+ env: {
140
+ ...process.env,
141
+ XOPCBOT_CONFIG: this.options.configPath,
142
+ XOPCBOT_WORKSPACE: this.options.workspacePath,
143
+ },
144
+ });
145
+ child.on('exit', (code) => {
146
+ log.info({ code }, 'Gateway process exited');
147
+ this.cleanup();
148
+ });
149
+ child.unref();
150
+ return child;
151
+ }
152
+ /**
153
+ * Stop the gateway process
154
+ */
155
+ async stop() {
156
+ const pid = await this.readPidFile();
157
+ if (!pid && !this.process) {
158
+ log.info('Gateway is not running');
159
+ return;
160
+ }
161
+ log.info({ pid }, 'Stopping gateway process');
162
+ try {
163
+ // Try to stop via PID file first
164
+ if (pid) {
165
+ process.kill(pid, 'SIGTERM');
166
+ // Wait for graceful shutdown
167
+ await this.waitForExit(pid, 5000);
168
+ // Force kill if still running
169
+ try {
170
+ process.kill(pid, 0);
171
+ process.kill(pid, 'SIGKILL');
172
+ }
173
+ catch {
174
+ // Process already exited
175
+ }
176
+ }
177
+ // Also stop our managed process if different
178
+ if (this.process && this.process.pid !== pid) {
179
+ this.process.kill('SIGTERM');
180
+ await new Promise(r => setTimeout(r, 2000));
181
+ if (!this.process.killed) {
182
+ this.process.kill('SIGKILL');
183
+ }
184
+ }
185
+ }
186
+ catch (err) {
187
+ log.warn({ err }, 'Error stopping gateway process');
188
+ }
189
+ finally {
190
+ await this.cleanup();
191
+ }
192
+ log.info('Gateway stopped');
193
+ }
194
+ /**
195
+ * Restart the gateway process
196
+ */
197
+ async restart() {
198
+ log.info('Restarting gateway');
199
+ await this.stop();
200
+ await new Promise(r => setTimeout(r, 1000));
201
+ await this.start();
202
+ }
203
+ /**
204
+ * Check if gateway is running
205
+ */
206
+ async isRunning() {
207
+ // First check via HTTP health endpoint
208
+ try {
209
+ const response = await fetch(`http://${this.options.host}:${this.options.port}/health`, {
210
+ signal: AbortSignal.timeout(2000),
211
+ });
212
+ if (response.ok) {
213
+ return true;
214
+ }
215
+ }
216
+ catch {
217
+ // Not reachable via HTTP
218
+ }
219
+ // Check PID file
220
+ const pid = await this.readPidFile();
221
+ if (pid) {
222
+ try {
223
+ process.kill(pid, 0);
224
+ return true;
225
+ }
226
+ catch {
227
+ // Process not running, clean up stale PID file
228
+ await this.removePidFile();
229
+ }
230
+ }
231
+ return false;
232
+ }
233
+ /**
234
+ * Get gateway status
235
+ */
236
+ async getStatus() {
237
+ const running = await this.isRunning();
238
+ const pid = await this.readPidFile();
239
+ return {
240
+ running,
241
+ pid: pid || undefined,
242
+ url: `http://${this.options.host}:${this.options.port}`,
243
+ uptime: running && this.startTime ? Date.now() - this.startTime : undefined,
244
+ };
245
+ }
246
+ /**
247
+ * Wait for gateway to become healthy
248
+ */
249
+ async waitForHealthy(timeoutMs) {
250
+ const startTime = Date.now();
251
+ const checkInterval = 500;
252
+ while (Date.now() - startTime < timeoutMs) {
253
+ try {
254
+ const response = await fetch(`http://${this.options.host}:${this.options.port}/health`, {
255
+ signal: AbortSignal.timeout(1000),
256
+ });
257
+ if (response.ok) {
258
+ return;
259
+ }
260
+ }
261
+ catch {
262
+ // Not ready yet
263
+ }
264
+ await new Promise(r => setTimeout(r, checkInterval));
265
+ }
266
+ throw new Error(`Gateway failed to become healthy within ${timeoutMs}ms`);
267
+ }
268
+ /**
269
+ * Wait for process to exit
270
+ */
271
+ async waitForExit(pid, timeoutMs) {
272
+ const startTime = Date.now();
273
+ while (Date.now() - startTime < timeoutMs) {
274
+ try {
275
+ process.kill(pid, 0);
276
+ // Process still running, wait
277
+ await new Promise(r => setTimeout(r, 200));
278
+ }
279
+ catch {
280
+ // Process exited
281
+ return;
282
+ }
283
+ }
284
+ }
285
+ /**
286
+ * Read PID from file
287
+ */
288
+ async readPidFile() {
289
+ try {
290
+ const content = await readFile(this.getPidFilePath(), 'utf-8');
291
+ const pid = parseInt(content.trim(), 10);
292
+ return isNaN(pid) ? null : pid;
293
+ }
294
+ catch {
295
+ return null;
296
+ }
297
+ }
298
+ /**
299
+ * Write PID to file
300
+ */
301
+ async writePidFile(pid) {
302
+ await mkdir(join(homedir(), '.xopcbot'), { recursive: true });
303
+ await writeFile(this.getPidFilePath(), String(pid), 'utf-8');
304
+ }
305
+ /**
306
+ * Remove PID file
307
+ */
308
+ async removePidFile() {
309
+ try {
310
+ await unlink(this.getPidFilePath());
311
+ }
312
+ catch {
313
+ // Ignore errors
314
+ }
315
+ }
316
+ /**
317
+ * Clean up resources
318
+ */
319
+ async cleanup() {
320
+ this.process = null;
321
+ await this.removePidFile();
322
+ }
323
+ }
324
+ //# sourceMappingURL=gateway-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gateway-manager.js","sourceRoot":"","sources":["../../src/daemon/gateway-manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Daemon module - Gateway process management
3
+ */
4
+ export { GatewayManager, type GatewayManagerOptions, type GatewayStatus } from './gateway-manager.js';
5
+ export { startGatewayInBackground, stopBackgroundGateway, restartBackgroundGateway, isGatewayRunning, getGatewayStatus, waitForGatewayHealthy, type BackgroundStartOptions, } from './background-start.js';
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Daemon module - Gateway process management
3
+ */
4
+ export { GatewayManager } from './gateway-manager.js';
5
+ export { startGatewayInBackground, stopBackgroundGateway, restartBackgroundGateway, isGatewayRunning, getGatewayStatus, waitForGatewayHealthy, } from './background-start.js';
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/daemon/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,cAAc,EAAkD,MAAM,sBAAsB,CAAC;AACtG,OAAO,EACL,wBAAwB,EACxB,qBAAqB,EACrB,wBAAwB,EACxB,gBAAgB,EAChB,gBAAgB,EAChB,qBAAqB,GAEtB,MAAM,uBAAuB,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,128 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { resolveGatewayAuth, safeCompare, validateToken, extractToken, assertGatewayAuthConfigured, } from '../auth.js';
3
+ describe('Gateway Auth', () => {
4
+ describe('resolveGatewayAuth', () => {
5
+ it('should use default token mode with auto-generated token', () => {
6
+ const result = resolveGatewayAuth({ authConfig: null });
7
+ expect(result.mode).toBe('token');
8
+ expect(result.token).toBeDefined();
9
+ expect(result.token?.length).toBe(48); // 24 bytes hex = 48 chars
10
+ });
11
+ it('should use token from config', () => {
12
+ const result = resolveGatewayAuth({
13
+ authConfig: { mode: 'token', token: 'test-token-123' },
14
+ });
15
+ expect(result.mode).toBe('token');
16
+ expect(result.token).toBe('test-token-123');
17
+ });
18
+ it('should use token from environment variable', () => {
19
+ const result = resolveGatewayAuth({
20
+ authConfig: { mode: 'token', token: 'config-token' },
21
+ env: { XOPCBOT_GATEWAY_TOKEN: 'env-token' },
22
+ });
23
+ expect(result.mode).toBe('token');
24
+ expect(result.token).toBe('env-token');
25
+ });
26
+ it('should use none mode from config', () => {
27
+ const result = resolveGatewayAuth({
28
+ authConfig: { mode: 'none' },
29
+ });
30
+ expect(result.mode).toBe('none');
31
+ expect(result.token).toBeUndefined();
32
+ });
33
+ it('should use none mode from environment variable', () => {
34
+ const result = resolveGatewayAuth({
35
+ authConfig: { mode: 'token', token: 'config-token' },
36
+ env: { XOPCBOT_GATEWAY_AUTH_MODE: 'none' },
37
+ });
38
+ expect(result.mode).toBe('none');
39
+ });
40
+ });
41
+ describe('safeCompare', () => {
42
+ it('should return true for equal strings', () => {
43
+ expect(safeCompare('abc123', 'abc123')).toBe(true);
44
+ });
45
+ it('should return false for different strings', () => {
46
+ expect(safeCompare('abc123', 'abc124')).toBe(false);
47
+ });
48
+ it('should return false for different lengths', () => {
49
+ expect(safeCompare('abc', 'abcdef')).toBe(false);
50
+ });
51
+ it('should handle empty strings', () => {
52
+ expect(safeCompare('', '')).toBe(true);
53
+ expect(safeCompare('', 'abc')).toBe(false);
54
+ });
55
+ });
56
+ describe('validateToken', () => {
57
+ it('should return true for none mode', () => {
58
+ const auth = { mode: 'none' };
59
+ expect(validateToken(auth, 'any-token')).toBe(true);
60
+ expect(validateToken(auth, null)).toBe(true);
61
+ expect(validateToken(auth, undefined)).toBe(true);
62
+ });
63
+ it('should return true for valid token', () => {
64
+ const auth = { mode: 'token', token: 'secret-123' };
65
+ expect(validateToken(auth, 'secret-123')).toBe(true);
66
+ });
67
+ it('should return false for invalid token', () => {
68
+ const auth = { mode: 'token', token: 'secret-123' };
69
+ expect(validateToken(auth, 'wrong-token')).toBe(false);
70
+ });
71
+ it('should return false for missing token', () => {
72
+ const auth = { mode: 'token', token: 'secret-123' };
73
+ expect(validateToken(auth, null)).toBe(false);
74
+ expect(validateToken(auth, undefined)).toBe(false);
75
+ });
76
+ it('should return false for missing auth token', () => {
77
+ const auth = { mode: 'token' };
78
+ expect(validateToken(auth, 'any-token')).toBe(false);
79
+ });
80
+ });
81
+ describe('extractToken', () => {
82
+ it('should extract token from Authorization Bearer header', () => {
83
+ const headers = { authorization: 'Bearer my-secret-token' };
84
+ expect(extractToken(headers)).toBe('my-secret-token');
85
+ });
86
+ it('should extract token from X-Api-Key header', () => {
87
+ const headers = { 'x-api-key': 'api-key-token' };
88
+ expect(extractToken(headers)).toBe('api-key-token');
89
+ });
90
+ it('should prefer Authorization over X-Api-Key', () => {
91
+ const headers = {
92
+ authorization: 'Bearer bearer-token',
93
+ 'x-api-key': 'api-key-token',
94
+ };
95
+ expect(extractToken(headers)).toBe('bearer-token');
96
+ });
97
+ it('should handle array header values', () => {
98
+ const headers = { authorization: ['Bearer token1', 'Bearer token2'] };
99
+ expect(extractToken(headers)).toBe('token1');
100
+ });
101
+ it('should return undefined for missing headers', () => {
102
+ expect(extractToken(undefined)).toBe(undefined);
103
+ expect(extractToken({})).toBe(undefined);
104
+ });
105
+ it('should return undefined for non-Bearer Authorization', () => {
106
+ const headers = { authorization: 'Basic dXNlcjpwYXNz' };
107
+ expect(extractToken(headers)).toBe(undefined);
108
+ });
109
+ });
110
+ describe('assertGatewayAuthConfigured', () => {
111
+ it('should not throw for none mode', () => {
112
+ expect(() => {
113
+ assertGatewayAuthConfigured({ mode: 'none' });
114
+ }).not.toThrow();
115
+ });
116
+ it('should not throw for token mode with token', () => {
117
+ expect(() => {
118
+ assertGatewayAuthConfigured({ mode: 'token', token: 'test-token' });
119
+ }).not.toThrow();
120
+ });
121
+ it('should throw for token mode without token', () => {
122
+ expect(() => {
123
+ assertGatewayAuthConfigured({ mode: 'token' });
124
+ }).toThrow(/no token was configured/);
125
+ });
126
+ });
127
+ });
128
+ //# sourceMappingURL=auth.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.test.js","sourceRoot":"","sources":["../../../src/gateway/__tests__/auth.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Gateway Process Manager Tests
3
+ */
4
+ export {};
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Gateway Process Manager Tests
3
+ */
4
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
5
+ import { GatewayProcessManager } from '../process-manager.js';
6
+ import { writePidFile, readPidFile, removePidFile, processExists, isPidFileStale, cleanupStalePidFile, } from '../pid-file.js';
7
+ import { checkPortAvailable } from '../port-checker.js';
8
+ describe('GatewayProcessManager', () => {
9
+ let manager;
10
+ beforeEach(() => {
11
+ manager = new GatewayProcessManager();
12
+ // Clean up any existing PID file
13
+ removePidFile();
14
+ });
15
+ afterEach(() => {
16
+ removePidFile();
17
+ });
18
+ describe('isRunning', () => {
19
+ it('should return false when no PID file exists', () => {
20
+ expect(manager.isRunning()).toBe(false);
21
+ });
22
+ it('should return false when PID file exists but process is gone', () => {
23
+ // Write a fake PID
24
+ writePidFile(99999);
25
+ // Should detect it's not running
26
+ expect(manager.isRunning()).toBe(false);
27
+ // Clean up
28
+ cleanupStalePidFile();
29
+ });
30
+ });
31
+ describe('getStatus', () => {
32
+ it('should return running: false when not running', () => {
33
+ const status = manager.getStatus();
34
+ expect(status.running).toBe(false);
35
+ });
36
+ });
37
+ describe('start with port conflict', () => {
38
+ it('should detect port in use', async () => {
39
+ // Find an available port first
40
+ let testPort = 18791;
41
+ while (!(await checkPortAvailable(testPort))) {
42
+ testPort++;
43
+ }
44
+ // Start a simple server to occupy the port
45
+ const { createServer } = await import('net');
46
+ const server = createServer();
47
+ await new Promise((resolve) => {
48
+ server.listen(testPort, '0.0.0.0', () => {
49
+ resolve();
50
+ });
51
+ });
52
+ // Try to start gateway on the same port
53
+ const result = await manager.start({
54
+ host: '0.0.0.0',
55
+ port: testPort,
56
+ background: true,
57
+ });
58
+ expect(result.success).toBe(false);
59
+ expect(result.portInUse).toBe(true);
60
+ expect(result.error).toContain('Port');
61
+ // Clean up
62
+ server.close();
63
+ });
64
+ });
65
+ });
66
+ describe('PID File', () => {
67
+ beforeEach(() => {
68
+ removePidFile();
69
+ });
70
+ afterEach(() => {
71
+ removePidFile();
72
+ });
73
+ it('should write and read PID file', () => {
74
+ const testPid = 12345;
75
+ writePidFile(testPid);
76
+ expect(readPidFile()).toBe(testPid);
77
+ });
78
+ it('should return null when no PID file', () => {
79
+ expect(readPidFile()).toBe(null);
80
+ });
81
+ it('should remove PID file', () => {
82
+ writePidFile(12345);
83
+ removePidFile();
84
+ expect(readPidFile()).toBe(null);
85
+ });
86
+ it('should detect stale PID file', () => {
87
+ // Write a fake PID that doesn't exist
88
+ writePidFile(99999);
89
+ expect(isPidFileStale()).toBe(true);
90
+ });
91
+ it('should clean up stale PID file', () => {
92
+ writePidFile(99999);
93
+ cleanupStalePidFile();
94
+ expect(readPidFile()).toBe(null);
95
+ });
96
+ it('should handle invalid PID in file', async () => {
97
+ const { writeFileSync } = await import('fs');
98
+ const { getPidFilePath } = await import('../pid-file.js');
99
+ writeFileSync(getPidFilePath(), 'invalid', 'utf-8');
100
+ expect(readPidFile()).toBe(null);
101
+ });
102
+ });
103
+ describe('processExists', () => {
104
+ it('should return true for current process', () => {
105
+ expect(processExists(process.pid)).toBe(true);
106
+ });
107
+ it('should return false for non-existent PID', () => {
108
+ expect(processExists(99999)).toBe(false);
109
+ });
110
+ });
111
+ describe('Port Checker', () => {
112
+ it('should detect available port', async () => {
113
+ // Find an available port
114
+ let testPort = 18792;
115
+ while (!(await checkPortAvailable(testPort))) {
116
+ testPort++;
117
+ }
118
+ const available = await checkPortAvailable(testPort);
119
+ expect(available).toBe(true);
120
+ });
121
+ it('should detect port in use', async () => {
122
+ const { createServer } = await import('net');
123
+ const server = createServer();
124
+ // Find an available port and occupy it
125
+ let testPort = 18793;
126
+ while (!(await checkPortAvailable(testPort))) {
127
+ testPort++;
128
+ }
129
+ await new Promise((resolve) => {
130
+ server.listen(testPort, '0.0.0.0', () => {
131
+ resolve();
132
+ });
133
+ });
134
+ // Should detect port is in use
135
+ const available = await checkPortAvailable(testPort);
136
+ expect(available).toBe(false);
137
+ server.close();
138
+ });
139
+ });
140
+ //# sourceMappingURL=process-manager.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process-manager.test.js","sourceRoot":"","sources":["../../../src/gateway/__tests__/process-manager.test.ts"],"names":[],"mappings":"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"}