@deepseek-ai/dsh-web-app 0.2.0-rc.2 → 0.2.1-alpha.2

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/lib/index.js CHANGED
@@ -1,11 +1,13 @@
1
+ import { t as parsePublicUrl } from "./public-url-D_dMK-yI.js";
1
2
  import { createRequire } from "node:module";
2
3
  import { spawn } from "node:child_process";
4
+ import { isIP } from "node:net";
3
5
  import { dirname, join } from "node:path";
4
- import { networkInterfaces } from "node:os";
5
6
  import { fileURLToPath } from "node:url";
6
7
  import z from "@deepseek-ai/schemastery";
7
8
  import { addHarnessSourceSection, auditStartupEntries } from "@deepseek-ai/dsh-app-boot";
8
9
  import * as FrontendStatic from "@deepseek-ai/dsh-host-frontend-static";
10
+ import { isLoopbackHost, normalizeBindAddress } from "@deepseek-ai/dsh-host-webserver";
9
11
  import { launchEnvironmentOf, launchedThroughSsh } from "@deepseek-ai/dsh-launch-environment";
10
12
  import { scrubbedParentEnv } from "@deepseek-ai/dsh-subprocess";
11
13
  //#region lib/types/index.js
@@ -16,9 +18,10 @@ import { scrubbedParentEnv } from "@deepseek-ai/dsh-subprocess";
16
18
  * the built frontend dist (workspace knowledge of this bundle, never user
17
19
  * config), mounts the `frontend-static` fallback owner over it, registers the
18
20
  * harness-source and web-surface prompt sections, the bash-visible web runtime
19
- * variable, the process-token URL line, and the default-browser handoff. The
20
- * model and shell retain the clean URL. App command-line values arrive through
21
- * the `webStartup` service expressions in the bundle patch.
21
+ * variable, the process-token URL line, and the default-browser handoff. An
22
+ * advertised `publicUrl` replaces the published bind-address URL. Non-loopback
23
+ * HTTP binds trigger an exposure warning. App command-line values arrive
24
+ * through the `webStartup` service expressions in the bundle patch.
22
25
  * @module @deepseek-ai/dsh-web-app
23
26
  */
24
27
  /** Stable Cordis plugin name. */
@@ -26,21 +29,16 @@ const name = "web-app";
26
29
  /** This dsh installation's root, from either this package's source or built entry. */
27
30
  const SOURCE_ROOT = fileURLToPath(new URL("../../../..", import.meta.url));
28
31
  const ANNOUNCED_ROOTS = /* @__PURE__ */ new WeakSet();
29
- /** Runtime service that releases Web rows after bind-dependent values resolve. */
30
- const WEB_RUNTIME_SERVICE = "webRuntime";
31
32
  /** Services required before the web runtime can mount. */
32
33
  const inject = ["webServer"];
33
34
  const Config = z.object({
34
35
  openBrowser: z.boolean().default(true),
35
36
  printUrl: z.boolean().default(true),
36
37
  surfaceContext: z.boolean().default(true),
37
- trustedHosts: z.array(String).default([])
38
+ publicUrl: z.transform(z.string(), (value) => parsePublicUrl(value).href)
38
39
  });
39
- /** Environment variable naming the canonical local URL of this Web GUI. */
40
+ /** Environment variable naming the advertised URL of this Web GUI. */
40
41
  const DSH_WEB_URL = "DSH_WEB_URL";
41
- const LOOPBACK_HOST = "127.0.0.1";
42
- /** The webserver schema's all-interfaces bind literal. */
43
- const ALL_INTERFACES_HOST = "0.0.0.0";
44
42
  const BROWSER_OPENER_MODULE = import.meta.resolve("open");
45
43
  const BROWSER_OPENER_PROGRAM = `
46
44
  try {
@@ -71,32 +69,46 @@ try {
71
69
  }
72
70
  `;
73
71
  /**
74
- * Resolve one LAN-trust snapshot from the active server bind.
75
- *
76
- * Derived entries are port-less IP literals: DNS rebinding needs an
77
- * attacker-controlled name, while an IP-literal Host is safe on any port and
78
- * an OS-assigned port is unknowable before bind.
79
- * @param bindHost - the active webserver bind host.
80
- * @param extra - explicit `--trusted-host` values, in argument order.
81
- * @returns the LAN display addresses and invocation-derived fence authorities.
72
+ * Local root URL of the active Web server, using the browser-compatible form
73
+ * of its bind address and brackets for IPv6 literals. Non-loopback binds such
74
+ * as a container's Pod address advertise that address.
75
+ * @throws when the runtime resolves without a bound webServer, or when the bind
76
+ * address has no URL form (see {@link advertisedBindHost}).
82
77
  */
83
- function resolveLanTrust(bindHost, extra) {
84
- const lanAddresses = bindHost === ALL_INTERFACES_HOST ? Object.values(networkInterfaces()).flat().filter((iface) => iface !== void 0 && iface.family === "IPv4" && !iface.internal).map((iface) => iface.address) : [];
85
- return {
86
- lanAddresses,
87
- trustedHosts: [...lanAddresses, ...extra]
88
- };
78
+ function localWebUrl(ctx) {
79
+ const webServer = ctx.get("webServer");
80
+ const port = webServer?.port;
81
+ if (webServer === void 0 || port === void 0) throw new Error("web-app: webServer service missing while resolving Web runtime");
82
+ const host = advertisedBindHost(webServer.host);
83
+ return `${webServer.protocol}//${isIP(host) === 6 ? `[${host}]` : host}:${String(port)}`;
84
+ }
85
+ /**
86
+ * Bind address in a URL. Loopback addresses use their canonical address text,
87
+ * so browsers treat them as local, trustworthy origins: a genuinely mapped
88
+ * literal such as `::ffff:127.0.0.1` reads as `127.0.0.1`, and a dotted-quad
89
+ * tail reads as the address it names, so `::0.0.0.1` reads as `::1` rather
90
+ * than as `0.0.0.1`. Interface zone IDs cannot appear in a URL; only a
91
+ * redundant loopback zone can be dropped.
92
+ * @param host - webserver bind address.
93
+ * @returns the zone-free URL host.
94
+ * @throws when a non-loopback zone requires an explicit public URL.
95
+ */
96
+ function advertisedBindHost(host) {
97
+ const zoneAt = host.indexOf("%");
98
+ const bare = zoneAt === -1 ? host : host.slice(0, zoneAt);
99
+ if (isLoopbackHost(bare)) return normalizeBindAddress(bare);
100
+ if (zoneAt === -1) return host;
101
+ throw new Error(`web-app: bind address ${JSON.stringify(host)} carries an interface zone id, which no URL can express; pass --public-url with the root browsers actually reach it through`);
102
+ }
103
+ /** Resolve the advertised root: this plugin's canonical `publicUrl`, or the bind-address URL (loopback when bound to loopback). */
104
+ function appRootUrl(ctx, publicUrl) {
105
+ if (publicUrl !== void 0) return publicUrl;
106
+ return localWebUrl(ctx);
89
107
  }
90
108
  /** Model-visible orientation and acceptance boundary for sessions created through `dsh web`. */
91
109
  function webSurfacePrompt(webUrl) {
92
110
  return `You are interacting with the user through the DeepSeek Harness Web GUI at ${webUrl}. When the user refers to "this page", "this GUI", or "this app" without naming another target, they mean this GUI. The browser provides no implicit DOM, route, or screenshot context. The client-plugin HMR receiver is active, but client-plugin changes reload without a refresh only while \`pnpm run dev:web\` is also running from this same checkout to rebuild their bundles; verify that watcher before promising automatic updates. Every other change — the apps/web shell and plain packages — requires rebuilding the affected Web artifacts and verifying this existing URL after a page refresh. Starting another server does not update this GUI. The apps/web Vite entry builds the shell but is not a standalone application because only dsh web injects window.__DSH_BOOT__. Do not start a replacement server unless the user asks; if one is needed, use a managed background job and verify its exact URL.`;
93
111
  }
94
- /** Resolve the canonical loopback URL from the active Web server. */
95
- function localWebUrl(ctx) {
96
- const port = ctx.get("webServer")?.port;
97
- if (port === void 0) throw new Error("web-app: webServer service missing while resolving Web runtime");
98
- return `http://${LOOPBACK_HOST}:${String(port)}`;
99
- }
100
112
  /**
101
113
  * Dist location is workspace knowledge of this bundle: anchored on the
102
114
  * frontend package manifest, not configured. Existence is a request-time
@@ -170,9 +182,11 @@ const internals = {
170
182
  * @param config - validated {@link Config}.
171
183
  */
172
184
  function apply(ctx, config) {
173
- const runtime = resolveLanTrust(ctx.webServer.host, config.trustedHosts);
185
+ const publicUrl = config.publicUrl ?? void 0;
186
+ const boundHost = ctx.webServer.host;
187
+ if (publicUrl === void 0) advertisedBindHost(boundHost);
188
+ if (ctx.webServer.protocol === "http:" && !isLoopbackHost(boundHost)) console.warn(`dsh web: listening on ${boundHost} over plain HTTP; restrict this port to a trusted proxy or network`);
174
189
  const handoffBrowser = config.openBrowser && !launchedThroughSsh(launchEnvironmentOf(ctx));
175
- ctx.provide(WEB_RUNTIME_SERVICE, runtime);
176
190
  ctx.plugin(FrontendStatic, { distIndex: internals.resolveDistIndex() });
177
191
  if (config.surfaceContext) {
178
192
  ctx.inject(["systemPrompt"], (promptCtx) => {
@@ -180,27 +194,24 @@ function apply(ctx, config) {
180
194
  promptCtx.systemPrompt.section({
181
195
  name: "app:web-surface",
182
196
  order: promptCtx.systemPrompt.getSectionOrder("WEB_SURFACE"),
183
- text: () => webSurfacePrompt(localWebUrl(promptCtx))
197
+ text: () => webSurfacePrompt(appRootUrl(promptCtx, publicUrl))
184
198
  });
185
199
  });
186
200
  ctx.inject(["shellEnv"], (runtimeCtx) => {
187
201
  runtimeCtx.shellEnv.register({
188
202
  name: "web-runtime",
189
- variables: { [DSH_WEB_URL]: { description: "Canonical local URL of the DeepSeek Harness Web GUI serving this session." } },
190
- resolve: () => ({ [DSH_WEB_URL]: localWebUrl(runtimeCtx) })
203
+ variables: { [DSH_WEB_URL]: { description: "Advertised URL of the DeepSeek Harness Web GUI serving this session." } },
204
+ resolve: () => ({ [DSH_WEB_URL]: appRootUrl(runtimeCtx, publicUrl) })
191
205
  });
192
206
  });
193
207
  }
194
208
  if (config.printUrl || handoffBrowser) ctx.inject(["connection"], (connectionCtx) => {
195
209
  const announceReady = () => {
196
210
  if (ANNOUNCED_ROOTS.has(connectionCtx.root)) return;
197
- const webUrl = localWebUrl(connectionCtx);
211
+ const webUrl = appRootUrl(connectionCtx, publicUrl);
198
212
  const authenticatedUrl = connectionCtx.connection.authenticatedUrl(webUrl);
199
- const lanCandidate = runtime.lanAddresses[0];
200
- const port = connectionCtx.webServer.port;
201
- const lanUrl = lanCandidate === void 0 ? void 0 : connectionCtx.connection.authenticatedUrl(`http://${lanCandidate}:${String(port)}`);
202
213
  ANNOUNCED_ROOTS.add(connectionCtx.root);
203
- if (config.printUrl) console.log(`dsh web: ${authenticatedUrl}${lanUrl === void 0 ? "" : ` (LAN: ${lanUrl})`}`);
214
+ if (config.printUrl) console.log(`dsh web: ${authenticatedUrl}`);
204
215
  if (handoffBrowser) {
205
216
  console.log("dsh web: opening the default browser; pass --no-open to disable");
206
217
  internals.openBrowser(authenticatedUrl).catch((error) => {
@@ -218,4 +229,4 @@ function apply(ctx, config) {
218
229
  });
219
230
  }
220
231
  //#endregion
221
- export { Config, apply, inject, internals, name, resolveLanTrust };
232
+ export { Config, apply, inject, internals, name };
@@ -0,0 +1,29 @@
1
+ //#region lib/types/public-url.js
2
+ /**
3
+ * Canonical public HTTP(S) root parsing shared by this bundle's `publicUrl`
4
+ * config and its CLI startup provider.
5
+ * @module @deepseek-ai/dsh-web-app/public-url
6
+ */
7
+ /**
8
+ * Parse the advertised public HTTP(S) root of a prefix-stripping proxy
9
+ * deployment, rejecting the hazards WHATWG URL parsing would accept on its own,
10
+ * then normalize the path to end in `/`.
11
+ * @param value - advertised public HTTP(S) root.
12
+ * @param label - field name or CLI flag included in rejection messages.
13
+ * @returns the parsed URL with a trailing-slash pathname.
14
+ * @throws when the value is not a usable advertised root, naming the reason.
15
+ */
16
+ function parsePublicUrl(value, label = "publicUrl") {
17
+ if (/[\u0000-\u0020\u007f]/u.test(value)) throw new Error(`${label} must not contain ASCII whitespace or control characters`);
18
+ if (!/^https?:\/\//iu.test(value)) throw new Error(`${label} must be an absolute http or https URL of the form http(s)://host[/prefix]`);
19
+ if (value.includes("?") || value.includes("#")) throw new Error(`${label} must not include a query or fragment (? or #)`);
20
+ const [authority = ""] = value.slice(value.indexOf("://") + 3).split(/[/\\]/, 1);
21
+ if (authority === "") throw new Error(`${label} must name a host after http:// or https://`);
22
+ if (authority.includes("@")) throw new Error(`${label} must not include credentials (a username or password before @, including an empty user)`);
23
+ const url = URL.parse(value);
24
+ if (url === null) throw new Error(`${label} must be an absolute http or https URL`);
25
+ if (!url.pathname.endsWith("/")) url.pathname += "/";
26
+ return url;
27
+ }
28
+ //#endregion
29
+ export { parsePublicUrl as t };
package/lib/startup.js CHANGED
@@ -1,11 +1,14 @@
1
+ import { t as parsePublicUrl } from "./public-url-D_dMK-yI.js";
2
+ import { isWildcardHost } from "@deepseek-ai/dsh-host-webserver";
1
3
  import { Command } from "commander";
2
4
  import { parseCmdline } from "@deepseek-ai/dsh-cmdline";
3
5
  //#region lib/types/startup.js
4
6
  /**
5
7
  * The web app's command-line provider: it parses the `dsh --profile web` flag
6
- * family (`--host`, `--port`, `--trusted-host`, `--no-open`) and its `--help`
7
- * text, then provides the immutable values as {@link WEB_STARTUP_SERVICE}.
8
- * Ordinary rows inject that service before reading it from lazy config.
8
+ * family (`--host`, `--port`, `--public-url`, `--trusted-host`, `--no-open`, TLS)
9
+ * and its `--help` text, then provides the immutable values as
10
+ * {@link WEB_STARTUP_SERVICE}. Ordinary rows inject that service before
11
+ * reading it from lazy config.
9
12
  * @module @deepseek-ai/dsh-web-app/startup
10
13
  */
11
14
  /** Stable Cordis plugin name. */
@@ -19,30 +22,48 @@ const WEB_STARTUP_SERVICE = "webStartup";
19
22
  * @returns a fresh program, so one process can parse more than once (tests).
20
23
  */
21
24
  function webCommand() {
22
- return new Command().name("dsh --profile web").description("Serve the DeepSeek Harness browser UI.").helpOption("-h, --help", "show this help").option("--host <host>", "bind host").option("--no-open", "do not open the Web UI in the default browser").option("--port <port>", "listen port; pass 0 to let the OS pick a free one").option("--trusted-host <authority...>", "extra authority the /api browser-trust fence accepts (host or host:port; repeatable)").addHelpText("after", `
25
+ return new Command().name("dsh --profile web").description("Serve the DeepSeek Harness browser UI.").helpOption("-h, --help", "show this help").option("--host <host>", "bind address: one concrete IPv4 or IPv6 literal of a local interface; wildcard addresses are rejected").option("--no-open", "do not open the Web UI in the default browser").option("--port <port>", "listen port; pass 0 to let the OS pick a free one").option("--public-url <url>", "advertise this HTTP(S) root in the printed, opened, web-surface, and DSH_WEB_URL forms; grants no trust").option("--trusted-host <authority...>", "extra authority the /api browser-trust fence accepts (host or host:port; repeatable)").option("--tls-cert <file>", "PEM server certificate chain; requires --tls-key").option("--tls-key <file>", "unencrypted PEM private key; requires --tls-cert").addHelpText("after", `
23
26
  Examples:
24
27
  dsh --profile web serve on the composed host and port
25
28
  dsh --profile web --no-open serve without opening a browser
26
29
  dsh --profile web --port 8080 serve on another port
30
+ dsh --profile web --host 10.0.0.7 bind one local interface address
31
+ dsh --profile web --public-url https://app.example/ui/ --trusted-host app.example
32
+ advertise a prefix-stripping HTTPS proxy entry and admit its authority
33
+ dsh --profile web --tls-cert ./server-chain.pem --tls-key ./server-key.pem
34
+ serve HTTPS with an existing certificate and key
27
35
  `);
28
36
  }
29
37
  /**
30
38
  * Parse and provide the Web invocation as an ordinary Cordis service. The
31
- * command's action publishes the flags this invocation named; `--host 0.0.0.0`
32
- * or a non-numeric `--port` is a usage error, so on rejection (and on `--help`)
33
- * nothing is provided.
39
+ * command's action publishes the flags this invocation named. Invalid flags,
40
+ * including an unpaired TLS certificate or key, are usage errors; rejection
41
+ * and `--help` provide no service.
34
42
  * @param ctx - plugin context carrying the command line.
35
43
  */
36
44
  function apply(ctx) {
37
45
  const program = webCommand();
38
46
  program.action(() => {
39
47
  const options = program.opts();
40
- if (options.host === "0.0.0.0") program.error("error: --host 0.0.0.0 is intentionally not supported yet for safety: it would expose remote code execution to the network; use 127.0.0.1 instead");
48
+ if (options.host !== void 0 && isWildcardHost(options.host)) program.error(`error: --host ${options.host} is an unspecified (wildcard) address, which is not supported: binding every interface would expose remote code execution to the network; bind one concrete IPv4 or IPv6 address instead`);
41
49
  if (options.port !== void 0 && !/^\d+$/.test(options.port)) program.error(`error: --port must be a number, got ${JSON.stringify(options.port)}`);
50
+ let tls;
51
+ if (options.tlsCert !== void 0 && options.tlsKey !== void 0) tls = {
52
+ certFile: options.tlsCert,
53
+ keyFile: options.tlsKey
54
+ };
55
+ else if (options.tlsCert !== void 0 || options.tlsKey !== void 0) program.error("error: --tls-cert and --tls-key must be supplied together");
56
+ if (options.publicUrl !== void 0) try {
57
+ parsePublicUrl(options.publicUrl, "--public-url");
58
+ } catch (error) {
59
+ program.error(`error: ${error.message}`);
60
+ }
42
61
  ctx.provide(WEB_STARTUP_SERVICE, {
43
62
  openBrowser: options.open,
44
63
  ...options.host !== void 0 && { host: options.host },
45
64
  ...options.port !== void 0 && { port: Number(options.port) },
65
+ ...options.publicUrl !== void 0 && { publicUrl: options.publicUrl },
66
+ ...tls !== void 0 && { tls },
46
67
  trustedHosts: options.trustedHost ?? []
47
68
  });
48
69
  });
@@ -5,9 +5,10 @@
5
5
  * the built frontend dist (workspace knowledge of this bundle, never user
6
6
  * config), mounts the `frontend-static` fallback owner over it, registers the
7
7
  * harness-source and web-surface prompt sections, the bash-visible web runtime
8
- * variable, the process-token URL line, and the default-browser handoff. The
9
- * model and shell retain the clean URL. App command-line values arrive through
10
- * the `webStartup` service expressions in the bundle patch.
8
+ * variable, the process-token URL line, and the default-browser handoff. An
9
+ * advertised `publicUrl` replaces the published bind-address URL. Non-loopback
10
+ * HTTP binds trigger an exposure warning. App command-line values arrive
11
+ * through the `webStartup` service expressions in the bundle patch.
11
12
  * @module @deepseek-ai/dsh-web-app
12
13
  */
13
14
  import type { Context } from '@deepseek-ai/cordis';
@@ -29,28 +30,16 @@ export interface Config {
29
30
  * orientation text would be false.
30
31
  */
31
32
  surfaceContext: boolean;
32
- /** Explicit `--trusted-host` authorities from this invocation. */
33
- trustedHosts: string[];
33
+ /**
34
+ * Canonical HTTP(S) root to advertise in the printed and opened URL,
35
+ * `DSH_WEB_URL`, and the web-surface orientation, e.g.
36
+ * `https://app.example/ui/`, normalized to end in `/`. Configures no routing
37
+ * or authentication; see [public deployments](../README.md#public-deployments).
38
+ * Absent or YAML `null` advertises the bind-address URL.
39
+ */
40
+ publicUrl?: string;
34
41
  }
35
42
  export declare const Config: z<Config>;
36
- /** Bind-dependent Web values shared by the trust fence and URL display. */
37
- export interface WebRuntimeValues {
38
- /** LAN IPv4 literals sampled once when the server binds all interfaces. */
39
- lanAddresses: string[];
40
- /** LAN literals followed by explicit invocation authorities. */
41
- trustedHosts: string[];
42
- }
43
- /**
44
- * Resolve one LAN-trust snapshot from the active server bind.
45
- *
46
- * Derived entries are port-less IP literals: DNS rebinding needs an
47
- * attacker-controlled name, while an IP-literal Host is safe on any port and
48
- * an OS-assigned port is unknowable before bind.
49
- * @param bindHost - the active webserver bind host.
50
- * @param extra - explicit `--trusted-host` values, in argument order.
51
- * @returns the LAN display addresses and invocation-derived fence authorities.
52
- */
53
- export declare function resolveLanTrust(bindHost: string, extra: readonly string[]): WebRuntimeValues;
54
43
  /** Test hooks for the built dist and native browser handoff; production never mutates them. */
55
44
  export declare const internals: {
56
45
  resolveDistIndex: () => string;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Canonical public HTTP(S) root parsing shared by this bundle's `publicUrl`
3
+ * config and its CLI startup provider.
4
+ * @module @deepseek-ai/dsh-web-app/public-url
5
+ */
6
+ /**
7
+ * Parse the advertised public HTTP(S) root of a prefix-stripping proxy
8
+ * deployment, rejecting the hazards WHATWG URL parsing would accept on its own,
9
+ * then normalize the path to end in `/`.
10
+ * @param value - advertised public HTTP(S) root.
11
+ * @param label - field name or CLI flag included in rejection messages.
12
+ * @returns the parsed URL with a trailing-slash pathname.
13
+ * @throws when the value is not a usable advertised root, naming the reason.
14
+ */
15
+ export declare function parsePublicUrl(value: string, label?: string): URL;
16
+ //# sourceMappingURL=public-url.d.ts.map
@@ -1,11 +1,13 @@
1
1
  /**
2
2
  * The web app's command-line provider: it parses the `dsh --profile web` flag
3
- * family (`--host`, `--port`, `--trusted-host`, `--no-open`) and its `--help`
4
- * text, then provides the immutable values as {@link WEB_STARTUP_SERVICE}.
5
- * Ordinary rows inject that service before reading it from lazy config.
3
+ * family (`--host`, `--port`, `--public-url`, `--trusted-host`, `--no-open`, TLS)
4
+ * and its `--help` text, then provides the immutable values as
5
+ * {@link WEB_STARTUP_SERVICE}. Ordinary rows inject that service before
6
+ * reading it from lazy config.
6
7
  * @module @deepseek-ai/dsh-web-app/startup
7
8
  */
8
9
  import type { Context } from '@deepseek-ai/cordis';
10
+ import { type TlsConfig } from '@deepseek-ai/dsh-host-webserver';
9
11
  /** Stable Cordis plugin name. */
10
12
  export declare const name = "web-startup";
11
13
  /** Services required before the flags can be resolved. */
@@ -20,14 +22,21 @@ export interface WebStartupValues {
20
22
  host?: string;
21
23
  /** `--port`, absent when the invocation did not name one. */
22
24
  port?: number;
25
+ /**
26
+ * `--public-url`, absent when not specified: the advertised HTTP(S) root.
27
+ * See [public deployments](../README.md#public-deployments).
28
+ */
29
+ publicUrl?: string;
23
30
  /** Explicit `--trusted-host` authorities, in argument order. */
24
31
  trustedHosts: string[];
32
+ /** Server certificate chain and unencrypted private key supplied together. */
33
+ tls?: TlsConfig;
25
34
  }
26
35
  /**
27
36
  * Parse and provide the Web invocation as an ordinary Cordis service. The
28
- * command's action publishes the flags this invocation named; `--host 0.0.0.0`
29
- * or a non-numeric `--port` is a usage error, so on rejection (and on `--help`)
30
- * nothing is provided.
37
+ * command's action publishes the flags this invocation named. Invalid flags,
38
+ * including an unpaired TLS certificate or key, are usage errors; rejection
39
+ * and `--help` provide no service.
31
40
  * @param ctx - plugin context carrying the command line.
32
41
  */
33
42
  export declare function apply(ctx: Context): void;