@vertaaux/cli 0.2.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 (198) hide show
  1. package/README.md +345 -0
  2. package/dist/auth/ci-token.d.ts +49 -0
  3. package/dist/auth/ci-token.d.ts.map +1 -0
  4. package/dist/auth/ci-token.js +83 -0
  5. package/dist/auth/device-flow.d.ts +66 -0
  6. package/dist/auth/device-flow.d.ts.map +1 -0
  7. package/dist/auth/device-flow.js +156 -0
  8. package/dist/auth/token-store.d.ts +53 -0
  9. package/dist/auth/token-store.d.ts.map +1 -0
  10. package/dist/auth/token-store.js +78 -0
  11. package/dist/baseline/diff.d.ts +57 -0
  12. package/dist/baseline/diff.d.ts.map +1 -0
  13. package/dist/baseline/diff.js +152 -0
  14. package/dist/baseline/hash.d.ts +54 -0
  15. package/dist/baseline/hash.d.ts.map +1 -0
  16. package/dist/baseline/hash.js +66 -0
  17. package/dist/baseline/manager.d.ts +89 -0
  18. package/dist/baseline/manager.d.ts.map +1 -0
  19. package/dist/baseline/manager.js +157 -0
  20. package/dist/cache/index.d.ts +8 -0
  21. package/dist/cache/index.d.ts.map +1 -0
  22. package/dist/cache/index.js +7 -0
  23. package/dist/cache/route-cache.d.ts +119 -0
  24. package/dist/cache/route-cache.d.ts.map +1 -0
  25. package/dist/cache/route-cache.js +213 -0
  26. package/dist/ci/changed-routes.d.ts +95 -0
  27. package/dist/ci/changed-routes.d.ts.map +1 -0
  28. package/dist/ci/changed-routes.js +304 -0
  29. package/dist/ci/github-api.d.ts +68 -0
  30. package/dist/ci/github-api.d.ts.map +1 -0
  31. package/dist/ci/github-api.js +138 -0
  32. package/dist/ci/gitlab-api.d.ts +75 -0
  33. package/dist/ci/gitlab-api.d.ts.map +1 -0
  34. package/dist/ci/gitlab-api.js +180 -0
  35. package/dist/ci/index.d.ts +6 -0
  36. package/dist/ci/index.d.ts.map +1 -0
  37. package/dist/ci/index.js +4 -0
  38. package/dist/commands/audit.d.ts +58 -0
  39. package/dist/commands/audit.d.ts.map +1 -0
  40. package/dist/commands/audit.js +862 -0
  41. package/dist/commands/baseline.d.ts +22 -0
  42. package/dist/commands/baseline.d.ts.map +1 -0
  43. package/dist/commands/baseline.js +210 -0
  44. package/dist/commands/comment.d.ts +14 -0
  45. package/dist/commands/comment.d.ts.map +1 -0
  46. package/dist/commands/comment.js +363 -0
  47. package/dist/commands/diff.d.ts +24 -0
  48. package/dist/commands/diff.d.ts.map +1 -0
  49. package/dist/commands/diff.js +196 -0
  50. package/dist/commands/doctor.d.ts +58 -0
  51. package/dist/commands/doctor.d.ts.map +1 -0
  52. package/dist/commands/doctor.js +338 -0
  53. package/dist/commands/download.d.ts +12 -0
  54. package/dist/commands/download.d.ts.map +1 -0
  55. package/dist/commands/download.js +183 -0
  56. package/dist/commands/explain.d.ts +62 -0
  57. package/dist/commands/explain.d.ts.map +1 -0
  58. package/dist/commands/explain.js +302 -0
  59. package/dist/commands/init.d.ts +12 -0
  60. package/dist/commands/init.d.ts.map +1 -0
  61. package/dist/commands/init.js +212 -0
  62. package/dist/commands/login.d.ts +14 -0
  63. package/dist/commands/login.d.ts.map +1 -0
  64. package/dist/commands/login.js +222 -0
  65. package/dist/commands/policy.d.ts +13 -0
  66. package/dist/commands/policy.d.ts.map +1 -0
  67. package/dist/commands/policy.js +347 -0
  68. package/dist/commands/upload.d.ts +12 -0
  69. package/dist/commands/upload.d.ts.map +1 -0
  70. package/dist/commands/upload.js +158 -0
  71. package/dist/config/defaults.d.ts +21 -0
  72. package/dist/config/defaults.d.ts.map +1 -0
  73. package/dist/config/defaults.js +49 -0
  74. package/dist/config/loader.d.ts +66 -0
  75. package/dist/config/loader.d.ts.map +1 -0
  76. package/dist/config/loader.js +167 -0
  77. package/dist/config/schema.d.ts +55 -0
  78. package/dist/config/schema.d.ts.map +1 -0
  79. package/dist/config/schema.js +6 -0
  80. package/dist/index.d.ts +9 -0
  81. package/dist/index.d.ts.map +1 -0
  82. package/dist/index.js +1090 -0
  83. package/dist/interactive/fix-wizard.d.ts +44 -0
  84. package/dist/interactive/fix-wizard.d.ts.map +1 -0
  85. package/dist/interactive/fix-wizard.js +286 -0
  86. package/dist/interactive/init-wizard.d.ts +32 -0
  87. package/dist/interactive/init-wizard.d.ts.map +1 -0
  88. package/dist/interactive/init-wizard.js +193 -0
  89. package/dist/interactive/prompts.d.ts +62 -0
  90. package/dist/interactive/prompts.d.ts.map +1 -0
  91. package/dist/interactive/prompts.js +78 -0
  92. package/dist/monorepo/detector.d.ts +70 -0
  93. package/dist/monorepo/detector.d.ts.map +1 -0
  94. package/dist/monorepo/detector.js +278 -0
  95. package/dist/monorepo/index.d.ts +9 -0
  96. package/dist/monorepo/index.d.ts.map +1 -0
  97. package/dist/monorepo/index.js +8 -0
  98. package/dist/monorepo/workspace.d.ts +142 -0
  99. package/dist/monorepo/workspace.d.ts.map +1 -0
  100. package/dist/monorepo/workspace.js +171 -0
  101. package/dist/output/envelope.d.ts +21 -0
  102. package/dist/output/envelope.d.ts.map +1 -0
  103. package/dist/output/envelope.js +27 -0
  104. package/dist/output/factory.d.ts +73 -0
  105. package/dist/output/factory.d.ts.map +1 -0
  106. package/dist/output/factory.js +60 -0
  107. package/dist/output/formats.d.ts +11 -0
  108. package/dist/output/formats.d.ts.map +1 -0
  109. package/dist/output/formats.js +41 -0
  110. package/dist/output/html.d.ts +45 -0
  111. package/dist/output/html.d.ts.map +1 -0
  112. package/dist/output/html.js +607 -0
  113. package/dist/output/human.d.ts +41 -0
  114. package/dist/output/human.d.ts.map +1 -0
  115. package/dist/output/human.js +274 -0
  116. package/dist/output/json.d.ts +42 -0
  117. package/dist/output/json.d.ts.map +1 -0
  118. package/dist/output/json.js +37 -0
  119. package/dist/output/junit.d.ts +56 -0
  120. package/dist/output/junit.d.ts.map +1 -0
  121. package/dist/output/junit.js +135 -0
  122. package/dist/output/markdown.d.ts +77 -0
  123. package/dist/output/markdown.d.ts.map +1 -0
  124. package/dist/output/markdown.js +411 -0
  125. package/dist/output/sarif.d.ts +160 -0
  126. package/dist/output/sarif.d.ts.map +1 -0
  127. package/dist/output/sarif.js +207 -0
  128. package/dist/policy/evaluator.d.ts +111 -0
  129. package/dist/policy/evaluator.d.ts.map +1 -0
  130. package/dist/policy/evaluator.js +362 -0
  131. package/dist/policy/index.d.ts +15 -0
  132. package/dist/policy/index.d.ts.map +1 -0
  133. package/dist/policy/index.js +11 -0
  134. package/dist/policy/loader.d.ts +97 -0
  135. package/dist/policy/loader.d.ts.map +1 -0
  136. package/dist/policy/loader.js +281 -0
  137. package/dist/policy/schema.d.ts +297 -0
  138. package/dist/policy/schema.d.ts.map +1 -0
  139. package/dist/policy/schema.js +230 -0
  140. package/dist/quality-gate/evaluator.d.ts +58 -0
  141. package/dist/quality-gate/evaluator.d.ts.map +1 -0
  142. package/dist/quality-gate/evaluator.js +274 -0
  143. package/dist/quality-gate/index.d.ts +10 -0
  144. package/dist/quality-gate/index.d.ts.map +1 -0
  145. package/dist/quality-gate/index.js +7 -0
  146. package/dist/quality-gate/types.d.ts +103 -0
  147. package/dist/quality-gate/types.d.ts.map +1 -0
  148. package/dist/quality-gate/types.js +23 -0
  149. package/dist/templates/azure-devops.d.ts +25 -0
  150. package/dist/templates/azure-devops.d.ts.map +1 -0
  151. package/dist/templates/azure-devops.js +109 -0
  152. package/dist/templates/circleci.d.ts +28 -0
  153. package/dist/templates/circleci.d.ts.map +1 -0
  154. package/dist/templates/circleci.js +86 -0
  155. package/dist/templates/github-actions.d.ts +81 -0
  156. package/dist/templates/github-actions.d.ts.map +1 -0
  157. package/dist/templates/github-actions.js +393 -0
  158. package/dist/templates/gitlab-ci.d.ts +26 -0
  159. package/dist/templates/gitlab-ci.d.ts.map +1 -0
  160. package/dist/templates/gitlab-ci.js +70 -0
  161. package/dist/templates/index.d.ts +72 -0
  162. package/dist/templates/index.d.ts.map +1 -0
  163. package/dist/templates/index.js +112 -0
  164. package/dist/templates/jenkins.d.ts +26 -0
  165. package/dist/templates/jenkins.d.ts.map +1 -0
  166. package/dist/templates/jenkins.js +110 -0
  167. package/dist/ui/banner.d.ts +31 -0
  168. package/dist/ui/banner.d.ts.map +1 -0
  169. package/dist/ui/banner.js +84 -0
  170. package/dist/ui/diagnostics.d.ts +39 -0
  171. package/dist/ui/diagnostics.d.ts.map +1 -0
  172. package/dist/ui/diagnostics.js +153 -0
  173. package/dist/ui/spinner.d.ts +61 -0
  174. package/dist/ui/spinner.d.ts.map +1 -0
  175. package/dist/ui/spinner.js +101 -0
  176. package/dist/ui/table.d.ts +63 -0
  177. package/dist/ui/table.d.ts.map +1 -0
  178. package/dist/ui/table.js +236 -0
  179. package/dist/utils/client.d.ts +82 -0
  180. package/dist/utils/client.d.ts.map +1 -0
  181. package/dist/utils/client.js +128 -0
  182. package/dist/utils/detect-env.d.ts +59 -0
  183. package/dist/utils/detect-env.d.ts.map +1 -0
  184. package/dist/utils/detect-env.js +115 -0
  185. package/dist/utils/exit-codes.d.ts +47 -0
  186. package/dist/utils/exit-codes.d.ts.map +1 -0
  187. package/dist/utils/exit-codes.js +61 -0
  188. package/dist/utils/logger.d.ts +87 -0
  189. package/dist/utils/logger.d.ts.map +1 -0
  190. package/dist/utils/logger.js +185 -0
  191. package/dist/utils/sanitize.d.ts +36 -0
  192. package/dist/utils/sanitize.d.ts.map +1 -0
  193. package/dist/utils/sanitize.js +64 -0
  194. package/dist/utils/validators.d.ts +41 -0
  195. package/dist/utils/validators.d.ts.map +1 -0
  196. package/dist/utils/validators.js +123 -0
  197. package/package.json +63 -0
  198. package/schemas/vertaaux.config.schema.json +103 -0
package/README.md ADDED
@@ -0,0 +1,345 @@
1
+ # VertaaUX CLI
2
+
3
+ Run UX and accessibility audits from the terminal or CI pipelines.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install -g @vertaaux/cli
9
+ ```
10
+
11
+ Or run with npx:
12
+
13
+ ```bash
14
+ npx @vertaaux/cli --help
15
+ ```
16
+
17
+ ## Quick Start
18
+
19
+ ```bash
20
+ # 1. Authenticate
21
+ vertaa login
22
+
23
+ # 2. Run an audit
24
+ vertaa audit https://example.com --wait
25
+
26
+ # 3. Check CLI health
27
+ vertaa doctor
28
+ ```
29
+
30
+ ## Authentication
31
+
32
+ The CLI checks for credentials in this order:
33
+
34
+ 1. **Environment variables** (checked in order):
35
+ - `VERTAAUX_TOKEN`
36
+ - `VERTAAUX_API_KEY`
37
+
38
+ 2. **Stored credentials** from interactive login:
39
+ ```bash
40
+ vertaa login
41
+ ```
42
+ Credentials are stored in `~/.vertaaux/credentials.json`.
43
+
44
+ 3. **Direct token** for CI/non-interactive use:
45
+ ```bash
46
+ vertaa login --token <api-key>
47
+ ```
48
+
49
+ Verify authentication:
50
+
51
+ ```bash
52
+ vertaa whoami
53
+ ```
54
+
55
+ ## Commands
56
+
57
+ ### Core Commands
58
+
59
+ | Command | Description |
60
+ |---------|-------------|
61
+ | `audit <url>` | Run UX and accessibility audit |
62
+ | `baseline [job-id]` | Create or update audit baseline |
63
+ | `diff` | Compare current audit against baseline |
64
+ | `policy init\|validate\|show\|schema` | Manage policy-as-code |
65
+
66
+ ### Analysis and Remediation
67
+
68
+ | Command | Description |
69
+ |---------|-------------|
70
+ | `explain <finding-id>` | Show evidence bundle for a finding |
71
+ | `comment` | Generate PR comment from audit results |
72
+ | `fix <job-id>` | Generate a fix patch for an issue |
73
+ | `fix-all <job-id>` | Generate fix patches for all issues |
74
+ | `verify` | Verify that a patch fixes an issue |
75
+
76
+ ### Utility
77
+
78
+ | Command | Description |
79
+ |---------|-------------|
80
+ | `doctor` | Diagnose CLI health (config, auth, network) |
81
+ | `login` | Authenticate with VertaaUX |
82
+ | `logout` | Clear stored credentials |
83
+ | `whoami` | Show current authentication status |
84
+ | `init` | Create `.vertaaux.yml` configuration |
85
+ | `status <job-id>` | Check audit job status |
86
+ | `upload <file>` | Upload audit results to cloud storage |
87
+ | `download <id>` | Download audit results from cloud storage |
88
+
89
+ ### Aliases
90
+
91
+ | Command | Alias For |
92
+ |---------|-----------|
93
+ | `a11y <url>` | Accessibility-focused audit (filters for a11y issues) |
94
+ | `scan <url>` | UX scan (alias for audit) |
95
+ | `compare <urlA> <urlB>` | Compare audits of two URLs |
96
+
97
+ ## Output Formats
98
+
99
+ Formats are **per-command**, not global. Each command supports a different set of formats:
100
+
101
+ | Command | Formats | Default |
102
+ |---------|---------|---------|
103
+ | `audit` | `human`, `json`, `sarif`, `junit`, `html` | `human` |
104
+ | `comment` | `json`, `markdown` | `markdown` |
105
+ | `explain` | `human`, `json` | `human` |
106
+ | `policy show` | `json`, `yaml` | `yaml` |
107
+ | `diff` | `human`, `json` | `human` |
108
+
109
+ Usage:
110
+
111
+ ```bash
112
+ vertaa audit https://example.com --format json
113
+ vertaa audit https://example.com --format sarif > results.sarif
114
+ vertaa comment --input results.json --format markdown
115
+ ```
116
+
117
+ ### Machine-Readable Output
118
+
119
+ The `--machine` global flag enables strict machine-readable mode:
120
+
121
+ - **stdout**: JSON data only (no banners, no diagnostics)
122
+ - **stderr**: All diagnostic and progress output
123
+ - JSON output is wrapped in an envelope:
124
+
125
+ ```json
126
+ {
127
+ "meta": {
128
+ "version": "0.1.0",
129
+ "timestamp": "2026-02-08T12:00:00.000Z",
130
+ "command": "audit",
131
+ "args": ["https://example.com", "--format", "json"]
132
+ },
133
+ "data": {
134
+ "scores": { "overall": 85 },
135
+ "issues": []
136
+ }
137
+ }
138
+ ```
139
+
140
+ ### Piping
141
+
142
+ All diagnostic output goes to stderr, keeping stdout clean for piping:
143
+
144
+ ```bash
145
+ vertaa audit https://example.com --format json | jq '.data.scores'
146
+ vertaa audit https://example.com --format json > results.json
147
+ ```
148
+
149
+ ## Global Options
150
+
151
+ These options work with any command:
152
+
153
+ | Option | Description |
154
+ |--------|-------------|
155
+ | `-b, --base <url>` | API base URL override |
156
+ | `-c, --config <path>` | Explicit config file path |
157
+ | `-q, --quiet` | Suppress banner and non-essential output |
158
+ | `--no-banner` | Hide the V-mark banner |
159
+ | `--machine` | Strict machine-readable output mode |
160
+ | `-v, --version` | Show version number |
161
+ | `-h, --help` | Show help for command |
162
+
163
+ ## Exit Codes
164
+
165
+ | Code | Meaning | When |
166
+ |------|---------|------|
167
+ | `0` | Success | Audit passed, no issues above threshold |
168
+ | `1` | Issues found | Issues at or above `--fail-on` severity |
169
+ | `2` | Error | Invalid input, validation errors, network failures |
170
+ | `3` | Threshold breach | Score below `--threshold` value |
171
+
172
+ Exit code `2` is used for all validation errors, including:
173
+ - Invalid flag values (`--timeout abc`)
174
+ - Unknown enum values (`--mode bogus`)
175
+ - Missing required arguments
176
+
177
+ ## Configuration
178
+
179
+ The CLI uses [cosmiconfig](https://github.com/cosmiconfig/cosmiconfig) for configuration file auto-detection.
180
+
181
+ ### Config File Search Order
182
+
183
+ 1. `.vertaaux.yml`
184
+ 2. `.vertaaux.yaml`
185
+ 3. `.vertaaux.json`
186
+ 4. `vertaaux.config.js`
187
+ 5. `vertaaux.config.mjs`
188
+ 6. `vertaaux.config.cjs`
189
+ 7. `package.json` (`vertaaux` key)
190
+
191
+ Or specify explicitly:
192
+
193
+ ```bash
194
+ vertaa audit https://example.com --config path/to/.vertaaux.yml
195
+ ```
196
+
197
+ ### Configuration Precedence
198
+
199
+ ```
200
+ flag > env var > config file > default
201
+ ```
202
+
203
+ ### Key Config Fields
204
+
205
+ From `VertaauxConfig` interface in `src/config/schema.ts`:
206
+
207
+ | Field | Type | Default | Description |
208
+ |-------|------|---------|-------------|
209
+ | `mode` | `basic\|standard\|deep` | `basic` | Audit depth |
210
+ | `threshold` | `number` | `0` | Minimum passing score (0-100) |
211
+ | `failOn` | `error\|warning\|info` | - | Fail on severity |
212
+ | `output.format` | `auto\|json\|sarif\|...` | `auto` | Output format |
213
+ | `output.groupBy` | `severity\|category\|route` | `severity` | Issue grouping |
214
+ | `baseline.path` | `string` | `.vertaaux/baseline.json` | Baseline file path |
215
+ | `baseline.autoUpdate` | `boolean` | `false` | Auto-update baseline |
216
+ | `ci.template` | `github\|gitlab\|...` | `none` | CI template |
217
+ | `timeout` | `number` | `60000` | Audit timeout (ms) |
218
+ | `interval` | `number` | `5000` | Poll interval (ms) |
219
+
220
+ ### Example Configuration
221
+
222
+ ```yaml
223
+ # .vertaaux.yml
224
+ $schema: https://vertaaux.ai/schemas/config.json
225
+
226
+ mode: standard
227
+ threshold: 80
228
+ failOn: error
229
+
230
+ output:
231
+ format: auto
232
+ groupBy: severity
233
+
234
+ baseline:
235
+ path: .vertaaux/baseline.json
236
+ autoUpdate: false
237
+
238
+ ci:
239
+ template: github
240
+
241
+ timeout: 60000
242
+ interval: 5000
243
+ ```
244
+
245
+ Create a starter configuration:
246
+
247
+ ```bash
248
+ vertaa init
249
+ vertaa init --ci github --yes
250
+ ```
251
+
252
+ ## Security
253
+
254
+ ### Branch Name Validation
255
+
256
+ Branch names passed via `--base-branch` and `--branch` flags are validated against an allowlist regex:
257
+
258
+ ```
259
+ /^[a-zA-Z0-9._\/-]+$/
260
+ ```
261
+
262
+ - Maximum length: 255 characters
263
+ - Shell metacharacters (`;`, `|`, `$`, `` ` ``, etc.) are rejected
264
+ - Standard git branch names work as expected: `main`, `feature/login`, `release/v1.2.3`
265
+
266
+ ### Artifact Path Protection
267
+
268
+ Downloaded artifact filenames are validated to stay within the output directory:
269
+
270
+ - Path traversal attempts (`../`) are rejected with an error
271
+ - All artifact paths are resolved and checked against the target directory boundary
272
+ - This prevents writing to arbitrary filesystem locations
273
+
274
+ ### Credential Filtering
275
+
276
+ JSON envelope output automatically filters CLI arguments containing API keys or Bearer tokens from the `args` metadata field.
277
+
278
+ ## Environment Variables
279
+
280
+ | Variable | Purpose |
281
+ |----------|---------|
282
+ | `VERTAAUX_API_KEY` | API authentication key |
283
+ | `VERTAAUX_TOKEN` | Alternative auth token (checked first) |
284
+ | `VERTAAUX_API_BASE` | API base URL override |
285
+ | `NO_COLOR` | Disable colored output |
286
+ | `FORCE_COLOR` | Force colored output |
287
+
288
+ ## CI/CD Integration
289
+
290
+ ### GitHub Actions
291
+
292
+ ```yaml
293
+ - name: Run audit
294
+ env:
295
+ VERTAAUX_API_KEY: ${{ secrets.VERTAAUX_API_KEY }}
296
+ run: |
297
+ npx @vertaaux/cli audit https://example.com \
298
+ --format sarif \
299
+ --fail-on error \
300
+ --threshold 80
301
+ ```
302
+
303
+ ### Exit Code Gating
304
+
305
+ ```bash
306
+ # Fail CI if score below 80
307
+ vertaa audit https://example.com --threshold 80
308
+
309
+ # Fail CI if any error-severity issues found
310
+ vertaa audit https://example.com --fail-on error
311
+
312
+ # Both
313
+ vertaa audit https://example.com --threshold 80 --fail-on error
314
+ ```
315
+
316
+ ## Error Messages
317
+
318
+ The CLI provides branded error messages with contextual help:
319
+
320
+ ```
321
+ vertaa error: expected a number, got "abc"
322
+ ──────────────────────────────────
323
+ │ flag: --timeout
324
+ │ value: abc
325
+
326
+ │ hint: Run vertaa <command> --help for all options
327
+ ──────────────────────────────────
328
+ ```
329
+
330
+ For enum values, typo suggestions are provided:
331
+
332
+ ```
333
+ vertaa error: invalid value for --mode
334
+ │ flag: --mode
335
+ │ value: depp
336
+
337
+ │ hint: Did you mean "deep"?
338
+ │ valid: basic, standard, deep
339
+ ```
340
+
341
+ ## Related
342
+
343
+ - [Migration Guide](./MIGRATION.md) -- Breaking changes in this release
344
+ - [Configuration Docs](https://vertaaux.ai/docs/cli/configuration) -- Full config reference
345
+ - [CI/CD Integration](https://vertaaux.ai/docs/cli/cicd) -- Pipeline setup guides
@@ -0,0 +1,49 @@
1
+ /**
2
+ * CI token handling for VertaaUX CLI.
3
+ *
4
+ * CI tokens are long-lived API keys used in CI/CD pipelines.
5
+ * They can be passed via environment variables or --token flag.
6
+ */
7
+ /**
8
+ * Get CI token from environment variables.
9
+ *
10
+ * Checks VERTAAUX_TOKEN first, then VERTAAUX_API_KEY.
11
+ *
12
+ * @returns Token string or null if not found
13
+ */
14
+ export declare function getCIToken(): string | null;
15
+ /**
16
+ * API response for token validation.
17
+ */
18
+ interface TokenValidationResponse {
19
+ /** Whether token is valid */
20
+ valid: boolean;
21
+ /** User or organization ID */
22
+ user_id?: string;
23
+ /** Organization name */
24
+ organization?: string;
25
+ /** Scopes granted to token */
26
+ scopes?: string[];
27
+ /** Token expiration (if any) */
28
+ expires_at?: string;
29
+ /** Error message if invalid */
30
+ error?: string;
31
+ }
32
+ /**
33
+ * Validate a CI token against the API.
34
+ *
35
+ * @param token - Token to validate
36
+ * @param apiBase - API base URL (optional)
37
+ * @returns true if token is valid
38
+ */
39
+ export declare function validateCIToken(token: string, apiBase?: string): Promise<boolean>;
40
+ /**
41
+ * Get token info from the API.
42
+ *
43
+ * @param token - Token to check
44
+ * @param apiBase - API base URL (optional)
45
+ * @returns Token info or null if invalid/error
46
+ */
47
+ export declare function getTokenInfo(token: string, apiBase?: string): Promise<TokenValidationResponse | null>;
48
+ export {};
49
+ //# sourceMappingURL=ci-token.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ci-token.d.ts","sourceRoot":"","sources":["../../src/auth/ci-token.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAQH;;;;;;GAMG;AACH,wBAAgB,UAAU,IAAI,MAAM,GAAG,IAAI,CAQ1C;AAED;;GAEG;AACH,UAAU,uBAAuB;IAC/B,6BAA6B;IAC7B,KAAK,EAAE,OAAO,CAAC;IACf,8BAA8B;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,wBAAwB;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,8BAA8B;IAC9B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,gCAAgC;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,+BAA+B;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAOD;;;;;;GAMG;AACH,wBAAsB,eAAe,CACnC,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,MAAyB,GACjC,OAAO,CAAC,OAAO,CAAC,CAoBlB;AAED;;;;;;GAMG;AACH,wBAAsB,YAAY,CAChC,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,MAAyB,GACjC,OAAO,CAAC,uBAAuB,GAAG,IAAI,CAAC,CAkBzC"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * CI token handling for VertaaUX CLI.
3
+ *
4
+ * CI tokens are long-lived API keys used in CI/CD pipelines.
5
+ * They can be passed via environment variables or --token flag.
6
+ */
7
+ /**
8
+ * Environment variable names for CI tokens.
9
+ * Checked in order of preference.
10
+ */
11
+ const TOKEN_ENV_VARS = ["VERTAAUX_TOKEN", "VERTAAUX_API_KEY"];
12
+ /**
13
+ * Get CI token from environment variables.
14
+ *
15
+ * Checks VERTAAUX_TOKEN first, then VERTAAUX_API_KEY.
16
+ *
17
+ * @returns Token string or null if not found
18
+ */
19
+ export function getCIToken() {
20
+ for (const envVar of TOKEN_ENV_VARS) {
21
+ const value = process.env[envVar];
22
+ if (value) {
23
+ return value;
24
+ }
25
+ }
26
+ return null;
27
+ }
28
+ /**
29
+ * Default API base URL.
30
+ */
31
+ const DEFAULT_API_BASE = "https://vertaaux.ai/v1";
32
+ /**
33
+ * Validate a CI token against the API.
34
+ *
35
+ * @param token - Token to validate
36
+ * @param apiBase - API base URL (optional)
37
+ * @returns true if token is valid
38
+ */
39
+ export async function validateCIToken(token, apiBase = DEFAULT_API_BASE) {
40
+ try {
41
+ const response = await fetch(`${apiBase}/auth/validate`, {
42
+ method: "GET",
43
+ headers: {
44
+ "X-API-Key": token,
45
+ "Content-Type": "application/json",
46
+ },
47
+ });
48
+ if (!response.ok) {
49
+ return false;
50
+ }
51
+ const data = (await response.json());
52
+ return data.valid === true;
53
+ }
54
+ catch {
55
+ // Network errors mean we can't validate, treat as invalid
56
+ return false;
57
+ }
58
+ }
59
+ /**
60
+ * Get token info from the API.
61
+ *
62
+ * @param token - Token to check
63
+ * @param apiBase - API base URL (optional)
64
+ * @returns Token info or null if invalid/error
65
+ */
66
+ export async function getTokenInfo(token, apiBase = DEFAULT_API_BASE) {
67
+ try {
68
+ const response = await fetch(`${apiBase}/auth/validate`, {
69
+ method: "GET",
70
+ headers: {
71
+ "X-API-Key": token,
72
+ "Content-Type": "application/json",
73
+ },
74
+ });
75
+ if (!response.ok) {
76
+ return null;
77
+ }
78
+ return response.json();
79
+ }
80
+ catch {
81
+ return null;
82
+ }
83
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * OAuth Device Code Flow implementation (RFC 8628).
3
+ *
4
+ * Used for interactive authentication where the user authenticates
5
+ * in a browser and the CLI polls for completion.
6
+ *
7
+ * @see https://datatracker.ietf.org/doc/html/rfc8628
8
+ */
9
+ /**
10
+ * Device code response from authorization server.
11
+ */
12
+ export interface DeviceCodeResponse {
13
+ /** Device verification code */
14
+ device_code: string;
15
+ /** User code to display */
16
+ user_code: string;
17
+ /** Verification URL for user to visit */
18
+ verification_uri: string;
19
+ /** Optional complete URL with user_code embedded */
20
+ verification_uri_complete?: string;
21
+ /** Recommended polling interval in seconds */
22
+ interval: number;
23
+ /** Code expiration in seconds */
24
+ expires_in: number;
25
+ }
26
+ /**
27
+ * Token response from authorization server.
28
+ */
29
+ export interface TokenResponse {
30
+ /** Access token */
31
+ access_token: string;
32
+ /** Token type (usually "Bearer") */
33
+ token_type: string;
34
+ /** Token lifetime in seconds */
35
+ expires_in: number;
36
+ /** Refresh token for getting new access tokens */
37
+ refresh_token?: string;
38
+ /** Scope granted */
39
+ scope?: string;
40
+ }
41
+ /**
42
+ * Device flow result returned to caller.
43
+ */
44
+ export interface DeviceFlowResult {
45
+ /** Access token for API calls */
46
+ accessToken: string;
47
+ /** Refresh token for token renewal */
48
+ refreshToken?: string;
49
+ /** Token expiration in seconds from now */
50
+ expiresIn: number;
51
+ }
52
+ /**
53
+ * Start the OAuth Device Code Flow.
54
+ *
55
+ * 1. Request device code from authorization server
56
+ * 2. Display user code and verification URL
57
+ * 3. Poll for token with countdown
58
+ * 4. Return tokens on success
59
+ *
60
+ * @param clientId - OAuth client ID for the CLI
61
+ * @param authBase - Base URL for auth endpoints (default: https://vertaaux.ai)
62
+ * @returns Device flow result with tokens
63
+ * @throws Error if authorization fails or times out
64
+ */
65
+ export declare function startDeviceFlow(clientId: string, authBase?: string): Promise<DeviceFlowResult>;
66
+ //# sourceMappingURL=device-flow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"device-flow.d.ts","sourceRoot":"","sources":["../../src/auth/device-flow.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,+BAA+B;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,2BAA2B;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,yCAAyC;IACzC,gBAAgB,EAAE,MAAM,CAAC;IACzB,oDAAoD;IACpD,yBAAyB,CAAC,EAAE,MAAM,CAAC;IACnC,8CAA8C;IAC9C,QAAQ,EAAE,MAAM,CAAC;IACjB,iCAAiC;IACjC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,mBAAmB;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,oCAAoC;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,gCAAgC;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,kDAAkD;IAClD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,oBAAoB;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,iCAAiC;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,sCAAsC;IACtC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,2CAA2C;IAC3C,SAAS,EAAE,MAAM,CAAC;CACnB;AAmCD;;;;;;;;;;;;GAYG;AACH,wBAAsB,eAAe,CACnC,QAAQ,EAAE,MAAM,EAChB,QAAQ,GAAE,MAA0B,GACnC,OAAO,CAAC,gBAAgB,CAAC,CA2B3B"}
@@ -0,0 +1,156 @@
1
+ /**
2
+ * OAuth Device Code Flow implementation (RFC 8628).
3
+ *
4
+ * Used for interactive authentication where the user authenticates
5
+ * in a browser and the CLI polls for completion.
6
+ *
7
+ * @see https://datatracker.ietf.org/doc/html/rfc8628
8
+ */
9
+ import ora from "ora";
10
+ /**
11
+ * Format remaining time as MM:SS.
12
+ */
13
+ function formatRemaining(seconds) {
14
+ const mins = Math.floor(seconds / 60);
15
+ const secs = seconds % 60;
16
+ return `${mins}:${secs.toString().padStart(2, "0")}`;
17
+ }
18
+ /**
19
+ * Default API base URL for VertaaUX.
20
+ */
21
+ const DEFAULT_AUTH_BASE = "https://vertaaux.ai";
22
+ /**
23
+ * Default timeout for device flow (5 minutes).
24
+ */
25
+ const DEFAULT_TIMEOUT_SECONDS = 300;
26
+ /**
27
+ * Start the OAuth Device Code Flow.
28
+ *
29
+ * 1. Request device code from authorization server
30
+ * 2. Display user code and verification URL
31
+ * 3. Poll for token with countdown
32
+ * 4. Return tokens on success
33
+ *
34
+ * @param clientId - OAuth client ID for the CLI
35
+ * @param authBase - Base URL for auth endpoints (default: https://vertaaux.ai)
36
+ * @returns Device flow result with tokens
37
+ * @throws Error if authorization fails or times out
38
+ */
39
+ export async function startDeviceFlow(clientId, authBase = DEFAULT_AUTH_BASE) {
40
+ // Step 1: Request device code
41
+ const deviceCodeResponse = await requestDeviceCode(clientId, authBase);
42
+ // Step 2: Display instructions
43
+ console.log("\n");
44
+ console.log(" To authenticate, visit:");
45
+ console.log(` ${deviceCodeResponse.verification_uri}`);
46
+ console.log("\n");
47
+ console.log(` Enter code: ${deviceCodeResponse.user_code}`);
48
+ console.log("\n");
49
+ if (deviceCodeResponse.verification_uri_complete) {
50
+ console.log(` Or open: ${deviceCodeResponse.verification_uri_complete}`);
51
+ console.log("\n");
52
+ }
53
+ // Step 3: Poll for token with countdown
54
+ const tokens = await pollForToken(clientId, deviceCodeResponse.device_code, deviceCodeResponse.interval, deviceCodeResponse.expires_in, authBase);
55
+ return tokens;
56
+ }
57
+ /**
58
+ * Request a device code from the authorization server.
59
+ */
60
+ async function requestDeviceCode(clientId, authBase) {
61
+ const url = `${authBase}/oauth/device/code`;
62
+ const response = await fetch(url, {
63
+ method: "POST",
64
+ headers: {
65
+ "Content-Type": "application/x-www-form-urlencoded",
66
+ },
67
+ body: new URLSearchParams({
68
+ client_id: clientId,
69
+ scope: "audit:read audit:write baseline:read baseline:write",
70
+ }),
71
+ });
72
+ if (!response.ok) {
73
+ const text = await response.text();
74
+ throw new Error(`Failed to request device code: ${response.status} ${text}`);
75
+ }
76
+ return response.json();
77
+ }
78
+ /**
79
+ * Poll the token endpoint until authorization completes.
80
+ */
81
+ async function pollForToken(clientId, deviceCode, intervalSeconds, expiresInSeconds, authBase) {
82
+ const url = `${authBase}/oauth/token`;
83
+ const startTime = Date.now();
84
+ const timeoutMs = Math.min(expiresInSeconds, DEFAULT_TIMEOUT_SECONDS) * 1000;
85
+ let interval = intervalSeconds * 1000; // Convert to milliseconds
86
+ // Start spinner with countdown
87
+ const spinner = ora({
88
+ text: `Waiting for authorization... (${formatRemaining(Math.round(timeoutMs / 1000))} remaining)`,
89
+ stream: process.stderr,
90
+ }).start();
91
+ try {
92
+ while (true) {
93
+ // Check timeout
94
+ const elapsed = Date.now() - startTime;
95
+ const remaining = Math.max(0, timeoutMs - elapsed);
96
+ if (remaining === 0) {
97
+ throw new Error("Authorization timed out. Please try again.");
98
+ }
99
+ // Update spinner with countdown
100
+ spinner.text = `Waiting for authorization... (${formatRemaining(Math.ceil(remaining / 1000))} remaining)`;
101
+ // Wait for poll interval
102
+ await sleep(interval);
103
+ // Poll token endpoint
104
+ const response = await fetch(url, {
105
+ method: "POST",
106
+ headers: {
107
+ "Content-Type": "application/x-www-form-urlencoded",
108
+ },
109
+ body: new URLSearchParams({
110
+ client_id: clientId,
111
+ device_code: deviceCode,
112
+ grant_type: "urn:ietf:params:oauth:grant-type:device_code",
113
+ }),
114
+ });
115
+ // Success
116
+ if (response.ok) {
117
+ const tokens = (await response.json());
118
+ spinner.succeed("Authorization successful!");
119
+ return {
120
+ accessToken: tokens.access_token,
121
+ refreshToken: tokens.refresh_token,
122
+ expiresIn: tokens.expires_in,
123
+ };
124
+ }
125
+ // Handle error responses
126
+ const error = (await response.json());
127
+ switch (error.error) {
128
+ case "authorization_pending":
129
+ // User hasn't completed authorization yet, continue polling
130
+ continue;
131
+ case "slow_down":
132
+ // Server requests slower polling
133
+ interval += 5000; // Add 5 seconds
134
+ continue;
135
+ case "access_denied":
136
+ throw new Error("Authorization denied by user.");
137
+ case "expired_token":
138
+ throw new Error("Device code expired. Please try again.");
139
+ default:
140
+ throw new Error(error.error_description || `Authorization failed: ${error.error}`);
141
+ }
142
+ }
143
+ }
144
+ finally {
145
+ // Ensure spinner is stopped
146
+ if (spinner.isSpinning) {
147
+ spinner.stop();
148
+ }
149
+ }
150
+ }
151
+ /**
152
+ * Sleep for specified milliseconds.
153
+ */
154
+ function sleep(ms) {
155
+ return new Promise((resolve) => setTimeout(resolve, ms));
156
+ }