@dereekb/dbx-cli 13.11.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.
Files changed (53) hide show
  1. package/LICENSE +21 -0
  2. package/firebase-api-manifest/src/generate-api-manifest/bind-validators.d.ts +44 -0
  3. package/firebase-api-manifest/src/generate-api-manifest/emit.d.ts +29 -0
  4. package/firebase-api-manifest/src/generate-api-manifest/extract-crud.d.ts +25 -0
  5. package/firebase-api-manifest/src/generate-api-manifest/find-api-files.d.ts +16 -0
  6. package/firebase-api-manifest/src/generate-api-manifest/main.d.ts +46 -0
  7. package/firebase-api-manifest/src/generate-api-manifest/parse-functions.d.ts +18 -0
  8. package/firebase-api-manifest/src/generate-api-manifest/resolve-package.d.ts +59 -0
  9. package/firebase-api-manifest/src/generate-api-manifest/types.d.ts +44 -0
  10. package/index.cjs.default.js +1 -0
  11. package/index.cjs.js +8162 -0
  12. package/index.cjs.mjs +2 -0
  13. package/index.d.ts +1 -0
  14. package/index.esm.js +8084 -0
  15. package/package.json +27 -0
  16. package/src/index.d.ts +1 -0
  17. package/src/lib/api/call-model.client.d.ts +37 -0
  18. package/src/lib/api/call-model.command.factory.d.ts +42 -0
  19. package/src/lib/api/call.passthrough.command.d.ts +8 -0
  20. package/src/lib/api/index.d.ts +3 -0
  21. package/src/lib/auth/auth.command.factory.d.ts +29 -0
  22. package/src/lib/auth/index.d.ts +3 -0
  23. package/src/lib/auth/oidc.client.d.ts +125 -0
  24. package/src/lib/auth/oidc.flow.d.ts +79 -0
  25. package/src/lib/config/cli.config.d.ts +112 -0
  26. package/src/lib/config/env.d.ts +183 -0
  27. package/src/lib/config/index.d.ts +4 -0
  28. package/src/lib/config/paths.d.ts +33 -0
  29. package/src/lib/config/token.cache.d.ts +51 -0
  30. package/src/lib/context/cli.context.d.ts +41 -0
  31. package/src/lib/context/index.d.ts +1 -0
  32. package/src/lib/doctor/doctor.command.factory.d.ts +48 -0
  33. package/src/lib/doctor/index.d.ts +1 -0
  34. package/src/lib/env/env.command.factory.d.ts +22 -0
  35. package/src/lib/env/index.d.ts +1 -0
  36. package/src/lib/index.d.ts +11 -0
  37. package/src/lib/manifest/build-manifest-commands.d.ts +77 -0
  38. package/src/lib/manifest/index.d.ts +2 -0
  39. package/src/lib/manifest/types.d.ts +20 -0
  40. package/src/lib/middleware/auth.middleware.d.ts +29 -0
  41. package/src/lib/middleware/index.d.ts +2 -0
  42. package/src/lib/middleware/output.middleware.d.ts +54 -0
  43. package/src/lib/output/index.d.ts +1 -0
  44. package/src/lib/output/output.command.factory.d.ts +55 -0
  45. package/src/lib/runner/index.d.ts +1 -0
  46. package/src/lib/runner/run.d.ts +71 -0
  47. package/src/lib/util/args.d.ts +78 -0
  48. package/src/lib/util/context.slot.d.ts +50 -0
  49. package/src/lib/util/handler.d.ts +13 -0
  50. package/src/lib/util/index.d.ts +6 -0
  51. package/src/lib/util/interactive.d.ts +26 -0
  52. package/src/lib/util/output.d.ts +152 -0
  53. package/src/lib/util/pagination.d.ts +91 -0
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@dereekb/dbx-cli",
3
+ "version": "13.11.0",
4
+ "sideEffects": false,
5
+ "peerDependencies": {
6
+ "@dereekb/firebase": "13.11.0",
7
+ "@dereekb/nestjs": "13.11.0",
8
+ "@dereekb/util": "13.11.0",
9
+ "arktype": "^2.2.0",
10
+ "yargs": "^18.0.0"
11
+ },
12
+ "devDependencies": {
13
+ "@types/yargs": "^17.0.35"
14
+ },
15
+ "exports": {
16
+ "./package.json": "./package.json",
17
+ ".": {
18
+ "module": "./index.esm.js",
19
+ "types": "./index.d.ts",
20
+ "import": "./index.cjs.mjs",
21
+ "default": "./index.cjs.js"
22
+ }
23
+ },
24
+ "module": "./index.esm.js",
25
+ "main": "./index.cjs.js",
26
+ "types": "./index.d.ts"
27
+ }
package/src/index.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './lib';
@@ -0,0 +1,37 @@
1
+ import type { OnCallTypedModelParams } from '@dereekb/firebase';
2
+ export declare const CALL_MODEL_API_PATH = "/model/call";
3
+ export interface CallModelOverHttpInput<T = unknown> {
4
+ /**
5
+ * The API base URL — typically `<host>/<project>/us-central1/api` or `https://<domain>/api`.
6
+ *
7
+ * The `/model/call` path is appended automatically.
8
+ */
9
+ readonly apiBaseUrl: string;
10
+ readonly accessToken: string;
11
+ readonly params: OnCallTypedModelParams<T>;
12
+ /**
13
+ * Custom fetch implementation for tests.
14
+ */
15
+ readonly fetcher?: typeof fetch;
16
+ }
17
+ export interface CallModelOverHttpResponse<R = unknown> {
18
+ readonly status: number;
19
+ readonly body: R;
20
+ }
21
+ /**
22
+ * Posts a typed model call to `<apiBaseUrl>/model/call` with a Bearer access token and parses the JSON response.
23
+ *
24
+ * Maps non-2xx responses to a {@link CliError} with a stable code derived from the status — this lets the
25
+ * error envelope downstream emit a sensible suggestion.
26
+ *
27
+ * The function name is referenced via {@link CALL_MODEL_APP_FUNCTION_KEY} for consistency with the demo's wiring.
28
+ *
29
+ * @param input - The call envelope describing the API target, access token, model params, and optional fetch override.
30
+ * @param input.apiBaseUrl - The API base URL (the `/model/call` path is appended automatically).
31
+ * @param input.accessToken - The Bearer access token sent in the `Authorization` header.
32
+ * @param input.params - The {@link OnCallTypedModelParams} payload posted as JSON.
33
+ * @param input.fetcher - Optional fetch implementation override (used by tests).
34
+ * @returns The parsed JSON response body cast to `R`.
35
+ */
36
+ export declare function callModelOverHttp<T = unknown, R = unknown>(input: CallModelOverHttpInput<T>): Promise<R>;
37
+ export { CALL_MODEL_APP_FUNCTION_KEY } from '@dereekb/firebase';
@@ -0,0 +1,42 @@
1
+ import type { Argv, CommandModule } from 'yargs';
2
+ export interface CallModelCommandSpec<TParams = unknown, TResult = unknown> {
3
+ /**
4
+ * The yargs command string (e.g. `'profile read username <value>'`).
5
+ */
6
+ readonly command: string;
7
+ readonly describe: string;
8
+ /**
9
+ * The Firestore model type (e.g. `'profile'`).
10
+ */
11
+ readonly model: string;
12
+ /**
13
+ * The CRUD verb (`'create' | 'read' | 'update' | 'delete' | 'query'`) or a custom action type.
14
+ */
15
+ readonly verb: string;
16
+ /**
17
+ * Optional sub-function specifier (e.g. `'username'`).
18
+ */
19
+ readonly specifier?: string;
20
+ /**
21
+ * Optional yargs option/positional builder for the command.
22
+ */
23
+ readonly builder?: (yargs: Argv) => Argv;
24
+ /**
25
+ * Maps the parsed argv to the typed call payload.
26
+ */
27
+ readonly buildParams: (argv: any) => TParams;
28
+ /**
29
+ * Optional transform applied before printing the result envelope.
30
+ */
31
+ readonly mapResult?: (result: TResult) => unknown;
32
+ }
33
+ /**
34
+ * Builder primitive: turns a {@link CallModelCommandSpec} into a yargs `CommandModule`.
35
+ *
36
+ * The handler reads the {@link CliContext} (built by the auth middleware), constructs the
37
+ * {@link OnCallTypedModelParams} envelope, and executes via `context.callModel(...)`.
38
+ *
39
+ * @param spec - The command spec describing the yargs command surface plus how to map argv into the typed call params and the result.
40
+ * @returns A yargs `CommandModule` whose handler runs the spec against the active CLI context.
41
+ */
42
+ export declare function createCallModelCommand<TParams = unknown, TResult = unknown>(spec: CallModelCommandSpec<TParams, TResult>): CommandModule;
@@ -0,0 +1,8 @@
1
+ import type { CommandModule } from 'yargs';
2
+ /**
3
+ * Generic `call <model> <verb> [specifier] --data <json>` passthrough.
4
+ *
5
+ * Provides direct access to the demo-api's typed model dispatch without typed wrappers — useful
6
+ * for ad-hoc admin tasks, scripting, and the MVP demo CLI before model-specific commands ship.
7
+ */
8
+ export declare const callPassthroughCommand: CommandModule;
@@ -0,0 +1,3 @@
1
+ export * from './call-model.client';
2
+ export * from './call-model.command.factory';
3
+ export * from './call.passthrough.command';
@@ -0,0 +1,29 @@
1
+ import type { CommandModule } from 'yargs';
2
+ import { type CliEnvDefault } from '../config/env';
3
+ export interface CreateAuthCommandInput {
4
+ readonly cliName: string;
5
+ /**
6
+ * The env var name used to resolve the active env when no flag is provided.
7
+ *
8
+ * Conventionally `<CLINAME>_ENV` (e.g. `DEMO_CLI_ENV`).
9
+ */
10
+ readonly envVarName?: string;
11
+ /**
12
+ * Built-in env presets. Merged underneath the user's stored env when the env name matches.
13
+ */
14
+ readonly defaultEnvs?: readonly CliEnvDefault[];
15
+ }
16
+ /**
17
+ * Factory for the built-in `auth` command tree.
18
+ *
19
+ * Wires `setup`, `login`, `logout`, `status`, `show`, and `check` subcommands that drive the OIDC
20
+ * PKCE flow against the active env, persist tokens via the per-CLI token cache, and print a
21
+ * structured envelope.
22
+ *
23
+ * @param input - Factory configuration.
24
+ * @param input.cliName - The CLI's binary name. Used for the per-user config dir, env-var prefix, and error messages.
25
+ * @param input.envVarName - Override for the env-name env var. Defaults to `<CLINAME>_ENV` (e.g. `DEMO_CLI_ENV`).
26
+ * @param input.defaultEnvs - Built-in env presets merged underneath the user's stored env when names match.
27
+ * @returns A yargs `CommandModule` exposing the full `auth` subcommand surface.
28
+ */
29
+ export declare function createAuthCommand(input: CreateAuthCommandInput): CommandModule;
@@ -0,0 +1,3 @@
1
+ export * from './auth.command.factory';
2
+ export * from './oidc.client';
3
+ export * from './oidc.flow';
@@ -0,0 +1,125 @@
1
+ /**
2
+ * The subset of fields we read from an OIDC discovery document.
3
+ *
4
+ * The CLI only uses the authorization, token, userinfo, and revocation endpoints to drive
5
+ * the authorization-code-with-PKCE flow.
6
+ */
7
+ export interface OidcDiscoveryMetadata {
8
+ readonly issuer: string;
9
+ readonly authorization_endpoint: string;
10
+ readonly token_endpoint: string;
11
+ readonly userinfo_endpoint?: string;
12
+ readonly revocation_endpoint?: string;
13
+ readonly end_session_endpoint?: string;
14
+ readonly jwks_uri?: string;
15
+ readonly scopes_supported?: string[];
16
+ }
17
+ /**
18
+ * Token response from a successful `authorization_code` or `refresh_token` exchange.
19
+ */
20
+ export interface OidcTokenResponse {
21
+ readonly access_token: string;
22
+ readonly token_type?: string;
23
+ readonly expires_in?: number;
24
+ readonly refresh_token?: string;
25
+ readonly id_token?: string;
26
+ readonly scope?: string;
27
+ }
28
+ export interface DiscoverOidcMetadataInput {
29
+ readonly issuer: string;
30
+ /**
31
+ * Optional sibling base URL to try after the issuer-prefixed and origin-rooted paths.
32
+ *
33
+ * Useful when the discovery doc is served at `<api-base-url>/.well-known/openid-configuration`
34
+ * — e.g. when the issuer is reached via a different host than the API and the origin-rooted
35
+ * candidate would target the wrong host.
36
+ */
37
+ readonly fallbackBaseUrl?: string;
38
+ }
39
+ /**
40
+ * Fetches the OIDC discovery document for the given issuer, trying these candidates in order:
41
+ *
42
+ * 1. `<issuer>/.well-known/openid-configuration` (OpenID Connect Discovery 1.0).
43
+ * 2. `<issuer-origin>/.well-known/openid-configuration` (host-rooted; matches projects that
44
+ * mount the discovery controller at the API/dev-server root rather than under the issuer
45
+ * sub-path — e.g. demo's `OidcWellKnownController`).
46
+ * 3. `<fallbackBaseUrl>/.well-known/openid-configuration` when supplied and not already covered.
47
+ *
48
+ * @param input - The discovery request.
49
+ * @param input.issuer - The OIDC issuer URL whose `.well-known/openid-configuration` is fetched first.
50
+ * @param input.fallbackBaseUrl - Optional sibling base URL tried after the issuer-prefixed and origin-rooted candidates.
51
+ * @returns The parsed {@link OidcDiscoveryMetadata}. Throws a {@link CliError} (`OIDC_DISCOVERY_FAILED`) when every candidate fails.
52
+ */
53
+ export declare function discoverOidcMetadata(input: DiscoverOidcMetadataInput): Promise<OidcDiscoveryMetadata>;
54
+ export interface ExchangeAuthorizationCodeInput {
55
+ readonly tokenEndpoint: string;
56
+ readonly clientId: string;
57
+ readonly clientSecret: string;
58
+ readonly redirectUri: string;
59
+ readonly code: string;
60
+ readonly codeVerifier: string;
61
+ }
62
+ /**
63
+ * Exchanges an authorization code (from the redirect) for an access token + refresh token.
64
+ *
65
+ * Uses `client_secret_post` auth — the demo's oidc-provider config registers clients with that method.
66
+ *
67
+ * @param input - The token exchange parameters.
68
+ * @param input.tokenEndpoint - The OIDC token endpoint URL discovered from the issuer.
69
+ * @param input.clientId - The OAuth client ID.
70
+ * @param input.clientSecret - The OAuth client secret used for `client_secret_post` auth.
71
+ * @param input.redirectUri - The redirect URI registered with the OAuth client (must match the value used when requesting the code).
72
+ * @param input.code - The authorization code returned to the redirect URI.
73
+ * @param input.codeVerifier - The PKCE code verifier originally paired with the code challenge in the authorization request.
74
+ * @returns The parsed {@link OidcTokenResponse} with access/refresh tokens.
75
+ */
76
+ export declare function exchangeAuthorizationCode(input: ExchangeAuthorizationCodeInput): Promise<OidcTokenResponse>;
77
+ export interface RefreshAccessTokenInput {
78
+ readonly tokenEndpoint: string;
79
+ readonly clientId: string;
80
+ readonly clientSecret: string;
81
+ readonly refreshToken: string;
82
+ }
83
+ /**
84
+ * Exchanges a refresh token for a new access token (and possibly a rotated refresh token).
85
+ *
86
+ * @param input - The refresh request.
87
+ * @param input.tokenEndpoint - The OIDC token endpoint URL discovered from the issuer.
88
+ * @param input.clientId - The OAuth client ID.
89
+ * @param input.clientSecret - The OAuth client secret used for `client_secret_post` auth.
90
+ * @param input.refreshToken - The cached refresh token to redeem.
91
+ * @returns The parsed {@link OidcTokenResponse} with the refreshed access token.
92
+ */
93
+ export declare function refreshAccessToken(input: RefreshAccessTokenInput): Promise<OidcTokenResponse>;
94
+ export interface RevokeTokenInput {
95
+ readonly revocationEndpoint: string;
96
+ readonly clientId: string;
97
+ readonly clientSecret: string;
98
+ readonly token: string;
99
+ readonly tokenTypeHint?: 'access_token' | 'refresh_token';
100
+ }
101
+ /**
102
+ * Revokes an access or refresh token at the OIDC revocation endpoint.
103
+ *
104
+ * @param input - The revocation request.
105
+ * @param input.revocationEndpoint - The OIDC revocation endpoint URL.
106
+ * @param input.clientId - The OAuth client ID used for `client_secret_post` auth.
107
+ * @param input.clientSecret - The OAuth client secret used for `client_secret_post` auth.
108
+ * @param input.token - The access or refresh token to revoke.
109
+ * @param input.tokenTypeHint - Optional hint passed as `token_type_hint` (`access_token` or `refresh_token`).
110
+ * @returns Resolves when the server returns a non-error status. Throws a {@link CliError} (`TOKEN_REVOCATION_FAILED`) on error.
111
+ */
112
+ export declare function revokeToken(input: RevokeTokenInput): Promise<void>;
113
+ export interface FetchUserInfoInput {
114
+ readonly userinfoEndpoint: string;
115
+ readonly accessToken: string;
116
+ }
117
+ /**
118
+ * Fetches the OIDC `userinfo` endpoint and returns the parsed claims object.
119
+ *
120
+ * @param input - The userinfo request.
121
+ * @param input.userinfoEndpoint - The OIDC userinfo endpoint URL discovered from the issuer.
122
+ * @param input.accessToken - The Bearer access token sent in the `Authorization` header.
123
+ * @returns The parsed userinfo claims. Throws a {@link CliError} (`USERINFO_FAILED`) on a non-OK response.
124
+ */
125
+ export declare function fetchUserInfo(input: FetchUserInfoInput): Promise<Record<string, unknown>>;
@@ -0,0 +1,79 @@
1
+ import { type Maybe } from '@dereekb/util';
2
+ export interface BuildAuthorizationUrlInput {
3
+ readonly authorizationEndpoint: string;
4
+ /**
5
+ * Optional API base URL. When set and `appClientUrl` is not provided, the CLI sends the user
6
+ * to `<apiBaseUrl>/oidc/login/client?<params>` instead of the raw authorization endpoint. The
7
+ * API redirects to the configured app login URL with the OAuth query string preserved, so the
8
+ * CLI does not need to know the client origin directly.
9
+ */
10
+ readonly apiBaseUrl?: Maybe<string>;
11
+ /**
12
+ * Optional client (frontend) origin to rebase the authorization endpoint onto.
13
+ *
14
+ * When set, the path + search of `authorizationEndpoint` are kept and the origin is replaced
15
+ * with this URL. Useful when the frontend dev server proxies `/oidc/**` to the API and the
16
+ * user-facing URL should target the app, not the API directly. Takes precedence over
17
+ * `apiBaseUrl` when both are provided.
18
+ */
19
+ readonly appClientUrl?: Maybe<string>;
20
+ readonly clientId: string;
21
+ readonly redirectUri: string;
22
+ readonly scopes?: string;
23
+ readonly state: string;
24
+ readonly codeChallenge: string;
25
+ }
26
+ /**
27
+ * Builds the authorization URL the user opens in a browser to start the PKCE flow.
28
+ *
29
+ * Resolves the user-facing endpoint by preferring `appClientUrl` (rebases the discovered
30
+ * authorization endpoint onto that origin) over `apiBaseUrl` (`/oidc/login/client` shortcut),
31
+ * and finally falls back to the discovered `authorizationEndpoint` itself.
32
+ *
33
+ * @param input - The authorization URL inputs.
34
+ * @param input.authorizationEndpoint - The authorization endpoint discovered from OIDC metadata.
35
+ * @param input.apiBaseUrl - Optional API base URL; when set without `appClientUrl`, the URL is built against `<apiBaseUrl>/oidc/login/client`.
36
+ * @param input.appClientUrl - Optional frontend origin to rebase the authorization endpoint onto. Takes precedence over `apiBaseUrl`.
37
+ * @param input.clientId - The OAuth client ID.
38
+ * @param input.redirectUri - The redirect URI registered with the OAuth client.
39
+ * @param input.scopes - Space-separated scope list. Defaults to {@link DEFAULT_CLI_OIDC_SCOPES}.
40
+ * @param input.state - The opaque OAuth state token used for CSRF protection.
41
+ * @param input.codeChallenge - The PKCE code challenge derived from the verifier.
42
+ * @returns The full authorization URL with all OAuth params merged in.
43
+ */
44
+ export declare function buildAuthorizationUrl(input: BuildAuthorizationUrlInput): string;
45
+ export interface PkceMaterial {
46
+ readonly codeVerifier: string;
47
+ readonly codeChallenge: string;
48
+ }
49
+ /**
50
+ * Generates a fresh PKCE code verifier and the matching SHA-256 code challenge.
51
+ *
52
+ * @returns A {@link PkceMaterial} pair consisting of the random `codeVerifier` and its derived `codeChallenge`.
53
+ */
54
+ export declare function generatePkceMaterial(): Promise<PkceMaterial>;
55
+ /**
56
+ * Generates a random URL-safe state value for the OAuth state parameter.
57
+ *
58
+ * @returns A 32-character hex string derived from 16 random bytes.
59
+ */
60
+ export declare function generateOAuthState(): string;
61
+ export interface ParsePastedRedirectInput {
62
+ readonly pasted: string;
63
+ readonly expectedState?: string;
64
+ }
65
+ export interface ParsedRedirect {
66
+ readonly code: string;
67
+ readonly state?: string;
68
+ }
69
+ /**
70
+ * Parses an authorization code out of a pasted redirect URL or a bare code string.
71
+ *
72
+ * Validates the `state` parameter when an expected value is provided.
73
+ *
74
+ * @param input - The parse inputs.
75
+ * @param input.pasted - The redirect URL or bare authorization code pasted by the user.
76
+ * @param input.expectedState - Optional state value to assert against `state` when present in the URL.
77
+ * @returns The {@link ParsedRedirect} containing `code` and (when present) `state`.
78
+ */
79
+ export declare function parsePastedRedirect(input: ParsePastedRedirectInput): ParsedRedirect;
@@ -0,0 +1,112 @@
1
+ import { type Maybe } from '@dereekb/util';
2
+ import { type CliEnvConfig } from './env';
3
+ /**
4
+ * Output settings that can be applied per-command.
5
+ */
6
+ export interface CliCommandOutputConfig {
7
+ readonly dumpDir?: string;
8
+ readonly pick?: string;
9
+ }
10
+ /**
11
+ * Output configuration with global defaults and optional per-command overrides.
12
+ *
13
+ * Command keys use dot-separated paths matching the yargs command hierarchy
14
+ * (e.g. `call.profile.read`).
15
+ */
16
+ export interface CliOutputConfig extends CliCommandOutputConfig {
17
+ readonly commands?: Record<string, CliCommandOutputConfig>;
18
+ }
19
+ /**
20
+ * The persisted shape of `<configDir>/config.json`.
21
+ */
22
+ export interface CliConfig {
23
+ readonly activeEnv?: string;
24
+ readonly envs?: Record<string, CliEnvConfig>;
25
+ readonly output?: CliOutputConfig;
26
+ }
27
+ export interface LoadCliConfigInput {
28
+ readonly configFilePath: string;
29
+ }
30
+ /**
31
+ * Loads the persisted CLI config from disk. Returns `undefined` when the file is missing.
32
+ *
33
+ * @param input - The load inputs.
34
+ * @param input.configFilePath - Absolute path to the JSON config file.
35
+ * @returns The parsed {@link CliConfig}, or `undefined` when the file does not exist.
36
+ */
37
+ export declare function loadCliConfig(input: LoadCliConfigInput): Promise<Maybe<CliConfig>>;
38
+ export interface SaveCliConfigInput extends LoadCliConfigInput {
39
+ readonly configDir: string;
40
+ readonly config: CliConfig;
41
+ }
42
+ /**
43
+ * Writes the full {@link CliConfig} to disk, creating the parent directory if needed.
44
+ *
45
+ * @param input - The save inputs.
46
+ * @param input.configFilePath - Absolute path to the JSON config file.
47
+ * @param input.configDir - Absolute path to the config directory (created if missing).
48
+ * @param input.config - The full config object to persist.
49
+ * @returns Resolves once the file has been written.
50
+ */
51
+ export declare function saveCliConfig(input: SaveCliConfigInput): Promise<void>;
52
+ export interface MergeCliConfigInput extends Omit<SaveCliConfigInput, 'config'> {
53
+ readonly updates: Partial<CliConfig>;
54
+ }
55
+ /**
56
+ * Merges updates into the existing config and saves to disk.
57
+ *
58
+ * `envs` is shallow-merged so a partial env update preserves existing keys for other envs.
59
+ * `output` is merged via {@link mergeOutputConfig}.
60
+ *
61
+ * @param input - The merge inputs.
62
+ * @param input.configFilePath - Absolute path to the JSON config file.
63
+ * @param input.configDir - Absolute path to the config directory (created if missing).
64
+ * @param input.updates - The partial {@link CliConfig} to merge on top of the existing on-disk config.
65
+ * @returns The merged {@link CliConfig} that was just written.
66
+ */
67
+ export declare function mergeCliConfig(input: MergeCliConfigInput): Promise<CliConfig>;
68
+ /**
69
+ * Merges a partial {@link CliOutputConfig} update on top of an existing config slice.
70
+ *
71
+ * Exported so downstream CLIs that nest the output config under a different config tree (e.g.
72
+ * zoho-cli's `shared/recruit/crm/desk/output` shape) can reuse the same merge semantics for the
73
+ * output sub-tree without forking the implementation.
74
+ *
75
+ * @param existing - The existing output config slice (or `null`/`undefined` if none was stored).
76
+ * @param updates - The partial output config to merge on top.
77
+ * @returns The merged {@link CliOutputConfig}.
78
+ */
79
+ export declare function mergeOutputConfig(existing: Maybe<CliOutputConfig>, updates: CliOutputConfig): CliOutputConfig;
80
+ export interface ResolveOutputConfigInput {
81
+ readonly outputConfig: Maybe<CliOutputConfig>;
82
+ readonly commandPath: string[];
83
+ readonly cliFlags: {
84
+ dumpDir?: string;
85
+ pick?: string;
86
+ };
87
+ }
88
+ /**
89
+ * Resolves output settings for a given command path.
90
+ *
91
+ * Resolution order (highest priority first):
92
+ * 1. CLI flags (dumpDir, pick from argv)
93
+ * 2. Per-command config (output.commands["call.profile.read"])
94
+ * 3. Global output config (output.dumpDir, output.pick)
95
+ *
96
+ * @param input - The resolution inputs.
97
+ * @param input.outputConfig - The persisted {@link CliOutputConfig} (or `null`/`undefined`).
98
+ * @param input.commandPath - The yargs command-path segments (joined with `.` to look up per-command overrides).
99
+ * @param input.cliFlags - Per-invocation overrides parsed from argv.
100
+ * @returns The resolved `dumpDir` / `pick` pair to apply for this command.
101
+ */
102
+ export declare function resolveOutputConfig(input: ResolveOutputConfigInput): {
103
+ dumpDir?: string;
104
+ pick?: string;
105
+ };
106
+ /**
107
+ * Masks a secret string by keeping the first 4 chars and replacing the rest with `***`.
108
+ *
109
+ * @param value - The string to mask. `null`/`undefined` are returned unchanged.
110
+ * @returns The masked string (`***` when the input is 4 chars or shorter), or the input unchanged when nullish.
111
+ */
112
+ export declare function maskSecret(value: Maybe<string>): Maybe<string>;
@@ -0,0 +1,183 @@
1
+ import { type Maybe } from '@dereekb/util';
2
+ /**
3
+ * The default OAuth/OIDC scopes requested by the CLI when none are configured.
4
+ */
5
+ export declare const DEFAULT_CLI_OIDC_SCOPES = "openid profile email";
6
+ /**
7
+ * The `model.*` write scopes filtered out by {@link filterReadOnlyModelScopes}.
8
+ *
9
+ * Mirrors the write half of the dbx-components callModel CRUD scope set
10
+ * (`CALL_MODEL_OIDC_SCOPES` in `@dereekb/firebase-server/oidc`) — duplicated here so the
11
+ * CLI doesn't take a server-side dependency just to know the names.
12
+ */
13
+ export declare const MODEL_WRITE_OIDC_SCOPES: readonly ["model.create", "model.update", "model.delete"];
14
+ /**
15
+ * Returns the input scope string with the `model.create`, `model.update`, and `model.delete`
16
+ * scopes removed, preserving every other scope (including `model.read` and `model.query`).
17
+ *
18
+ * Drives the `auth login --read-only-scopes` flag: when a CLI's env defaults request the
19
+ * full callModel CRUD scope set, this trims the request down to read/query only.
20
+ *
21
+ * @param scopes - Space-separated scope list, or `undefined` to filter the default scopes.
22
+ * @returns The filtered space-separated scope list.
23
+ */
24
+ export declare function filterReadOnlyModelScopes(scopes: Maybe<string>): string;
25
+ /**
26
+ * A built-in env config preset shipped with a CLI app.
27
+ *
28
+ * Each preset is addressable by one or more {@link names} (so e.g. `dev` and `local` can resolve
29
+ * to the same default). Values from the user's persisted env shadow these defaults; missing fields
30
+ * fall back to the default.
31
+ */
32
+ export interface CliEnvDefault {
33
+ /**
34
+ * Names this default config is addressable by. Each name must be unique across the registered
35
+ * defaults — an env name resolves to at most one default.
36
+ */
37
+ readonly names: readonly string[];
38
+ /**
39
+ * The default config values. Any field can be omitted; the user's stored env (and env-var
40
+ * overrides) shadow these values at resolution time.
41
+ */
42
+ readonly env: Partial<CliEnvConfig>;
43
+ }
44
+ /**
45
+ * Returns the {@link CliEnvDefault} whose `names` includes the given env name, or `undefined`.
46
+ */
47
+ export interface FindCliEnvDefaultInput {
48
+ readonly name: string;
49
+ readonly defaults?: readonly CliEnvDefault[];
50
+ }
51
+ /**
52
+ * Returns the {@link CliEnvDefault} whose `names` includes the given env name, or `undefined`.
53
+ *
54
+ * @param input - The lookup inputs.
55
+ * @param input.name - The env name to look up.
56
+ * @param input.defaults - The list of registered defaults to search.
57
+ * @returns The matching {@link CliEnvDefault}, or `undefined` when no default registers `name`.
58
+ */
59
+ export declare function findCliEnvDefault(input: FindCliEnvDefaultInput): Maybe<CliEnvDefault>;
60
+ /**
61
+ * Merges a stored env on top of a default env. User-set fields take precedence; empty strings are
62
+ * treated as "not set" so that an `env add <name>` call that didn't pass `--api-base-url` still
63
+ * picks up the default.
64
+ */
65
+ export interface MergeCliEnvWithDefaultInput {
66
+ readonly env?: Maybe<CliEnvConfig>;
67
+ readonly defaultEnv?: Maybe<Partial<CliEnvConfig>>;
68
+ }
69
+ /**
70
+ * Merges a stored env on top of a default env, treating empty strings on either side as "not set".
71
+ *
72
+ * @param input - The merge inputs.
73
+ * @param input.env - The user's persisted env config (or `null`/`undefined`).
74
+ * @param input.defaultEnv - The matching {@link CliEnvDefault}'s partial env values, if any.
75
+ * @returns The merged {@link CliEnvConfig}, or `undefined` when both inputs are empty.
76
+ */
77
+ export declare function mergeCliEnvWithDefault(input: MergeCliEnvWithDefaultInput): Maybe<CliEnvConfig>;
78
+ /**
79
+ * Environment-targeting config for a CLI invocation.
80
+ *
81
+ * Each env (e.g. `local`, `staging`, `prod`) holds the API base URL plus the OIDC client
82
+ * registration the user copied from the target app's web UI. Tokens are cached separately
83
+ * (see {@link CliTokenEntry}).
84
+ *
85
+ * The term "env" is used instead of "profile" to avoid colliding with the demo's
86
+ * `Profile` Firestore model — so user-facing flags read `--env local`, not `--profile local`.
87
+ */
88
+ export interface CliEnvConfig {
89
+ /**
90
+ * The base URL for the API. The CLI POSTs `<apiBaseUrl>/model/call` for the callModel passthrough.
91
+ */
92
+ readonly apiBaseUrl: string;
93
+ /**
94
+ * The OIDC issuer URL — typically the OIDC controller mount under the API.
95
+ *
96
+ * The CLI fetches `<oidcIssuer>/.well-known/openid-configuration` first (per RFC 8414) and
97
+ * falls back to `<apiBaseUrl>/.well-known/openid-configuration` if the issuer-prefixed path
98
+ * is not served.
99
+ */
100
+ readonly oidcIssuer: string;
101
+ /**
102
+ * Optional base URL for the app's client (frontend). When set, the CLI rebases the discovered
103
+ * `authorization_endpoint` onto this origin so the user is sent to the frontend (which proxies
104
+ * `/oidc/**` to the backend) instead of being sent directly to the API.
105
+ *
106
+ * Useful in local development where the API runs on a separate port from the frontend
107
+ * dev server. In production, leave this unset when the API and frontend share an origin.
108
+ */
109
+ readonly appClientUrl?: string;
110
+ /**
111
+ * The OAuth client ID registered with the target app.
112
+ */
113
+ readonly clientId?: string;
114
+ /**
115
+ * The OAuth client secret registered with the target app.
116
+ */
117
+ readonly clientSecret?: string;
118
+ /**
119
+ * The redirect URI registered with the OAuth client. The CLI does not bind a server — it parses
120
+ * the URL the user pastes back, so this can be any value the OIDC provider accepts as a
121
+ * registered redirect URI (e.g. `http://127.0.0.1:0/callback` or another loopback/placeholder URL).
122
+ */
123
+ readonly redirectUri?: string;
124
+ /**
125
+ * Space-separated OAuth scopes to request. Defaults to {@link DEFAULT_CLI_OIDC_SCOPES}.
126
+ */
127
+ readonly scopes?: string;
128
+ }
129
+ /**
130
+ * Resolves the active env name from a flag, env-var, or the persisted config default.
131
+ *
132
+ * Resolution order:
133
+ * 1. CLI `--env` flag
134
+ * 2. `<CLINAME>_ENV` environment variable
135
+ * 3. The `activeEnv` field in the persisted config
136
+ */
137
+ export interface ResolveActiveEnvInput {
138
+ readonly flagEnv?: string;
139
+ readonly envVarName: string;
140
+ readonly defaultEnv?: string;
141
+ }
142
+ /**
143
+ * Resolves the active env name from a flag, env var, or persisted default.
144
+ *
145
+ * @param input - The resolution inputs.
146
+ * @param input.flagEnv - The value passed via `--env` (highest priority).
147
+ * @param input.envVarName - The name of the `<CLINAME>_ENV` env var to consult.
148
+ * @param input.defaultEnv - The persisted `activeEnv` from the config (lowest priority).
149
+ * @returns The first non-empty value among the inputs, or `undefined` when none is set.
150
+ */
151
+ export declare function resolveActiveEnvName(input: ResolveActiveEnvInput): Maybe<string>;
152
+ /**
153
+ * Applies env-var overrides on top of a stored {@link CliEnvConfig}.
154
+ *
155
+ * The conventional env vars for a CLI named `demo-cli` are:
156
+ * - `DEMO_CLI_API_BASE_URL`
157
+ * - `DEMO_CLI_OIDC_ISSUER`
158
+ * - `DEMO_CLI_APP_CLIENT_URL`
159
+ * - `DEMO_CLI_CLIENT_ID`
160
+ * - `DEMO_CLI_CLIENT_SECRET`
161
+ * - `DEMO_CLI_REDIRECT_URI`
162
+ * - `DEMO_CLI_SCOPES`
163
+ */
164
+ export interface EnvVarOverrideInput {
165
+ readonly cliName: string;
166
+ readonly env: Maybe<CliEnvConfig>;
167
+ }
168
+ /**
169
+ * Reads `<CLINAME_PREFIX>_*` env vars and overlays them on top of the stored env.
170
+ *
171
+ * @param input - The override inputs.
172
+ * @param input.cliName - The CLI name (used to derive the env-var prefix; e.g. `demo-cli` → `DEMO_CLI`).
173
+ * @param input.env - The base {@link CliEnvConfig} to overlay env-var values on top of.
174
+ * @returns The merged {@link CliEnvConfig}, or `undefined` when both the stored env and every override are empty.
175
+ */
176
+ export declare function applyEnvVarOverrides(input: EnvVarOverrideInput): Maybe<CliEnvConfig>;
177
+ /**
178
+ * Returns true when the env has the minimum fields needed to attempt an OAuth login or token refresh.
179
+ *
180
+ * @param env - The env config to check.
181
+ * @returns `true` when `apiBaseUrl`, `oidcIssuer`, `clientId`, `clientSecret`, and `redirectUri` are all present and non-empty.
182
+ */
183
+ export declare function isCliEnvConfigComplete(env: Maybe<CliEnvConfig>): env is Required<Pick<CliEnvConfig, 'apiBaseUrl' | 'oidcIssuer' | 'clientId' | 'clientSecret' | 'redirectUri'>> & CliEnvConfig;
@@ -0,0 +1,4 @@
1
+ export * from './cli.config';
2
+ export * from './env';
3
+ export * from './paths';
4
+ export * from './token.cache';