@fabrika/installation-init 0.8.2 → 0.9.0-cloudflare.1
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/package.json +9 -6
- package/src/index.ts +33 -0
- package/src/mcp.ts +102 -0
- package/src/pin.ts +26 -0
- package/src/proxy-manifest.ts +221 -0
- package/src/secrets-file.ts +61 -0
- package/src/secrets.ts +78 -0
- package/src/service-environment.ts +206 -0
- package/types/src/admin.d.ts +91 -0
- package/types/src/checkout.d.ts +9 -0
- package/types/src/confirmations.d.ts +21 -0
- package/types/src/email.d.ts +15 -0
- package/types/src/environment.d.ts +49 -0
- package/types/src/gh.d.ts +9 -0
- package/types/src/index.d.ts +18 -0
- package/types/src/log.d.ts +29 -0
- package/types/src/mcp.d.ts +18 -0
- package/types/src/options.d.ts +9 -0
- package/types/src/pin.d.ts +2 -0
- package/types/src/prompt.d.ts +11 -0
- package/types/src/protection.d.ts +57 -0
- package/types/src/proxy-manifest.d.ts +109 -0
- package/types/src/scaffold.d.ts +49 -0
- package/types/src/secrets-file.d.ts +18 -0
- package/types/src/secrets.d.ts +32 -0
- package/types/src/service-environment.d.ts +78 -0
- package/types/src/shell.d.ts +50 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { type InstallationEndpointTable } from '@fabrika/auth-core';
|
|
2
|
+
import { type InstallationProxyApp } from '@fabrika/proxy-core/manifest';
|
|
3
|
+
/** The platform services an installation's proxy fronts. Each name is a service hostname in the topology. */
|
|
4
|
+
export type PlatformProxyService = 'iam' | 'control' | 'operations';
|
|
5
|
+
/** The public hostname of each platform service, without a scheme and without a port. */
|
|
6
|
+
export type PlatformHosts = Readonly<Record<PlatformProxyService, string>>;
|
|
7
|
+
/** `https://<host>` — the origin a browser, a token's `iss` and a return-origin registry all use. */
|
|
8
|
+
export declare const platformOrigin: (scheme: "http" | "https", host: string) => string;
|
|
9
|
+
/** What one OAuth scope lets a token do: the owning app's actions, resolved by IAM at the token endpoint. */
|
|
10
|
+
export interface PlatformOAuthScopeGrant {
|
|
11
|
+
readonly scope: string;
|
|
12
|
+
readonly actions: readonly string[];
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* A path an app serves to OAuth clients, protected only on an installation that enables it.
|
|
16
|
+
*
|
|
17
|
+
* The resource URI is the app's public origin plus `path`, so the template holds the path and a deploy
|
|
18
|
+
* binds the origin. Copied from the owning service module by the generator, like `table`.
|
|
19
|
+
*/
|
|
20
|
+
export interface PlatformOAuthResourceTemplate {
|
|
21
|
+
readonly path: string;
|
|
22
|
+
readonly scopes: readonly string[];
|
|
23
|
+
readonly scopeGrants: readonly PlatformOAuthScopeGrant[];
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* One fronted app, in the form that is the same on every installation: protected by its service contract (ADR-0022
|
|
27
|
+
* "Installation entries"), copied in by the generator and never written by hand.
|
|
28
|
+
*/
|
|
29
|
+
export interface PlatformProxyApp {
|
|
30
|
+
/** The IAM app id. It is the audience of every token the proxy mints for this app's host. */
|
|
31
|
+
readonly id: string;
|
|
32
|
+
/** The topology service that answers for it. */
|
|
33
|
+
readonly service: PlatformProxyService;
|
|
34
|
+
/**
|
|
35
|
+
* The proxy's public listener this app answers on.
|
|
36
|
+
*
|
|
37
|
+
* On a `zerops-subdomain` installation a port IS a hostname — Zerops mints one generated host per
|
|
38
|
+
* HTTP port — so this is the only installation-independent half of `hosts`. A custom-domain
|
|
39
|
+
* installation ignores it: every domain arrives on one listener and the `Host` header decides.
|
|
40
|
+
*/
|
|
41
|
+
readonly port: number;
|
|
42
|
+
/** Where the proxy dials it over the project's private network. */
|
|
43
|
+
readonly upstream: string;
|
|
44
|
+
/** The service's rows; with an `oauthResource`, the table served when the composition names an authorization server. */
|
|
45
|
+
readonly table: InstallationEndpointTable;
|
|
46
|
+
/** Present only on an app that can serve OAuth clients; off unless the composition names an authorization server. */
|
|
47
|
+
readonly oauthResource?: PlatformOAuthResourceTemplate;
|
|
48
|
+
/**
|
|
49
|
+
* Present exactly with `oauthResource`: the service's table when it serves no OAuth client. The service picks the
|
|
50
|
+
* same one, by the digest the deploy writes beside it (`platformServiceTableDigest`).
|
|
51
|
+
*/
|
|
52
|
+
readonly tableWithoutOAuth?: InstallationEndpointTable;
|
|
53
|
+
}
|
|
54
|
+
/** Everything about an installation's manifest that does not depend on WHICH installation it is. */
|
|
55
|
+
export interface PlatformProxyManifestTemplate {
|
|
56
|
+
readonly apps: readonly PlatformProxyApp[];
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* The template's entry for one platform service.
|
|
60
|
+
*
|
|
61
|
+
* The app IDS live in the template, so a caller that needs one — the deploy's schema reconcile needs
|
|
62
|
+
* the console's — reads it from there rather than restating a literal that could drift from the
|
|
63
|
+
* document the proxy is actually configured with.
|
|
64
|
+
*/
|
|
65
|
+
export declare const platformProxyAppFor: (template: PlatformProxyManifestTemplate, service: PlatformProxyService) => PlatformProxyApp;
|
|
66
|
+
/** Where one app answers in a particular composition. */
|
|
67
|
+
export interface PlatformProxyPlacement {
|
|
68
|
+
/** Public hostnames, without a port — `parseProxyManifest` refuses a `:port` suffix. */
|
|
69
|
+
readonly hosts: readonly string[];
|
|
70
|
+
/** Overrides the private dial address. The local composition runs IAM on a port of its own. */
|
|
71
|
+
readonly upstream?: string;
|
|
72
|
+
}
|
|
73
|
+
/** One composition of the platform: a deployed installation, or the local Docker stack. */
|
|
74
|
+
export interface PlatformProxyComposition {
|
|
75
|
+
/** The scheme the BROWSER speaks to these apps — configuration, never a header (see `ProxyApp.scheme`). */
|
|
76
|
+
readonly scheme: 'http' | 'https';
|
|
77
|
+
readonly placement: Readonly<Record<PlatformProxyService, PlatformProxyPlacement>>;
|
|
78
|
+
/**
|
|
79
|
+
* The public IAM origin that issues OAuth tokens. Present only when the installation enables MCP
|
|
80
|
+
* authoring; absent leaves every `oauthResource` unregistered and serves each service's table without OAuth.
|
|
81
|
+
*/
|
|
82
|
+
readonly authorizationServer?: string;
|
|
83
|
+
/** The port browsers reach every app on, when it is not the scheme's default. The local composition's is 18080. */
|
|
84
|
+
readonly publicPort?: number;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* The RFC 8707 resource URI and RFC 9728 metadata URL of an app's OAuth resource, bound to the app's
|
|
88
|
+
* FIRST host: the proxy refuses a resource on a host the app does not answer on.
|
|
89
|
+
*/
|
|
90
|
+
export declare const platformOAuthResourceUris: (composition: Pick<PlatformProxyComposition, "scheme" | "publicPort">, hosts: readonly string[], template: PlatformOAuthResourceTemplate) => {
|
|
91
|
+
readonly resource: string;
|
|
92
|
+
readonly resourceMetadata: string;
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* Bind a template to one composition and get the document the proxy actually parses.
|
|
96
|
+
*
|
|
97
|
+
* The result is returned THROUGH `parseProxyManifest`, so a duplicate id, a repeated host or a `:port`
|
|
98
|
+
* suffix is refused right here instead of at a deployed proxy's next build.
|
|
99
|
+
*/
|
|
100
|
+
export declare const resolvePlatformProxyManifest: (template: PlatformProxyManifestTemplate, composition: PlatformProxyComposition) => PlatformProxyManifest;
|
|
101
|
+
/** The platform proxy's manifest: one installation entry per platform service. */
|
|
102
|
+
export interface PlatformProxyManifest {
|
|
103
|
+
apps: InstallationProxyApp[];
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* `FABRIKA_SERVICE_TABLE_DIGEST` for a service: the digest of the table this manifest publishes for it, which the
|
|
107
|
+
* service compares with its own and refuses to start under another.
|
|
108
|
+
*/
|
|
109
|
+
export declare const platformServiceTableDigest: (manifest: PlatformProxyManifest, appId: string) => string;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The mechanics of a sidecar repository: ensure `<owner>/<name>` exists on GitHub, materialize the
|
|
3
|
+
* files the CLI owns, commit and push. Idempotent, and the same on every provider:
|
|
4
|
+
* - repo absent → create a fresh local checkout, commit the scaffold, `gh repo create … --source --push`,
|
|
5
|
+
* - repo present → clone it (if not already local), refresh the CLI-owned files, commit + push on drift.
|
|
6
|
+
*
|
|
7
|
+
* WHAT the files are is the provider's answer, not this module's — `materialize` writes them and `files`
|
|
8
|
+
* lists everything the scaffold ever stages. Scaffold commits carry `[skip ci]`, so a push here cannot
|
|
9
|
+
* deploy anything before `init` has written the GitHub Environment the pipeline reads.
|
|
10
|
+
*
|
|
11
|
+
* A freshly created repository is protected before anything else is written to it. An EXISTING one is
|
|
12
|
+
* only PROBED, never PUT to: its protection is whatever its operator chose, and a blind PUT would replace
|
|
13
|
+
* stricter rules with ours. Either way the outcome is reported, so an unprotected sidecar is never silent.
|
|
14
|
+
*/
|
|
15
|
+
import { type ProtectionOutcome } from './protection';
|
|
16
|
+
import { type ShellStep } from './shell';
|
|
17
|
+
export declare const INITIAL_SCAFFOLD_COMMIT_MESSAGE = "chore: initial fabrika platform scaffold [skip ci]";
|
|
18
|
+
export declare const REFRESH_SCAFFOLD_COMMIT_MESSAGE = "chore: refresh fabrika platform scaffold [skip ci]";
|
|
19
|
+
export interface SidecarScaffoldInput {
|
|
20
|
+
/** The sidecar repository, e.g. `contember/fabrika-zerops-test`. */
|
|
21
|
+
readonly repo: string;
|
|
22
|
+
/** Local directory to check the repository out into. */
|
|
23
|
+
readonly dir: string;
|
|
24
|
+
/** Every path the scaffold stages — CLI-owned files plus any operator-owned, write-once pin. */
|
|
25
|
+
readonly files: readonly string[];
|
|
26
|
+
/** Write the scaffold into an existing directory. Called for a fresh creation and for a refresh alike. */
|
|
27
|
+
materialize(dir: string): Promise<void>;
|
|
28
|
+
}
|
|
29
|
+
/** Result of scaffolding: the local checkout dir + whether the repository was freshly created. */
|
|
30
|
+
export interface SidecarScaffoldResult {
|
|
31
|
+
readonly dir: string;
|
|
32
|
+
readonly created: boolean;
|
|
33
|
+
/** Applied for a repository this run created, probed read-only for one that already existed. */
|
|
34
|
+
readonly branchProtection?: ProtectionOutcome;
|
|
35
|
+
}
|
|
36
|
+
/** The child processes and GitHub calls the scaffold drives. Replaced wholesale in tests. */
|
|
37
|
+
export interface SidecarScaffoldEffects {
|
|
38
|
+
hasGhCli(): Promise<boolean>;
|
|
39
|
+
repositoryExists(repo: string): Promise<boolean>;
|
|
40
|
+
run(step: ShellStep): Promise<void>;
|
|
41
|
+
probe(step: ShellStep): Promise<number>;
|
|
42
|
+
protect(repo: string): Promise<ProtectionOutcome>;
|
|
43
|
+
probeProtection(repo: string): Promise<ProtectionOutcome>;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Ensure the sidecar repository exists and carries the current scaffold. Returns the local checkout.
|
|
47
|
+
* Requires `gh` authenticated with rights to create/administer the repository.
|
|
48
|
+
*/
|
|
49
|
+
export declare function scaffoldSidecarRepository(input: SidecarScaffoldInput, effects?: SidecarScaffoldEffects): Promise<SidecarScaffoldResult>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** `--secrets-file`: where the local copy goes. A new file, outside the source checkout. */
|
|
2
|
+
export declare const SECRETS_FILE_FLAG = "--secrets-file";
|
|
3
|
+
export declare const SECRETS_FILE_VARIABLE = "FABRIKA_PLATFORM_SECRETS_FILE";
|
|
4
|
+
/**
|
|
5
|
+
* Settle the path before anything is contacted.
|
|
6
|
+
*
|
|
7
|
+
* Refused when it exists, because overwriting one would destroy another installation's only copy, and
|
|
8
|
+
* when it is inside the source checkout, where it would dirty the checkout a deploy uploads and could be
|
|
9
|
+
* committed.
|
|
10
|
+
*/
|
|
11
|
+
export declare const checkedSecretsFilePath: (path: string, sourceDir: string) => string;
|
|
12
|
+
/**
|
|
13
|
+
* The file's text: comment lines, then shell-sourceable assignments, named as the commands that read
|
|
14
|
+
* them name them. `header` is the provider's own lines, each written as a `#` comment.
|
|
15
|
+
*/
|
|
16
|
+
export declare const renderSecretsFile: (header: readonly string[], assignments: readonly (readonly [string, string])[]) => string;
|
|
17
|
+
/** Create the file with mode 0600, failing rather than replacing one that appeared since the check. */
|
|
18
|
+
export declare const writeSecretsFile: (path: string, text: string) => Promise<void>;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A 256-bit secret in base64url, never starting with `-` or `_`.
|
|
3
|
+
*
|
|
4
|
+
* The re-roll is copied from `@fabrika/local-stack`'s `prepare.ts` and it is not superstition: a
|
|
5
|
+
* credential beginning with `-` reads as a FLAG to anything that later takes it on a command line, no
|
|
6
|
+
* matter how it is quoted. One rolled password beginning with `-` made `mc alias set` print its usage
|
|
7
|
+
* and loop forever. It costs nothing and removes a bug that appears about one install in sixteen.
|
|
8
|
+
*/
|
|
9
|
+
export declare const randomSecret: () => string;
|
|
10
|
+
/** The `px_` prefix IAM's own credentials carry, so a key looks like what it is wherever it turns up. */
|
|
11
|
+
export declare const randomPxKey: () => string;
|
|
12
|
+
/** The control plane's vault KEK. base64, NOT base64url: the vault imports these bytes as an AES key. */
|
|
13
|
+
export declare const generateVaultKey: () => string;
|
|
14
|
+
/** IAM's token-signing material: a JSON array with one ES256 private JWK, index 0 the active signer. */
|
|
15
|
+
export declare const generateSigningKeys: () => string;
|
|
16
|
+
/** Everything a container installation is seeded with. Several of these are shared by more than one service. */
|
|
17
|
+
export interface InstallationSecrets {
|
|
18
|
+
/** IAM's token-signing material: a JSON array of ES256 private JWKs, index 0 active. */
|
|
19
|
+
readonly signingKeys: string;
|
|
20
|
+
/** Authenticates IAM's management `/rpc/*` surface. Held by IAM, control and Operations. */
|
|
21
|
+
readonly rpcKey: string;
|
|
22
|
+
/** Authenticates IAM's `/auth/mint/*` surface, which the proxy calls on the cold path. */
|
|
23
|
+
readonly proxyKey: string;
|
|
24
|
+
/** The `px_` admin bearer IAM admits as a synthetic global admin. `platform init` and `admin` take it. */
|
|
25
|
+
readonly provisioningKey: string;
|
|
26
|
+
/** The catalog credential control and Operations share. No `px_` prefix — it is not an IAM credential. */
|
|
27
|
+
readonly operationsSyncKey: string;
|
|
28
|
+
/** The control plane's vault KEK: 32 raw bytes at base64. Its loss is unrecoverable by design. */
|
|
29
|
+
readonly vaultKey: string;
|
|
30
|
+
}
|
|
31
|
+
/** Roll one installation's secrets. Pure entropy in, no I/O, nothing persisted. */
|
|
32
|
+
export declare const generateInstallationSecrets: () => InstallationSecrets;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { InstallationEmail } from './email';
|
|
2
|
+
import type { IamOAuthConfig } from './mcp';
|
|
3
|
+
import { type PlatformHosts } from './proxy-manifest';
|
|
4
|
+
import type { InstallationSecrets } from './secrets';
|
|
5
|
+
/** The four services an installation configures. Each is a service of the platform's own topology. */
|
|
6
|
+
export type PlatformService = 'iam' | 'operations' | 'control' | 'proxy';
|
|
7
|
+
/** Each service's variables, in the order they are written. */
|
|
8
|
+
export type PlatformServiceEnvironment = Readonly<Record<PlatformService, ReadonlyMap<string, string>>>;
|
|
9
|
+
/** IAM's OAuth configuration for MCP clients (ADR-0049): written only when the installation enables MCP authoring. */
|
|
10
|
+
export declare const IAM_OAUTH_CONFIG_VARIABLE = "FABRIKA_IAM_OAUTH_CONFIG";
|
|
11
|
+
/** An object store's coordinates, in the provider's own spelling (values or references to them). */
|
|
12
|
+
export interface PlatformStorage {
|
|
13
|
+
readonly bucket: string;
|
|
14
|
+
readonly accessKeyId: string;
|
|
15
|
+
readonly secretAccessKey: string;
|
|
16
|
+
readonly endpoint: string;
|
|
17
|
+
}
|
|
18
|
+
/** Where each service's data lives. */
|
|
19
|
+
export interface PlatformData {
|
|
20
|
+
readonly iamDatabase: string;
|
|
21
|
+
readonly controlDatabase: string;
|
|
22
|
+
readonly operationsDatabase: string;
|
|
23
|
+
/** Control's run logs. */
|
|
24
|
+
readonly runLogs: PlatformStorage;
|
|
25
|
+
/** Operations' artifacts and source maps. */
|
|
26
|
+
readonly operationsBlobs: PlatformStorage;
|
|
27
|
+
}
|
|
28
|
+
/** Where the installation answers. Both the bring-up and every deploy derive their variables from these. */
|
|
29
|
+
export interface PlatformPlacementInput {
|
|
30
|
+
readonly environment: string;
|
|
31
|
+
readonly scheme: 'http' | 'https';
|
|
32
|
+
readonly hosts: PlatformHosts;
|
|
33
|
+
/** The private address the platform proxy dials IAM at, which owns the session (`host:port`). */
|
|
34
|
+
readonly iamUpstream: string;
|
|
35
|
+
}
|
|
36
|
+
/** What `installationServiceEnvironment` needs. Everything in it is settled before the first write. */
|
|
37
|
+
export interface InstallationServiceInput extends PlatformPlacementInput {
|
|
38
|
+
readonly secrets: InstallationSecrets;
|
|
39
|
+
readonly data: PlatformData;
|
|
40
|
+
/** IAM sends its own mail with it, and Control hands it to applications whose job declares `mailer: 'installation'`. */
|
|
41
|
+
readonly email: InstallationEmail;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* EVERY fabrika variable a bring-up places, per service.
|
|
45
|
+
*
|
|
46
|
+
* The proxy manifest is deliberately absent: the deploy owns it, in the same run as the code it
|
|
47
|
+
* describes (ADR-0027).
|
|
48
|
+
*/
|
|
49
|
+
export declare const installationServiceEnvironment: (input: InstallationServiceInput) => PlatformServiceEnvironment;
|
|
50
|
+
/**
|
|
51
|
+
* The fabrika variables whose VALUE a bring-up generates, per service.
|
|
52
|
+
*
|
|
53
|
+
* Their presence on a live service is what "this project already holds an installation" means, and they
|
|
54
|
+
* are written as credentials. A provider adds the names of its own that carry a generated value.
|
|
55
|
+
*/
|
|
56
|
+
export declare const INSTALLATION_SECRET_VARIABLES: Readonly<Record<PlatformService, readonly string[]>>;
|
|
57
|
+
/**
|
|
58
|
+
* The secret the platform proxy sends IAM's route, so IAM honours the client address and request id only from the proxy
|
|
59
|
+
* (ADR-0022 rule 5). Derived from the provisioning key, which every deploy holds, so each deploy writes the same value
|
|
60
|
+
* and nothing stores or reads one back. Never from the proxy key: namespace proxies hold that one.
|
|
61
|
+
*/
|
|
62
|
+
export declare const deriveIamProxySecret: (provisioningKey: string) => string;
|
|
63
|
+
/** What a deploy derives the service environment from. */
|
|
64
|
+
export interface DeployedServiceInput extends PlatformPlacementInput {
|
|
65
|
+
/** `FABRIKA_IAM_PROVISIONING_KEY`, which the IAM proxy secret is derived from. */
|
|
66
|
+
readonly provisioningKey: string;
|
|
67
|
+
/** The digest of the console table the deploy's manifest publishes (`platformServiceTableDigest`). */
|
|
68
|
+
readonly controlTableDigest: string;
|
|
69
|
+
/** Present only when the installation enables MCP authoring. */
|
|
70
|
+
readonly oauthConfig?: IamOAuthConfig;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* The per-installation configuration a deploy writes, derived from where the installation answers.
|
|
74
|
+
*
|
|
75
|
+
* IAM's OAuth configuration is present only when MCP authoring is enabled; without it the variable is left
|
|
76
|
+
* as it is, and the manifest — whose `/mcp` row then admits no OAuth token — is what turns MCP off.
|
|
77
|
+
*/
|
|
78
|
+
export declare const deployedServiceEnvironment: (input: DeployedServiceInput) => PlatformServiceEnvironment;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shell-out helpers for the CLI, over `Bun.spawn`. Every orchestrated step (git, gh, the deploy
|
|
3
|
+
* trigger) goes through here so the rules are enforced in ONE place:
|
|
4
|
+
* - argv is passed verbatim (no shell) — nothing interpolated can be a metacharacter,
|
|
5
|
+
* - the child gets ONLY `process.env` + the explicit `env` the step needs,
|
|
6
|
+
* - on a non-zero exit we throw a SHORT message naming the command — never the child env, never a
|
|
7
|
+
* secret value, never the raw error object (which could carry a token-bearing URL).
|
|
8
|
+
*/
|
|
9
|
+
/** A child step to run: the command + args, its cwd, and the extra env it needs (merged over process.env). */
|
|
10
|
+
export interface ShellStep {
|
|
11
|
+
command: string;
|
|
12
|
+
args: string[];
|
|
13
|
+
cwd: string;
|
|
14
|
+
/** Extra env vars for this child only. Secret VALUES live here and nowhere else. */
|
|
15
|
+
env?: Record<string, string>;
|
|
16
|
+
/** Optional stdin written to the child (used for `gh secret set`, which reads the value from stdin). */
|
|
17
|
+
stdin?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* What a streaming child gets on stdin.
|
|
21
|
+
*
|
|
22
|
+
* A TTY child may INHERIT it — `git` and `gh` prompt on their own, and a terminal is shared by turns.
|
|
23
|
+
* Without a terminal nobody can answer a child's question, so it gets none and fails instead of waiting.
|
|
24
|
+
*/
|
|
25
|
+
export declare const childStdin: (step: ShellStep, isTty?: boolean) => Uint8Array | "inherit" | "ignore";
|
|
26
|
+
/**
|
|
27
|
+
* Run a step, STREAMING its stdout/stderr to this terminal so the operator watches the underlying
|
|
28
|
+
* tool live (git/gh print progress). Throws on a non-zero exit. The thrown message names the command
|
|
29
|
+
* + exit code only.
|
|
30
|
+
*/
|
|
31
|
+
export declare function run(step: ShellStep): Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* Run a step and CAPTURE its stdout (returned), while streaming stderr to this terminal. Used when we
|
|
34
|
+
* need to parse a tool's output (e.g. `gh repo view`). Throws on a non-zero exit; the captured stdout
|
|
35
|
+
* is NOT echoed by this helper.
|
|
36
|
+
*/
|
|
37
|
+
export declare function capture(step: ShellStep): Promise<string>;
|
|
38
|
+
/** Run a step quietly: no stdout/stderr to the terminal, returns the exit code. For existence probes. */
|
|
39
|
+
export declare function probe(step: ShellStep): Promise<number>;
|
|
40
|
+
/** What a step that is allowed to fail reported: its exit code plus everything it wrote. */
|
|
41
|
+
export interface ShellAttempt {
|
|
42
|
+
readonly exitCode: number;
|
|
43
|
+
readonly stdout: string;
|
|
44
|
+
readonly stderr: string;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Run a step WITHOUT throwing, capturing both pipes. For a call whose failure is an ANSWER rather than
|
|
48
|
+
* an error — a `gh api` PUT that GitHub refuses because the plan does not offer the feature.
|
|
49
|
+
*/
|
|
50
|
+
export declare function attempt(step: ShellStep): Promise<ShellAttempt>;
|