dominds 1.25.19 → 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 CHANGED
@@ -253,6 +253,23 @@ Platform note:
253
253
 
254
254
  - Windows also registers `codex_inspect_and_patch_tools`; its `readonly_shell` tool runs through `cmd.exe`, so prefer allowlisted commands available in that shell/PATH, such as `rg`, `git`, `dir`, `type`, and `where`.
255
255
 
256
+ ### Local HTTPS Certificates
257
+
258
+ Dominds always keeps an HTTP listener. When a valid certificate matching the current `--host` exists under `~/.dominds/certs/`, Dominds also starts HTTPS. Certificates match DNS/IP hostnames, not ports, so one certificate covers all Dominds WebUI ports on that host.
259
+
260
+ ```bash
261
+ # Create a 10-year self-signed certificate for detected non-loopback LAN hosts
262
+ dominds cert create
263
+
264
+ # Add explicit SAN hosts; --host can be repeated
265
+ dominds cert create --host 192.168.1.10 --host my-host.local
266
+
267
+ # Inspect whether a host can use an HTTPS certificate
268
+ dominds cert status --host 0.0.0.0
269
+ ```
270
+
271
+ `localhost`, `loopback`, `127.0.0.0/8`, `::1`, `0.0.0.0`, and `::` are not certificate hosts. `0.0.0.0` / `::` only mean bind-all; certificate matching uses detected non-loopback LAN hosts. A bare `--port 5666` disables Dominds built-in HTTPS and assumes a front proxy handles HTTPS, while the default port behavior and `--port 5666+/-` may auto-enable built-in HTTPS when a matching certificate exists.
272
+
256
273
  ## Start from scratch
257
274
 
258
275
  Starting from scratch means: create an empty folder, run `dominds`, let Setup generate the minimal `.minds/team.yaml`, then use the shadow team manager (`@fuxi`) to design a real team for your product.
package/README.zh.md CHANGED
@@ -192,6 +192,23 @@ dominds
192
192
 
193
193
  - Windows 也会注册 `codex_inspect_and_patch_tools`;其中 `readonly_shell` 通过 `cmd.exe` 执行,因此优先使用该 shell/PATH 中可用的白名单命令,例如 `rg`、`git`、`dir`、`type` 和 `where`。
194
194
 
195
+ ### 本地 HTTPS 证书
196
+
197
+ Dominds 默认始终提供 HTTP 监听;当 `~/.dominds/certs/` 下存在匹配当前 `--host` 的有效证书时,会额外启用 HTTPS。证书按 DNS/IP 主机名匹配,不绑定端口,因此同一张证书可覆盖该主机上的所有 Dominds WebUI 端口。
198
+
199
+ ```bash
200
+ # 自动为检测到的一个或多个非 loopback LAN 主机创建 10 年有效的自签名证书
201
+ dominds cert create
202
+
203
+ # 显式指定证书 SAN,可重复 --host
204
+ dominds cert create --host 192.168.1.10 --host my-host.local
205
+
206
+ # 查看指定 host 是否能匹配到证书
207
+ dominds cert status --host 0.0.0.0
208
+ ```
209
+
210
+ `localhost`、`loopback`、`127.0.0.0/8`、`::1`、`0.0.0.0`、`::` 不会作为 HTTPS 证书主机;其中 `0.0.0.0` / `::` 仅表示绑定所有地址,匹配证书时会使用检测到的非 loopback LAN 主机。使用裸 `--port 5666` 启动时,Dominds 会认为 HTTPS 由前置代理负责,不启用内建 HTTPS;使用默认端口或 `--port 5666+/-` 时才会自动启用内建 HTTPS。
211
+
195
212
  ## 从零开始(空文件夹启动)
196
213
 
197
214
  若暂无模板或团队配置,可直接从空目录启动:
@@ -1,3 +1,4 @@
1
+ import type { DomindsKernelEndpoint } from '@longrun-ai/kernel/app-host-contract';
1
2
  import { type AppsHostClient } from '../apps-host/client';
2
3
  export type DynamicAppToolAvailabilityResult = Readonly<{
3
4
  status: 'ready';
@@ -36,8 +37,5 @@ export declare function resolveDynamicAppToolAvailabilityForMember(_params: {
36
37
  }): Promise<DynamicAppToolAvailabilityResult>;
37
38
  export declare function initAppsRuntime(params: {
38
39
  rtwsRootAbs: string;
39
- kernel: Readonly<{
40
- host: string;
41
- port: number;
42
- }>;
40
+ kernel: DomindsKernelEndpoint;
43
41
  }): Promise<void>;
@@ -1,4 +1,4 @@
1
- import type { DomindsAppDynamicToolsetsContext, DomindsAppReminderOwnerApplyContext, DomindsAppReminderOwnerRenderContext, DomindsAppReminderOwnerUpdateContext, DomindsAppReminderRenderedMessage, DomindsAppRunControlContext, DomindsAppRunControlResult } from '@longrun-ai/kernel/app-host-contract';
1
+ import type { DomindsAppDynamicToolsetsContext, DomindsAppReminderOwnerApplyContext, DomindsAppReminderOwnerRenderContext, DomindsAppReminderOwnerUpdateContext, DomindsAppReminderRenderedMessage, DomindsAppRunControlContext, DomindsAppRunControlResult, DomindsKernelEndpoint } from '@longrun-ai/kernel/app-host-contract';
2
2
  import type { DomindsAppHostReminderUpdateResult, DomindsAppHostToolResult, DomindsAppInstallJson, DomindsAppReminderApplyRequest, DomindsAppReminderApplyResult } from '@longrun-ai/kernel/app-json';
3
3
  import type { ToolArguments } from '../tool';
4
4
  import type { AppsHostMessageToKernel } from './ipc-types';
@@ -29,10 +29,7 @@ export type AppsHostReadyMessage = Extract<AppsHostMessageToKernel, {
29
29
  }>;
30
30
  export declare function startAppsHost(params: {
31
31
  rtwsRootAbs: string;
32
- kernel: Readonly<{
33
- host: string;
34
- port: number;
35
- }>;
32
+ kernel: DomindsKernelEndpoint;
36
33
  apps: ReadonlyArray<EnabledAppForHost>;
37
34
  }): Promise<Readonly<{
38
35
  client: AppsHostClient;
@@ -1,14 +1,11 @@
1
- import type { DomindsAppReminderRenderedMessage, DomindsAppRunControlResult } from '@longrun-ai/kernel/app-host-contract';
1
+ import type { DomindsAppReminderRenderedMessage, DomindsAppRunControlResult, DomindsKernelEndpoint } from '@longrun-ai/kernel/app-host-contract';
2
2
  import type { DomindsAppDialogReminderRequestBatch, DomindsAppHostReminderUpdateResult, DomindsAppInstallJson, DomindsAppReminderApplyRequest, DomindsAppReminderApplyResult, DomindsAppReminderState } from '@longrun-ai/kernel/app-json';
3
3
  import type { LanguageCode } from '@longrun-ai/kernel/types/language';
4
4
  import type { ToolArguments, ToolCallOutput } from '../tool';
5
5
  export type AppsHostKernelInitMessage = Readonly<{
6
6
  type: 'init';
7
7
  rtwsRootAbs: string;
8
- kernel: Readonly<{
9
- host: string;
10
- port: number;
11
- }>;
8
+ kernel: DomindsKernelEndpoint;
12
9
  apps: ReadonlyArray<Readonly<{
13
10
  appId: string;
14
11
  runtimePort: number | null;
@@ -126,9 +126,13 @@ function parseAppsHostMessageFromKernel(v) {
126
126
  throw new Error('Invalid init message: rtwsRootAbs required');
127
127
  if (!isRecord(kernel))
128
128
  throw new Error('Invalid init message: kernel must be object');
129
+ const scheme = kernel['scheme'];
129
130
  const host = asString(kernel['host']);
130
131
  const portRaw = kernel['port'];
131
132
  const port = typeof portRaw === 'number' && Number.isFinite(portRaw) ? Math.floor(portRaw) : null;
133
+ if (scheme !== 'http' && scheme !== 'https') {
134
+ throw new Error('Invalid init message: kernel.scheme must be http|https');
135
+ }
132
136
  if (!host)
133
137
  throw new Error('Invalid init message: kernel.host required');
134
138
  if (port === null || port < 0) {
@@ -157,7 +161,7 @@ function parseAppsHostMessageFromKernel(v) {
157
161
  }
158
162
  return { appId, runtimePort, installJson: installJson };
159
163
  });
160
- return { type: 'init', rtwsRootAbs, kernel: { host, port }, apps: parsedApps };
164
+ return { type: 'init', rtwsRootAbs, kernel: { scheme, host, port }, apps: parsedApps };
161
165
  }
162
166
  if (type === 'tool_call') {
163
167
  const callId = asString(v['callId']);
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export declare function main(args?: readonly string[]): Promise<void>;
@@ -0,0 +1,198 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.main = main;
5
+ const auth_1 = require("../server/auth");
6
+ const certificates_1 = require("../server/certificates");
7
+ function printHelp() {
8
+ console.log(`
9
+ Dominds certificate tools
10
+
11
+ Usage:
12
+ dominds cert create [--host <host>] [--days <days>] [--force]
13
+ dominds cert self-cert [--host <host>] [--days <days>] [--force]
14
+ dominds cert status --host <host> [--port <port>] [--origin]
15
+
16
+ Options:
17
+ --host <host> Host name or IP address (default for create: detected non-loopback LAN hosts)
18
+ --days <days> Certificate validity in days (default: ${certificates_1.DEFAULT_SELF_SIGNED_CERT_DAYS})
19
+ --force Overwrite existing generated files
20
+ --port <port> Port for status --origin output
21
+ --origin Print only the effective origin, using https when a matching cert exists
22
+ --help Show this help message
23
+
24
+ Certificates live in ~/.dominds/certs/. A WebUI server automatically enables HTTPS when it finds
25
+ a certificate/key pair that matches the bind host or, for 0.0.0.0/::, a detected non-loopback LAN host.
26
+ `);
27
+ }
28
+ async function main(args = process.argv.slice(2)) {
29
+ const first = args[0];
30
+ if (first === undefined || first === '--help' || first === '-h') {
31
+ printHelp();
32
+ return;
33
+ }
34
+ const action = parseAction(first);
35
+ if (action === null) {
36
+ console.error(`Error: Unknown cert subcommand '${first}'`);
37
+ printHelp();
38
+ process.exit(1);
39
+ }
40
+ const options = parseOptions(args.slice(1), action);
41
+ if (options.kind === 'error') {
42
+ console.error(`Error: ${options.message}`);
43
+ printHelp();
44
+ process.exit(1);
45
+ }
46
+ if (action === 'create') {
47
+ const created = await (0, certificates_1.createSelfSignedCertificate)({
48
+ host: options.host,
49
+ altHosts: options.altHosts,
50
+ days: options.days,
51
+ force: options.force,
52
+ });
53
+ console.log(`Created self-signed certificate for ${created.host}`);
54
+ console.log(` hosts: ${created.hosts.join(', ')}`);
55
+ console.log(` cert: ${created.certPath}`);
56
+ console.log(` key: ${created.keyPath}`);
57
+ console.log(` meta: ${created.metadataPath}`);
58
+ console.log(` days: ${created.days}`);
59
+ return;
60
+ }
61
+ if (options.host === undefined) {
62
+ console.error('Error: cert status requires --host <host>');
63
+ printHelp();
64
+ process.exit(1);
65
+ }
66
+ const lookup = await (0, certificates_1.findAutoHttpsCertificateForHost)({ host: options.host });
67
+ if (options.origin) {
68
+ if (options.port === undefined) {
69
+ console.error('Error: cert status --origin requires --port <port>');
70
+ process.exit(1);
71
+ }
72
+ console.log((0, auth_1.formatServerOrigin)({
73
+ scheme: lookup.kind === 'found' ? 'https' : 'http',
74
+ host: lookup.kind === 'found' ? lookup.certificate.matchedHost : lookup.endpoint.urlHost,
75
+ port: options.port,
76
+ }));
77
+ return;
78
+ }
79
+ for (const diagnostic of lookup.diagnostics) {
80
+ console.warn(`warning: ${diagnostic.message}`);
81
+ }
82
+ if (lookup.kind === 'found') {
83
+ console.log(`HTTPS certificate: found`);
84
+ console.log(` cert: ${lookup.certificate.certPath}`);
85
+ console.log(` key: ${lookup.certificate.keyPath}`);
86
+ console.log(` matchedHost: ${lookup.certificate.matchedHost}`);
87
+ console.log(` validTo: ${lookup.certificate.validTo.toISOString()}`);
88
+ }
89
+ else {
90
+ console.log(`HTTPS certificate: not found`);
91
+ console.log(` certsDir: ${lookup.certsDirAbs}`);
92
+ }
93
+ }
94
+ function parseAction(value) {
95
+ switch (value) {
96
+ case 'create':
97
+ case 'self-cert':
98
+ return 'create';
99
+ case 'status':
100
+ return 'status';
101
+ default:
102
+ return null;
103
+ }
104
+ }
105
+ function parseOptions(args, action) {
106
+ let host;
107
+ let days;
108
+ const altHosts = [];
109
+ let force = false;
110
+ let port;
111
+ let origin = false;
112
+ for (let i = 0; i < args.length; i += 1) {
113
+ const arg = args[i];
114
+ if (arg === '--host') {
115
+ const next = args[i + 1];
116
+ if (!next)
117
+ return { kind: 'error', message: '--host requires a value' };
118
+ if (host === undefined)
119
+ host = next;
120
+ else
121
+ altHosts.push(next);
122
+ i += 1;
123
+ continue;
124
+ }
125
+ if (arg.startsWith('--host=')) {
126
+ const value = arg.slice('--host='.length);
127
+ if (host === undefined)
128
+ host = value;
129
+ else
130
+ altHosts.push(value);
131
+ continue;
132
+ }
133
+ if (arg === '--days') {
134
+ const next = args[i + 1];
135
+ const parsed = next === undefined ? null : parseInteger(next);
136
+ if (parsed === null)
137
+ return { kind: 'error', message: '--days requires an integer' };
138
+ days = parsed;
139
+ i += 1;
140
+ continue;
141
+ }
142
+ if (arg.startsWith('--days=')) {
143
+ const parsed = parseInteger(arg.slice('--days='.length));
144
+ if (parsed === null)
145
+ return { kind: 'error', message: '--days requires an integer' };
146
+ days = parsed;
147
+ continue;
148
+ }
149
+ if (arg === '--port') {
150
+ const next = args[i + 1];
151
+ const parsed = next === undefined ? null : parseInteger(next);
152
+ if (parsed === null || parsed < 1 || parsed > 65535) {
153
+ return { kind: 'error', message: '--port requires a valid TCP port' };
154
+ }
155
+ port = parsed;
156
+ i += 1;
157
+ continue;
158
+ }
159
+ if (arg.startsWith('--port=')) {
160
+ const parsed = parseInteger(arg.slice('--port='.length));
161
+ if (parsed === null || parsed < 1 || parsed > 65535) {
162
+ return { kind: 'error', message: '--port requires a valid TCP port' };
163
+ }
164
+ port = parsed;
165
+ continue;
166
+ }
167
+ if (arg === '--force') {
168
+ force = true;
169
+ continue;
170
+ }
171
+ if (arg === '--origin') {
172
+ origin = true;
173
+ continue;
174
+ }
175
+ if (arg === '--help' || arg === '-h') {
176
+ printHelp();
177
+ process.exit(0);
178
+ }
179
+ if (!arg.startsWith('-') && host === undefined && action === 'create') {
180
+ host = arg;
181
+ continue;
182
+ }
183
+ return { kind: 'error', message: `Unknown option '${arg}'` };
184
+ }
185
+ return { kind: 'ok', host, days, altHosts, force, port, origin };
186
+ }
187
+ function parseInteger(raw) {
188
+ if (!/^[0-9]+$/.test(raw))
189
+ return null;
190
+ const parsed = Number(raw);
191
+ return Number.isSafeInteger(parsed) ? parsed : null;
192
+ }
193
+ if (require.main === module) {
194
+ main().catch((error) => {
195
+ console.error(error instanceof Error ? error.message : String(error));
196
+ process.exit(1);
197
+ });
198
+ }
@@ -9,7 +9,7 @@
9
9
  * -p, --port <port> Port to listen on. Bare port is strict; suffix + tries higher ports; suffix - tries lower ports.
10
10
  * -h, --host <host> Host to bind to (default: localhost)
11
11
  * --nobrowser Do not open a browser (opt-out)
12
- * -h, --help Show help
12
+ * --help Show help
13
13
  */
14
14
  declare function main(args?: readonly string[]): Promise<void>;
15
15
  export { main };
package/dist/cli/webui.js CHANGED
@@ -10,7 +10,7 @@
10
10
  * -p, --port <port> Port to listen on. Bare port is strict; suffix + tries higher ports; suffix - tries lower ports.
11
11
  * -h, --host <host> Host to bind to (default: localhost)
12
12
  * --nobrowser Do not open a browser (opt-out)
13
- * -h, --help Show help
13
+ * --help Show help
14
14
  */
15
15
  Object.defineProperty(exports, "__esModule", { value: true });
16
16
  exports.main = main;
@@ -22,6 +22,14 @@ const server_1 = require("../server");
22
22
  const auth_1 = require("../server/auth");
23
23
  const port_selection_1 = require("../server/port-selection");
24
24
  const log = (0, log_1.createLogger)('webui');
25
+ function formatWebSocketEndpoint(params) {
26
+ const httpOrigin = (0, auth_1.formatServerOrigin)({
27
+ scheme: params.scheme === 'ws' ? 'http' : 'https',
28
+ host: params.host,
29
+ port: params.port,
30
+ });
31
+ return `${params.scheme}${httpOrigin.slice(httpOrigin.indexOf(':'))}/ws`;
32
+ }
25
33
  function printHelp() {
26
34
  console.log(`
27
35
  WebUI Server for dominds
@@ -145,12 +153,34 @@ async function main(args = process.argv.slice(2)) {
145
153
  });
146
154
  const httpServer = started.httpServer;
147
155
  const auth = started.auth;
148
- const baseUrl = `http://${started.host}:${started.port}`;
156
+ const baseUrl = (0, auth_1.formatServerOrigin)({
157
+ scheme: 'http',
158
+ host: started.urlHost,
159
+ port: started.port,
160
+ });
149
161
  log.info(`WebUI ready: ${baseUrl}`);
150
- log.debug(`WebSocket endpoint: ws://${started.host}:${started.port}/ws`);
162
+ log.debug(`WebSocket endpoint: ${formatWebSocketEndpoint({
163
+ scheme: 'ws',
164
+ host: started.urlHost,
165
+ port: started.port,
166
+ })}`);
167
+ if (started.httpsPort !== undefined && started.httpsUrlHost !== undefined) {
168
+ const httpsBaseUrl = (0, auth_1.formatServerOrigin)({
169
+ scheme: 'https',
170
+ host: started.httpsUrlHost,
171
+ port: started.httpsPort,
172
+ });
173
+ log.info(`HTTPS WebUI ready: ${httpsBaseUrl}`);
174
+ log.debug(`HTTPS WebSocket endpoint: ${formatWebSocketEndpoint({
175
+ scheme: 'wss',
176
+ host: started.httpsUrlHost,
177
+ port: started.httpsPort,
178
+ })}`);
179
+ }
151
180
  if (auth.kind === 'enabled') {
152
181
  const autoAuthUrl = (0, auth_1.formatAutoAuthUrl)({
153
- host: started.host,
182
+ scheme: 'http',
183
+ host: started.urlHost,
154
184
  port: started.port,
155
185
  authKey: auth.key,
156
186
  });
package/dist/cli.d.ts CHANGED
@@ -13,6 +13,7 @@
13
13
  * man - Render toolset manual to stdout
14
14
  * manual - Alias for man
15
15
  * validate_team_def - Validate explicit team toolset declarations
16
+ * cert - Create and inspect local HTTPS certificates
16
17
  * create - Create a new runtime workspace (rtws) from a template
17
18
  * install - Install a Dominds App into this rtws
18
19
  * doctor - Diagnose Dominds App state in this rtws
package/dist/cli.js CHANGED
@@ -14,6 +14,7 @@
14
14
  * man - Render toolset manual to stdout
15
15
  * manual - Alias for man
16
16
  * validate_team_def - Validate explicit team toolset declarations
17
+ * cert - Create and inspect local HTTPS certificates
17
18
  * create - Create a new runtime workspace (rtws) from a template
18
19
  * install - Install a Dominds App into this rtws
19
20
  * doctor - Diagnose Dominds App state in this rtws
@@ -70,6 +71,7 @@ const path = __importStar(require("path"));
70
71
  const runtime_1 = require("./apps/runtime");
71
72
  const dotenv_1 = require("./bootstrap/dotenv");
72
73
  const rtws_cli_1 = require("./bootstrap/rtws-cli");
74
+ const cert_1 = require("./cli/cert");
73
75
  const create_1 = require("./cli/create");
74
76
  const disable_1 = require("./cli/disable");
75
77
  const doctor_1 = require("./cli/doctor");
@@ -120,6 +122,7 @@ Subcommands:
120
122
  man [options] Render toolset manual to stdout
121
123
  manual [options] Alias for man
122
124
  validate_team_def [options] Validate explicit team toolset declarations
125
+ cert [options] Create and inspect local HTTPS certificates
123
126
  create [options] Create a new runtime workspace (rtws) from a template
124
127
  install [options] Install a Dominds App into this rtws
125
128
  doctor [options] Read-only diagnosis across manifest/lock/configuration/resolution/handshake
@@ -139,6 +142,7 @@ Examples:
139
142
  dominds read # Read team configuration
140
143
  dominds man ws_read --lang zh --all
141
144
  dominds validate_team_def # Validate toolset references in .minds/team.yaml
145
+ dominds cert create --host 192.168.1.10
142
146
  dominds create web-scaffold my-project # Create rtws from a template
143
147
  dominds doctor @longrun-ai/web-dev # Diagnose a single app across all app-state layers
144
148
 
@@ -195,7 +199,8 @@ async function main(argv = process.argv.slice(2)) {
195
199
  printVersion();
196
200
  process.exit(0);
197
201
  }
198
- const shouldSkipRtwsSetup = subcommandArgs.includes('--help') ||
202
+ const shouldSkipRtwsSetup = subcommand === 'cert' ||
203
+ subcommandArgs.includes('--help') ||
199
204
  (subcommand === 'tui' && subcommandArgs.includes('-h')) ||
200
205
  (subcommand === 'run' && subcommandArgs.includes('-h')) ||
201
206
  (subcommand === 'read' && subcommandArgs.includes('-h')) ||
@@ -232,6 +237,7 @@ async function main(argv = process.argv.slice(2)) {
232
237
  configureEnvProxySupport();
233
238
  }
234
239
  const shouldLoadApps = subcommand !== 'webui' &&
240
+ subcommand !== 'cert' &&
235
241
  subcommand !== 'create' &&
236
242
  subcommand !== 'new' &&
237
243
  subcommand !== 'install' &&
@@ -249,7 +255,7 @@ async function main(argv = process.argv.slice(2)) {
249
255
  if (shouldStartAppsHost) {
250
256
  await (0, runtime_1.initAppsRuntime)({
251
257
  rtwsRootAbs: process.cwd(),
252
- kernel: { host: '127.0.0.1', port: 0 },
258
+ kernel: { scheme: 'http', host: '127.0.0.1', port: 0 },
253
259
  });
254
260
  }
255
261
  }
@@ -279,6 +285,9 @@ async function main(argv = process.argv.slice(2)) {
279
285
  case 'validate_team_def':
280
286
  await runSubcommand('validate_team_def', subcommandArgs);
281
287
  break;
288
+ case 'cert':
289
+ await runSubcommand('cert', subcommandArgs);
290
+ break;
282
291
  case 'create':
283
292
  case 'new':
284
293
  await runSubcommand('create', subcommandArgs);
@@ -325,6 +334,9 @@ async function runSubcommand(subcommand, args) {
325
334
  else if (subcommand === 'validate_team_def') {
326
335
  await (0, validate_team_def_1.main)(args);
327
336
  }
337
+ else if (subcommand === 'cert') {
338
+ await (0, cert_1.main)(args);
339
+ }
328
340
  else if (subcommand === 'create') {
329
341
  await (0, create_1.main)(args);
330
342
  }
@@ -352,7 +364,7 @@ async function runSubcommand(subcommand, args) {
352
364
  }
353
365
  }
354
366
  catch (err) {
355
- console.error(`Failed to execute subcommand '${subcommand}':`, err);
367
+ console.error(`Failed to execute subcommand '${subcommand}': ${err instanceof Error ? err.message : String(err)}`);
356
368
  process.exit(1);
357
369
  }
358
370
  }
@@ -18,6 +18,7 @@ The `dominds` CLI provides a unified entry point, but the **primary interaction
18
18
  - [Web UI Interface](#web-ui-interface)
19
19
  - [Text User Interface (TUI) (Not Implemented Yet)](#text-user-interface-tui-not-implemented-yet)
20
20
  - [Minds Reader](#minds-reader)
21
+ - [Certificate Tools](#certificate-tools)
21
22
  - [rtws Creation](#rtws-creation)
22
23
  - [Usage Examples](#usage-examples)
23
24
  - [Dialog Storage](#dialog-storage)
@@ -32,6 +33,7 @@ The `dominds` package provides a unified CLI with subcommands:
32
33
  | `dominds` or `dominds webui` | Start Web UI (default, recommended) | Web UI |
33
34
  | `dominds tui` or `dominds run` | Terminal UI (planned; no stable implementation at the moment) | N/A |
34
35
  | `dominds read` | Read and inspect rtws/team minds configuration | CLI |
36
+ | `dominds cert` | Create and inspect local WebUI HTTPS certificates | CLI |
35
37
  | `dominds create` or `dominds new` | Create a new rtws (runtime workspace) from a template | CLI |
36
38
  | `dominds help` | Show help message | CLI |
37
39
  | `dominds --version` | Show version information | CLI |
@@ -54,6 +56,11 @@ dominds webui -C /path/to/my-rtws
54
56
  # Minds reader: inspect team configuration
55
57
  dominds read [options] [member-id]
56
58
 
59
+ # Certificate tools: create/inspect local HTTPS certificates
60
+ dominds cert create [--host <host>] [--days <days>] [--force]
61
+ dominds cert self-cert [--host <host>] [--days <days>] [--force]
62
+ dominds cert status --host <host> [--port <port>] [--origin]
63
+
57
64
  # rtws creation: scaffold a new runtime workspace
58
65
  dominds create <template> [directory]
59
66
  dominds new <template> [directory] # alias for create
@@ -137,6 +144,35 @@ dominds read --only-prompt
137
144
  dominds read --only-mem
138
145
  ```
139
146
 
147
+ ### Certificate Tools
148
+
149
+ ```bash
150
+ dominds cert create [--host <host>] [--days <days>] [--force]
151
+ dominds cert self-cert [--host <host>] [--days <days>] [--force]
152
+ dominds cert status --host <host> [--port <port>] [--origin]
153
+ ```
154
+
155
+ Create or inspect local HTTPS certificates for the Dominds WebUI. Certificates live in `~/.dominds/certs/` and match DNS/IP hostnames, not ports; one certificate covers every WebUI port on that host.
156
+
157
+ **Options:**
158
+
159
+ - `--host <host>` - Certificate SAN hostname or IP; repeatable for certificate creation; defaults to one or more detected non-loopback LAN hosts
160
+ - `--days <days>` - Certificate validity in days (default: 3650, or 10 years)
161
+ - `--force` - Overwrite existing generated files
162
+ - `--port <port>` - Port used when formatting `status --origin`
163
+ - `--origin` - Print only the effective origin; HTTPS when a cert matches, HTTP otherwise
164
+
165
+ **Examples:**
166
+
167
+ ```bash
168
+ dominds cert create
169
+ dominds cert create --host 192.168.1.10 --host my-host.local
170
+ dominds cert status --host 0.0.0.0
171
+ dominds cert status --host 0.0.0.0 --port 5666 --origin
172
+ ```
173
+
174
+ `localhost`, `loopback`, `127.0.0.0/8`, `::1`, `0.0.0.0`, and `::` are not certificate hosts. `0.0.0.0` / `::` only mean bind-all; certificate matching uses detected non-loopback LAN hosts.
175
+
140
176
  ### rtws Creation
141
177
 
142
178
  ```bash
@@ -18,6 +18,7 @@
18
18
  - [Web UI 界面](#web-ui-界面)
19
19
  - [文本用户界面 (TUI)(尚未实现)](#文本用户界面-tui尚未实现)
20
20
  - [Minds 阅读器](#minds-阅读器)
21
+ - [证书工具](#证书工具)
21
22
  - [rtws 创建](#rtws-创建)
22
23
  - [使用示例](#使用示例)
23
24
  - [对话存储](#对话存储)
@@ -32,6 +33,7 @@
32
33
  | `dominds` 或 `dominds webui` | 启动 Web UI(默认、推荐) | Web UI |
33
34
  | `dominds tui` 或 `dominds run` | 终端界面(规划中;当前版本暂不提供稳定) | N/A |
34
35
  | `dominds read` | 读取/分析团队 minds 配置 | CLI |
36
+ | `dominds cert` | 创建/检查本机 WebUI HTTPS 证书 | CLI |
35
37
  | `dominds create` 或 `dominds new` | 从模板创建新 rtws(运行时工作区) | CLI |
36
38
  | `dominds help` | 显示帮助消息 | CLI |
37
39
  | `dominds --version` | 显示版本信息 | CLI |
@@ -54,6 +56,11 @@ dominds webui -C /path/to/my-rtws
54
56
  # Minds 阅读器:分析团队配置
55
57
  dominds read [options] [member-id]
56
58
 
59
+ # 证书工具:创建/检查本机 HTTPS 证书
60
+ dominds cert create [--host <host>] [--days <days>] [--force]
61
+ dominds cert self-cert [--host <host>] [--days <days>] [--force]
62
+ dominds cert status --host <host> [--port <port>] [--origin]
63
+
57
64
  # rtws 创建:搭建新项目/运行时工作区
58
65
  dominds create <template> [directory]
59
66
  dominds new <template> [directory] # create 的别名
@@ -144,6 +151,35 @@ dominds read --only-prompt
144
151
  dominds read --only-mem
145
152
  ```
146
153
 
154
+ ### 证书工具
155
+
156
+ ```bash
157
+ dominds cert create [--host <host>] [--days <days>] [--force]
158
+ dominds cert self-cert [--host <host>] [--days <days>] [--force]
159
+ dominds cert status --host <host> [--port <port>] [--origin]
160
+ ```
161
+
162
+ 创建或检查 Dominds WebUI 的本机 HTTPS 证书。证书保存在 `~/.dominds/certs/`,按 DNS/IP 主机名匹配,不绑定端口;同一张证书可覆盖该主机上的所有 WebUI 端口。
163
+
164
+ **选项:**
165
+
166
+ - `--host <host>` - 证书 SAN 主机名或 IP;创建证书时可重复指定;未指定时使用检测到的一个或多个非 loopback LAN 主机
167
+ - `--days <days>` - 证书有效天数(默认:3650,即 10 年)
168
+ - `--force` - 覆盖已有的同名生成文件
169
+ - `--port <port>` - `status --origin` 输出 URL 时使用的端口
170
+ - `--origin` - 仅输出有效访问 origin;找到证书时输出 HTTPS,否则输出 HTTP
171
+
172
+ **示例:**
173
+
174
+ ```bash
175
+ dominds cert create
176
+ dominds cert create --host 192.168.1.10 --host my-host.local
177
+ dominds cert status --host 0.0.0.0
178
+ dominds cert status --host 0.0.0.0 --port 5666 --origin
179
+ ```
180
+
181
+ `localhost`、`loopback`、`127.0.0.0/8`、`::1`、`0.0.0.0`、`::` 不会作为 HTTPS 证书主机。`0.0.0.0` / `::` 只表示绑定所有地址,匹配证书时会使用检测到的非 loopback LAN 主机。
182
+
147
183
  ### rtws 创建
148
184
 
149
185
  ```bash
@@ -37,7 +37,14 @@ export declare function isAuthEnabled(auth: AuthConfig): auth is Extract<AuthCon
37
37
  export declare function getHttpAuthCheck(req: IncomingMessage, auth: AuthConfig): AuthCheckResult;
38
38
  export declare function getHttpAuthCheckAllowUrlParam(req: IncomingMessage, auth: AuthConfig, requestUrl: URL): AuthCheckResult;
39
39
  export declare function getWebSocketAuthCheck(req: IncomingMessage, auth: AuthConfig): AuthCheckResult;
40
+ export type ServerUrlScheme = 'http' | 'https';
41
+ export declare function formatServerOrigin(params: {
42
+ scheme: ServerUrlScheme;
43
+ host: string;
44
+ port: number;
45
+ }): string;
40
46
  export declare function formatAutoAuthUrl(params: {
47
+ scheme?: ServerUrlScheme;
41
48
  host: string;
42
49
  port: number;
43
50
  authKey: string;