@deepseek-ai/dsh-web-app 0.2.1-alpha.1 → 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/README.i18n.yaml +18 -21
- package/README.md +30 -22
- package/README.zh.md +30 -22
- package/cordis.patch.yml +18 -24
- package/lib/index.js +44 -41
- package/lib/startup.js +17 -6
- package/lib/types/index.d.ts +6 -26
- package/lib/types/startup.d.ts +7 -4
- package/package.json +142 -143
- package/presets/cordis.patch.yml +0 -25
- package/presets/ptc.patch.yml +0 -25
- package/presets/standard.patch.yml +0 -25
package/lib/index.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { t as parsePublicUrl } from "./public-url-D_dMK-yI.js";
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import { spawn } from "node:child_process";
|
|
4
|
+
import { isIP } from "node:net";
|
|
4
5
|
import { dirname, join } from "node:path";
|
|
5
|
-
import { networkInterfaces } from "node:os";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
7
|
import z from "@deepseek-ai/schemastery";
|
|
8
8
|
import { addHarnessSourceSection, auditStartupEntries } from "@deepseek-ai/dsh-app-boot";
|
|
9
9
|
import * as FrontendStatic from "@deepseek-ai/dsh-host-frontend-static";
|
|
10
|
+
import { isLoopbackHost, normalizeBindAddress } from "@deepseek-ai/dsh-host-webserver";
|
|
10
11
|
import { launchEnvironmentOf, launchedThroughSsh } from "@deepseek-ai/dsh-launch-environment";
|
|
11
12
|
import { scrubbedParentEnv } from "@deepseek-ai/dsh-subprocess";
|
|
12
13
|
//#region lib/types/index.js
|
|
@@ -18,9 +19,9 @@ import { scrubbedParentEnv } from "@deepseek-ai/dsh-subprocess";
|
|
|
18
19
|
* config), mounts the `frontend-static` fallback owner over it, registers the
|
|
19
20
|
* harness-source and web-surface prompt sections, the bash-visible web runtime
|
|
20
21
|
* variable, the process-token URL line, and the default-browser handoff. An
|
|
21
|
-
* advertised `publicUrl` replaces the published
|
|
22
|
-
*
|
|
23
|
-
* expressions in the bundle patch.
|
|
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.
|
|
24
25
|
* @module @deepseek-ai/dsh-web-app
|
|
25
26
|
*/
|
|
26
27
|
/** Stable Cordis plugin name. */
|
|
@@ -28,22 +29,16 @@ const name = "web-app";
|
|
|
28
29
|
/** This dsh installation's root, from either this package's source or built entry. */
|
|
29
30
|
const SOURCE_ROOT = fileURLToPath(new URL("../../../..", import.meta.url));
|
|
30
31
|
const ANNOUNCED_ROOTS = /* @__PURE__ */ new WeakSet();
|
|
31
|
-
/** Runtime service that releases Web rows after bind-dependent values resolve. */
|
|
32
|
-
const WEB_RUNTIME_SERVICE = "webRuntime";
|
|
33
32
|
/** Services required before the web runtime can mount. */
|
|
34
33
|
const inject = ["webServer"];
|
|
35
34
|
const Config = z.object({
|
|
36
35
|
openBrowser: z.boolean().default(true),
|
|
37
36
|
printUrl: z.boolean().default(true),
|
|
38
37
|
surfaceContext: z.boolean().default(true),
|
|
39
|
-
publicUrl: z.transform(z.string(), (value) => parsePublicUrl(value).href)
|
|
40
|
-
trustedHosts: z.array(String).default([])
|
|
38
|
+
publicUrl: z.transform(z.string(), (value) => parsePublicUrl(value).href)
|
|
41
39
|
});
|
|
42
40
|
/** Environment variable naming the advertised URL of this Web GUI. */
|
|
43
41
|
const DSH_WEB_URL = "DSH_WEB_URL";
|
|
44
|
-
const LOOPBACK_HOST = "127.0.0.1";
|
|
45
|
-
/** The webserver schema's all-interfaces bind literal. */
|
|
46
|
-
const ALL_INTERFACES_HOST = "0.0.0.0";
|
|
47
42
|
const BROWSER_OPENER_MODULE = import.meta.resolve("open");
|
|
48
43
|
const BROWSER_OPENER_PROGRAM = `
|
|
49
44
|
try {
|
|
@@ -74,36 +69,46 @@ try {
|
|
|
74
69
|
}
|
|
75
70
|
`;
|
|
76
71
|
/**
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
* @param bindHost - the active webserver bind host.
|
|
83
|
-
* @param extra - explicit `--trusted-host` values, in argument order.
|
|
84
|
-
* @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}).
|
|
85
77
|
*/
|
|
86
|
-
function resolveLanTrust(bindHost, extra) {
|
|
87
|
-
const lanAddresses = bindHost === ALL_INTERFACES_HOST ? Object.values(networkInterfaces()).flat().filter((iface) => iface !== void 0 && iface.family === "IPv4" && !iface.internal).map((iface) => iface.address) : [];
|
|
88
|
-
return {
|
|
89
|
-
lanAddresses,
|
|
90
|
-
trustedHosts: [...lanAddresses, ...extra]
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
/** Model-visible orientation and acceptance boundary for sessions created through `dsh web`. */
|
|
94
|
-
function webSurfacePrompt(webUrl) {
|
|
95
|
-
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.`;
|
|
96
|
-
}
|
|
97
|
-
/** Resolve the canonical loopback URL from the active Web server. */
|
|
98
78
|
function localWebUrl(ctx) {
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
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
102
|
}
|
|
103
|
+
/** Resolve the advertised root: this plugin's canonical `publicUrl`, or the bind-address URL (loopback when bound to loopback). */
|
|
103
104
|
function appRootUrl(ctx, publicUrl) {
|
|
104
105
|
if (publicUrl !== void 0) return publicUrl;
|
|
105
106
|
return localWebUrl(ctx);
|
|
106
107
|
}
|
|
108
|
+
/** Model-visible orientation and acceptance boundary for sessions created through `dsh web`. */
|
|
109
|
+
function webSurfacePrompt(webUrl) {
|
|
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.`;
|
|
111
|
+
}
|
|
107
112
|
/**
|
|
108
113
|
* Dist location is workspace knowledge of this bundle: anchored on the
|
|
109
114
|
* frontend package manifest, not configured. Existence is a request-time
|
|
@@ -177,10 +182,11 @@ const internals = {
|
|
|
177
182
|
* @param config - validated {@link Config}.
|
|
178
183
|
*/
|
|
179
184
|
function apply(ctx, config) {
|
|
180
|
-
const runtime = resolveLanTrust(ctx.webServer.host, config.trustedHosts);
|
|
181
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`);
|
|
182
189
|
const handoffBrowser = config.openBrowser && !launchedThroughSsh(launchEnvironmentOf(ctx));
|
|
183
|
-
ctx.provide(WEB_RUNTIME_SERVICE, runtime);
|
|
184
190
|
ctx.plugin(FrontendStatic, { distIndex: internals.resolveDistIndex() });
|
|
185
191
|
if (config.surfaceContext) {
|
|
186
192
|
ctx.inject(["systemPrompt"], (promptCtx) => {
|
|
@@ -204,11 +210,8 @@ function apply(ctx, config) {
|
|
|
204
210
|
if (ANNOUNCED_ROOTS.has(connectionCtx.root)) return;
|
|
205
211
|
const webUrl = appRootUrl(connectionCtx, publicUrl);
|
|
206
212
|
const authenticatedUrl = connectionCtx.connection.authenticatedUrl(webUrl);
|
|
207
|
-
const lanCandidate = runtime.lanAddresses[0];
|
|
208
|
-
const port = connectionCtx.webServer.port;
|
|
209
|
-
const lanUrl = lanCandidate === void 0 ? void 0 : connectionCtx.connection.authenticatedUrl(`http://${lanCandidate}:${String(port)}`);
|
|
210
213
|
ANNOUNCED_ROOTS.add(connectionCtx.root);
|
|
211
|
-
if (config.printUrl) console.log(`dsh web: ${authenticatedUrl}
|
|
214
|
+
if (config.printUrl) console.log(`dsh web: ${authenticatedUrl}`);
|
|
212
215
|
if (handoffBrowser) {
|
|
213
216
|
console.log("dsh web: opening the default browser; pass --no-open to disable");
|
|
214
217
|
internals.openBrowser(authenticatedUrl).catch((error) => {
|
|
@@ -226,4 +229,4 @@ function apply(ctx, config) {
|
|
|
226
229
|
});
|
|
227
230
|
}
|
|
228
231
|
//#endregion
|
|
229
|
-
export { Config, apply, inject, internals, name
|
|
232
|
+
export { Config, apply, inject, internals, name };
|
package/lib/startup.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { t as parsePublicUrl } from "./public-url-D_dMK-yI.js";
|
|
2
|
+
import { isWildcardHost } from "@deepseek-ai/dsh-host-webserver";
|
|
2
3
|
import { Command } from "commander";
|
|
3
4
|
import { parseCmdline } from "@deepseek-ai/dsh-cmdline";
|
|
4
5
|
//#region lib/types/startup.js
|
|
5
6
|
/**
|
|
6
7
|
* The web app's command-line provider: it parses the `dsh --profile web` flag
|
|
7
|
-
* family (`--host`, `--port`, `--public-url`, `--trusted-host`, `--no-open
|
|
8
|
+
* family (`--host`, `--port`, `--public-url`, `--trusted-host`, `--no-open`, TLS)
|
|
8
9
|
* and its `--help` text, then provides the immutable values as
|
|
9
10
|
* {@link WEB_STARTUP_SERVICE}. Ordinary rows inject that service before
|
|
10
11
|
* reading it from lazy config.
|
|
@@ -21,28 +22,37 @@ const WEB_STARTUP_SERVICE = "webStartup";
|
|
|
21
22
|
* @returns a fresh program, so one process can parse more than once (tests).
|
|
22
23
|
*/
|
|
23
24
|
function webCommand() {
|
|
24
|
-
return new Command().name("dsh --profile web").description("Serve the DeepSeek Harness browser UI.").helpOption("-h, --help", "show this help").option("--host <host>", "bind
|
|
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", `
|
|
25
26
|
Examples:
|
|
26
27
|
dsh --profile web serve on the composed host and port
|
|
27
28
|
dsh --profile web --no-open serve without opening a browser
|
|
28
29
|
dsh --profile web --port 8080 serve on another port
|
|
30
|
+
dsh --profile web --host 10.0.0.7 bind one local interface address
|
|
29
31
|
dsh --profile web --public-url https://app.example/ui/ --trusted-host app.example
|
|
30
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
|
|
31
35
|
`);
|
|
32
36
|
}
|
|
33
37
|
/**
|
|
34
38
|
* Parse and provide the Web invocation as an ordinary Cordis service. The
|
|
35
|
-
* command's action publishes the flags this invocation named
|
|
36
|
-
*
|
|
37
|
-
*
|
|
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.
|
|
38
42
|
* @param ctx - plugin context carrying the command line.
|
|
39
43
|
*/
|
|
40
44
|
function apply(ctx) {
|
|
41
45
|
const program = webCommand();
|
|
42
46
|
program.action(() => {
|
|
43
47
|
const options = program.opts();
|
|
44
|
-
if (options.host
|
|
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`);
|
|
45
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");
|
|
46
56
|
if (options.publicUrl !== void 0) try {
|
|
47
57
|
parsePublicUrl(options.publicUrl, "--public-url");
|
|
48
58
|
} catch (error) {
|
|
@@ -53,6 +63,7 @@ function apply(ctx) {
|
|
|
53
63
|
...options.host !== void 0 && { host: options.host },
|
|
54
64
|
...options.port !== void 0 && { port: Number(options.port) },
|
|
55
65
|
...options.publicUrl !== void 0 && { publicUrl: options.publicUrl },
|
|
66
|
+
...tls !== void 0 && { tls },
|
|
56
67
|
trustedHosts: options.trustedHost ?? []
|
|
57
68
|
});
|
|
58
69
|
});
|
package/lib/types/index.d.ts
CHANGED
|
@@ -6,9 +6,9 @@
|
|
|
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
8
|
* variable, the process-token URL line, and the default-browser handoff. An
|
|
9
|
-
* advertised `publicUrl` replaces the published
|
|
10
|
-
*
|
|
11
|
-
* expressions in the bundle patch.
|
|
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.
|
|
12
12
|
* @module @deepseek-ai/dsh-web-app
|
|
13
13
|
*/
|
|
14
14
|
import type { Context } from '@deepseek-ai/cordis';
|
|
@@ -33,33 +33,13 @@ export interface Config {
|
|
|
33
33
|
/**
|
|
34
34
|
* Canonical HTTP(S) root to advertise in the printed and opened URL,
|
|
35
35
|
* `DSH_WEB_URL`, and the web-surface orientation, e.g.
|
|
36
|
-
* `https://app.example/ui/`, normalized to end in `/`.
|
|
37
|
-
* see [public deployments](../README.md#public-deployments).
|
|
38
|
-
* `null` advertises the
|
|
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
39
|
*/
|
|
40
40
|
publicUrl?: string;
|
|
41
|
-
/** Explicit `--trusted-host` authorities from this invocation. */
|
|
42
|
-
trustedHosts: string[];
|
|
43
41
|
}
|
|
44
42
|
export declare const Config: z<Config>;
|
|
45
|
-
/** Bind-dependent Web values shared by the trust fence and URL display. */
|
|
46
|
-
export interface WebRuntimeValues {
|
|
47
|
-
/** LAN IPv4 literals sampled once when the server binds all interfaces. */
|
|
48
|
-
lanAddresses: string[];
|
|
49
|
-
/** LAN literals followed by explicit invocation authorities. */
|
|
50
|
-
trustedHosts: string[];
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* Resolve one LAN-trust snapshot from the active server bind.
|
|
54
|
-
*
|
|
55
|
-
* Derived entries are port-less IP literals: DNS rebinding needs an
|
|
56
|
-
* attacker-controlled name, while an IP-literal Host is safe on any port and
|
|
57
|
-
* an OS-assigned port is unknowable before bind.
|
|
58
|
-
* @param bindHost - the active webserver bind host.
|
|
59
|
-
* @param extra - explicit `--trusted-host` values, in argument order.
|
|
60
|
-
* @returns the LAN display addresses and invocation-derived fence authorities.
|
|
61
|
-
*/
|
|
62
|
-
export declare function resolveLanTrust(bindHost: string, extra: readonly string[]): WebRuntimeValues;
|
|
63
43
|
/** Test hooks for the built dist and native browser handoff; production never mutates them. */
|
|
64
44
|
export declare const internals: {
|
|
65
45
|
resolveDistIndex: () => string;
|
package/lib/types/startup.d.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The web app's command-line provider: it parses the `dsh --profile web` flag
|
|
3
|
-
* family (`--host`, `--port`, `--public-url`, `--trusted-host`, `--no-open
|
|
3
|
+
* family (`--host`, `--port`, `--public-url`, `--trusted-host`, `--no-open`, TLS)
|
|
4
4
|
* and its `--help` text, then provides the immutable values as
|
|
5
5
|
* {@link WEB_STARTUP_SERVICE}. Ordinary rows inject that service before
|
|
6
6
|
* reading it from lazy config.
|
|
7
7
|
* @module @deepseek-ai/dsh-web-app/startup
|
|
8
8
|
*/
|
|
9
9
|
import type { Context } from '@deepseek-ai/cordis';
|
|
10
|
+
import { type TlsConfig } from '@deepseek-ai/dsh-host-webserver';
|
|
10
11
|
/** Stable Cordis plugin name. */
|
|
11
12
|
export declare const name = "web-startup";
|
|
12
13
|
/** Services required before the flags can be resolved. */
|
|
@@ -28,12 +29,14 @@ export interface WebStartupValues {
|
|
|
28
29
|
publicUrl?: string;
|
|
29
30
|
/** Explicit `--trusted-host` authorities, in argument order. */
|
|
30
31
|
trustedHosts: string[];
|
|
32
|
+
/** Server certificate chain and unencrypted private key supplied together. */
|
|
33
|
+
tls?: TlsConfig;
|
|
31
34
|
}
|
|
32
35
|
/**
|
|
33
36
|
* Parse and provide the Web invocation as an ordinary Cordis service. The
|
|
34
|
-
* command's action publishes the flags this invocation named
|
|
35
|
-
*
|
|
36
|
-
*
|
|
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.
|
|
37
40
|
* @param ctx - plugin context carrying the command line.
|
|
38
41
|
*/
|
|
39
42
|
export declare function apply(ctx: Context): void;
|