@showly/mcp-server 0.1.0 → 0.3.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 +45 -7
- package/dist/cli.d.ts +291 -3
- package/dist/cli.js +905 -43
- package/dist/showly-hosting-skill.d.ts +14 -0
- package/dist/showly-hosting-skill.js +15 -0
- package/manifest.json +63 -5
- package/package.json +8 -7
- package/skills/showly-hosting/SKILL.md +67 -0
- package/skills/showly-hosting/agents/openai.yaml +4 -0
package/README.md
CHANGED
|
@@ -6,37 +6,75 @@ you can preview and deploy a site directly from your terminal AI.
|
|
|
6
6
|
## Quickstart
|
|
7
7
|
|
|
8
8
|
```sh
|
|
9
|
-
npx @showly/mcp-server install --to
|
|
9
|
+
npx @showly/mcp-server install --to codex --with-skill
|
|
10
|
+
# or: npx @showly/mcp-server install --to claude-code --with-skill
|
|
10
11
|
```
|
|
11
12
|
|
|
12
13
|
The installer writes the Showly MCP server block to `~/.claude.json`
|
|
13
|
-
(or `~/.codex/config.toml`)
|
|
14
|
+
(or `~/.codex/config.toml`) and installs the reusable `showly-hosting` skill.
|
|
15
|
+
The skill covers the whole lifecycle of a hosted site — listing and inspecting
|
|
16
|
+
the sites you already have, creating one, updating it, sharing a private
|
|
17
|
+
password-protected Preview, publishing it Live, rolling back, and connecting a
|
|
18
|
+
custom domain. It supersedes the narrower `showly-publish` skill, which
|
|
19
|
+
`install` removes from your skills directory when it finds it.
|
|
20
|
+
|
|
21
|
+
No token to paste: the next time you call a Showly
|
|
14
22
|
tool, your agent discovers Showly's OAuth authorization server from the
|
|
15
23
|
endpoint and opens a browser tab for you to sign in and approve the scopes
|
|
16
24
|
(standard MCP authorization).
|
|
17
25
|
|
|
18
26
|
## What you get
|
|
19
27
|
|
|
28
|
+
`manifest.json` in this package is the authoritative list — the server
|
|
29
|
+
derives `INVOKABLE_TOOLS` and `MCP_BLOCKED_TOOLS` from it, so read it
|
|
30
|
+
rather than trusting a hand-kept summary. It currently ships 33 tools,
|
|
31
|
+
grouped below by its own `kind` field.
|
|
32
|
+
|
|
20
33
|
Read-only tools available to any token:
|
|
21
34
|
|
|
22
35
|
- `list_projects`, `list_sites`, `get_site_context`, `list_templates`
|
|
23
36
|
- `get_preview_status`, `get_deployment_logs`, `diagnose_deployment`
|
|
37
|
+
- `list_deployments`, `list_site_versions`, `diff_site_versions`, `get_site_files`
|
|
38
|
+
- `list_site_domains`, `request_download_url`
|
|
24
39
|
|
|
25
40
|
Write tools that stage and materialise previews:
|
|
26
41
|
|
|
27
42
|
- `create_change_plan` — turn a natural-language request into a plan
|
|
28
43
|
- `apply_site_patch` — stage one or more file edits as a _changeset_
|
|
29
44
|
- `create_preview` — build the changeset and return a real preview URL
|
|
45
|
+
- `create_github_preview` — build a private preview from the latest commit on a connected GitHub branch (static targets; dynamic container builds fail before enqueue)
|
|
30
46
|
- `retry_deployment` — re-run a failed preview build
|
|
31
47
|
- `run_checks` — read the lint / typecheck / build status of a preview
|
|
32
48
|
- `create_site_from_template` — bootstrap a new site from a template
|
|
49
|
+
- `create_site_from_html` — create a site from inline HTML
|
|
50
|
+
- `request_upload_url` — signed URL for a file too large to pass inline
|
|
51
|
+
- `set_preview_access` — change who can open a preview URL
|
|
33
52
|
- `request_publish` — open a publish approval (completed in the web UI)
|
|
34
53
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
54
|
+
Write tools for custom domains (ADR-0015):
|
|
55
|
+
|
|
56
|
+
- `add_custom_domain`, `verify_custom_domain`
|
|
57
|
+
|
|
58
|
+
Write tools for version history and deletion:
|
|
59
|
+
|
|
60
|
+
- `rollback_to_version` — restore a previous site version
|
|
61
|
+
- `delete_preview`, `delete_site`
|
|
62
|
+
|
|
63
|
+
Write tools for the guest trial flow:
|
|
64
|
+
|
|
65
|
+
- `create_trial_site`, `claim_trial_site`
|
|
66
|
+
|
|
67
|
+
`publish_site` is exposed through a narrower `publish:confirm` scope and never
|
|
68
|
+
publishes on its first call: it returns a short-lived, deployment-bound token
|
|
69
|
+
that the agent may echo only after the user confirms. Team and Enterprise
|
|
70
|
+
workspaces use `request_publish` and its `webApprovalUrl` for second-person
|
|
71
|
+
approval. Ordinary Live publishing does not require OTP/MFA. The legacy
|
|
72
|
+
`rollback_deployment` path remains Web-only.
|
|
73
|
+
|
|
74
|
+
Both publish paths require the human user bound to the MCP token to have a
|
|
75
|
+
verified Showly account email. An `email_verification_required` result includes
|
|
76
|
+
`webVerificationUrl`; the agent must direct the user there and must not report
|
|
77
|
+
the site as live until verification succeeds and publishing is retried.
|
|
40
78
|
|
|
41
79
|
## Environment
|
|
42
80
|
|
package/dist/cli.d.ts
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
type Target = "claude-code" | "codex" | "stdout";
|
|
2
|
+
export type Target = "claude-code" | "codex" | "stdout";
|
|
3
|
+
/**
|
|
4
|
+
* The version of THIS copy of the package, read from the manifest that ships
|
|
5
|
+
* beside it. `manifest.test.ts` pins manifest.json, package.json,
|
|
6
|
+
* claude-code-skill and codex-plugin to one string, so this cannot print a
|
|
7
|
+
* version the package does not actually have.
|
|
8
|
+
*
|
|
9
|
+
* It is printed on every help screen and on every unknown-command error, and
|
|
10
|
+
* that is the whole point. When an agent runs `showly-mcp` from a stale npx
|
|
11
|
+
* cache and sees a command list that is missing what the docs told it to run,
|
|
12
|
+
* the most natural inference is "the docs are wrong" or "I misremembered the
|
|
13
|
+
* name" — because nothing on screen suggests a newer copy exists. A version
|
|
14
|
+
* string plus the `@latest` hint is the one signal that redirects that guess
|
|
15
|
+
* from "the instructions are wrong" to "my copy is old".
|
|
16
|
+
*/
|
|
17
|
+
export declare const CLI_VERSION: string;
|
|
18
|
+
/** The commands this copy dispatches. Anything else is an error, never a no-op. */
|
|
19
|
+
export declare const KNOWN_COMMANDS: readonly ["install", "login", "manifest"];
|
|
20
|
+
export type KnownCommand = (typeof KNOWN_COMMANDS)[number];
|
|
21
|
+
/**
|
|
22
|
+
* The sentence that turns "this tool cannot do that" into "this COPY cannot do
|
|
23
|
+
* that". Printed on unknown commands and on unknown flags, because both are
|
|
24
|
+
* reached by the same route: documentation written against a newer release
|
|
25
|
+
* than the one npx resolved.
|
|
26
|
+
*/
|
|
27
|
+
export declare const UPGRADE_HINT = "If you expected this, your copy is out of date \u2014 re-run with `npx @showly/mcp-server@latest`.";
|
|
3
28
|
export declare function buildClaudeCodeSnippet(opts: {
|
|
4
29
|
url: string;
|
|
5
30
|
apiUrl?: string;
|
|
@@ -18,8 +43,272 @@ export type InstallResult = {
|
|
|
18
43
|
snippet: string;
|
|
19
44
|
wrote: boolean;
|
|
20
45
|
alreadyConfigured: boolean;
|
|
46
|
+
skill: SkillInstallResult | null;
|
|
47
|
+
};
|
|
48
|
+
export type SkillInstallResult = {
|
|
49
|
+
path: string;
|
|
50
|
+
wrote: boolean;
|
|
51
|
+
alreadyConfigured: boolean;
|
|
52
|
+
/** Path of the superseded showly-publish skill this install removed, if any. */
|
|
53
|
+
removedLegacyPath: string | null;
|
|
54
|
+
};
|
|
55
|
+
export type InstallOptions = {
|
|
56
|
+
withSkill?: boolean;
|
|
21
57
|
};
|
|
22
|
-
export declare function performInstall(target: Target, env?: NodeJS.ProcessEnv): InstallResult;
|
|
58
|
+
export declare function performInstall(target: Target, env?: NodeJS.ProcessEnv, options?: InstallOptions): InstallResult;
|
|
59
|
+
export declare function performSkillInstall(target: Exclude<Target, "stdout">, env?: NodeJS.ProcessEnv): SkillInstallResult;
|
|
60
|
+
/**
|
|
61
|
+
* The client_id this CLI starts device flows under.
|
|
62
|
+
*
|
|
63
|
+
* Deliberately NOT a first-party id (the API rejects those on /oauth/device
|
|
64
|
+
* precisely so nobody can wear the product's identity on a consent screen),
|
|
65
|
+
* and deliberately not registered: a device client_id is free text, so the
|
|
66
|
+
* consent page treats whatever name it carries as self-declared and asks the
|
|
67
|
+
* human to check the code echo instead.
|
|
68
|
+
*/
|
|
69
|
+
export declare const LOGIN_CLIENT_ID = "showly-mcp-cli";
|
|
70
|
+
/** The env var name emitted into config snippets that must not hold a secret. */
|
|
71
|
+
export declare const TOKEN_ENV_VAR = "SHOWLY_TOKEN";
|
|
72
|
+
export type DeviceStart = {
|
|
73
|
+
device_code: string;
|
|
74
|
+
user_code: string;
|
|
75
|
+
verification_uri: string;
|
|
76
|
+
verification_uri_complete: string;
|
|
77
|
+
expires_in: number;
|
|
78
|
+
interval: number;
|
|
79
|
+
};
|
|
80
|
+
export type DeviceToken = {
|
|
81
|
+
access_token: string;
|
|
82
|
+
scope: string;
|
|
83
|
+
expires_in?: number;
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* The exact text `login` prints while it waits. Pure, and pinned by a test,
|
|
87
|
+
* because this block is the entire user interface of headless sign-in — the
|
|
88
|
+
* docs quote it verbatim so the page and the binary cannot drift.
|
|
89
|
+
*
|
|
90
|
+
* Reading order is not cosmetic:
|
|
91
|
+
* • The bare URL plus the separately-printed code comes FIRST, because that
|
|
92
|
+
* is the pair that works when the human is on a phone or another machine —
|
|
93
|
+
* which is the entire reason this flow exists. The one-click deep link is
|
|
94
|
+
* offered second, for the case where the browser is on this box.
|
|
95
|
+
* • The code is repeated three times on purpose. RFC 8628 §3.3.1's code echo
|
|
96
|
+
* is the ONLY anti-phishing signal that survives an attacker-controlled
|
|
97
|
+
* client_name (Storm-2372 phished 340+ M365 tenants on exactly this flow),
|
|
98
|
+
* so the human has to be told what they will see and what to do if it
|
|
99
|
+
* differs.
|
|
100
|
+
* • The expiry is a wall-clock time, not a duration. A human who wanders off
|
|
101
|
+
* to create an account and verify an email cannot subtract "15 minutes"
|
|
102
|
+
* from a moment they have forgotten.
|
|
103
|
+
*/
|
|
104
|
+
export declare function buildLoginPrompt(input: {
|
|
105
|
+
verificationUri: string;
|
|
106
|
+
verificationUriComplete: string;
|
|
107
|
+
userCode: string;
|
|
108
|
+
expiresAt: Date;
|
|
109
|
+
}): string;
|
|
110
|
+
/** Claude Code reads a `headers` map on an http MCP server entry. */
|
|
111
|
+
export declare function buildClaudeCodeAuthSnippet(opts: {
|
|
112
|
+
url: string;
|
|
113
|
+
token: string;
|
|
114
|
+
}): Record<string, unknown>;
|
|
115
|
+
export declare function buildCodexAuthSnippet(opts: {
|
|
116
|
+
url: string;
|
|
117
|
+
tokenEnvVar?: string;
|
|
118
|
+
}): string;
|
|
119
|
+
export type LoginDeps = {
|
|
120
|
+
fetchImpl?: typeof fetch;
|
|
121
|
+
/**
|
|
122
|
+
* Injectable wait. Takes the cancellation signal too, so the real one can
|
|
123
|
+
* return the instant Ctrl+C arrives instead of finishing a five-second nap
|
|
124
|
+
* first — a cancel the user has to wait out does not read as a cancel.
|
|
125
|
+
*/
|
|
126
|
+
sleep?: (ms: number, signal?: AbortSignal) => Promise<void>;
|
|
127
|
+
/** Injectable clock (ms). Lets the poll deadline be tested without waiting. */
|
|
128
|
+
now?: () => number;
|
|
129
|
+
/** stderr sink. Keeps stdout clean for --print-token. */
|
|
130
|
+
log?: (line: string) => void;
|
|
131
|
+
/**
|
|
132
|
+
* Cancellation from outside — Ctrl+C, in production (see createCancelScope).
|
|
133
|
+
*
|
|
134
|
+
* Without it there was no way out of this command but killing the process,
|
|
135
|
+
* which is a poor deal for the human who is already stuck: `login` blocks for
|
|
136
|
+
* a quarter of an hour by design, and the whole reason a reader reaches it is
|
|
137
|
+
* that something else (their editor's browser window) failed to appear.
|
|
138
|
+
*/
|
|
139
|
+
signal?: AbortSignal;
|
|
140
|
+
/**
|
|
141
|
+
* Per-request deadline override, in ms. Production uses the constants below;
|
|
142
|
+
* the tests use a tiny value so a hung server can be exercised in
|
|
143
|
+
* milliseconds rather than in the fifteen seconds a real one gets.
|
|
144
|
+
*/
|
|
145
|
+
requestTimeoutMs?: number;
|
|
146
|
+
};
|
|
147
|
+
/**
|
|
148
|
+
* How long ONE HTTP request may take before it is abandoned.
|
|
149
|
+
*
|
|
150
|
+
* Neither fetch had any bound at all, and both are inside the flow whose entire
|
|
151
|
+
* job is to unstick a human: a TCP connection that opens and then goes quiet
|
|
152
|
+
* (a captive portal, a proxy that swallows the response, a machine suspended
|
|
153
|
+
* mid-poll) leaves `login` hanging with no output and no deadline — the same
|
|
154
|
+
* silent wait, one layer down, that this command exists to replace.
|
|
155
|
+
*
|
|
156
|
+
* The start POST is short because nothing has been printed yet: until it
|
|
157
|
+
* returns there is no code, no URL, and nothing on screen, so the reader cannot
|
|
158
|
+
* tell a slow network from a dead one. A poll gets longer, because by then the
|
|
159
|
+
* prompt is on screen and the loop can absorb a slow answer without the reader
|
|
160
|
+
* seeing anything at all.
|
|
161
|
+
*/
|
|
162
|
+
export declare const START_REQUEST_TIMEOUT_MS = 15000;
|
|
163
|
+
export declare const POLL_REQUEST_TIMEOUT_MS = 30000;
|
|
164
|
+
/** Thrown when the human cancels; the CLI exits 130 rather than 1 on it. */
|
|
165
|
+
export declare class LoginCancelledError extends Error {
|
|
166
|
+
constructor();
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Wire Ctrl+C (and SIGTERM) to an AbortSignal the login flow watches.
|
|
170
|
+
*
|
|
171
|
+
* Registering a SIGINT listener also SUPPRESSES Node's default "die now", which
|
|
172
|
+
* is the point: the flow gets to unwind, say what happened, and exit with the
|
|
173
|
+
* conventional 130 instead of leaving the reader guessing whether anything was
|
|
174
|
+
* half-written. Exported so the wiring itself is testable — `main` is not.
|
|
175
|
+
*/
|
|
176
|
+
export type SignalTarget = {
|
|
177
|
+
on(event: string, listener: () => void): unknown;
|
|
178
|
+
off(event: string, listener: () => void): unknown;
|
|
179
|
+
};
|
|
180
|
+
export declare function createCancelScope(target?: SignalTarget): {
|
|
181
|
+
signal: AbortSignal;
|
|
182
|
+
release: () => void;
|
|
183
|
+
};
|
|
184
|
+
/**
|
|
185
|
+
* How far PAST the expiry we were handed at start the poll keeps going.
|
|
186
|
+
*
|
|
187
|
+
* `expires_in` from POST /oauth/device is a floor, not a deadline: when a
|
|
188
|
+
* signed-in human lands on the consent page the server pushes expires_at out
|
|
189
|
+
* (see DEVICE_LOOKUP_EXTENSION_MS), so a client that stopped at the advertised
|
|
190
|
+
* expiry would hang up on the human mid-approval. It is still bounded — the
|
|
191
|
+
* server caps a device flow at 30 minutes from creation whatever happens — so
|
|
192
|
+
* one absolute cap past the advertised expiry is comfortably beyond any answer
|
|
193
|
+
* that could still become a token, and stopping there is what keeps a deploy
|
|
194
|
+
* that stops resolving dead rows from turning this loop into a permanent 300
|
|
195
|
+
* requests/min against the token endpoint.
|
|
196
|
+
*/
|
|
197
|
+
export declare const SERVER_EXTENSION_ALLOWANCE_MS: number;
|
|
198
|
+
export declare function startDeviceFlow(apiUrl: string, deps?: LoginDeps): Promise<DeviceStart>;
|
|
199
|
+
/**
|
|
200
|
+
* Poll /oauth/token until the human decides, per RFC 8628 §3.4-3.5.
|
|
201
|
+
*
|
|
202
|
+
* Every terminal answer is turned into a sentence a human can act on. The
|
|
203
|
+
* server sends `error_description` for exactly this reason, so we prefer it
|
|
204
|
+
* over anything we could invent, and fall back only when an older deploy
|
|
205
|
+
* sends the bare code.
|
|
206
|
+
*
|
|
207
|
+
* `expiresAt` is the client-side backstop, not the contract: the server's own
|
|
208
|
+
* `expired_token` / `invalid_grant` is what normally ends the loop, and this
|
|
209
|
+
* only fires when no answer ever arrives at all. It sits a full
|
|
210
|
+
* SERVER_EXTENSION_ALLOWANCE_MS past the advertised expiry so an arrival
|
|
211
|
+
* extension is never cut short.
|
|
212
|
+
*/
|
|
213
|
+
export declare function pollForDeviceToken(input: {
|
|
214
|
+
apiUrl: string;
|
|
215
|
+
deviceCode: string;
|
|
216
|
+
intervalSec: number;
|
|
217
|
+
expiresAt: Date;
|
|
218
|
+
}, deps?: LoginDeps): Promise<DeviceToken>;
|
|
219
|
+
export type LoginResult = {
|
|
220
|
+
target: Target;
|
|
221
|
+
token: string;
|
|
222
|
+
scope: string;
|
|
223
|
+
expiresAt: Date | null;
|
|
224
|
+
path: string | null;
|
|
225
|
+
wrote: boolean;
|
|
226
|
+
snippet: string;
|
|
227
|
+
};
|
|
228
|
+
/**
|
|
229
|
+
* Run the whole headless sign-in and put the credential where the host will
|
|
230
|
+
* find it.
|
|
231
|
+
*
|
|
232
|
+
* No secret is ever accepted on argv — there is no `--token` flag, and the
|
|
233
|
+
* only credential this command handles is the one it just fetched. Anything
|
|
234
|
+
* pasted from /app/admin/mcp-tokens goes in through the environment
|
|
235
|
+
* (SHOWLY_TOKEN) or the host config by hand, so it stays out of shell history
|
|
236
|
+
* and out of every `ps` listing on the machine.
|
|
237
|
+
*/
|
|
238
|
+
export declare function performLogin(opts: {
|
|
239
|
+
target: Target;
|
|
240
|
+
env?: NodeJS.ProcessEnv;
|
|
241
|
+
}, deps?: LoginDeps): Promise<LoginResult>;
|
|
242
|
+
/**
|
|
243
|
+
* The env var names a host config tells its client to read the credential
|
|
244
|
+
* from: Codex's `bearer_token_env_var = "NAME"`, and the `${NAME}` placeholder
|
|
245
|
+
* in the pasteable snippet.
|
|
246
|
+
*
|
|
247
|
+
* Deliberately derived FROM the config text rather than hardcoded, because the
|
|
248
|
+
* bug this closes was a config and an output that disagreed. Whatever a future
|
|
249
|
+
* host schema calls its indirection, the name it references is what the human
|
|
250
|
+
* has to be handed a value for.
|
|
251
|
+
*/
|
|
252
|
+
export declare function envVarsReferencedBy(configText: string): string[];
|
|
253
|
+
export type LoginOutputLine = {
|
|
254
|
+
stream: "out" | "err";
|
|
255
|
+
line: string;
|
|
256
|
+
};
|
|
257
|
+
/**
|
|
258
|
+
* Everything `login` says after the token lands. Pure, so the one property
|
|
259
|
+
* that matters can be tested: a human who ran this command ends up CONNECTED.
|
|
260
|
+
*
|
|
261
|
+
* `--to codex` used to break that. It writes `bearer_token_env_var =
|
|
262
|
+
* "SHOWLY_TOKEN"` — Codex reads the credential from the environment, never
|
|
263
|
+
* from the file — and then printed `Connected. Wrote <path>` and dropped the
|
|
264
|
+
* token on the floor. Nothing on the machine ever set SHOWLY_TOKEN, so every
|
|
265
|
+
* tool call went out with no Authorization header and 401'd, after the human
|
|
266
|
+
* had already spent their one approval; recovering meant a second device flow
|
|
267
|
+
* and a second approval, which is the dead end this whole command exists to
|
|
268
|
+
* remove. `--to stdout` had the same hole with a sentence over it ("re-run
|
|
269
|
+
* with --print-token"), which is also a second approval.
|
|
270
|
+
*
|
|
271
|
+
* So the value is supplied here, once, on stderr — the only place it can go
|
|
272
|
+
* without landing in a file. The config keeps the env indirection, which is
|
|
273
|
+
* what makes it safe to commit; stdout keeps carrying only the snippet (or,
|
|
274
|
+
* under --print-token, only the token) so both remain pipeable.
|
|
275
|
+
*/
|
|
276
|
+
export declare function buildLoginOutput(result: LoginResult, opts?: {
|
|
277
|
+
printToken?: boolean;
|
|
278
|
+
}): LoginOutputLine[];
|
|
279
|
+
export type ParsedCommandArgs = {
|
|
280
|
+
ok: true;
|
|
281
|
+
help: boolean;
|
|
282
|
+
flags: Set<string>;
|
|
283
|
+
values: Map<string, string>;
|
|
284
|
+
} | {
|
|
285
|
+
ok: false;
|
|
286
|
+
message: string;
|
|
287
|
+
};
|
|
288
|
+
/**
|
|
289
|
+
* Strict argv parse for one command: every token must be a flag this command
|
|
290
|
+
* declares, a value for a flag that takes one, or `--help`.
|
|
291
|
+
*
|
|
292
|
+
* Bare positionals are rejected too, for the same reason flags are: `install
|
|
293
|
+
* claude-code` (no `--to`) is a plausible typo whose only old outcome was a
|
|
294
|
+
* usage error about the missing `--to`, and `manifest extra` simply printed the
|
|
295
|
+
* manifest as if the word were not there.
|
|
296
|
+
*/
|
|
297
|
+
export declare function parseCommandArgs(command: KnownCommand, rest: string[]): ParsedCommandArgs;
|
|
298
|
+
/** stdout/stderr sinks, injected so the dispatcher is testable in-process. */
|
|
299
|
+
export type CliIo = {
|
|
300
|
+
out: (line: string) => void;
|
|
301
|
+
err: (line: string) => void;
|
|
302
|
+
};
|
|
303
|
+
/**
|
|
304
|
+
* The whole command dispatcher, as a function that RETURNS its exit code.
|
|
305
|
+
*
|
|
306
|
+
* It used to be a `main` that assigned `process.exitCode` and was neither
|
|
307
|
+
* exported nor reachable from a test, which is why nothing noticed that the
|
|
308
|
+
* failure paths through it were not failures at all. The code is the contract
|
|
309
|
+
* here — the strings around it are not — so it is what a caller gets back.
|
|
310
|
+
*/
|
|
311
|
+
export declare function runCli(argv: string[], io?: CliIo, env?: NodeJS.ProcessEnv): Promise<number>;
|
|
23
312
|
/**
|
|
24
313
|
* True when this module is the program entrypoint.
|
|
25
314
|
*
|
|
@@ -35,4 +324,3 @@ export declare function performInstall(target: Target, env?: NodeJS.ProcessEnv):
|
|
|
35
324
|
* exercised from an in-process test runner otherwise).
|
|
36
325
|
*/
|
|
37
326
|
export declare function isMainModule(argv1: string | undefined, metaUrl: string): boolean;
|
|
38
|
-
export {};
|