@getbrevo/cli 0.0.1-alpha.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.
Files changed (171) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/LICENSE +21 -0
  3. package/README.md +182 -0
  4. package/dist/api/client.d.ts +23 -0
  5. package/dist/api/client.d.ts.map +1 -0
  6. package/dist/api/client.js +186 -0
  7. package/dist/api/client.js.map +1 -0
  8. package/dist/bin/index.d.ts +3 -0
  9. package/dist/bin/index.d.ts.map +1 -0
  10. package/dist/bin/index.js +179 -0
  11. package/dist/bin/index.js.map +1 -0
  12. package/dist/commands/app/create.d.ts +7 -0
  13. package/dist/commands/app/create.d.ts.map +1 -0
  14. package/dist/commands/app/create.js +261 -0
  15. package/dist/commands/app/create.js.map +1 -0
  16. package/dist/commands/app/credentials.d.ts +6 -0
  17. package/dist/commands/app/credentials.d.ts.map +1 -0
  18. package/dist/commands/app/credentials.js +106 -0
  19. package/dist/commands/app/credentials.js.map +1 -0
  20. package/dist/commands/app/delete.d.ts +6 -0
  21. package/dist/commands/app/delete.d.ts.map +1 -0
  22. package/dist/commands/app/delete.js +146 -0
  23. package/dist/commands/app/delete.js.map +1 -0
  24. package/dist/commands/app/list.d.ts +4 -0
  25. package/dist/commands/app/list.d.ts.map +1 -0
  26. package/dist/commands/app/list.js +46 -0
  27. package/dist/commands/app/list.js.map +1 -0
  28. package/dist/commands/app/scaffold.d.ts +5 -0
  29. package/dist/commands/app/scaffold.d.ts.map +1 -0
  30. package/dist/commands/app/scaffold.js +199 -0
  31. package/dist/commands/app/scaffold.js.map +1 -0
  32. package/dist/commands/app/start.d.ts +5 -0
  33. package/dist/commands/app/start.d.ts.map +1 -0
  34. package/dist/commands/app/start.js +138 -0
  35. package/dist/commands/app/start.js.map +1 -0
  36. package/dist/commands/app/test.d.ts +6 -0
  37. package/dist/commands/app/test.d.ts.map +1 -0
  38. package/dist/commands/app/test.js +495 -0
  39. package/dist/commands/app/test.js.map +1 -0
  40. package/dist/commands/app/update.d.ts +2 -0
  41. package/dist/commands/app/update.d.ts.map +1 -0
  42. package/dist/commands/app/update.js +324 -0
  43. package/dist/commands/app/update.js.map +1 -0
  44. package/dist/commands/definitions.d.ts +4 -0
  45. package/dist/commands/definitions.d.ts.map +1 -0
  46. package/dist/commands/definitions.js +197 -0
  47. package/dist/commands/definitions.js.map +1 -0
  48. package/dist/commands/init.d.ts +2 -0
  49. package/dist/commands/init.d.ts.map +1 -0
  50. package/dist/commands/init.js +90 -0
  51. package/dist/commands/init.js.map +1 -0
  52. package/dist/commands/login.d.ts +6 -0
  53. package/dist/commands/login.d.ts.map +1 -0
  54. package/dist/commands/login.js +219 -0
  55. package/dist/commands/login.js.map +1 -0
  56. package/dist/commands/logout.d.ts +5 -0
  57. package/dist/commands/logout.d.ts.map +1 -0
  58. package/dist/commands/logout.js +54 -0
  59. package/dist/commands/logout.js.map +1 -0
  60. package/dist/commands/whoami.d.ts +4 -0
  61. package/dist/commands/whoami.d.ts.map +1 -0
  62. package/dist/commands/whoami.js +74 -0
  63. package/dist/commands/whoami.js.map +1 -0
  64. package/dist/container.d.ts +7 -0
  65. package/dist/container.d.ts.map +1 -0
  66. package/dist/container.js +28 -0
  67. package/dist/container.js.map +1 -0
  68. package/dist/lang/en.d.ts +110 -0
  69. package/dist/lang/en.d.ts.map +1 -0
  70. package/dist/lang/en.js +133 -0
  71. package/dist/lang/en.js.map +1 -0
  72. package/dist/lib/abort.d.ts +3 -0
  73. package/dist/lib/abort.d.ts.map +1 -0
  74. package/dist/lib/abort.js +30 -0
  75. package/dist/lib/abort.js.map +1 -0
  76. package/dist/lib/auth-guard.d.ts +3 -0
  77. package/dist/lib/auth-guard.d.ts.map +1 -0
  78. package/dist/lib/auth-guard.js +25 -0
  79. package/dist/lib/auth-guard.js.map +1 -0
  80. package/dist/lib/browser.d.ts +2 -0
  81. package/dist/lib/browser.d.ts.map +1 -0
  82. package/dist/lib/browser.js +16 -0
  83. package/dist/lib/browser.js.map +1 -0
  84. package/dist/lib/command-handler.d.ts +10 -0
  85. package/dist/lib/command-handler.d.ts.map +1 -0
  86. package/dist/lib/command-handler.js +16 -0
  87. package/dist/lib/command-handler.js.map +1 -0
  88. package/dist/lib/command-registry.d.ts +28 -0
  89. package/dist/lib/command-registry.d.ts.map +1 -0
  90. package/dist/lib/command-registry.js +54 -0
  91. package/dist/lib/command-registry.js.map +1 -0
  92. package/dist/lib/config.d.ts +76 -0
  93. package/dist/lib/config.d.ts.map +1 -0
  94. package/dist/lib/config.js +314 -0
  95. package/dist/lib/config.js.map +1 -0
  96. package/dist/lib/constants.d.ts +35 -0
  97. package/dist/lib/constants.d.ts.map +1 -0
  98. package/dist/lib/constants.js +97 -0
  99. package/dist/lib/constants.js.map +1 -0
  100. package/dist/lib/errors.d.ts +30 -0
  101. package/dist/lib/errors.d.ts.map +1 -0
  102. package/dist/lib/errors.js +66 -0
  103. package/dist/lib/errors.js.map +1 -0
  104. package/dist/lib/exit-codes.d.ts +9 -0
  105. package/dist/lib/exit-codes.d.ts.map +1 -0
  106. package/dist/lib/exit-codes.js +12 -0
  107. package/dist/lib/exit-codes.js.map +1 -0
  108. package/dist/lib/hidden-input.d.ts +2 -0
  109. package/dist/lib/hidden-input.d.ts.map +1 -0
  110. package/dist/lib/hidden-input.js +78 -0
  111. package/dist/lib/hidden-input.js.map +1 -0
  112. package/dist/lib/json-output.d.ts +6 -0
  113. package/dist/lib/json-output.d.ts.map +1 -0
  114. package/dist/lib/json-output.js +11 -0
  115. package/dist/lib/json-output.js.map +1 -0
  116. package/dist/lib/logger.d.ts +11 -0
  117. package/dist/lib/logger.d.ts.map +1 -0
  118. package/dist/lib/logger.js +87 -0
  119. package/dist/lib/logger.js.map +1 -0
  120. package/dist/lib/port.d.ts +12 -0
  121. package/dist/lib/port.d.ts.map +1 -0
  122. package/dist/lib/port.js +79 -0
  123. package/dist/lib/port.js.map +1 -0
  124. package/dist/lib/ui.d.ts +14 -0
  125. package/dist/lib/ui.d.ts.map +1 -0
  126. package/dist/lib/ui.js +75 -0
  127. package/dist/lib/ui.js.map +1 -0
  128. package/dist/lib/validators.d.ts +32 -0
  129. package/dist/lib/validators.d.ts.map +1 -0
  130. package/dist/lib/validators.js +96 -0
  131. package/dist/lib/validators.js.map +1 -0
  132. package/dist/services/account.d.ts +8 -0
  133. package/dist/services/account.d.ts.map +1 -0
  134. package/dist/services/account.js +15 -0
  135. package/dist/services/account.js.map +1 -0
  136. package/dist/services/app.d.ts +35 -0
  137. package/dist/services/app.d.ts.map +1 -0
  138. package/dist/services/app.js +117 -0
  139. package/dist/services/app.js.map +1 -0
  140. package/dist/services/browser-auth.d.ts +15 -0
  141. package/dist/services/browser-auth.d.ts.map +1 -0
  142. package/dist/services/browser-auth.js +187 -0
  143. package/dist/services/browser-auth.js.map +1 -0
  144. package/dist/services/normalize-app-id.d.ts +12 -0
  145. package/dist/services/normalize-app-id.d.ts.map +1 -0
  146. package/dist/services/normalize-app-id.js +38 -0
  147. package/dist/services/normalize-app-id.js.map +1 -0
  148. package/dist/services/oauth-refresh.d.ts +14 -0
  149. package/dist/services/oauth-refresh.d.ts.map +1 -0
  150. package/dist/services/oauth-refresh.js +70 -0
  151. package/dist/services/oauth-refresh.js.map +1 -0
  152. package/dist/templates/files/AGENTS.md.tmpl +41 -0
  153. package/dist/templates/files/CLAUDE.md.tmpl +50 -0
  154. package/dist/templates/files/README.md.tmpl +77 -0
  155. package/dist/templates/files/app-config.json.tmpl +23 -0
  156. package/dist/templates/files/gitignore.tmpl +9 -0
  157. package/dist/templates/files/src/oauth/.env.example.tmpl +13 -0
  158. package/dist/templates/files/src/oauth/.env.local.tmpl +10 -0
  159. package/dist/templates/files/src/oauth/handler.js.tmpl +203 -0
  160. package/dist/templates/files/src/oauth/package.json.tmpl +15 -0
  161. package/dist/templates/files/src/oauth/server.js.tmpl +226 -0
  162. package/dist/templates/files/src/oauth/token-store.js.tmpl +39 -0
  163. package/dist/templates/index.d.ts +36 -0
  164. package/dist/templates/index.d.ts.map +1 -0
  165. package/dist/templates/index.js +105 -0
  166. package/dist/templates/index.js.map +1 -0
  167. package/dist/types.d.ts +30 -0
  168. package/dist/types.d.ts.map +1 -0
  169. package/dist/types.js +3 -0
  170. package/dist/types.js.map +1 -0
  171. package/package.json +81 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,13 @@
1
+ # @getbrevo/cli
2
+
3
+ ## 0.0.1-alpha.1
4
+
5
+ ### Patch Changes
6
+
7
+ - first cli package
8
+
9
+ ## 0.0.1-alpha.0
10
+
11
+ ### Patch Changes
12
+
13
+ - afbe341: Publish to the public npm registry (`registry.npmjs.org`) instead of GitHub Package Registry. The package is now installable via `npm install -g @getbrevo/cli` without any registry configuration.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Brevo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,182 @@
1
+ # Brevo Developer CLI
2
+
3
+ Command-line tool to create, manage, and test [Brevo](https://www.brevo.com/) OAuth integrations from your terminal.
4
+
5
+ ## Requirements
6
+
7
+ - **Node.js** 20.15.0 or newer (required to run the CLI)
8
+ - **Yarn** 1.19.1 or newer (only required for developing/building from source; not needed when installing via `npm install -g @getbrevo/cli`)
9
+
10
+ ## Installation
11
+
12
+ Install globally from the public npm registry:
13
+
14
+ ```bash
15
+ npm install -g @getbrevo/cli
16
+ # or:
17
+ yarn global add @getbrevo/cli
18
+ ```
19
+
20
+ This puts the `brevo` binary on your PATH. Verify:
21
+
22
+ ```bash
23
+ brevo --version
24
+ ```
25
+
26
+ To upgrade later: `npm install -g @getbrevo/cli@latest`.
27
+
28
+ > Building from source? See [Development](#development) below.
29
+
30
+ ## Quick start
31
+
32
+ The fastest path is `brevo app init`, which walks you through login, creating your first app, and generating starter code:
33
+
34
+ ```bash
35
+ brevo app init
36
+ ```
37
+
38
+ Or step by step:
39
+
40
+ 1. **Authenticate.** `brevo login` defaults to a browser sign-in; you can also pick API-key auth from the prompt or pass `--browser` to skip it:
41
+
42
+ ```bash
43
+ brevo login # interactive — choose browser (default) or API key
44
+ brevo login --browser # force the browser flow
45
+ ```
46
+
47
+ For non-interactive use (CI), set `BREVO_API_KEY` ([create or copy a key](https://app.brevo.com/settings/keys/api)) before running `brevo login`:
48
+
49
+ ```bash
50
+ export BREVO_API_KEY=xkeysib-...
51
+ brevo login
52
+ ```
53
+
54
+ 2. **Confirm** the active account:
55
+
56
+ ```bash
57
+ brevo whoami
58
+ ```
59
+
60
+ 3. **Manage OAuth apps** (examples):
61
+
62
+ ```bash
63
+ brevo app list
64
+ brevo app create --name "My App" --distribution private
65
+ brevo app scaffold --app-id 42
66
+ brevo app start oauth --port 3000
67
+ ```
68
+
69
+ Run `brevo --help` or `brevo <command> --help` for full command and option lists. Every command supports `--json` for machine-readable output.
70
+
71
+ ## Commands
72
+
73
+ | Command | Description |
74
+ | --- | --- |
75
+ | `brevo login` | Authenticate — browser sign-in by default, or `BREVO_API_KEY` for CI |
76
+ | `brevo logout` | Clear stored credentials (`--force` to skip confirmation) |
77
+ | `brevo whoami` | Show the authenticated user |
78
+ | `brevo app init` | Guided setup — login, create app, and scaffold in one go |
79
+ | `brevo app create` | Create an OAuth app (`--name`, `--distribution private\|public`, repeatable `--redirect-uri`) |
80
+ | `brevo app list` | List OAuth apps in your account |
81
+ | `brevo app credentials` | Show client ID and secret (`--app-id`, `--reveal-secret`) |
82
+ | `brevo app update` | Update app name or redirect URLs (`--app-id`, `--name`, repeatable `--redirect-url`, `--yes`) |
83
+ | `brevo app delete` | Delete an app (`--app-id`, `--force`) |
84
+ | `brevo app scaffold` | Generate starter code for an app (`--app-id`) |
85
+ | `brevo app start` | Run a scaffolded feature locally (e.g. `brevo app start oauth --port 3000`) |
86
+
87
+ Most commands require a successful `brevo login` first, except authentication/help flows (`brevo login`, `brevo logout`, `brevo app init`, `--help`). Every command accepts `--json` for machine-readable output.
88
+
89
+ ### Browser login
90
+
91
+ `brevo login` defaults to a browser-based sign-in. The CLI starts a temporary loopback server, opens your browser to the Brevo CLI login service, and stores the returned tokens in `~/.brevo/credentials.json`. Access tokens refresh automatically on expiry.
92
+
93
+ Flags:
94
+
95
+ - `--browser` — force browser flow.
96
+
97
+ For non-interactive use (CI), set `BREVO_API_KEY=<key>` before running `brevo login`. The legacy `--api-key <key>` flag was removed because it leaks the secret into process listings and shell history; the env var is the only supported way to pass an API key non-interactively.
98
+
99
+ Environment overrides:
100
+
101
+ - `BREVO_API_URL` — points the CLI at a different Brevo API (defaults to `https://api.brevo.com`).
102
+ - `BREVO_OAUTH_PROXY_URL` — points the browser-login flow at a different OAuth proxy (defaults to `https://oauth-cli.brevo.com`; useful for staging or local Worker development).
103
+
104
+ ## Exit codes
105
+
106
+ | Code | Meaning |
107
+ | --- | --- |
108
+ | `0` | Success |
109
+ | `1` | General error |
110
+ | `2` | Aborted (Ctrl+C or SIGTERM) |
111
+ | `3` | Authentication failure (401) |
112
+ | `4` | Network error (API unreachable) |
113
+ | `5` | Not found (404) |
114
+
115
+ ## Configuration
116
+
117
+ | Variable | Purpose | Default |
118
+ | --- | --- | --- |
119
+ | `BREVO_API_KEY` | API key used for non-interactive `brevo login` | – |
120
+ | `BREVO_API_URL` | API base URL (HTTPS required, except for `localhost`) | `https://api.brevo.com` |
121
+ | `BREVO_OAUTH_PROXY_URL` | OAuth proxy used by browser login (HTTPS required, except for `localhost`) | `https://oauth-cli.brevo.com` |
122
+ | `BREVO_CONFIG_HOME` | Override for the credentials directory | `~/.brevo/` |
123
+ | `NO_COLOR` / `FORCE_COLOR` | Disable / force ANSI colour output | – |
124
+ | `DEBUG` or `--debug` | Verbose HTTP and error logging | off |
125
+
126
+ Credentials are stored at `~/.brevo/credentials.json`; per-app client secrets are cached under an `apps` key. Linked project config lives in `./.brevo.json` (gitignored).
127
+
128
+ ## Development
129
+
130
+ ```bash
131
+ git clone https://github.com/getbrevo/brevo-cli.git
132
+ cd brevo-cli
133
+ yarn install
134
+ yarn build # compile TypeScript + copy templates to dist/
135
+ yarn link:dev # build and yarn link the binary for local testing
136
+ yarn dev # watch mode (rebuilds on save)
137
+ yarn test # run jest
138
+ yarn test:ci # jest --coverage
139
+ yarn lint # ESLint on src/
140
+ yarn format # prettier --write
141
+ yarn clean # remove dist/
142
+ ```
143
+
144
+ A husky pre-commit hook runs prettier and eslint on staged `.ts` files and then runs the full test suite.
145
+
146
+ ### Using the mock API
147
+
148
+ A Prism-based mock server (spec in `mock/openapi.yaml`) is bundled for offline development:
149
+
150
+ ```bash
151
+ yarn mock # starts on http://localhost:3112
152
+ BREVO_API_URL=http://localhost:3112 brevo app init
153
+ ```
154
+
155
+ ## Reporting issues
156
+
157
+ Found a bug or have a feature request? Please [open an issue](https://github.com/getbrevo/brevo-cli/issues/new/choose) on GitHub.
158
+
159
+ When filing a bug, please include:
160
+
161
+ - CLI version (`brevo --version`)
162
+ - Node.js version (`node --version`) and OS
163
+ - The command you ran and the full output (run with `--debug` for verbose logging if relevant)
164
+ - **Never paste real credentials** — redact API keys, client secrets, and access tokens before sharing
165
+
166
+ For security vulnerabilities, please **do not open a public issue**. Email the Brevo security team or use GitHub's [private vulnerability reporting](https://github.com/getbrevo/brevo-cli/security/advisories/new) instead.
167
+
168
+ ## Contributing
169
+
170
+ Contributions are welcome. See [`AGENTS.md`](./AGENTS.md) and [`CLAUDE.md`](./CLAUDE.md) for the project structure, conventions, and what to check before opening a PR.
171
+
172
+ ## Resources
173
+
174
+ - [Brevo Developers](https://developers.brevo.com)
175
+ - [Package on npm](https://www.npmjs.com/package/@getbrevo/cli)
176
+ - [Repository](https://github.com/getbrevo/brevo-cli)
177
+ - [Issue tracker](https://github.com/getbrevo/brevo-cli/issues)
178
+ - [Changelog](./CHANGELOG.md)
179
+
180
+ ## License
181
+
182
+ [MIT](./LICENSE)
@@ -0,0 +1,23 @@
1
+ type AuthFailureHandler = () => Promise<void>;
2
+ export interface ApiClientDeps {
3
+ baseUrl: string;
4
+ getAuthHeader: () => Record<string, string> | undefined;
5
+ }
6
+ export declare function parseRetryAfter(header: string | null): number;
7
+ export declare function sanitizeErrorMessage(msg: string): string;
8
+ export declare class ApiClient {
9
+ private deps;
10
+ private onAuthFailure?;
11
+ constructor(deps: ApiClientDeps);
12
+ setOnAuthFailure(handler: AuthFailureHandler): void;
13
+ get<T>(path: string): Promise<T>;
14
+ post<T>(path: string, body?: unknown): Promise<T>;
15
+ patch<T>(path: string, body?: unknown): Promise<T>;
16
+ put<T>(path: string, body?: unknown): Promise<T>;
17
+ delete<T>(path: string): Promise<T>;
18
+ getWithKey<T>(path: string, apiKey: string): Promise<T>;
19
+ getWithBearer<T>(path: string, accessToken: string, tokenType?: string): Promise<T>;
20
+ private request;
21
+ }
22
+ export {};
23
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"AAaA,KAAK,kBAAkB,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;AAE9C,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;CACzD;AAqBD,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAK7D;AAKD,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAcxD;AAoBD,qBAAa,SAAS;IAGR,OAAO,CAAC,IAAI;IAFxB,OAAO,CAAC,aAAa,CAAC,CAAqB;gBAEvB,IAAI,EAAE,aAAa;IAEvC,gBAAgB,CAAC,OAAO,EAAE,kBAAkB,GAAG,IAAI;IAInD,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;IAIhC,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IAIjD,KAAK,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IAIlD,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IAIhD,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;IAInC,UAAU,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;IASvD,aAAa,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,SAAW,GAAG,OAAO,CAAC,CAAC,CAAC;YASvE,OAAO;CA0FtB"}
@@ -0,0 +1,186 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ApiClient = void 0;
4
+ exports.parseRetryAfter = parseRetryAfter;
5
+ exports.sanitizeErrorMessage = sanitizeErrorMessage;
6
+ const errors_1 = require("../lib/errors");
7
+ const logger_1 = require("../lib/logger");
8
+ const en_1 = require("../lang/en");
9
+ const MAX_RETRIES = 3;
10
+ const DEFAULT_RETRY_AFTER_SECONDS = 5;
11
+ const MAX_RETRY_AFTER_SECONDS = 300;
12
+ const apiCodeMessages = {
13
+ APP_LIMIT_REACHED: en_1.messages.APP_CREATE_LIMIT_REACHED,
14
+ REGISTRY_ERROR: en_1.messages.ERR_REGISTRY,
15
+ };
16
+ function resolveErrorMessage(apiCode, fallback) {
17
+ if (apiCode && apiCode in apiCodeMessages) {
18
+ return apiCodeMessages[apiCode];
19
+ }
20
+ return fallback;
21
+ }
22
+ // Server-provided `retry-after` is untrusted input. Cap it to avoid indefinite
23
+ // waits from a malicious or misconfigured server, and reject NaN/negatives
24
+ // which would otherwise turn into a 0ms tight retry loop.
25
+ function parseRetryAfter(header) {
26
+ if (!header)
27
+ return DEFAULT_RETRY_AFTER_SECONDS;
28
+ const parsed = Number.parseInt(header, 10);
29
+ if (!Number.isFinite(parsed) || parsed <= 0)
30
+ return DEFAULT_RETRY_AFTER_SECONDS;
31
+ return Math.min(parsed, MAX_RETRY_AFTER_SECONDS);
32
+ }
33
+ // Sanitize untrusted server-provided error strings before they reach the terminal:
34
+ // strip ANSI escape sequences and non-printable control characters that could
35
+ // reposition the cursor, clear the screen, or inject fake prompts.
36
+ function sanitizeErrorMessage(msg) {
37
+ return (msg
38
+ // ANSI CSI/OSC escape sequences
39
+ // eslint-disable-next-line no-control-regex
40
+ .replace(/\x1B\[[0-?]*[ -/]*[@-~]/g, '')
41
+ // eslint-disable-next-line no-control-regex
42
+ .replace(/\x1B\][^\x07]*(?:\x07|\x1B\\)/g, '')
43
+ // C0 + DEL + C1 control chars, keeping \t \n \r.
44
+ // Includes 0x80–0x9F so 8-bit CSI (0x9B) / OSC (0x9D) introducers
45
+ // are stripped alongside their 7-bit ESC-prefixed equivalents.
46
+ // eslint-disable-next-line no-control-regex
47
+ .replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F]/g, ''));
48
+ }
49
+ function mapErrorCode(status, apiCode) {
50
+ if (apiCode === 'APP_LIMIT_REACHED')
51
+ return errors_1.ErrorCode.APP_LIMIT_REACHED;
52
+ if (apiCode === 'REGISTRY_ERROR')
53
+ return errors_1.ErrorCode.REGISTRY_ERROR;
54
+ switch (status) {
55
+ case 401:
56
+ return errors_1.ErrorCode.AUTH_INVALID;
57
+ case 403:
58
+ return errors_1.ErrorCode.ACCESS_DENIED;
59
+ case 404:
60
+ return errors_1.ErrorCode.APP_NOT_FOUND;
61
+ case 429:
62
+ return errors_1.ErrorCode.RATE_LIMITED;
63
+ default:
64
+ return undefined;
65
+ }
66
+ }
67
+ class ApiClient {
68
+ deps;
69
+ onAuthFailure;
70
+ constructor(deps) {
71
+ this.deps = deps;
72
+ }
73
+ setOnAuthFailure(handler) {
74
+ this.onAuthFailure = handler;
75
+ }
76
+ get(path) {
77
+ return this.request({ method: 'GET', path });
78
+ }
79
+ post(path, body) {
80
+ return this.request({ method: 'POST', path, body });
81
+ }
82
+ patch(path, body) {
83
+ return this.request({ method: 'PATCH', path, body });
84
+ }
85
+ put(path, body) {
86
+ return this.request({ method: 'PUT', path, body });
87
+ }
88
+ delete(path) {
89
+ return this.request({ method: 'DELETE', path });
90
+ }
91
+ getWithKey(path, apiKey) {
92
+ return this.request({
93
+ method: 'GET',
94
+ path,
95
+ skipAuth: true,
96
+ authHeader: { 'api-key': apiKey },
97
+ });
98
+ }
99
+ getWithBearer(path, accessToken, tokenType = 'Bearer') {
100
+ return this.request({
101
+ method: 'GET',
102
+ path,
103
+ skipAuth: true,
104
+ authHeader: { Authorization: `${tokenType} ${accessToken}` },
105
+ });
106
+ }
107
+ async request(opts, isRetry = false, retryCount = 0) {
108
+ const url = `${this.deps.baseUrl}${opts.path}`;
109
+ const explicitAuth = opts.authHeader;
110
+ const authHeader = explicitAuth ?? (opts.skipAuth ? undefined : this.deps.getAuthHeader());
111
+ const headers = {
112
+ 'Content-Type': 'application/json',
113
+ Accept: 'application/json',
114
+ ...opts.headers,
115
+ ...(authHeader ?? {}),
116
+ };
117
+ (0, logger_1.logHttp)(opts.method, opts.path);
118
+ let response;
119
+ try {
120
+ response = await fetch(url, {
121
+ method: opts.method,
122
+ headers,
123
+ body: opts.body ? JSON.stringify(opts.body) : undefined,
124
+ signal: AbortSignal.timeout(30_000),
125
+ });
126
+ }
127
+ catch (err) {
128
+ throw new errors_1.ApiError(en_1.messages.ERR_NETWORK, 0, errors_1.ErrorCode.NETWORK_ERROR);
129
+ }
130
+ (0, logger_1.logHttpResponse)(response.status, opts.path);
131
+ if (response.status === 401 && !isRetry && !opts.skipAuth) {
132
+ if (this.onAuthFailure) {
133
+ await this.onAuthFailure();
134
+ return this.request(opts, true, retryCount);
135
+ }
136
+ throw new errors_1.ApiError(en_1.messages.AUTH_EXPIRED, 401, errors_1.ErrorCode.AUTH_EXPIRED);
137
+ }
138
+ if (response.status === 429) {
139
+ if (retryCount >= MAX_RETRIES) {
140
+ throw new errors_1.ApiError('Rate limited — max retries exceeded.', 429, errors_1.ErrorCode.RATE_LIMITED);
141
+ }
142
+ const retryAfter = parseRetryAfter(response.headers.get('retry-after'));
143
+ process.stderr.write(` ${en_1.messages.ERR_RATE_LIMITED(retryAfter)}\n`);
144
+ await new Promise((r) => setTimeout(r, retryAfter * 1000));
145
+ return this.request(opts, isRetry, retryCount + 1);
146
+ }
147
+ if (response.status === 502 && retryCount < 1) {
148
+ await new Promise((r) => setTimeout(r, 2000));
149
+ return this.request(opts, isRetry, retryCount + 1);
150
+ }
151
+ const text = await response.text();
152
+ // Detect HTML bodies returned by auth gateways (Cloudflare Access, SSO proxies).
153
+ // Matches case-insensitively and runs regardless of response status, since
154
+ // gateways frequently return HTML on 401/403 as well as 2xx.
155
+ const looksLikeHtml = (s) => {
156
+ const lower = s.toLowerCase();
157
+ return lower.includes('<!doctype html') || lower.includes('<html');
158
+ };
159
+ let data;
160
+ try {
161
+ data = text ? JSON.parse(text) : {};
162
+ }
163
+ catch {
164
+ if (looksLikeHtml(text)) {
165
+ throw new errors_1.ApiError(en_1.messages.ERR_AUTH_GATEWAY, response.status, errors_1.ErrorCode.AUTH_GATEWAY);
166
+ }
167
+ data = { message: text };
168
+ }
169
+ if (typeof data?.message === 'string' && looksLikeHtml(data.message)) {
170
+ throw new errors_1.ApiError(en_1.messages.ERR_AUTH_GATEWAY, response.status, errors_1.ErrorCode.AUTH_GATEWAY);
171
+ }
172
+ (0, logger_1.logDebug)(`response ${opts.path}`, data);
173
+ if (!response.ok) {
174
+ const apiCode = data?.code;
175
+ const rawFallback = typeof data?.message === 'string' && data.message
176
+ ? data.message
177
+ : `Request failed with status ${response.status}`;
178
+ const fallback = sanitizeErrorMessage(rawFallback);
179
+ const message = resolveErrorMessage(apiCode, fallback);
180
+ throw new errors_1.ApiError(message, response.status, mapErrorCode(response.status, apiCode), apiCode);
181
+ }
182
+ return data;
183
+ }
184
+ }
185
+ exports.ApiClient = ApiClient;
186
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/bin/index.ts"],"names":[],"mappings":""}
@@ -0,0 +1,179 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
36
+ var __importDefault = (this && this.__importDefault) || function (mod) {
37
+ return (mod && mod.__esModule) ? mod : { "default": mod };
38
+ };
39
+ Object.defineProperty(exports, "__esModule", { value: true });
40
+ const fs = __importStar(require("fs"));
41
+ const path = __importStar(require("path"));
42
+ const commander_1 = require("commander");
43
+ const auth_guard_1 = require("../lib/auth-guard");
44
+ const logger_1 = require("../lib/logger");
45
+ const exit_codes_1 = require("../lib/exit-codes");
46
+ const errors_1 = require("../lib/errors");
47
+ const en_1 = require("../lang/en");
48
+ const hidden_input_1 = require("../lib/hidden-input");
49
+ const config_1 = require("../lib/config");
50
+ const constants_1 = require("../lib/constants");
51
+ const oauth_refresh_1 = require("../services/oauth-refresh");
52
+ const errors_2 = require("../lib/errors");
53
+ const ui_1 = require("../lib/ui");
54
+ const container_1 = require("../container");
55
+ const command_registry_1 = require("../lib/command-registry");
56
+ const definitions_1 = require("../commands/definitions");
57
+ const simple_update_notifier_1 = __importDefault(require("simple-update-notifier"));
58
+ const pkg = JSON.parse(fs.readFileSync(path.resolve(__dirname, '../../package.json'), 'utf-8'));
59
+ const version = pkg.version;
60
+ // Version update check — fire-and-forget, swallows network errors so the CLI
61
+ // never blocks or fails because the npm registry is unreachable.
62
+ void (0, simple_update_notifier_1.default)({ pkg: { name: pkg.name, version: pkg.version } }).catch(() => { });
63
+ if (process.env.NODE_TLS_REJECT_UNAUTHORIZED === '0') {
64
+ (0, logger_1.logWarn)(en_1.messages.TLS_VERIFICATION_DISABLED);
65
+ }
66
+ const program = new commander_1.Command();
67
+ program
68
+ .name('brevo')
69
+ .description('Brevo Developer CLI — create, manage, and test OAuth integrations')
70
+ .version(version)
71
+ .option('--debug', 'Enable debug logging')
72
+ .configureHelp({
73
+ formatHelp: (_cmd, helper) => {
74
+ const version = helper.commandDescription(_cmd);
75
+ return [
76
+ `Usage: brevo [options] [command]`,
77
+ ``,
78
+ version,
79
+ ``,
80
+ `Options:`,
81
+ ` -V, --version output the version number`,
82
+ ` -h, --help display help for command`,
83
+ ``,
84
+ `Commands:`,
85
+ ` login [--browser] [--json] Authenticate with your Brevo account`,
86
+ ` logout [--json] Clear stored credentials`,
87
+ ` whoami [--json] Show current authenticated user`,
88
+ ` app init Quick setup — login, create app, and scaffold`,
89
+ ` app create [--name] [--distribution private|public] [--json]`,
90
+ ` app list [--json]`,
91
+ ` app credentials [--app-id <id>] [--reveal-secret] [--json]`,
92
+ ` app update [--json]`,
93
+ ` app delete [--app-id <id>] [--force] [--json]`,
94
+ ` app scaffold [--app-id <id>] [--json]`,
95
+ ` app start [feature] [--port <port>]`,
96
+ ``,
97
+ `Run \`brevo <command> --help\` for details on a specific command.`,
98
+ ``,
99
+ `Examples:`,
100
+ ` $ brevo login # authenticate interactively`,
101
+ ` $ brevo app init # guided setup`,
102
+ ` $ brevo app create --name "My App" --json # create app, JSON output`,
103
+ ` $ brevo app list --json # list apps as JSON`,
104
+ ` $ brevo app scaffold --app-id APPID # generate starter code`,
105
+ ` $ brevo app start oauth --port 3000 # start OAuth test server`,
106
+ ``,
107
+ ].join('\n');
108
+ },
109
+ })
110
+ .action(() => program.help());
111
+ // Auth guard — blocks unauthenticated access (except login, logout, help)
112
+ (0, auth_guard_1.installAuthGuard)(program);
113
+ // ──────────────── Register all commands ────────────────
114
+ (0, command_registry_1.registerAll)(program, definitions_1.topLevelCommands, [definitions_1.appCommandGroup]);
115
+ // ──────────────── Re-auth handler ────────────────
116
+ container_1.client.setOnAuthFailure(async () => {
117
+ const auth = (0, config_1.getAuthCred)();
118
+ // OAuth: silently refresh the access token and let the original request retry.
119
+ // If the refresh itself returns 401, the refresh token is dead — clear creds
120
+ // and surface a friendly "please log in again" message instead of falling
121
+ // through to the api-key prompt.
122
+ if (auth?.kind === 'oauth') {
123
+ try {
124
+ const refreshed = await (0, oauth_refresh_1.refreshAccessToken)(auth.refreshToken, constants_1.OAUTH_PROXY_URL);
125
+ (0, config_1.updateOauthTokens)(refreshed);
126
+ return;
127
+ }
128
+ catch (err) {
129
+ if (err instanceof oauth_refresh_1.RefreshError && err.unauthorized) {
130
+ (0, config_1.clearCredentials)();
131
+ throw new errors_2.AuthExpiredError();
132
+ }
133
+ throw err;
134
+ }
135
+ }
136
+ // API-key: prompt for a new key and re-validate.
137
+ (0, ui_1.stopActiveSpinner)();
138
+ (0, config_1.clearCredentials)();
139
+ (0, logger_1.logWarn)(en_1.messages.AUTH_EXPIRED);
140
+ (0, logger_1.logInfo)(` ${en_1.messages.AUTH_GET_KEY_URL}\n`);
141
+ const newKey = await (0, hidden_input_1.readHiddenInput)(en_1.messages.AUTH_EXPIRED_PROMPT + ' ');
142
+ const account = await container_1.client.getWithKey(constants_1.ENDPOINTS.ACCOUNT, newKey);
143
+ (0, config_1.saveCredentials)(newKey, {
144
+ email: account.email,
145
+ organizationId: account.organization_id,
146
+ userId: account.user_id,
147
+ });
148
+ (0, logger_1.logSuccess)(en_1.messages.AUTH_SUCCESS(account.email));
149
+ });
150
+ // ──────────────── Signal handling ────────────────
151
+ for (const signal of ['SIGINT', 'SIGTERM']) {
152
+ process.on(signal, () => {
153
+ (0, logger_1.logInfo)(`\n Received ${signal}, shutting down.\n`);
154
+ process.exit(exit_codes_1.EXIT_CODES.ABORTED);
155
+ });
156
+ }
157
+ // ──────────────── Error handling ────────────────
158
+ // Emit deferred warning if BREVO_API_URL had a path stripped
159
+ (0, constants_1.warnIfPathStripped)();
160
+ program
161
+ .parseAsync(process.argv)
162
+ .then(() => {
163
+ // Force exit — Node's native fetch keeps TCP connections alive which can
164
+ // prevent the process from exiting when running against local servers.
165
+ process.exit(0);
166
+ })
167
+ .catch((err) => {
168
+ if (err instanceof errors_1.AbortError) {
169
+ (0, logger_1.logInfo)(`\n ${en_1.messages.ABORTED}`);
170
+ process.exit(exit_codes_1.EXIT_CODES.ABORTED);
171
+ }
172
+ if (err instanceof errors_1.CliError) {
173
+ (0, logger_1.logError)(err.message);
174
+ process.exit(err.exitCode);
175
+ }
176
+ (0, logger_1.logError)(err.message, err);
177
+ process.exit(exit_codes_1.EXIT_CODES.ERROR);
178
+ });
179
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/bin/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEA,uCAAyB;AACzB,2CAA6B;AAC7B,yCAAoC;AACpC,kDAAqD;AACrD,0CAAuE;AACvE,kDAA+C;AAC/C,0CAAqD;AACrD,mCAAsC;AACtC,sDAAsD;AACtD,0CAAkG;AAClG,gDAAkF;AAClF,6DAA6E;AAC7E,0CAAiD;AACjD,kCAA8C;AAE9C,4CAAsC;AACtC,8DAAsD;AACtD,yDAA4E;AAC5E,oFAA0D;AAE1D,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,oBAAoB,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;AAChG,MAAM,OAAO,GAAW,GAAG,CAAC,OAAO,CAAC;AAEpC,6EAA6E;AAC7E,iEAAiE;AACjE,KAAK,IAAA,gCAAoB,EAAC,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;AAE7F,IAAI,OAAO,CAAC,GAAG,CAAC,4BAA4B,KAAK,GAAG,EAAE,CAAC;IACrD,IAAA,gBAAO,EAAC,aAAQ,CAAC,yBAAyB,CAAC,CAAC;AAC9C,CAAC;AAED,MAAM,OAAO,GAAG,IAAI,mBAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,OAAO,CAAC;KACb,WAAW,CAAC,mEAAmE,CAAC;KAChF,OAAO,CAAC,OAAO,CAAC;KAChB,MAAM,CAAC,SAAS,EAAE,sBAAsB,CAAC;KACzC,aAAa,CAAC;IACb,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE;QAC3B,MAAM,OAAO,GAAG,MAAM,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC;QAChD,OAAO;YACL,kCAAkC;YAClC,EAAE;YACF,OAAO;YACP,EAAE;YACF,UAAU;YACV,8CAA8C;YAC9C,6CAA6C;YAC7C,EAAE;YACF,WAAW;YACX,iFAAiF;YACjF,oEAAoE;YACpE,2EAA2E;YAC3E,yFAAyF;YACzF,qEAAqE;YACrE,4BAA4B;YAC5B,8DAA8D;YAC9D,4BAA4B;YAC5B,sDAAsD;YACtD,4CAA4C;YAC5C,6CAA6C;YAC7C,EAAE;YACF,mEAAmE;YACnE,EAAE;YACF,WAAW;YACX,gFAAgF;YAChF,kEAAkE;YAClE,6EAA6E;YAC7E,uEAAuE;YACvE,2EAA2E;YAC3E,6EAA6E;YAC7E,EAAE;SACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,CAAC;CACF,CAAC;KACD,MAAM,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;AAEhC,0EAA0E;AAC1E,IAAA,6BAAgB,EAAC,OAAO,CAAC,CAAC;AAE1B,0DAA0D;AAE1D,IAAA,8BAAW,EAAC,OAAO,EAAE,8BAAgB,EAAE,CAAC,6BAAe,CAAC,CAAC,CAAC;AAE1D,oDAAoD;AAEpD,kBAAM,CAAC,gBAAgB,CAAC,KAAK,IAAI,EAAE;IACjC,MAAM,IAAI,GAAG,IAAA,oBAAW,GAAE,CAAC;IAE3B,+EAA+E;IAC/E,6EAA6E;IAC7E,0EAA0E;IAC1E,iCAAiC;IACjC,IAAI,IAAI,EAAE,IAAI,KAAK,OAAO,EAAE,CAAC;QAC3B,IAAI,CAAC;YACH,MAAM,SAAS,GAAG,MAAM,IAAA,kCAAkB,EAAC,IAAI,CAAC,YAAY,EAAE,2BAAe,CAAC,CAAC;YAC/E,IAAA,0BAAiB,EAAC,SAAS,CAAC,CAAC;YAC7B,OAAO;QACT,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,GAAG,YAAY,4BAAY,IAAI,GAAG,CAAC,YAAY,EAAE,CAAC;gBACpD,IAAA,yBAAgB,GAAE,CAAC;gBACnB,MAAM,IAAI,yBAAgB,EAAE,CAAC;YAC/B,CAAC;YACD,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC;IAED,iDAAiD;IACjD,IAAA,sBAAiB,GAAE,CAAC;IACpB,IAAA,yBAAgB,GAAE,CAAC;IACnB,IAAA,gBAAO,EAAC,aAAQ,CAAC,YAAY,CAAC,CAAC;IAC/B,IAAA,gBAAO,EAAC,KAAK,aAAQ,CAAC,gBAAgB,IAAI,CAAC,CAAC;IAC5C,MAAM,MAAM,GAAG,MAAM,IAAA,8BAAe,EAAC,aAAQ,CAAC,mBAAmB,GAAG,GAAG,CAAC,CAAC;IACzE,MAAM,OAAO,GAAG,MAAM,kBAAM,CAAC,UAAU,CAAkB,qBAAS,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IACpF,IAAA,wBAAe,EAAC,MAAM,EAAE;QACtB,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,cAAc,EAAE,OAAO,CAAC,eAAe;QACvC,MAAM,EAAE,OAAO,CAAC,OAAO;KACxB,CAAC,CAAC;IACH,IAAA,mBAAU,EAAC,aAAQ,CAAC,YAAY,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;AACnD,CAAC,CAAC,CAAC;AAEH,oDAAoD;AAEpD,KAAK,MAAM,MAAM,IAAI,CAAC,QAAQ,EAAE,SAAS,CAAU,EAAE,CAAC;IACpD,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE;QACtB,IAAA,gBAAO,EAAC,gBAAgB,MAAM,oBAAoB,CAAC,CAAC;QACpD,OAAO,CAAC,IAAI,CAAC,uBAAU,CAAC,OAAO,CAAC,CAAC;IACnC,CAAC,CAAC,CAAC;AACL,CAAC;AAED,mDAAmD;AAEnD,6DAA6D;AAC7D,IAAA,8BAAkB,GAAE,CAAC;AAErB,OAAO;KACJ,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC;KACxB,IAAI,CAAC,GAAG,EAAE;IACT,yEAAyE;IACzE,uEAAuE;IACvE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC;KACD,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACb,IAAI,GAAG,YAAY,mBAAU,EAAE,CAAC;QAC9B,IAAA,gBAAO,EAAC,OAAO,aAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;QACnC,OAAO,CAAC,IAAI,CAAC,uBAAU,CAAC,OAAO,CAAC,CAAC;IACnC,CAAC;IACD,IAAI,GAAG,YAAY,iBAAQ,EAAE,CAAC;QAC5B,IAAA,iBAAQ,EAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACtB,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC7B,CAAC;IACD,IAAA,iBAAQ,EAAC,GAAG,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IAC3B,OAAO,CAAC,IAAI,CAAC,uBAAU,CAAC,KAAK,CAAC,CAAC;AACjC,CAAC,CAAC,CAAC"}
@@ -0,0 +1,7 @@
1
+ export declare const createCommand: (opts: {
2
+ name?: string;
3
+ distribution?: string;
4
+ redirectUri?: string[];
5
+ json?: boolean;
6
+ }) => Promise<void>;
7
+ //# sourceMappingURL=create.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../../src/commands/app/create.ts"],"names":[],"mappings":"AAeA,eAAO,MAAM,aAAa;WAEf,MAAM;mBACE,MAAM;kBACP,MAAM,EAAE;WACf,OAAO;mBA0PjB,CAAC"}