@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
@@ -0,0 +1,347 @@
1
+ /**
2
+ * Policy management commands for VertaaUX CLI.
3
+ *
4
+ * Provides commands to initialize, validate, and display policy files.
5
+ *
6
+ * Implements CICD-17: Policy-as-code support.
7
+ */
8
+ import fs from "fs";
9
+ import path from "path";
10
+ import yaml from "yaml";
11
+ import chalk from "chalk";
12
+ import { loadPolicy, loadPolicyFile, resolveBranchPolicy, POLICY_TEMPLATES, policyJsonSchema, PolicyValidationError, PolicyLoadError, } from "../policy/index.js";
13
+ import { ExitCode } from "../utils/exit-codes.js";
14
+ import { writeJsonOutput, writeOutput } from "../output/envelope.js";
15
+ import { resolveCommandFormat } from "../output/formats.js";
16
+ /**
17
+ * Default policy file path.
18
+ */
19
+ const DEFAULT_POLICY_FILE = "vertaa.policy.yml";
20
+ /**
21
+ * Generate a policy file with comments.
22
+ */
23
+ function generatePolicyYaml(template) {
24
+ const policy = POLICY_TEMPLATES[template];
25
+ const assertions = policy.assertions;
26
+ // Build YAML with helpful comments
27
+ const lines = [
28
+ "# VertaaUX Policy Configuration",
29
+ "# https://vertaaux.ai/docs/policy",
30
+ "",
31
+ "# JSON Schema for IDE validation",
32
+ "$schema: https://vertaaux.ai/schemas/policy.json",
33
+ "",
34
+ `# Policy format version`,
35
+ `version: ${policy.version}`,
36
+ "",
37
+ ];
38
+ if (policy.name) {
39
+ lines.push(`# Human-readable policy name`);
40
+ lines.push(`name: "${policy.name}"`);
41
+ lines.push("");
42
+ }
43
+ if (policy.description) {
44
+ lines.push(`# Policy description`);
45
+ lines.push(`description: "${policy.description}"`);
46
+ lines.push("");
47
+ }
48
+ // Assertions section
49
+ lines.push("# Quality gate assertions");
50
+ lines.push("# All assertions must pass for the build to succeed");
51
+ lines.push("assertions:");
52
+ if (assertions.fail_on) {
53
+ lines.push(` # Fail on issues at or above this severity: error|warning|info`);
54
+ lines.push(` fail_on: ${assertions.fail_on}`);
55
+ }
56
+ if (assertions.overall_score !== undefined) {
57
+ lines.push(` # Minimum overall score (0-100)`);
58
+ lines.push(` overall_score: ${assertions.overall_score}`);
59
+ }
60
+ if (assertions.max_new_errors !== undefined) {
61
+ lines.push(` # Maximum new error-severity issues allowed`);
62
+ lines.push(` max_new_errors: ${assertions.max_new_errors}`);
63
+ }
64
+ if (assertions.max_new_warnings !== undefined) {
65
+ lines.push(` # Maximum new warning-severity issues allowed`);
66
+ lines.push(` max_new_warnings: ${assertions.max_new_warnings}`);
67
+ }
68
+ lines.push("");
69
+ // Branches section (if present)
70
+ const branches = policy.branches;
71
+ if (branches) {
72
+ lines.push("# Branch-specific policy overrides");
73
+ lines.push("# Use stricter rules for protected branches");
74
+ lines.push("branches:");
75
+ for (const [branchKey, branchConfig] of Object.entries(branches)) {
76
+ lines.push(` ${branchKey}:`);
77
+ if (branchConfig.pattern) {
78
+ lines.push(` pattern: "${branchConfig.pattern}"`);
79
+ }
80
+ if (branchConfig.assertions) {
81
+ lines.push(" assertions:");
82
+ for (const [key, value] of Object.entries(branchConfig.assertions)) {
83
+ lines.push(` ${key}: ${value}`);
84
+ }
85
+ }
86
+ }
87
+ lines.push("");
88
+ }
89
+ // Bypass labels
90
+ if (policy.bypass_labels) {
91
+ lines.push("# PR labels that bypass quality gate entirely");
92
+ lines.push("# Use sparingly for genuine emergencies");
93
+ lines.push("bypass_labels:");
94
+ for (const label of policy.bypass_labels) {
95
+ lines.push(` - "${label}"`);
96
+ }
97
+ lines.push("");
98
+ }
99
+ // Rule overrides placeholder
100
+ lines.push("# Rule-specific overrides (uncomment to customize)");
101
+ lines.push("# rules:");
102
+ lines.push("# color-contrast:");
103
+ lines.push("# severity: warning # Downgrade from error");
104
+ lines.push('# reason: "Using brand colors, manually verified"');
105
+ lines.push("# image-alt:");
106
+ lines.push("# severity: ignore # Handled by CMS");
107
+ lines.push("# paths:");
108
+ lines.push('# - "src/components/cms/**"');
109
+ lines.push("");
110
+ // Exclude paths placeholder
111
+ lines.push("# Paths to exclude from auditing (uncomment to customize)");
112
+ lines.push("# exclude_paths:");
113
+ lines.push('# - "src/legacy/**"');
114
+ lines.push('# - "vendor/**"');
115
+ return lines.join("\n");
116
+ }
117
+ /**
118
+ * Initialize a new policy file.
119
+ */
120
+ async function initPolicy(options) {
121
+ const template = options.template || "basic";
122
+ const outputPath = options.output || DEFAULT_POLICY_FILE;
123
+ const absolutePath = path.resolve(process.cwd(), outputPath);
124
+ // Check if file exists
125
+ if (fs.existsSync(absolutePath) && !options.force) {
126
+ console.error(chalk.red(`Policy file already exists: ${absolutePath}`));
127
+ console.error(chalk.dim("Use --force to overwrite."));
128
+ process.exit(ExitCode.ERROR);
129
+ }
130
+ // Validate template
131
+ if (!POLICY_TEMPLATES[template]) {
132
+ console.error(chalk.red(`Unknown template: ${template}`));
133
+ console.error(chalk.dim(`Available templates: ${Object.keys(POLICY_TEMPLATES).join(", ")}`));
134
+ process.exit(ExitCode.ERROR);
135
+ }
136
+ // Generate policy
137
+ const policyYaml = generatePolicyYaml(template);
138
+ // Write file
139
+ fs.writeFileSync(absolutePath, policyYaml, "utf-8");
140
+ console.error(chalk.green(`Created policy file: ${absolutePath}`));
141
+ console.error(chalk.dim(`Template: ${template}`));
142
+ console.error("");
143
+ console.error("Next steps:");
144
+ console.error(` 1. Review and customize ${outputPath}`);
145
+ console.error(" 2. Commit the file to your repository");
146
+ console.error(" 3. Run 'vertaa policy validate' to verify");
147
+ }
148
+ /**
149
+ * Validate a policy file.
150
+ */
151
+ async function validatePolicyCommand(filePath, options) {
152
+ const quiet = options.quiet ?? false;
153
+ try {
154
+ // Load policy from path or search
155
+ let policy;
156
+ let policyPath;
157
+ if (filePath) {
158
+ policy = await loadPolicyFile(filePath);
159
+ policyPath = filePath;
160
+ }
161
+ else {
162
+ const result = await loadPolicy();
163
+ policy = result.policy;
164
+ policyPath = result.path;
165
+ }
166
+ if (!policyPath) {
167
+ console.error(chalk.yellow("No policy file found. Using defaults."));
168
+ console.error(chalk.dim("Create a policy file with: vertaa policy init"));
169
+ return;
170
+ }
171
+ // Validation passed if we got here
172
+ if (!quiet) {
173
+ console.error(chalk.green(`Policy file valid: ${policyPath}`));
174
+ console.error("");
175
+ console.error("Summary:");
176
+ console.error(` Version: ${policy.version}`);
177
+ console.error(` Name: ${policy.name || "(unnamed)"}`);
178
+ if (policy.assertions) {
179
+ console.error(" Assertions:");
180
+ if (policy.assertions.fail_on) {
181
+ console.error(` - Fail on: ${policy.assertions.fail_on}`);
182
+ }
183
+ if (policy.assertions.overall_score !== undefined) {
184
+ console.error(` - Overall score >= ${policy.assertions.overall_score}`);
185
+ }
186
+ if (policy.assertions.max_new_errors !== undefined) {
187
+ console.error(` - Max new errors: ${policy.assertions.max_new_errors}`);
188
+ }
189
+ }
190
+ if (policy.branches) {
191
+ console.error(` Branches: ${Object.keys(policy.branches).join(", ")}`);
192
+ }
193
+ if (policy.rules) {
194
+ console.error(` Rule overrides: ${Object.keys(policy.rules).length}`);
195
+ }
196
+ if (policy.bypass_labels?.length) {
197
+ console.error(` Bypass labels: ${policy.bypass_labels.join(", ")}`);
198
+ }
199
+ }
200
+ }
201
+ catch (error) {
202
+ if (error instanceof PolicyValidationError) {
203
+ console.error(chalk.red("Policy validation failed:"));
204
+ for (const err of error.errors) {
205
+ console.error(chalk.red(` - ${err}`));
206
+ }
207
+ process.exit(ExitCode.ERROR);
208
+ }
209
+ if (error instanceof PolicyLoadError) {
210
+ console.error(chalk.red(`Failed to load policy: ${error.message}`));
211
+ process.exit(ExitCode.ERROR);
212
+ }
213
+ throw error;
214
+ }
215
+ }
216
+ /**
217
+ * Show effective policy for a branch.
218
+ */
219
+ async function showPolicy(options) {
220
+ const branch = options.branch || "";
221
+ const format = options.format || "yaml";
222
+ try {
223
+ // Load policy
224
+ let policy;
225
+ let policyPath;
226
+ if (options.policy) {
227
+ policy = await loadPolicyFile(options.policy);
228
+ policyPath = options.policy;
229
+ }
230
+ else {
231
+ const result = await loadPolicy();
232
+ policy = result.policy;
233
+ policyPath = result.path;
234
+ }
235
+ // Resolve branch-specific overrides
236
+ let effectivePolicy = policy;
237
+ let branchInfo = "";
238
+ if (branch) {
239
+ effectivePolicy = resolveBranchPolicy(policy, branch);
240
+ branchInfo = ` (branch: ${branch})`;
241
+ }
242
+ // Output
243
+ if (policyPath) {
244
+ console.error(chalk.dim(`Policy: ${policyPath}${branchInfo}`));
245
+ console.error("");
246
+ }
247
+ else {
248
+ console.error(chalk.dim(`Using default policy${branchInfo}`));
249
+ console.error("");
250
+ }
251
+ if (format === "json") {
252
+ writeJsonOutput(effectivePolicy, "policy-show");
253
+ }
254
+ else {
255
+ // YAML output
256
+ const yamlOutput = yaml.stringify(effectivePolicy, {
257
+ indent: 2,
258
+ lineWidth: 100,
259
+ });
260
+ writeOutput(yamlOutput);
261
+ }
262
+ }
263
+ catch (error) {
264
+ if (error instanceof PolicyValidationError) {
265
+ console.error(chalk.red("Policy validation failed:"));
266
+ for (const err of error.errors) {
267
+ console.error(chalk.red(` - ${err}`));
268
+ }
269
+ process.exit(ExitCode.ERROR);
270
+ }
271
+ if (error instanceof PolicyLoadError) {
272
+ console.error(chalk.red(`Failed to load policy: ${error.message}`));
273
+ process.exit(ExitCode.ERROR);
274
+ }
275
+ throw error;
276
+ }
277
+ }
278
+ /**
279
+ * Output JSON Schema for policy file.
280
+ */
281
+ function outputSchema() {
282
+ writeJsonOutput(policyJsonSchema, "policy-schema");
283
+ }
284
+ /**
285
+ * Register the policy command with the Commander program.
286
+ */
287
+ export function registerPolicyCommand(program) {
288
+ const policyCmd = program
289
+ .command("policy")
290
+ .description("Manage policy-as-code configuration");
291
+ // policy init
292
+ policyCmd
293
+ .command("init")
294
+ .description("Create a new policy file")
295
+ .option("-t, --template <template>", "Policy template: basic|strict|lenient", "basic")
296
+ .option("-o, --output <path>", "Output file path", DEFAULT_POLICY_FILE)
297
+ .option("-f, --force", "Overwrite existing file")
298
+ .action(async (options) => {
299
+ try {
300
+ await initPolicy(options);
301
+ }
302
+ catch (error) {
303
+ console.error("Error:", error instanceof Error ? error.message : String(error));
304
+ process.exit(ExitCode.ERROR);
305
+ }
306
+ });
307
+ // policy validate
308
+ policyCmd
309
+ .command("validate [file]")
310
+ .description("Validate a policy file")
311
+ .option("-q, --quiet", "Suppress output on success")
312
+ .action(async (filePath, options) => {
313
+ try {
314
+ await validatePolicyCommand(filePath, options);
315
+ }
316
+ catch (error) {
317
+ console.error("Error:", error instanceof Error ? error.message : String(error));
318
+ process.exit(ExitCode.ERROR);
319
+ }
320
+ });
321
+ // policy show
322
+ policyCmd
323
+ .command("show")
324
+ .description("Display effective policy for a branch")
325
+ .option("-b, --branch <name>", "Branch to show policy for")
326
+ .option("-f, --format <format>", "Output format: yaml|json", "yaml")
327
+ .option("-p, --policy <path>", "Path to policy file")
328
+ .action(async (options, cmd) => {
329
+ try {
330
+ // Validate format using per-command registry
331
+ const machineMode = cmd.optsWithGlobals?.().machine || false;
332
+ const validatedFormat = resolveCommandFormat("policy-show", options.format, machineMode);
333
+ await showPolicy({ ...options, format: validatedFormat });
334
+ }
335
+ catch (error) {
336
+ console.error("Error:", error instanceof Error ? error.message : String(error));
337
+ process.exit(ExitCode.ERROR);
338
+ }
339
+ });
340
+ // policy schema
341
+ policyCmd
342
+ .command("schema")
343
+ .description("Output JSON Schema for policy file validation")
344
+ .action(() => {
345
+ outputSchema();
346
+ });
347
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Upload command for VertaaUX CLI.
3
+ *
4
+ * Syncs local audit results to VertaaUX cloud.
5
+ * Enables sharing results across team members and CI runs.
6
+ */
7
+ import { Command } from "commander";
8
+ /**
9
+ * Register the upload command with the Commander program.
10
+ */
11
+ export declare function registerUploadCommand(program: Command): void;
12
+ //# sourceMappingURL=upload.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upload.d.ts","sourceRoot":"","sources":["../../src/commands/upload.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAyLpC;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAiB5D"}
@@ -0,0 +1,158 @@
1
+ /**
2
+ * Upload command for VertaaUX CLI.
3
+ *
4
+ * Syncs local audit results to VertaaUX cloud.
5
+ * Enables sharing results across team members and CI runs.
6
+ */
7
+ import fs from "fs";
8
+ import path from "path";
9
+ import chalk from "chalk";
10
+ import ora from "ora";
11
+ import { loadToken } from "../auth/token-store.js";
12
+ import { getCIToken } from "../auth/ci-token.js";
13
+ import { resolveApiBase } from "../utils/client.js";
14
+ import { resolveConfig } from "../config/loader.js";
15
+ import { loadBaseline, DEFAULT_BASELINE_PATH } from "../baseline/manager.js";
16
+ import { ExitCode } from "../utils/exit-codes.js";
17
+ /**
18
+ * Artifacts directory.
19
+ */
20
+ const ARTIFACTS_DIR = ".vertaaux/artifacts";
21
+ /**
22
+ * Get authentication token from stored credentials or environment.
23
+ */
24
+ async function getAuthToken() {
25
+ // Check stored token first
26
+ const storedToken = await loadToken();
27
+ if (storedToken?.accessToken) {
28
+ return storedToken.accessToken;
29
+ }
30
+ // Check environment
31
+ return getCIToken();
32
+ }
33
+ /**
34
+ * Handle the upload command.
35
+ */
36
+ async function handleUpload(jobId, options) {
37
+ // Get auth token
38
+ const token = await getAuthToken();
39
+ if (!token) {
40
+ console.error(chalk.red("Error: Not authenticated."));
41
+ console.error("Run `vertaa login` to authenticate or set VERTAAUX_TOKEN environment variable.");
42
+ process.exit(ExitCode.ERROR);
43
+ }
44
+ // Load config for API base (supports --config global option)
45
+ const config = await resolveConfig(options.configPath);
46
+ const apiBase = resolveApiBase(options.base);
47
+ // Determine what to upload
48
+ const spinner = ora("Preparing upload...").start();
49
+ try {
50
+ // If no job ID, find the most recent local result
51
+ let targetJobId = jobId;
52
+ let localResultPath;
53
+ if (!targetJobId) {
54
+ // Look for recent results in artifacts directory
55
+ const artifactsPath = path.resolve(process.cwd(), ARTIFACTS_DIR);
56
+ if (fs.existsSync(artifactsPath)) {
57
+ const dirs = fs.readdirSync(artifactsPath).filter((d) => {
58
+ const stat = fs.statSync(path.join(artifactsPath, d));
59
+ return stat.isDirectory();
60
+ });
61
+ if (dirs.length > 0) {
62
+ // Get most recent by mtime
63
+ const sorted = dirs
64
+ .map((d) => ({
65
+ name: d,
66
+ mtime: fs.statSync(path.join(artifactsPath, d)).mtime,
67
+ }))
68
+ .sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
69
+ targetJobId = sorted[0].name;
70
+ localResultPath = path.join(artifactsPath, targetJobId);
71
+ }
72
+ }
73
+ if (!targetJobId) {
74
+ spinner.fail("No job ID provided and no local results found.");
75
+ console.error("Run `vertaa audit --save-trace` to save local results first.");
76
+ process.exit(ExitCode.ERROR);
77
+ }
78
+ }
79
+ spinner.text = `Uploading audit ${targetJobId}...`;
80
+ // Prepare upload payload
81
+ const payload = {
82
+ job_id: targetJobId,
83
+ project: options.project || path.basename(process.cwd()),
84
+ };
85
+ // Add local results if available
86
+ if (localResultPath && fs.existsSync(localResultPath)) {
87
+ const files = fs.readdirSync(localResultPath);
88
+ const artifacts = {};
89
+ for (const file of files) {
90
+ const filePath = path.join(localResultPath, file);
91
+ const stat = fs.statSync(filePath);
92
+ // Skip large files (> 10MB)
93
+ if (stat.size > 10 * 1024 * 1024) {
94
+ continue;
95
+ }
96
+ // Read file content (base64 for binary)
97
+ const content = fs.readFileSync(filePath);
98
+ const ext = path.extname(file).toLowerCase();
99
+ const isBinary = [".png", ".jpg", ".jpeg", ".gif", ".zip"].includes(ext);
100
+ artifacts[file] = isBinary
101
+ ? content.toString("base64")
102
+ : content.toString("utf-8");
103
+ }
104
+ payload.artifacts = artifacts;
105
+ }
106
+ // Upload baseline if requested
107
+ if (options.baseline) {
108
+ const baseline = await loadBaseline(DEFAULT_BASELINE_PATH);
109
+ if (baseline) {
110
+ payload.baseline = baseline;
111
+ spinner.text = `Uploading audit ${targetJobId} with baseline...`;
112
+ }
113
+ }
114
+ // Make API request
115
+ const response = await fetch(`${apiBase}/sync/upload`, {
116
+ method: "POST",
117
+ headers: {
118
+ "Content-Type": "application/json",
119
+ "X-API-Key": token,
120
+ },
121
+ body: JSON.stringify(payload),
122
+ });
123
+ if (!response.ok) {
124
+ const error = await response.json().catch(() => ({ error: { message: response.statusText } }));
125
+ throw new Error(error.error?.message || `HTTP ${response.status}`);
126
+ }
127
+ const result = (await response.json());
128
+ if (!result.success) {
129
+ throw new Error(result.error?.message || "Upload failed");
130
+ }
131
+ spinner.succeed("Upload complete!");
132
+ console.error("");
133
+ console.error(` Job ID: ${result.job_id}`);
134
+ console.error(` URL: ${chalk.cyan(result.url)}`);
135
+ console.error("");
136
+ console.error("Share this URL with your team to view the results.");
137
+ }
138
+ catch (error) {
139
+ spinner.fail("Upload failed");
140
+ console.error(chalk.red("Error:"), error instanceof Error ? error.message : String(error));
141
+ process.exit(ExitCode.ERROR);
142
+ }
143
+ }
144
+ /**
145
+ * Register the upload command with the Commander program.
146
+ */
147
+ export function registerUploadCommand(program) {
148
+ program
149
+ .command("upload [job-id]")
150
+ .description("Upload audit results to VertaaUX cloud")
151
+ .option("--baseline", "Also upload baseline file")
152
+ .option("--project <name>", "Cloud project name (default: current directory name)")
153
+ .option("-b, --base <url>", "API base URL")
154
+ .action(async (jobId, options, command) => {
155
+ const globalOpts = command.optsWithGlobals();
156
+ await handleUpload(jobId, { ...options, configPath: globalOpts.config });
157
+ });
158
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Default configuration values for VertaaUX CLI.
3
+ *
4
+ * These defaults are merged with config file values, with the config file
5
+ * taking precedence. CLI flags override both.
6
+ *
7
+ * Precedence: CLI flags > env vars > config file > defaults
8
+ */
9
+ import type { VertaauxConfig } from "./schema.js";
10
+ /**
11
+ * Default configuration values.
12
+ */
13
+ export declare const DEFAULT_CONFIG: Required<Pick<VertaauxConfig, "mode" | "output" | "baseline" | "timeout" | "interval">> & {
14
+ ci: NonNullable<VertaauxConfig["ci"]>;
15
+ };
16
+ /**
17
+ * Deep merge two config objects.
18
+ * Target values take precedence over defaults.
19
+ */
20
+ export declare function mergeWithDefaults(config: VertaauxConfig): VertaauxConfig;
21
+ //# sourceMappingURL=defaults.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defaults.d.ts","sourceRoot":"","sources":["../../src/config/defaults.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD;;GAEG;AACH,eAAO,MAAM,cAAc,EAAE,QAAQ,CACnC,IAAI,CAAC,cAAc,EAAE,MAAM,GAAG,QAAQ,GAAG,UAAU,GAAG,SAAS,GAAG,UAAU,CAAC,CAC9E,GAAG;IACF,EAAE,EAAE,WAAW,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC;CAgBvC,CAAC;AAEF;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,cAAc,GAAG,cAAc,CAiBxE"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Default configuration values for VertaaUX CLI.
3
+ *
4
+ * These defaults are merged with config file values, with the config file
5
+ * taking precedence. CLI flags override both.
6
+ *
7
+ * Precedence: CLI flags > env vars > config file > defaults
8
+ */
9
+ /**
10
+ * Default configuration values.
11
+ */
12
+ export const DEFAULT_CONFIG = {
13
+ mode: "basic",
14
+ output: {
15
+ format: "auto",
16
+ groupBy: "severity",
17
+ },
18
+ baseline: {
19
+ path: ".vertaaux/baseline.json",
20
+ autoUpdate: false,
21
+ },
22
+ ci: {
23
+ template: "none",
24
+ },
25
+ timeout: 60000,
26
+ interval: 5000,
27
+ };
28
+ /**
29
+ * Deep merge two config objects.
30
+ * Target values take precedence over defaults.
31
+ */
32
+ export function mergeWithDefaults(config) {
33
+ return {
34
+ ...DEFAULT_CONFIG,
35
+ ...config,
36
+ output: {
37
+ ...DEFAULT_CONFIG.output,
38
+ ...config.output,
39
+ },
40
+ baseline: {
41
+ ...DEFAULT_CONFIG.baseline,
42
+ ...config.baseline,
43
+ },
44
+ ci: {
45
+ ...DEFAULT_CONFIG.ci,
46
+ ...config.ci,
47
+ },
48
+ };
49
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Configuration loader using cosmiconfig with Ajv validation.
3
+ *
4
+ * Searches for configuration in multiple formats:
5
+ * - .vertaaux.yml / .vertaaux.yaml
6
+ * - .vertaaux.json
7
+ * - vertaaux.config.js / vertaaux.config.mjs
8
+ * - package.json (vertaaux key)
9
+ *
10
+ * Validates against JSON Schema and merges with defaults.
11
+ */
12
+ import { type ErrorObject } from "ajv";
13
+ import type { VertaauxConfig } from "./schema.js";
14
+ /**
15
+ * Error thrown when config validation fails.
16
+ */
17
+ export declare class ConfigValidationError extends Error {
18
+ readonly errors: ErrorObject[] | null | undefined;
19
+ constructor(errors: ErrorObject[] | null | undefined, filePath?: string);
20
+ }
21
+ /**
22
+ * Error thrown when config file cannot be loaded.
23
+ */
24
+ export declare class ConfigLoadError extends Error {
25
+ readonly cause?: unknown | undefined;
26
+ constructor(message: string, cause?: unknown | undefined);
27
+ }
28
+ /**
29
+ * Load configuration from file and environment.
30
+ *
31
+ * Searches for config file starting from current working directory
32
+ * and walking up the directory tree.
33
+ *
34
+ * @param searchFrom - Directory to start search from (defaults to cwd)
35
+ * @returns Merged configuration with defaults
36
+ *
37
+ * @example
38
+ * ```typescript
39
+ * const config = await loadConfig();
40
+ * console.log(config.mode); // 'basic' (default or from config)
41
+ * ```
42
+ */
43
+ export declare function loadConfig(searchFrom?: string): Promise<VertaauxConfig>;
44
+ /**
45
+ * Load configuration from a specific file path.
46
+ *
47
+ * @param filePath - Path to config file
48
+ * @returns Merged configuration with defaults
49
+ */
50
+ export declare function loadConfigFile(filePath: string): Promise<VertaauxConfig>;
51
+ /**
52
+ * Resolve configuration from explicit path or auto-detection.
53
+ *
54
+ * If an explicit path is provided, loads that file directly.
55
+ * Otherwise, falls back to cosmiconfig auto-detection.
56
+ *
57
+ * @param explicitPath - Optional explicit config file path (from --config flag)
58
+ * @returns Merged configuration with defaults
59
+ */
60
+ export declare function resolveConfig(explicitPath?: string): Promise<VertaauxConfig>;
61
+ /**
62
+ * Clear the config cache.
63
+ * Useful for testing or when config files change.
64
+ */
65
+ export declare function clearConfigCache(): void;
66
+ //# sourceMappingURL=loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/config/loader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAY,EAAE,KAAK,WAAW,EAAyB,MAAM,KAAK,CAAC;AAGnE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAelD;;GAEG;AACH,qBAAa,qBAAsB,SAAQ,KAAK;aAE5B,MAAM,EAAE,WAAW,EAAE,GAAG,IAAI,GAAG,SAAS;gBAAxC,MAAM,EAAE,WAAW,EAAE,GAAG,IAAI,GAAG,SAAS,EACxD,QAAQ,CAAC,EAAE,MAAM;CAWpB;AAED;;GAEG;AACH,qBAAa,eAAgB,SAAQ,KAAK;aAGtB,KAAK,CAAC,EAAE,OAAO;gBAD/B,OAAO,EAAE,MAAM,EACC,KAAK,CAAC,EAAE,OAAO,YAAA;CAKlC;AAqBD;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,UAAU,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAsC7E;AAED;;;;;GAKG;AACH,wBAAsB,cAAc,CAClC,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,cAAc,CAAC,CA6BzB;AAED;;;;;;;;GAQG;AACH,wBAAsB,aAAa,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAWlF;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAEvC"}