dominds 1.25.18 → 1.26.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.
- package/README.md +17 -0
- package/README.zh.md +17 -0
- package/dist/apps/runtime.d.ts +2 -4
- package/dist/apps-host/client.d.ts +2 -5
- package/dist/apps-host/ipc-types.d.ts +2 -5
- package/dist/apps-host/ipc-types.js +5 -1
- package/dist/bootstrap/rtws-cli.d.ts +0 -1
- package/dist/bootstrap/rtws-cli.js +9 -3
- package/dist/cli/cert.d.ts +2 -0
- package/dist/cli/cert.js +198 -0
- package/dist/cli/create.d.ts +1 -1
- package/dist/cli/create.js +1 -1
- package/dist/cli/read.js +1 -1
- package/dist/cli/tui.js +1 -1
- package/dist/cli/webui.d.ts +1 -1
- package/dist/cli/webui.js +35 -5
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +21 -16
- package/dist/docs/cli-usage.md +43 -7
- package/dist/docs/cli-usage.zh.md +43 -7
- package/dist/docs/dialog-persistence.md +1 -1
- package/dist/docs/dominds-terminology.md +2 -2
- package/dist/server/auth.d.ts +7 -0
- package/dist/server/auth.js +15 -4
- package/dist/server/certificates.d.ts +61 -0
- package/dist/server/certificates.js +418 -0
- package/dist/server/dominds-self-update-restart-helper.d.ts +15 -0
- package/dist/server/dominds-self-update-restart-helper.js +305 -0
- package/dist/server/dominds-self-update.js +162 -120
- package/dist/server/network-hosts.d.ts +5 -0
- package/dist/server/network-hosts.js +188 -0
- package/dist/server/server-core.d.ts +12 -1
- package/dist/server/server-core.js +26 -4
- package/dist/server/websocket-handler.d.ts +3 -2
- package/dist/server/websocket-handler.js +15 -8
- package/dist/server.d.ts +5 -0
- package/dist/server.js +105 -10
- package/package.json +3 -3
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.normalizeNetworkHost = normalizeNetworkHost;
|
|
37
|
+
exports.isLanHttpsHost = isLanHttpsHost;
|
|
38
|
+
exports.resolveHttpUrlHostForBindHost = resolveHttpUrlHostForBindHost;
|
|
39
|
+
exports.resolveLanHttpsHostsForBindHost = resolveLanHttpsHostsForBindHost;
|
|
40
|
+
exports.resolveDefaultLanHttpsHosts = resolveDefaultLanHttpsHosts;
|
|
41
|
+
const dgram = __importStar(require("node:dgram"));
|
|
42
|
+
const net = __importStar(require("node:net"));
|
|
43
|
+
const os = __importStar(require("node:os"));
|
|
44
|
+
const OUTBOUND_PROBE_HOST = 'github.com';
|
|
45
|
+
const OUTBOUND_PROBE_PORT = 443;
|
|
46
|
+
const OUTBOUND_PROBE_TIMEOUT_MS = 800;
|
|
47
|
+
function normalizeNetworkHost(host) {
|
|
48
|
+
const trimmed = host.trim();
|
|
49
|
+
const unwrapped = trimmed.startsWith('[') && trimmed.endsWith(']') ? trimmed.slice(1, -1) : trimmed;
|
|
50
|
+
const withoutZone = stripIpv6ZoneId(unwrapped);
|
|
51
|
+
const mappedIpv4 = parseIpv4MappedIpv6Host(withoutZone.toLowerCase());
|
|
52
|
+
return mappedIpv4 ?? withoutZone;
|
|
53
|
+
}
|
|
54
|
+
function isLanHttpsHost(host) {
|
|
55
|
+
const normalizedHost = normalizeNetworkHost(host).toLowerCase();
|
|
56
|
+
if (normalizedHost === '' || normalizedHost === 'localhost' || normalizedHost === 'loopback') {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
const ipVersion = net.isIP(normalizedHost);
|
|
60
|
+
if (ipVersion === 4) {
|
|
61
|
+
return normalizedHost !== '0.0.0.0' && !normalizedHost.startsWith('127.');
|
|
62
|
+
}
|
|
63
|
+
if (ipVersion === 6) {
|
|
64
|
+
const mappedIpv4 = parseIpv4MappedIpv6Host(normalizedHost);
|
|
65
|
+
if (mappedIpv4 !== null) {
|
|
66
|
+
return isLanHttpsHost(mappedIpv4);
|
|
67
|
+
}
|
|
68
|
+
return (normalizedHost !== '::' &&
|
|
69
|
+
normalizedHost !== '0:0:0:0:0:0:0:0' &&
|
|
70
|
+
normalizedHost !== '::1' &&
|
|
71
|
+
normalizedHost !== '0:0:0:0:0:0:0:1');
|
|
72
|
+
}
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
function resolveHttpUrlHostForBindHost(bindHost) {
|
|
76
|
+
const normalizedHost = normalizeNetworkHost(bindHost);
|
|
77
|
+
if (normalizedHost === '0.0.0.0' ||
|
|
78
|
+
normalizedHost === '::' ||
|
|
79
|
+
normalizedHost === '0:0:0:0:0:0:0:0') {
|
|
80
|
+
return 'localhost';
|
|
81
|
+
}
|
|
82
|
+
return normalizedHost;
|
|
83
|
+
}
|
|
84
|
+
async function resolveLanHttpsHostsForBindHost(bindHost) {
|
|
85
|
+
const normalizedHost = normalizeNetworkHost(bindHost);
|
|
86
|
+
if (normalizedHost === '0.0.0.0') {
|
|
87
|
+
return uniqueHosts([
|
|
88
|
+
...(await getUdpOutboundHosts('ipv4')),
|
|
89
|
+
...getNetworkInterfaceHosts('ipv4'),
|
|
90
|
+
os.hostname(),
|
|
91
|
+
]).filter(isLanHttpsHost);
|
|
92
|
+
}
|
|
93
|
+
if (normalizedHost === '::' || normalizedHost === '0:0:0:0:0:0:0:0') {
|
|
94
|
+
return uniqueHosts([
|
|
95
|
+
...(await getUdpOutboundHosts('ipv6')),
|
|
96
|
+
...(await getUdpOutboundHosts('ipv4')),
|
|
97
|
+
...getNetworkInterfaceHosts('ipv6'),
|
|
98
|
+
...getNetworkInterfaceHosts('ipv4'),
|
|
99
|
+
os.hostname(),
|
|
100
|
+
]).filter(isLanHttpsHost);
|
|
101
|
+
}
|
|
102
|
+
return isLanHttpsHost(normalizedHost) ? [normalizedHost] : [];
|
|
103
|
+
}
|
|
104
|
+
async function resolveDefaultLanHttpsHosts() {
|
|
105
|
+
return await resolveLanHttpsHostsForBindHost('::');
|
|
106
|
+
}
|
|
107
|
+
async function getUdpOutboundHosts(kind) {
|
|
108
|
+
const host = await getUdpOutboundHost(kind);
|
|
109
|
+
return host === null ? [] : [host];
|
|
110
|
+
}
|
|
111
|
+
async function getUdpOutboundHost(kind) {
|
|
112
|
+
return await new Promise((resolve) => {
|
|
113
|
+
const socket = dgram.createSocket(kind === 'ipv4' ? 'udp4' : 'udp6');
|
|
114
|
+
let settled = false;
|
|
115
|
+
const finish = (host) => {
|
|
116
|
+
if (settled)
|
|
117
|
+
return;
|
|
118
|
+
settled = true;
|
|
119
|
+
clearTimeout(timeout);
|
|
120
|
+
socket.removeAllListeners('error');
|
|
121
|
+
socket.close();
|
|
122
|
+
resolve(host === null ? null : normalizeNetworkHost(host));
|
|
123
|
+
};
|
|
124
|
+
const timeout = setTimeout(() => {
|
|
125
|
+
finish(null);
|
|
126
|
+
}, OUTBOUND_PROBE_TIMEOUT_MS);
|
|
127
|
+
timeout.unref();
|
|
128
|
+
socket.once('error', () => {
|
|
129
|
+
finish(null);
|
|
130
|
+
});
|
|
131
|
+
socket.connect(OUTBOUND_PROBE_PORT, OUTBOUND_PROBE_HOST, () => {
|
|
132
|
+
const address = socket.address();
|
|
133
|
+
if (typeof address === 'string') {
|
|
134
|
+
finish(null);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
finish(address.address);
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
function getNetworkInterfaceHosts(kind) {
|
|
142
|
+
const hosts = [];
|
|
143
|
+
const families = os.networkInterfaces();
|
|
144
|
+
for (const entries of Object.values(families)) {
|
|
145
|
+
if (entries === undefined)
|
|
146
|
+
continue;
|
|
147
|
+
for (const entry of entries) {
|
|
148
|
+
if (entry.internal)
|
|
149
|
+
continue;
|
|
150
|
+
if (kind === 'ipv4' && entry.family === 'IPv4') {
|
|
151
|
+
hosts.push(entry.address);
|
|
152
|
+
}
|
|
153
|
+
if (kind === 'ipv6' && entry.family === 'IPv6') {
|
|
154
|
+
hosts.push(stripIpv6ZoneId(entry.address));
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return hosts;
|
|
159
|
+
}
|
|
160
|
+
function uniqueHosts(rawHosts) {
|
|
161
|
+
const hosts = [];
|
|
162
|
+
const seen = new Set();
|
|
163
|
+
for (const rawHost of rawHosts) {
|
|
164
|
+
const host = normalizeNetworkHost(rawHost);
|
|
165
|
+
if (host === '')
|
|
166
|
+
continue;
|
|
167
|
+
const key = host.toLowerCase();
|
|
168
|
+
if (seen.has(key))
|
|
169
|
+
continue;
|
|
170
|
+
seen.add(key);
|
|
171
|
+
hosts.push(host);
|
|
172
|
+
}
|
|
173
|
+
return hosts;
|
|
174
|
+
}
|
|
175
|
+
function stripIpv6ZoneId(address) {
|
|
176
|
+
const zoneIndex = address.indexOf('%');
|
|
177
|
+
return zoneIndex === -1 ? address : address.slice(0, zoneIndex);
|
|
178
|
+
}
|
|
179
|
+
function parseIpv4MappedIpv6Host(host) {
|
|
180
|
+
const dottedQuadIndex = host.lastIndexOf(':');
|
|
181
|
+
if (dottedQuadIndex === -1)
|
|
182
|
+
return null;
|
|
183
|
+
const dottedQuad = host.slice(dottedQuadIndex + 1);
|
|
184
|
+
if (net.isIP(dottedQuad) !== 4)
|
|
185
|
+
return null;
|
|
186
|
+
const prefix = host.slice(0, dottedQuadIndex).toLowerCase();
|
|
187
|
+
return prefix === '::ffff' || prefix === '0:0:0:0:0:ffff' ? dottedQuad : null;
|
|
188
|
+
}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import * as http from 'http';
|
|
7
7
|
import { IncomingMessage, ServerResponse } from 'http';
|
|
8
|
+
import type { Server as HttpsServer } from 'node:https';
|
|
8
9
|
import type { ParsedUrlQuery } from 'querystring';
|
|
9
10
|
import type { WebSocket } from 'ws';
|
|
10
11
|
import type { AuthConfig } from './auth';
|
|
@@ -12,11 +13,18 @@ export interface ServerConfig {
|
|
|
12
13
|
port: number;
|
|
13
14
|
host: string;
|
|
14
15
|
mode: 'development' | 'production';
|
|
16
|
+
tls?: {
|
|
17
|
+
cert: string;
|
|
18
|
+
key: string;
|
|
19
|
+
certPath: string;
|
|
20
|
+
keyPath: string;
|
|
21
|
+
};
|
|
15
22
|
clients?: Set<WebSocket>;
|
|
16
23
|
staticRoot?: string;
|
|
17
24
|
enableLiveReload?: boolean;
|
|
18
25
|
auth?: AuthConfig;
|
|
19
26
|
}
|
|
27
|
+
export type HttpServerLike = http.Server | HttpsServer;
|
|
20
28
|
export interface RequestHandler {
|
|
21
29
|
(req: IncomingMessage, res: ServerResponse, pathname: string, query: ParsedUrlQuery): Promise<boolean>;
|
|
22
30
|
}
|
|
@@ -27,6 +35,8 @@ export declare class HttpServerCore {
|
|
|
27
35
|
private server;
|
|
28
36
|
private config;
|
|
29
37
|
private customHandlers;
|
|
38
|
+
private started;
|
|
39
|
+
private stopping;
|
|
30
40
|
constructor(config: ServerConfig);
|
|
31
41
|
/**
|
|
32
42
|
* Add custom request handler
|
|
@@ -57,7 +67,8 @@ export declare class HttpServerCore {
|
|
|
57
67
|
/**
|
|
58
68
|
* Get the underlying HTTP server
|
|
59
69
|
*/
|
|
60
|
-
getHttpServer():
|
|
70
|
+
getHttpServer(): HttpServerLike;
|
|
71
|
+
getScheme(): 'http' | 'https';
|
|
61
72
|
/**
|
|
62
73
|
* Resolve dominds installation root
|
|
63
74
|
*/
|
|
@@ -44,6 +44,7 @@ exports.sendText = sendText;
|
|
|
44
44
|
* Core HTTP server functionality shared between production and development servers
|
|
45
45
|
*/
|
|
46
46
|
const http = __importStar(require("http"));
|
|
47
|
+
const https = __importStar(require("node:https"));
|
|
47
48
|
const path = __importStar(require("path"));
|
|
48
49
|
const log_1 = require("../log");
|
|
49
50
|
const api_routes_1 = require("./api-routes");
|
|
@@ -72,8 +73,14 @@ function toParsedUrlQuery(searchParams) {
|
|
|
72
73
|
class HttpServerCore {
|
|
73
74
|
constructor(config) {
|
|
74
75
|
this.customHandlers = [];
|
|
76
|
+
this.started = false;
|
|
77
|
+
this.stopping = null;
|
|
75
78
|
this.config = config;
|
|
76
|
-
|
|
79
|
+
const requestHandler = this.handleRequest.bind(this);
|
|
80
|
+
this.server =
|
|
81
|
+
config.tls === undefined
|
|
82
|
+
? http.createServer(requestHandler)
|
|
83
|
+
: https.createServer({ cert: config.tls.cert, key: config.tls.key }, requestHandler);
|
|
77
84
|
}
|
|
78
85
|
/**
|
|
79
86
|
* Add custom request handler
|
|
@@ -212,7 +219,8 @@ class HttpServerCore {
|
|
|
212
219
|
? address.port
|
|
213
220
|
: this.config.port;
|
|
214
221
|
this.config = { ...this.config, port: actualPort };
|
|
215
|
-
|
|
222
|
+
this.started = true;
|
|
223
|
+
log.debug(`Server listening on ${this.getScheme()}://${this.config.host}:${actualPort}`);
|
|
216
224
|
resolve(actualPort);
|
|
217
225
|
};
|
|
218
226
|
this.server.once('error', onError);
|
|
@@ -224,12 +232,23 @@ class HttpServerCore {
|
|
|
224
232
|
* Stop the server
|
|
225
233
|
*/
|
|
226
234
|
stop() {
|
|
227
|
-
|
|
228
|
-
this.
|
|
235
|
+
if (this.stopping !== null)
|
|
236
|
+
return this.stopping;
|
|
237
|
+
if (!this.started)
|
|
238
|
+
return Promise.resolve();
|
|
239
|
+
this.stopping = new Promise((resolve, reject) => {
|
|
240
|
+
this.server.close((error) => {
|
|
241
|
+
this.started = false;
|
|
242
|
+
this.stopping = null;
|
|
243
|
+
if (error !== undefined) {
|
|
244
|
+
reject(error);
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
229
247
|
log.info('Server stopped');
|
|
230
248
|
resolve();
|
|
231
249
|
});
|
|
232
250
|
});
|
|
251
|
+
return this.stopping;
|
|
233
252
|
}
|
|
234
253
|
/**
|
|
235
254
|
* Get the underlying HTTP server
|
|
@@ -237,6 +256,9 @@ class HttpServerCore {
|
|
|
237
256
|
getHttpServer() {
|
|
238
257
|
return this.server;
|
|
239
258
|
}
|
|
259
|
+
getScheme() {
|
|
260
|
+
return this.config.tls === undefined ? 'http' : 'https';
|
|
261
|
+
}
|
|
240
262
|
/**
|
|
241
263
|
* Resolve dominds installation root
|
|
242
264
|
*/
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { DomindsRuntimeMode, WebSocketMessage } from '@longrun-ai/kernel/types';
|
|
2
2
|
import { type LanguageCode } from '@longrun-ai/kernel/types/language';
|
|
3
|
-
import type { Server } from 'http';
|
|
3
|
+
import type { Server as HttpServer } from 'http';
|
|
4
|
+
import type { Server as HttpsServer } from 'https';
|
|
4
5
|
import { WebSocket, WebSocketServer } from 'ws';
|
|
5
6
|
import { Dialog } from '../dialog';
|
|
6
7
|
import type { AuthConfig } from './auth';
|
|
@@ -28,7 +29,7 @@ export declare function handleWebSocketMessage(ws: WebSocket, packet: WebSocketM
|
|
|
28
29
|
/**
|
|
29
30
|
* Setup WebSocket server with dialog handling
|
|
30
31
|
*/
|
|
31
|
-
export declare function setupWebSocketServer(
|
|
32
|
+
export declare function setupWebSocketServer(httpServers: HttpServer | HttpsServer | readonly (HttpServer | HttpsServer)[], clients: Set<WebSocket>, auth: AuthConfig, serverWorkLanguage: LanguageCode, mode: DomindsRuntimeMode): readonly WebSocketServer[];
|
|
32
33
|
/**
|
|
33
34
|
* Clean up all event channels and subscriptions
|
|
34
35
|
*/
|
|
@@ -1855,8 +1855,9 @@ async function handleReceiveHumanReply(ws, packet) {
|
|
|
1855
1855
|
/**
|
|
1856
1856
|
* Setup WebSocket server with dialog handling
|
|
1857
1857
|
*/
|
|
1858
|
-
function setupWebSocketServer(
|
|
1859
|
-
const
|
|
1858
|
+
function setupWebSocketServer(httpServers, clients, auth, serverWorkLanguage, mode) {
|
|
1859
|
+
const servers = Array.isArray(httpServers) ? httpServers : [httpServers];
|
|
1860
|
+
const webSocketServers = servers.map((server) => new ws_1.WebSocketServer({ server }));
|
|
1860
1861
|
const runtimeStatusPubChan = (0, evt_1.createPubChan)();
|
|
1861
1862
|
const broadcastToClients = (msg) => {
|
|
1862
1863
|
const data = JSON.stringify(msg);
|
|
@@ -1944,7 +1945,7 @@ function setupWebSocketServer(httpServer, clients, auth, serverWorkLanguage, mod
|
|
|
1944
1945
|
});
|
|
1945
1946
|
});
|
|
1946
1947
|
(0, team_config_updates_1.startTeamConfigWatcher)();
|
|
1947
|
-
|
|
1948
|
+
const cleanupRuntime = () => {
|
|
1948
1949
|
(0, team_config_updates_1.stopTeamConfigWatcher)();
|
|
1949
1950
|
(0, team_config_updates_1.clearTeamConfigBroadcaster)();
|
|
1950
1951
|
(0, tool_availability_updates_1.clearToolAvailabilityBroadcaster)();
|
|
@@ -1954,9 +1955,12 @@ function setupWebSocketServer(httpServer, clients, auth, serverWorkLanguage, mod
|
|
|
1954
1955
|
(0, persistence_1.setDialogsQuarantinedBroadcaster)(null);
|
|
1955
1956
|
broadcastDialogsIndexMessage = null;
|
|
1956
1957
|
broadcastDiligencePushUpdatedMessage = null;
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1958
|
+
for (const wss of webSocketServers) {
|
|
1959
|
+
void wss.close();
|
|
1960
|
+
}
|
|
1961
|
+
};
|
|
1962
|
+
servers[0]?.once('close', cleanupRuntime);
|
|
1963
|
+
const onConnection = (ws, req) => {
|
|
1960
1964
|
const authCheck = (0, auth_1.getWebSocketAuthCheck)(req, auth);
|
|
1961
1965
|
if (authCheck.kind !== 'ok') {
|
|
1962
1966
|
ws.close(4401, 'unauthorized');
|
|
@@ -2036,8 +2040,11 @@ function setupWebSocketServer(httpServer, clients, auth, serverWorkLanguage, mod
|
|
|
2036
2040
|
// Clean up client subscriptions on error
|
|
2037
2041
|
cleanupWsClient(ws);
|
|
2038
2042
|
});
|
|
2039
|
-
}
|
|
2040
|
-
|
|
2043
|
+
};
|
|
2044
|
+
for (const wss of webSocketServers) {
|
|
2045
|
+
wss.on('connection', onConnection);
|
|
2046
|
+
}
|
|
2047
|
+
return webSocketServers;
|
|
2041
2048
|
}
|
|
2042
2049
|
function isRecord(value) {
|
|
2043
2050
|
return typeof value === 'object' && value !== null;
|
package/dist/server.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export type ServerOptions = {
|
|
|
6
6
|
port?: number;
|
|
7
7
|
host?: string;
|
|
8
8
|
mode?: 'dev' | 'prod';
|
|
9
|
+
certsDirAbs?: string;
|
|
9
10
|
startBackendDriver?: boolean;
|
|
10
11
|
returnAfterListen?: boolean;
|
|
11
12
|
strictPort?: boolean;
|
|
@@ -13,9 +14,13 @@ export type ServerOptions = {
|
|
|
13
14
|
};
|
|
14
15
|
export type StartedServer = {
|
|
15
16
|
httpServer: HttpServerCore;
|
|
17
|
+
httpsServer?: HttpServerCore;
|
|
16
18
|
auth: AuthConfig;
|
|
17
19
|
host: string;
|
|
20
|
+
urlHost: string;
|
|
18
21
|
port: number;
|
|
22
|
+
httpsPort?: number;
|
|
23
|
+
httpsUrlHost?: string;
|
|
19
24
|
mode: 'dev' | 'prod';
|
|
20
25
|
};
|
|
21
26
|
export declare function startServer(opts?: ServerOptions): Promise<StartedServer>;
|
package/dist/server.js
CHANGED
|
@@ -54,7 +54,9 @@ const open_generation_recovery_1 = require("./recovery/open-generation-recovery"
|
|
|
54
54
|
const reply_delivery_recovery_1 = require("./recovery/reply-delivery-recovery");
|
|
55
55
|
const work_language_1 = require("./runtime/work-language");
|
|
56
56
|
const auth_1 = require("./server/auth");
|
|
57
|
+
const certificates_1 = require("./server/certificates");
|
|
57
58
|
const dominds_self_update_1 = require("./server/dominds-self-update");
|
|
59
|
+
const network_hosts_1 = require("./server/network-hosts");
|
|
58
60
|
const port_selection_1 = require("./server/port-selection");
|
|
59
61
|
const server_core_1 = require("./server/server-core");
|
|
60
62
|
const websocket_handler_1 = require("./server/websocket-handler");
|
|
@@ -128,6 +130,15 @@ function getErrnoCode(error) {
|
|
|
128
130
|
const withCode = error;
|
|
129
131
|
return typeof withCode.code === 'string' ? withCode.code : undefined;
|
|
130
132
|
}
|
|
133
|
+
function buildAdjacentHttpsPortCandidates(params) {
|
|
134
|
+
if (params.preferredPort < 1 || params.preferredPort > 65535)
|
|
135
|
+
return [];
|
|
136
|
+
return (0, port_selection_1.buildWebuiPortCandidates)({
|
|
137
|
+
preferredPort: params.preferredPort,
|
|
138
|
+
strictPort: false,
|
|
139
|
+
direction: params.direction,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
131
142
|
async function runPostListenStartup(params) {
|
|
132
143
|
await new Promise((resolve) => {
|
|
133
144
|
setImmediate(resolve);
|
|
@@ -177,7 +188,6 @@ async function startServer(opts = {}) {
|
|
|
177
188
|
strictPort,
|
|
178
189
|
direction: portAutoDirection,
|
|
179
190
|
});
|
|
180
|
-
log.info(`Starting server in ${mode} mode on ${host}:${preferredPort} (${strictPort ? 'strict port' : `auto port ${portAutoDirection}`}; working language: ${(0, work_language_1.getWorkLanguage)()} from ${source})`);
|
|
181
191
|
// WebSocket clients set
|
|
182
192
|
const clients = new Set();
|
|
183
193
|
const serverMode = mode === 'dev' ? 'development' : 'production';
|
|
@@ -185,6 +195,26 @@ async function startServer(opts = {}) {
|
|
|
185
195
|
mode: serverMode,
|
|
186
196
|
env: process.env,
|
|
187
197
|
});
|
|
198
|
+
const certificateLookup = await (0, certificates_1.findAutoHttpsCertificateForHost)({
|
|
199
|
+
host,
|
|
200
|
+
certsDirAbs: opts.certsDirAbs,
|
|
201
|
+
});
|
|
202
|
+
for (const diagnostic of certificateLookup.diagnostics) {
|
|
203
|
+
log.warn(diagnostic.message);
|
|
204
|
+
}
|
|
205
|
+
const tls = certificateLookup.kind === 'found' && !strictPort ? certificateLookup.certificate : undefined;
|
|
206
|
+
const urlHost = (0, network_hosts_1.resolveHttpUrlHostForBindHost)(host);
|
|
207
|
+
log.info(`Starting server in ${mode} mode on ${(0, auth_1.formatServerOrigin)({
|
|
208
|
+
scheme: 'http',
|
|
209
|
+
host: urlHost,
|
|
210
|
+
port: preferredPort,
|
|
211
|
+
})} (bind ${host}; ${strictPort ? 'strict port' : `auto port ${portAutoDirection}`}; working language: ${(0, work_language_1.getWorkLanguage)()} from ${source})`);
|
|
212
|
+
if (certificateLookup.kind === 'found' && strictPort) {
|
|
213
|
+
log.info(`HTTPS certificate found but Dominds HTTPS is disabled for strict --port; assuming HTTPS, if needed, is handled by a front proxy (${certificateLookup.certificate.certPath})`);
|
|
214
|
+
}
|
|
215
|
+
else if (tls !== undefined) {
|
|
216
|
+
log.info(`HTTPS certificate available: ${tls.certPath} (matched host ${tls.matchedHost})`);
|
|
217
|
+
}
|
|
188
218
|
let startedCore = null;
|
|
189
219
|
let boundPort = null;
|
|
190
220
|
for (const candidatePort of portCandidates) {
|
|
@@ -199,7 +229,6 @@ async function startServer(opts = {}) {
|
|
|
199
229
|
const candidateServer = (0, server_core_1.createHttpServer)(config);
|
|
200
230
|
try {
|
|
201
231
|
boundPort = await candidateServer.start();
|
|
202
|
-
(0, websocket_handler_1.setupWebSocketServer)(candidateServer.getHttpServer(), clients, auth, (0, work_language_1.getWorkLanguage)(), config.mode);
|
|
203
232
|
startedCore = candidateServer;
|
|
204
233
|
break;
|
|
205
234
|
}
|
|
@@ -209,9 +238,6 @@ async function startServer(opts = {}) {
|
|
|
209
238
|
log.warn(`WebUI port ${candidatePort} is already in use; trying the next ${nextDirection} port`);
|
|
210
239
|
continue;
|
|
211
240
|
}
|
|
212
|
-
if (boundPort !== null && startedCore === null) {
|
|
213
|
-
await candidateServer.stop();
|
|
214
|
-
}
|
|
215
241
|
throw error;
|
|
216
242
|
}
|
|
217
243
|
}
|
|
@@ -225,8 +251,66 @@ async function startServer(opts = {}) {
|
|
|
225
251
|
if (!strictPort && boundPort !== preferredPort) {
|
|
226
252
|
log.warn(`WebUI preferred port ${preferredPort} was unavailable; listening on ${boundPort}`);
|
|
227
253
|
}
|
|
254
|
+
let httpsServer;
|
|
255
|
+
let httpsPort;
|
|
256
|
+
let httpsUrlHost;
|
|
257
|
+
if (tls !== undefined) {
|
|
258
|
+
const preferredHttpsPort = portAutoDirection === 'up' ? boundPort + 1 : boundPort - 1;
|
|
259
|
+
const httpsPortCandidates = buildAdjacentHttpsPortCandidates({
|
|
260
|
+
preferredPort: preferredHttpsPort,
|
|
261
|
+
direction: portAutoDirection,
|
|
262
|
+
});
|
|
263
|
+
for (const candidatePort of httpsPortCandidates) {
|
|
264
|
+
const config = {
|
|
265
|
+
mode: serverMode,
|
|
266
|
+
staticRoot: 'webapp/dist',
|
|
267
|
+
host,
|
|
268
|
+
port: candidatePort,
|
|
269
|
+
tls,
|
|
270
|
+
clients,
|
|
271
|
+
auth,
|
|
272
|
+
};
|
|
273
|
+
const candidateServer = (0, server_core_1.createHttpServer)(config);
|
|
274
|
+
try {
|
|
275
|
+
httpsPort = await candidateServer.start();
|
|
276
|
+
httpsServer = candidateServer;
|
|
277
|
+
httpsUrlHost = tls.matchedHost;
|
|
278
|
+
break;
|
|
279
|
+
}
|
|
280
|
+
catch (error) {
|
|
281
|
+
if (getErrnoCode(error) === 'EADDRINUSE') {
|
|
282
|
+
const nextDirection = portAutoDirection === 'down' ? 'lower' : 'higher';
|
|
283
|
+
log.warn(`WebUI HTTPS port ${candidatePort} is already in use; trying the next ${nextDirection} port`);
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
await startedCore.stop();
|
|
287
|
+
throw error;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
if (httpsServer === undefined || httpsPort === undefined || httpsUrlHost === undefined) {
|
|
291
|
+
await startedCore.stop();
|
|
292
|
+
throw new Error(`Failed to start WebUI HTTPS: no available adjacent port found from ${preferredHttpsPort}`);
|
|
293
|
+
}
|
|
294
|
+
log.info(`HTTPS WebUI ready: ${(0, auth_1.formatServerOrigin)({
|
|
295
|
+
scheme: 'https',
|
|
296
|
+
host: httpsUrlHost,
|
|
297
|
+
port: httpsPort,
|
|
298
|
+
})}`);
|
|
299
|
+
}
|
|
300
|
+
(0, websocket_handler_1.setupWebSocketServer)(httpsServer === undefined
|
|
301
|
+
? startedCore.getHttpServer()
|
|
302
|
+
: [startedCore.getHttpServer(), httpsServer.getHttpServer()], clients, auth, (0, work_language_1.getWorkLanguage)(), serverMode);
|
|
228
303
|
const postListenStartupToken = { canceled: false };
|
|
229
304
|
const httpServer = attachPostListenStartupCancellation(startedCore, postListenStartupToken);
|
|
305
|
+
if (httpsServer !== undefined) {
|
|
306
|
+
const originalStop = httpServer.stop.bind(httpServer);
|
|
307
|
+
const httpsServerToStop = httpsServer;
|
|
308
|
+
httpServer.stop = async () => {
|
|
309
|
+
postListenStartupToken.canceled = true;
|
|
310
|
+
await httpsServerToStop.stop();
|
|
311
|
+
await originalStop();
|
|
312
|
+
};
|
|
313
|
+
}
|
|
230
314
|
try {
|
|
231
315
|
const rtwsRootAbs = process.cwd();
|
|
232
316
|
(0, dominds_self_update_1.configureDomindsSelfUpdate)({
|
|
@@ -259,7 +343,7 @@ async function startServer(opts = {}) {
|
|
|
259
343
|
}
|
|
260
344
|
const postListenStartup = runPostListenStartup({
|
|
261
345
|
rtwsRootAbs,
|
|
262
|
-
kernel: { host, port: boundPort },
|
|
346
|
+
kernel: { scheme: 'http', host: urlHost, port: boundPort },
|
|
263
347
|
startBackendDriver,
|
|
264
348
|
token: postListenStartupToken,
|
|
265
349
|
});
|
|
@@ -279,7 +363,17 @@ async function startServer(opts = {}) {
|
|
|
279
363
|
}
|
|
280
364
|
throw error;
|
|
281
365
|
}
|
|
282
|
-
return {
|
|
366
|
+
return {
|
|
367
|
+
httpServer,
|
|
368
|
+
httpsServer,
|
|
369
|
+
auth,
|
|
370
|
+
host,
|
|
371
|
+
urlHost,
|
|
372
|
+
port: boundPort,
|
|
373
|
+
httpsPort,
|
|
374
|
+
httpsUrlHost,
|
|
375
|
+
mode,
|
|
376
|
+
};
|
|
283
377
|
}
|
|
284
378
|
// Main function for CLI execution
|
|
285
379
|
async function main() {
|
|
@@ -287,13 +381,14 @@ async function main() {
|
|
|
287
381
|
// Handle working directory change from -C flag
|
|
288
382
|
const wsDir = cliArgs['C'];
|
|
289
383
|
if (wsDir) {
|
|
290
|
-
|
|
291
|
-
|
|
384
|
+
if (!path.isAbsolute(wsDir)) {
|
|
385
|
+
throw new Error(`-C requires an absolute directory path: ${wsDir}`);
|
|
386
|
+
}
|
|
292
387
|
try {
|
|
293
388
|
process.chdir(wsDir);
|
|
294
389
|
}
|
|
295
390
|
catch (err) {
|
|
296
|
-
|
|
391
|
+
throw new Error(`Failed to change working directory to ${wsDir}: ${err instanceof Error ? err.message : String(err)}`);
|
|
297
392
|
}
|
|
298
393
|
}
|
|
299
394
|
// Get port, host, and mode from CLI args
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dominds",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.26.0",
|
|
4
4
|
"description": "Dominds CLI and aggregation shell for the LongRun AI kernel/runtime packages.",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"publishConfig": {
|
|
@@ -52,9 +52,9 @@
|
|
|
52
52
|
"ws": "^8.19.0",
|
|
53
53
|
"yaml": "^2.8.2",
|
|
54
54
|
"zod": "^4.3.6",
|
|
55
|
+
"@longrun-ai/kernel": "1.16.0",
|
|
55
56
|
"@longrun-ai/codex-auth": "0.13.0",
|
|
56
|
-
"@longrun-ai/
|
|
57
|
-
"@longrun-ai/shell": "1.15.12"
|
|
57
|
+
"@longrun-ai/shell": "1.16.0"
|
|
58
58
|
},
|
|
59
59
|
"devDependencies": {
|
|
60
60
|
"@types/node": "^25.3.5",
|