@provartesting/provardx-cli 1.5.0-beta.1 → 1.5.0-beta.11

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 (78) hide show
  1. package/README.md +137 -13
  2. package/lib/commands/provar/auth/clear.d.ts +7 -0
  3. package/lib/commands/provar/auth/clear.js +36 -0
  4. package/lib/commands/provar/auth/clear.js.map +1 -0
  5. package/lib/commands/provar/auth/login.d.ts +10 -0
  6. package/lib/commands/provar/auth/login.js +90 -0
  7. package/lib/commands/provar/auth/login.js.map +1 -0
  8. package/lib/commands/provar/auth/rotate.d.ts +7 -0
  9. package/lib/commands/provar/auth/rotate.js +42 -0
  10. package/lib/commands/provar/auth/rotate.js.map +1 -0
  11. package/lib/commands/provar/auth/status.d.ts +7 -0
  12. package/lib/commands/provar/auth/status.js +107 -0
  13. package/lib/commands/provar/auth/status.js.map +1 -0
  14. package/lib/mcp/docs/PROVAR_TEST_STEP_REFERENCE.md +1430 -0
  15. package/lib/mcp/licensing/licenseValidator.d.ts +3 -3
  16. package/lib/mcp/licensing/licenseValidator.js +4 -4
  17. package/lib/mcp/prompts/index.d.ts +2 -0
  18. package/lib/mcp/prompts/index.js +19 -0
  19. package/lib/mcp/prompts/index.js.map +1 -0
  20. package/lib/mcp/prompts/loopPrompts.d.ts +6 -0
  21. package/lib/mcp/prompts/loopPrompts.js +435 -0
  22. package/lib/mcp/prompts/loopPrompts.js.map +1 -0
  23. package/lib/mcp/prompts/migrationPrompts.d.ts +4 -0
  24. package/lib/mcp/prompts/migrationPrompts.js +207 -0
  25. package/lib/mcp/prompts/migrationPrompts.js.map +1 -0
  26. package/lib/mcp/rules/provar_best_practices_rules.json +256 -544
  27. package/lib/mcp/server.js +45 -1
  28. package/lib/mcp/server.js.map +1 -1
  29. package/lib/mcp/tools/antTools.d.ts +15 -0
  30. package/lib/mcp/tools/antTools.js +204 -50
  31. package/lib/mcp/tools/antTools.js.map +1 -1
  32. package/lib/mcp/tools/automationTools.d.ts +36 -3
  33. package/lib/mcp/tools/automationTools.js +335 -42
  34. package/lib/mcp/tools/automationTools.js.map +1 -1
  35. package/lib/mcp/tools/bestPracticesEngine.js +161 -23
  36. package/lib/mcp/tools/bestPracticesEngine.js.map +1 -1
  37. package/lib/mcp/tools/connectionTools.d.ts +4 -0
  38. package/lib/mcp/tools/connectionTools.js +168 -0
  39. package/lib/mcp/tools/connectionTools.js.map +1 -0
  40. package/lib/mcp/tools/nitroXTools.d.ts +22 -0
  41. package/lib/mcp/tools/nitroXTools.js +750 -0
  42. package/lib/mcp/tools/nitroXTools.js.map +1 -0
  43. package/lib/mcp/tools/pageObjectGenerate.js +103 -35
  44. package/lib/mcp/tools/pageObjectGenerate.js.map +1 -1
  45. package/lib/mcp/tools/propertiesTools.d.ts +2 -0
  46. package/lib/mcp/tools/propertiesTools.js +277 -39
  47. package/lib/mcp/tools/propertiesTools.js.map +1 -1
  48. package/lib/mcp/tools/qualityHubApiTools.d.ts +3 -0
  49. package/lib/mcp/tools/qualityHubApiTools.js +134 -0
  50. package/lib/mcp/tools/qualityHubApiTools.js.map +1 -0
  51. package/lib/mcp/tools/qualityHubTools.js +127 -19
  52. package/lib/mcp/tools/qualityHubTools.js.map +1 -1
  53. package/lib/mcp/tools/rcaTools.d.ts +3 -2
  54. package/lib/mcp/tools/rcaTools.js +145 -20
  55. package/lib/mcp/tools/rcaTools.js.map +1 -1
  56. package/lib/mcp/tools/testCaseGenerate.js +88 -59
  57. package/lib/mcp/tools/testCaseGenerate.js.map +1 -1
  58. package/lib/mcp/tools/testCaseStepTools.d.ts +4 -0
  59. package/lib/mcp/tools/testCaseStepTools.js +221 -0
  60. package/lib/mcp/tools/testCaseStepTools.js.map +1 -0
  61. package/lib/mcp/tools/testCaseValidate.d.ts +11 -0
  62. package/lib/mcp/tools/testCaseValidate.js +146 -19
  63. package/lib/mcp/tools/testCaseValidate.js.map +1 -1
  64. package/lib/services/auth/credentials.d.ts +21 -0
  65. package/lib/services/auth/credentials.js +75 -0
  66. package/lib/services/auth/credentials.js.map +1 -0
  67. package/lib/services/auth/loginFlow.d.ts +68 -0
  68. package/lib/services/auth/loginFlow.js +216 -0
  69. package/lib/services/auth/loginFlow.js.map +1 -0
  70. package/lib/services/qualityHub/client.d.ts +161 -0
  71. package/lib/services/qualityHub/client.js +226 -0
  72. package/lib/services/qualityHub/client.js.map +1 -0
  73. package/messages/sf.provar.auth.clear.md +16 -0
  74. package/messages/sf.provar.auth.login.md +31 -0
  75. package/messages/sf.provar.auth.rotate.md +23 -0
  76. package/messages/sf.provar.auth.status.md +16 -0
  77. package/oclif.manifest.json +214 -1
  78. package/package.json +8 -4
package/README.md CHANGED
@@ -3,6 +3,7 @@
3
3
  [![Version](https://img.shields.io/npm/v/@provartesting/provardx-cli.svg)](https://npmjs.org/package/@provartesting/provardx-cli)
4
4
  [![Downloads/week](https://img.shields.io/npm/dw/@provartesting/provardx-cli.svg)](https://npmjs.org/package/@provartesting/provardx-cli)
5
5
  [![License](https://img.shields.io/npm/l/@provartesting/provardx-cli.svg)](https://github.com/ProvarTesting/provardx-cli/blob/main/LICENSE.md)
6
+ [![Get Access](https://img.shields.io/badge/Quality%20Hub%20API-Get%20Access-blue)](https://aqqlrlhga7.execute-api.us-east-1.amazonaws.com/dev/auth/request-access)
6
7
 
7
8
  # What is the ProvarDX CLI?
8
9
 
@@ -10,10 +11,12 @@ The Provar DX CLI is a Salesforce CLI plugin for Provar customers who want to au
10
11
 
11
12
  # Installation, Update, and Uninstall
12
13
 
14
+ **Requires Node.js 18–24 (LTS 22 recommended).** Node 25+ is not yet supported due to a breaking change in a transitive dependency. Check with `node --version`.
15
+
13
16
  Install the plugin
14
17
 
15
18
  ```sh-session
16
- $ sf plugins install @provartesting/provardx-cli
19
+ $ sf plugins install @provartesting/provardx-cli@beta
17
20
  ```
18
21
 
19
22
  Update plugins
@@ -30,33 +33,56 @@ $ sf plugins uninstall @provartesting/provardx-cli
30
33
 
31
34
  # MCP Server (AI-Assisted Quality)
32
35
 
33
- The Provar DX CLI includes a built-in **Model Context Protocol (MCP) server** that connects AI assistants (Claude Desktop, Claude Code, Cursor) directly to your Provar project. Once connected, an AI agent can inspect your project structure, generate Page Objects and test cases, and validate every level of the test hierarchy with quality scores that match the Provar Quality Hub API.
36
+ The Provar DX CLI includes a built-in **Model Context Protocol (MCP) server** that connects AI assistants (Claude Desktop, Claude Code, Cursor) directly to your Provar project. Once connected, an AI agent can inspect your project structure, generate Page Objects and test cases, validate every level of the test hierarchy with quality scores, and work with NitroX (Hybrid Model) component page objects for LWC, Screen Flow, Industry Components, Experience Cloud, and HTML5.
37
+
38
+ Validation runs in two modes: **local only** (structural rules, no key required) or **Quality Hub API** (170+ rules, quality scoring — requires a `pv_k_` API key). Don't have an account? **[Request access](https://aqqlrlhga7.execute-api.us-east-1.amazonaws.com/dev/auth/request-access)**.
39
+
40
+ ## Quick setup
41
+
42
+ **Requires:** Provar Automation IDE installed with an activated license.
34
43
 
35
44
  ```sh
36
- sf provar mcp start --allowed-paths /path/to/your/provar/project
45
+ # 1. Install the plugin — @beta is required for MCP support
46
+ sf plugins install @provartesting/provardx-cli@beta
47
+
48
+ # 2. (Optional) Authenticate for full 170+ rule validation
49
+ sf provar auth login
37
50
  ```
38
51
 
39
- 📖 **See [docs/mcp.md](https://github.com/ProvarTesting/provardx-cli/blob/main/docs/mcp.md) for full setup and tool documentation.**
52
+ **Claude Code** run once to register the server:
40
53
 
41
- ## License Validation
54
+ ```sh
55
+ claude mcp add provar -s user -- sf provar mcp start --allowed-paths /path/to/your/provar/project
56
+ ```
42
57
 
43
- The MCP server verifies your Provar license before accepting any connections. Validation is automatic — no extra flags are required for standard usage.
58
+ **Claude Desktop** add to your config file and restart the app:
44
59
 
45
- **How it works:**
60
+ - macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
61
+ - Windows: `%APPDATA%\Claude\claude_desktop_config.json`
46
62
 
47
- 1. **Auto-detection** — the server reads `~/Provar/.licenses/*.properties` (the same files written by Provar's IDE plugins). If a valid, activated license is found the server starts immediately.
48
- 2. **Cache** — successful validations are cached at `~/Provar/.licenses/.mcp-license-cache.json` (2 h TTL). Subsequent starts within the TTL window skip the disk scan.
49
- 3. **Grace fallback** — if the IDE license files cannot be found or read and the cache is stale (but ≤ 48 h old), the server starts with a warning on stderr using the cached result so CI pipelines are not broken by transient local file-access issues.
50
- 4. **Fail closed** — if no valid license is detected the command exits with a non-zero exit code and a clear error message.
63
+ ```json
64
+ {
65
+ "mcpServers": {
66
+ "provar": {
67
+ "command": "sf",
68
+ "args": ["provar", "mcp", "start", "--allowed-paths", "/path/to/your/provar/project"]
69
+ }
70
+ }
71
+ }
72
+ ```
51
73
 
52
- **`NODE_ENV=test` fast-path:**
74
+ > **Windows (Claude Desktop):** Use `sf.cmd` instead of `sf` if the server fails to start.
53
75
 
54
- When `NODE_ENV=test` the validation step is skipped entirely. This is intended only for the plugin's own unit-test suite.
76
+ 📖 **[docs/mcp.md](https://github.com/ProvarTesting/provardx-cli/blob/main/docs/mcp.md) full setup, all 35+ tools, 7 MCP prompts, troubleshooting.**
55
77
 
56
78
  ---
57
79
 
58
80
  # Commands
59
81
 
82
+ - [`sf provar auth login`](#sf-provar-auth-login)
83
+ - [`sf provar auth rotate`](#sf-provar-auth-rotate)
84
+ - [`sf provar auth status`](#sf-provar-auth-status)
85
+ - [`sf provar auth clear`](#sf-provar-auth-clear)
60
86
  - [`sf provar mcp start`](#sf-provar-mcp-start)
61
87
  - [`sf provar config get`](#sf-provar-config-get)
62
88
  - [`sf provar config set`](#sf-provar-config-set)
@@ -84,6 +110,99 @@ When `NODE_ENV=test` the validation step is skipped entirely. This is intended o
84
110
  - [`sf provar manager test run report`](#sf-provar-manager-test-run-report) _(deprecated — use `sf provar quality-hub test run report`)_
85
111
  - [`sf provar manager test run abort`](#sf-provar-manager-test-run-abort) _(deprecated — use `sf provar quality-hub test run abort`)_
86
112
 
113
+ ## `sf provar auth login`
114
+
115
+ Log in to Provar Quality Hub and store your API key.
116
+
117
+ ```
118
+ USAGE
119
+ $ sf provar auth login [--url <value>]
120
+
121
+ FLAGS
122
+ --url=<value> Override the Quality Hub API base URL (for non-production environments).
123
+
124
+ DESCRIPTION
125
+ Opens a browser to the Provar login page. After you authenticate, your API key is
126
+ stored at ~/.provar/credentials.json and used automatically by the Provar MCP tools
127
+ and CI/CD integrations. The key is valid for approximately 90 days.
128
+
129
+ For CI/CD pipelines (GitHub Actions, Jenkins, etc.) where a browser cannot open:
130
+ run sf provar auth login once on your local machine, copy the api_key value from
131
+ ~/.provar/credentials.json, and store it as the PROVAR_API_KEY environment variable
132
+ or secret in your pipeline. Rotate the secret every ~90 days when the key expires.
133
+
134
+ Don't have an account? Request access at:
135
+ https://aqqlrlhga7.execute-api.us-east-1.amazonaws.com/dev/auth/request-access
136
+
137
+ EXAMPLES
138
+ Log in interactively:
139
+
140
+ $ sf provar auth login
141
+
142
+ Log in against a staging environment:
143
+
144
+ $ sf provar auth login --url https://dev.api.example.com
145
+ ```
146
+
147
+ ## `sf provar auth rotate`
148
+
149
+ Rotate your stored API key without re-authenticating via browser.
150
+
151
+ ```
152
+ USAGE
153
+ $ sf provar auth rotate
154
+
155
+ DESCRIPTION
156
+ Exchanges your current pv_k_ key for a new one atomically. The old key is
157
+ invalidated immediately. The new key is written to ~/.provar/credentials.json.
158
+
159
+ Use this to rotate your key on a regular schedule (~every 90 days) without
160
+ going through the browser login flow. If your current key is already expired,
161
+ run sf provar auth login instead.
162
+
163
+ EXAMPLES
164
+ Rotate the stored API key:
165
+
166
+ $ sf provar auth rotate
167
+ ```
168
+
169
+ ## `sf provar auth status`
170
+
171
+ Show the current API key configuration and validate it against Quality Hub.
172
+
173
+ ```
174
+ USAGE
175
+ $ sf provar auth status
176
+
177
+ DESCRIPTION
178
+ Reports whether an API key is configured, where it came from (environment variable
179
+ or credentials file), and performs a live check against the Quality Hub API to
180
+ confirm the key is still valid.
181
+
182
+ EXAMPLES
183
+ Check auth status:
184
+
185
+ $ sf provar auth status
186
+ ```
187
+
188
+ ## `sf provar auth clear`
189
+
190
+ Remove the stored API key.
191
+
192
+ ```
193
+ USAGE
194
+ $ sf provar auth clear
195
+
196
+ DESCRIPTION
197
+ Deletes ~/.provar/credentials.json and revokes the key server-side. After clearing,
198
+ the MCP tools fall back to local validation mode. Has no effect if no key is stored.
199
+
200
+ EXAMPLES
201
+ Remove the stored key:
202
+
203
+ $ sf provar auth clear
204
+ ```
205
+
87
206
  ## `sf provar mcp start`
88
207
 
89
208
  Start a local MCP server for Provar tools over stdio transport.
@@ -136,6 +255,11 @@ TOOLS EXPOSED
136
255
  provar.testplan.add-instance — wire a test case into a plan suite by writing a .testinstance file
137
256
  provar.testplan.create-suite — create a new test suite directory with .planitem inside a plan
138
257
  provar.testplan.remove-instance — remove a .testinstance file from a plan suite
258
+ provar.nitrox.discover — discover projects containing NitroX (Hybrid Model) page objects
259
+ provar.nitrox.read — read NitroX .po.json files and return parsed content
260
+ provar.nitrox.validate — validate a NitroX .po.json against schema rules
261
+ provar.nitrox.generate — generate a new NitroX .po.json from a component description
262
+ provar.nitrox.patch — apply a JSON merge-patch to an existing NitroX .po.json file
139
263
 
140
264
  EXAMPLES
141
265
  Start MCP server (accepts stdio connections from Claude Desktop / Cursor):
@@ -0,0 +1,7 @@
1
+ import { SfCommand } from '@salesforce/sf-plugins-core';
2
+ export default class SfProvarAuthClear extends SfCommand<void> {
3
+ static readonly summary: string;
4
+ static readonly description: string;
5
+ static readonly examples: string[];
6
+ run(): Promise<void>;
7
+ }
@@ -0,0 +1,36 @@
1
+ /*
2
+ * Copyright (c) 2024 Provar Limited.
3
+ * All rights reserved.
4
+ * Licensed under the BSD 3-Clause license.
5
+ * For full license text, see LICENSE.md file in the repo root or https://opensource.org/licenses/BSD-3-Clause
6
+ */
7
+ import { SfCommand } from '@salesforce/sf-plugins-core';
8
+ import { Messages } from '@provartesting/provardx-plugins-utils';
9
+ import { clearCredentials, readStoredCredentials } from '../../../services/auth/credentials.js';
10
+ import { qualityHubClient, getQualityHubBaseUrl } from '../../../services/qualityHub/client.js';
11
+ Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
12
+ const messages = Messages.loadMessages('@provartesting/provardx-cli', 'sf.provar.auth.clear');
13
+ export default class SfProvarAuthClear extends SfCommand {
14
+ static summary = messages.getMessage('summary');
15
+ static description = messages.getMessage('description');
16
+ static examples = messages.getMessages('examples');
17
+ async run() {
18
+ const stored = readStoredCredentials();
19
+ if (stored) {
20
+ const baseUrl = getQualityHubBaseUrl();
21
+ try {
22
+ await qualityHubClient.revokeKey(stored.api_key, baseUrl);
23
+ }
24
+ catch {
25
+ this.log(' Note: could not reach Quality Hub to revoke key server-side (offline?).');
26
+ this.log(' The local credentials have been removed — the key may still be valid until it expires.');
27
+ }
28
+ }
29
+ clearCredentials();
30
+ this.log('API key cleared.');
31
+ this.log(' Next validation will use local rules only (structural checks, no quality scoring).');
32
+ this.log(' To reconfigure: sf provar auth login');
33
+ this.log(' For CI/CD: set the PROVAR_API_KEY environment variable.');
34
+ }
35
+ }
36
+ //# sourceMappingURL=clear.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clear.js","sourceRoot":"","sources":["../../../../src/commands/provar/auth/clear.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AACxD,OAAO,EAAE,QAAQ,EAAE,MAAM,uCAAuC,CAAC;AACjE,OAAO,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAC;AAChG,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,wCAAwC,CAAC;AAEhG,QAAQ,CAAC,kCAAkC,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC7D,MAAM,QAAQ,GAAG,QAAQ,CAAC,YAAY,CAAC,6BAA6B,EAAE,sBAAsB,CAAC,CAAC;AAE9F,MAAM,CAAC,OAAO,OAAO,iBAAkB,SAAQ,SAAe;IACrD,MAAM,CAAU,OAAO,GAAG,QAAQ,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;IACzD,MAAM,CAAU,WAAW,GAAG,QAAQ,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;IACjE,MAAM,CAAU,QAAQ,GAAG,QAAQ,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;IAE5D,KAAK,CAAC,GAAG;QACd,MAAM,MAAM,GAAG,qBAAqB,EAAE,CAAC;QACvC,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,OAAO,GAAG,oBAAoB,EAAE,CAAC;YACvC,IAAI,CAAC;gBACH,MAAM,gBAAgB,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC5D,CAAC;YAAC,MAAM,CAAC;gBACP,IAAI,CAAC,GAAG,CAAC,2EAA2E,CAAC,CAAC;gBACtF,IAAI,CAAC,GAAG,CAAC,0FAA0F,CAAC,CAAC;YACvG,CAAC;QACH,CAAC;QAED,gBAAgB,EAAE,CAAC;QACnB,IAAI,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;QAC7B,IAAI,CAAC,GAAG,CAAC,sFAAsF,CAAC,CAAC;QACjG,IAAI,CAAC,GAAG,CAAC,wCAAwC,CAAC,CAAC;QACnD,IAAI,CAAC,GAAG,CAAC,gEAAgE,CAAC,CAAC;IAC7E,CAAC"}
@@ -0,0 +1,10 @@
1
+ import { SfCommand } from '@salesforce/sf-plugins-core';
2
+ export default class SfProvarAuthLogin extends SfCommand<void> {
3
+ static readonly summary: string;
4
+ static readonly description: string;
5
+ static readonly examples: string[];
6
+ static readonly flags: {
7
+ url: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
8
+ };
9
+ run(): Promise<void>;
10
+ }
@@ -0,0 +1,90 @@
1
+ /*
2
+ * Copyright (c) 2024 Provar Limited.
3
+ * All rights reserved.
4
+ * Licensed under the BSD 3-Clause license.
5
+ * For full license text, see LICENSE.md file in the repo root or https://opensource.org/licenses/BSD-3-Clause
6
+ */
7
+ /* eslint-disable camelcase */
8
+ import { Flags, SfCommand } from '@salesforce/sf-plugins-core';
9
+ import { Messages } from '@provartesting/provardx-plugins-utils';
10
+ import { writeCredentials } from '../../../services/auth/credentials.js';
11
+ import { loginFlowClient } from '../../../services/auth/loginFlow.js';
12
+ import { qualityHubClient, getQualityHubBaseUrl, QualityHubAuthError, REQUEST_ACCESS_URL, } from '../../../services/qualityHub/client.js';
13
+ Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
14
+ const messages = Messages.loadMessages('@provartesting/provardx-cli', 'sf.provar.auth.login');
15
+ // Production values bundled at auth handoff (2026-04-11).
16
+ // Override via PROVAR_COGNITO_DOMAIN / PROVAR_COGNITO_CLIENT_ID for non-prod environments.
17
+ const DEFAULT_COGNITO_DOMAIN = 'us-east-1xpfwzwmop.auth.us-east-1.amazoncognito.com';
18
+ const DEFAULT_CLIENT_ID = '29cs1a784r4cervmth8ugbkkri';
19
+ export default class SfProvarAuthLogin extends SfCommand {
20
+ static summary = messages.getMessage('summary');
21
+ static description = messages.getMessage('description');
22
+ static examples = messages.getMessages('examples');
23
+ static flags = {
24
+ url: Flags.string({
25
+ summary: messages.getMessage('flags.url.summary'),
26
+ required: false,
27
+ }),
28
+ };
29
+ async run() {
30
+ const { flags } = await this.parse(SfProvarAuthLogin);
31
+ const cognitoDomain = process.env.PROVAR_COGNITO_DOMAIN ?? DEFAULT_COGNITO_DOMAIN;
32
+ const clientId = process.env.PROVAR_COGNITO_CLIENT_ID ?? DEFAULT_CLIENT_ID;
33
+ const baseUrl = flags.url ?? getQualityHubBaseUrl();
34
+ // ── Step 1: Generate PKCE pair, nonce, and state ───────────────────────
35
+ const { verifier, challenge } = loginFlowClient.generatePkce();
36
+ const nonce = loginFlowClient.generateNonce();
37
+ const state = loginFlowClient.generateState();
38
+ // ── Step 2: Find an available registered callback port ──────────────────
39
+ const port = await loginFlowClient.findAvailablePort();
40
+ const redirectUri = `http://localhost:${port}/callback`;
41
+ // ── Step 3: Build the Cognito authorize URL ────────────────────────────
42
+ const authorizeUrl = new URL(`https://${cognitoDomain}/oauth2/authorize`);
43
+ authorizeUrl.searchParams.set('response_type', 'code');
44
+ authorizeUrl.searchParams.set('client_id', clientId);
45
+ authorizeUrl.searchParams.set('redirect_uri', redirectUri);
46
+ authorizeUrl.searchParams.set('code_challenge', challenge);
47
+ authorizeUrl.searchParams.set('code_challenge_method', 'S256');
48
+ authorizeUrl.searchParams.set('scope', 'openid email aws.cognito.signin.user.admin');
49
+ authorizeUrl.searchParams.set('state', state);
50
+ authorizeUrl.searchParams.set('nonce', nonce);
51
+ // ── Step 4: Open browser and wait for callback ──────────────────────────
52
+ this.log('Opening browser for login...');
53
+ this.log(` If the browser did not open, visit:\n ${authorizeUrl.toString()}`);
54
+ loginFlowClient.openBrowser(authorizeUrl.toString());
55
+ this.log('\nWaiting for authentication... (Ctrl-C to cancel)');
56
+ const authCode = await loginFlowClient.listenForCallback(port, state);
57
+ // ── Step 5: Exchange code for Cognito tokens ────────────────────────────
58
+ const tokens = await loginFlowClient.exchangeCodeForTokens({
59
+ code: authCode,
60
+ redirectUri,
61
+ clientId,
62
+ verifier,
63
+ tokenEndpoint: `https://${cognitoDomain}/oauth2/token`,
64
+ });
65
+ // ── Step 6: Exchange Cognito access token for pv_k_ key ─────────────────
66
+ // Cognito tokens are held in memory only — discarded after this call.
67
+ let keyData;
68
+ try {
69
+ keyData = await qualityHubClient.exchangeTokenForKey(tokens.access_token, baseUrl);
70
+ }
71
+ catch (err) {
72
+ if (err instanceof QualityHubAuthError) {
73
+ this.error(`No Provar MCP account found for this login.\nRequest access at: ${REQUEST_ACCESS_URL}`, {
74
+ exit: 1,
75
+ });
76
+ }
77
+ throw err;
78
+ }
79
+ // ── Step 7: Persist the pv_k_ key ──────────────────────────────────────
80
+ writeCredentials(keyData.api_key, keyData.prefix, 'cognito', {
81
+ username: keyData.username,
82
+ tier: keyData.tier,
83
+ expires_at: keyData.expires_at,
84
+ });
85
+ this.log(`\nAuthenticated as ${keyData.username} (${keyData.tier} tier)`);
86
+ this.log(`API key stored (prefix: ${keyData.prefix}). Valid until ${keyData.expires_at}.`);
87
+ this.log(" Run 'sf provar auth status' to check at any time.");
88
+ }
89
+ }
90
+ //# sourceMappingURL=login.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../../../../src/commands/provar/auth/login.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import { SfCommand } from '@salesforce/sf-plugins-core';
2
+ export default class SfProvarAuthRotate extends SfCommand<void> {
3
+ static readonly summary: string;
4
+ static readonly description: string;
5
+ static readonly examples: string[];
6
+ run(): Promise<void>;
7
+ }
@@ -0,0 +1,42 @@
1
+ /*
2
+ * Copyright (c) 2024 Provar Limited.
3
+ * All rights reserved.
4
+ * Licensed under the BSD 3-Clause license.
5
+ * For full license text, see LICENSE.md file in the repo root or https://opensource.org/licenses/BSD-3-Clause
6
+ */
7
+ /* eslint-disable camelcase */
8
+ import { SfCommand } from '@salesforce/sf-plugins-core';
9
+ import { Messages } from '@provartesting/provardx-plugins-utils';
10
+ import { readStoredCredentials, writeCredentials } from '../../../services/auth/credentials.js';
11
+ import { qualityHubClient, getQualityHubBaseUrl, QualityHubAuthError } from '../../../services/qualityHub/client.js';
12
+ Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
13
+ const messages = Messages.loadMessages('@provartesting/provardx-cli', 'sf.provar.auth.rotate');
14
+ export default class SfProvarAuthRotate extends SfCommand {
15
+ static summary = messages.getMessage('summary');
16
+ static description = messages.getMessage('description');
17
+ static examples = messages.getMessages('examples');
18
+ async run() {
19
+ const stored = readStoredCredentials();
20
+ if (!stored) {
21
+ this.error('No API key stored. Run `sf provar auth login` first.', { exit: 1 });
22
+ }
23
+ const baseUrl = getQualityHubBaseUrl();
24
+ try {
25
+ const keyData = await qualityHubClient.rotateKey(stored.api_key, baseUrl);
26
+ writeCredentials(keyData.api_key, keyData.prefix, 'cognito', {
27
+ username: keyData.username,
28
+ tier: keyData.tier,
29
+ expires_at: keyData.expires_at,
30
+ });
31
+ this.log(`API key rotated (new prefix: ${keyData.prefix}). Valid until ${keyData.expires_at}.`);
32
+ this.log(" Run 'sf provar auth status' to verify.");
33
+ }
34
+ catch (err) {
35
+ if (err instanceof QualityHubAuthError) {
36
+ this.error('Current key is invalid or expired — rotation requires a valid key.\nRun `sf provar auth login` to authenticate via browser and get a fresh key.', { exit: 1 });
37
+ }
38
+ throw err;
39
+ }
40
+ }
41
+ }
42
+ //# sourceMappingURL=rotate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rotate.js","sourceRoot":"","sources":["../../../../src/commands/provar/auth/rotate.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,8BAA8B;AAC9B,OAAO,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AACxD,OAAO,EAAE,QAAQ,EAAE,MAAM,uCAAuC,CAAC;AACjE,OAAO,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;AAChG,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,wCAAwC,CAAC;AAErH,QAAQ,CAAC,kCAAkC,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC7D,MAAM,QAAQ,GAAG,QAAQ,CAAC,YAAY,CAAC,6BAA6B,EAAE,uBAAuB,CAAC,CAAC;AAE/F,MAAM,CAAC,OAAO,OAAO,kBAAmB,SAAQ,SAAe;IACtD,MAAM,CAAU,OAAO,GAAG,QAAQ,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;IACzD,MAAM,CAAU,WAAW,GAAG,QAAQ,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;IACjE,MAAM,CAAU,QAAQ,GAAG,QAAQ,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;IAE5D,KAAK,CAAC,GAAG;QACd,MAAM,MAAM,GAAG,qBAAqB,EAAE,CAAC;QAEvC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,IAAI,CAAC,KAAK,CAAC,sDAAsD,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC;QAClF,CAAC;QAED,MAAM,OAAO,GAAG,oBAAoB,EAAE,CAAC;QAEvC,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,gBAAgB,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC1E,gBAAgB,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE;gBAC3D,QAAQ,EAAE,OAAO,CAAC,QAAQ;gBAC1B,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,UAAU,EAAE,OAAO,CAAC,UAAU;aAC/B,CAAC,CAAC;YACH,IAAI,CAAC,GAAG,CAAC,gCAAgC,OAAO,CAAC,MAAM,kBAAkB,OAAO,CAAC,UAAU,GAAG,CAAC,CAAC;YAChG,IAAI,CAAC,GAAG,CAAC,0CAA0C,CAAC,CAAC;QACvD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,GAAG,YAAY,mBAAmB,EAAE,CAAC;gBACvC,IAAI,CAAC,KAAK,CACR,iJAAiJ,EACjJ,EAAE,IAAI,EAAE,CAAC,EAAE,CACZ,CAAC;YACJ,CAAC;YACD,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { SfCommand } from '@salesforce/sf-plugins-core';
2
+ export default class SfProvarAuthStatus extends SfCommand<void> {
3
+ static readonly summary: string;
4
+ static readonly description: string;
5
+ static readonly examples: string[];
6
+ run(): Promise<void>;
7
+ }
@@ -0,0 +1,107 @@
1
+ /*
2
+ * Copyright (c) 2024 Provar Limited.
3
+ * All rights reserved.
4
+ * Licensed under the BSD 3-Clause license.
5
+ * For full license text, see LICENSE.md file in the repo root or https://opensource.org/licenses/BSD-3-Clause
6
+ */
7
+ /* eslint-disable camelcase */
8
+ import { SfCommand } from '@salesforce/sf-plugins-core';
9
+ import { Messages } from '@provartesting/provardx-plugins-utils';
10
+ import { readStoredCredentials } from '../../../services/auth/credentials.js';
11
+ import { qualityHubClient, getQualityHubBaseUrl, REQUEST_ACCESS_URL } from '../../../services/qualityHub/client.js';
12
+ Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
13
+ const messages = Messages.loadMessages('@provartesting/provardx-cli', 'sf.provar.auth.status');
14
+ export default class SfProvarAuthStatus extends SfCommand {
15
+ static summary = messages.getMessage('summary');
16
+ static description = messages.getMessage('description');
17
+ static examples = messages.getMessages('examples');
18
+ async run() {
19
+ const envKey = process.env.PROVAR_API_KEY?.trim();
20
+ if (envKey) {
21
+ if (!envKey.startsWith('pv_k_')) {
22
+ this.log('Warning: PROVAR_API_KEY is set but invalid (does not start with "pv_k_").');
23
+ this.log(` Value: "${envKey.substring(0, 10)}..." — ignored for API calls.`);
24
+ this.log(' Fix: update PROVAR_API_KEY to a valid pv_k_ key from https://success.provartesting.com');
25
+ this.log('');
26
+ // Fall through to check stored credentials (matches resolveApiKey behaviour)
27
+ }
28
+ else {
29
+ this.log('API key configured');
30
+ this.log(' Source: environment variable (PROVAR_API_KEY)');
31
+ this.log(` Prefix: ${envKey.substring(0, 12)}`);
32
+ this.log('');
33
+ this.log(' Validation mode: Quality Hub API');
34
+ return;
35
+ }
36
+ }
37
+ const stored = readStoredCredentials();
38
+ if (stored) {
39
+ // Best-effort live check — silent fallback to cached values if offline or unconfigured.
40
+ // Does not run for env var keys (CI environments may not have outbound access).
41
+ let liveValid;
42
+ try {
43
+ const live = await qualityHubClient.fetchKeyStatus(stored.api_key, getQualityHubBaseUrl());
44
+ liveValid = live.valid;
45
+ if (live.username)
46
+ stored.username = live.username;
47
+ if (live.tier)
48
+ stored.tier = live.tier;
49
+ if (live.expires_at)
50
+ stored.expires_at = live.expires_at;
51
+ }
52
+ catch {
53
+ // Offline or API not yet configured — use locally cached values
54
+ }
55
+ if (liveValid === false) {
56
+ this.log('API key expired or revoked.');
57
+ this.log(' Source: ~/.provar/credentials.json');
58
+ this.log(` Prefix: ${stored.prefix}`);
59
+ this.log('');
60
+ this.log(' Run: sf provar auth login to refresh your key.');
61
+ return;
62
+ }
63
+ this.log('API key configured');
64
+ this.log(' Source: ~/.provar/credentials.json');
65
+ this.log(` Prefix: ${stored.prefix}`);
66
+ this.log(` Set at: ${stored.set_at}`);
67
+ if (stored.username)
68
+ this.log(` Account: ${stored.username}`);
69
+ if (stored.tier)
70
+ this.log(` Tier: ${stored.tier}`);
71
+ if (stored.expires_at) {
72
+ this.log(` Expires: ${stored.expires_at}`);
73
+ const expiresMs = new Date(stored.expires_at).getTime();
74
+ if (Number.isFinite(expiresMs)) {
75
+ const daysLeft = Math.ceil((expiresMs - Date.now()) / (1000 * 60 * 60 * 24));
76
+ if (daysLeft <= 14 && daysLeft > 0) {
77
+ this.log('');
78
+ this.log(` Warning: API key expires in ${daysLeft} day${daysLeft === 1 ? '' : 's'}.`);
79
+ this.log(" Run 'sf provar auth rotate' now to avoid CI/CD disruption.");
80
+ }
81
+ else if (daysLeft <= 0) {
82
+ this.log('');
83
+ this.log(' Warning: API key has expired. Run: sf provar auth login');
84
+ }
85
+ }
86
+ else {
87
+ this.log('');
88
+ this.log(' Warning: API key expiry timestamp is invalid.');
89
+ }
90
+ }
91
+ this.log('');
92
+ this.log(' Validation mode: Quality Hub API');
93
+ return;
94
+ }
95
+ this.log('No API key configured.');
96
+ this.log('');
97
+ this.log('To enable Quality Hub validation (170 rules):');
98
+ this.log(' Run: sf provar auth login');
99
+ this.log('');
100
+ this.log('For CI/CD: set the PROVAR_API_KEY environment variable.');
101
+ this.log('');
102
+ this.log(`No account? Request access at: ${REQUEST_ACCESS_URL}`);
103
+ this.log('');
104
+ this.log('Validation mode: local only (structural rules, no quality scoring)');
105
+ }
106
+ }
107
+ //# sourceMappingURL=status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.js","sourceRoot":"","sources":["../../../../src/commands/provar/auth/status.ts"],"names":[],"mappings":"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"}