@provartesting/provardx-cli 1.5.0-dev.2 → 1.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +163 -12
- package/bin/mcp-start.js +74 -0
- package/lib/commands/provar/auth/clear.d.ts +7 -0
- package/lib/commands/provar/auth/clear.js +36 -0
- package/lib/commands/provar/auth/clear.js.map +1 -0
- package/lib/commands/provar/auth/login.d.ts +10 -0
- package/lib/commands/provar/auth/login.js +90 -0
- package/lib/commands/provar/auth/login.js.map +1 -0
- package/lib/commands/provar/auth/rotate.d.ts +7 -0
- package/lib/commands/provar/auth/rotate.js +42 -0
- package/lib/commands/provar/auth/rotate.js.map +1 -0
- package/lib/commands/provar/auth/status.d.ts +7 -0
- package/lib/commands/provar/auth/status.js +107 -0
- package/lib/commands/provar/auth/status.js.map +1 -0
- package/lib/commands/provar/mcp/start.d.ts +2 -0
- package/lib/commands/provar/mcp/start.js +14 -1
- package/lib/commands/provar/mcp/start.js.map +1 -1
- package/lib/mcp/docs/NITROX_CATALOG_SOURCE.json +6 -0
- package/lib/mcp/docs/NITROX_COMPONENT_CATALOG.md +2001 -0
- package/lib/mcp/docs/PROVAR_TEST_STEP_REFERENCE.md +1430 -0
- package/lib/mcp/docs/PROVAR_TOOL_GUIDE.md +187 -0
- package/lib/mcp/licensing/algasClient.js +14 -5
- package/lib/mcp/licensing/algasClient.js.map +1 -1
- package/lib/mcp/licensing/ideDetection.d.ts +0 -12
- package/lib/mcp/licensing/ideDetection.js +1 -73
- package/lib/mcp/licensing/ideDetection.js.map +1 -1
- package/lib/mcp/licensing/licenseCache.js +7 -1
- package/lib/mcp/licensing/licenseCache.js.map +1 -1
- package/lib/mcp/licensing/licenseValidator.d.ts +3 -3
- package/lib/mcp/licensing/licenseValidator.js +11 -4
- package/lib/mcp/licensing/licenseValidator.js.map +1 -1
- package/lib/mcp/prompts/guidePrompts.d.ts +4 -0
- package/lib/mcp/prompts/guidePrompts.js +334 -0
- package/lib/mcp/prompts/guidePrompts.js.map +1 -0
- package/lib/mcp/prompts/index.d.ts +2 -0
- package/lib/mcp/prompts/index.js +23 -0
- package/lib/mcp/prompts/index.js.map +1 -0
- package/lib/mcp/prompts/loopPrompts.d.ts +6 -0
- package/lib/mcp/prompts/loopPrompts.js +435 -0
- package/lib/mcp/prompts/loopPrompts.js.map +1 -0
- package/lib/mcp/prompts/migrationPrompts.d.ts +4 -0
- package/lib/mcp/prompts/migrationPrompts.js +207 -0
- package/lib/mcp/prompts/migrationPrompts.js.map +1 -0
- package/lib/mcp/rules/provar_best_practices_rules.json +256 -544
- package/lib/mcp/security/pathPolicy.d.ts +5 -0
- package/lib/mcp/security/pathPolicy.js +58 -3
- package/lib/mcp/security/pathPolicy.js.map +1 -1
- package/lib/mcp/server.d.ts +18 -0
- package/lib/mcp/server.js +232 -19
- package/lib/mcp/server.js.map +1 -1
- package/lib/mcp/tools/antTools.d.ts +15 -0
- package/lib/mcp/tools/antTools.js +369 -170
- package/lib/mcp/tools/antTools.js.map +1 -1
- package/lib/mcp/tools/automationTools.d.ts +18 -8
- package/lib/mcp/tools/automationTools.js +333 -176
- package/lib/mcp/tools/automationTools.js.map +1 -1
- package/lib/mcp/tools/bestPracticesEngine.js +161 -23
- package/lib/mcp/tools/bestPracticesEngine.js.map +1 -1
- package/lib/mcp/tools/connectionTools.d.ts +4 -0
- package/lib/mcp/tools/connectionTools.js +242 -0
- package/lib/mcp/tools/connectionTools.js.map +1 -0
- package/lib/mcp/tools/defectTools.d.ts +1 -1
- package/lib/mcp/tools/defectTools.js +61 -50
- package/lib/mcp/tools/defectTools.js.map +1 -1
- package/lib/mcp/tools/descHelper.d.ts +5 -0
- package/lib/mcp/tools/descHelper.js +14 -0
- package/lib/mcp/tools/descHelper.js.map +1 -0
- package/lib/mcp/tools/hierarchyValidate.d.ts +1 -1
- package/lib/mcp/tools/hierarchyValidate.js +127 -42
- package/lib/mcp/tools/hierarchyValidate.js.map +1 -1
- package/lib/mcp/tools/nitroXTools.d.ts +23 -0
- package/lib/mcp/tools/nitroXTools.js +863 -0
- package/lib/mcp/tools/nitroXTools.js.map +1 -0
- package/lib/mcp/tools/pageObjectGenerate.js +150 -57
- package/lib/mcp/tools/pageObjectGenerate.js.map +1 -1
- package/lib/mcp/tools/pageObjectValidate.js +143 -46
- package/lib/mcp/tools/pageObjectValidate.js.map +1 -1
- package/lib/mcp/tools/projectInspect.js +79 -32
- package/lib/mcp/tools/projectInspect.js.map +1 -1
- package/lib/mcp/tools/projectValidateFromPath.js +185 -58
- package/lib/mcp/tools/projectValidateFromPath.js.map +1 -1
- package/lib/mcp/tools/propertiesTools.d.ts +2 -0
- package/lib/mcp/tools/propertiesTools.js +358 -78
- package/lib/mcp/tools/propertiesTools.js.map +1 -1
- package/lib/mcp/tools/qualityHubApiTools.d.ts +3 -0
- package/lib/mcp/tools/qualityHubApiTools.js +139 -0
- package/lib/mcp/tools/qualityHubApiTools.js.map +1 -0
- package/lib/mcp/tools/qualityHubTools.js +292 -72
- package/lib/mcp/tools/qualityHubTools.js.map +1 -1
- package/lib/mcp/tools/rcaTools.d.ts +3 -2
- package/lib/mcp/tools/rcaTools.js +194 -56
- package/lib/mcp/tools/rcaTools.js.map +1 -1
- package/lib/mcp/tools/sfSpawn.d.ts +25 -3
- package/lib/mcp/tools/sfSpawn.js +154 -6
- package/lib/mcp/tools/sfSpawn.js.map +1 -1
- package/lib/mcp/tools/testCaseGenerate.js +285 -78
- package/lib/mcp/tools/testCaseGenerate.js.map +1 -1
- package/lib/mcp/tools/testCaseStepTools.d.ts +4 -0
- package/lib/mcp/tools/testCaseStepTools.js +244 -0
- package/lib/mcp/tools/testCaseStepTools.js.map +1 -0
- package/lib/mcp/tools/testCaseValidate.d.ts +11 -0
- package/lib/mcp/tools/testCaseValidate.js +381 -46
- package/lib/mcp/tools/testCaseValidate.js.map +1 -1
- package/lib/mcp/tools/testPlanTools.d.ts +1 -0
- package/lib/mcp/tools/testPlanTools.js +316 -59
- package/lib/mcp/tools/testPlanTools.js.map +1 -1
- package/lib/mcp/tools/testPlanValidate.js +114 -23
- package/lib/mcp/tools/testPlanValidate.js.map +1 -1
- package/lib/mcp/tools/testSuiteValidate.js +130 -15
- package/lib/mcp/tools/testSuiteValidate.js.map +1 -1
- package/lib/mcp/update/updateChecker.d.ts +14 -0
- package/lib/mcp/update/updateChecker.js +228 -0
- package/lib/mcp/update/updateChecker.js.map +1 -0
- package/lib/mcp/utils/detailLevel.d.ts +9 -0
- package/lib/mcp/utils/detailLevel.js +20 -0
- package/lib/mcp/utils/detailLevel.js.map +1 -0
- package/lib/mcp/utils/fieldMask.d.ts +17 -0
- package/lib/mcp/utils/fieldMask.js +75 -0
- package/lib/mcp/utils/fieldMask.js.map +1 -0
- package/lib/mcp/utils/tokenMeta.d.ts +40 -0
- package/lib/mcp/utils/tokenMeta.js +90 -0
- package/lib/mcp/utils/tokenMeta.js.map +1 -0
- package/lib/mcp/utils/validationDiff.d.ts +57 -0
- package/lib/mcp/utils/validationDiff.js +191 -0
- package/lib/mcp/utils/validationDiff.js.map +1 -0
- package/lib/mcp/utils/validationScore.d.ts +15 -0
- package/lib/mcp/utils/validationScore.js +31 -0
- package/lib/mcp/utils/validationScore.js.map +1 -0
- package/lib/services/auth/credentials.d.ts +21 -0
- package/lib/services/auth/credentials.js +75 -0
- package/lib/services/auth/credentials.js.map +1 -0
- package/lib/services/auth/loginFlow.d.ts +68 -0
- package/lib/services/auth/loginFlow.js +216 -0
- package/lib/services/auth/loginFlow.js.map +1 -0
- package/lib/services/projectValidation.d.ts +5 -2
- package/lib/services/projectValidation.js +83 -31
- package/lib/services/projectValidation.js.map +1 -1
- package/lib/services/qualityHub/client.d.ts +161 -0
- package/lib/services/qualityHub/client.js +226 -0
- package/lib/services/qualityHub/client.js.map +1 -0
- package/messages/sf.provar.auth.clear.md +16 -0
- package/messages/sf.provar.auth.login.md +31 -0
- package/messages/sf.provar.auth.rotate.md +23 -0
- package/messages/sf.provar.auth.status.md +16 -0
- package/messages/sf.provar.mcp.start.md +83 -48
- package/oclif.manifest.json +325 -28
- package/package.json +35 -12
|
@@ -0,0 +1,191 @@
|
|
|
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 fs from 'node:fs';
|
|
9
|
+
import os from 'node:os';
|
|
10
|
+
import path from 'node:path';
|
|
11
|
+
import { createHash } from 'node:crypto';
|
|
12
|
+
const MAX_RUNS = 20;
|
|
13
|
+
const INDEX_FILE = '.runs.json';
|
|
14
|
+
const DEFAULT_ROOT_NAME = '.provardx';
|
|
15
|
+
const VALIDATION_SUBDIR = 'validation';
|
|
16
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
17
|
+
/** Stable 8-char hash of a string for use in run IDs. */
|
|
18
|
+
function shortHash(input) {
|
|
19
|
+
return createHash('sha1').update(input).digest('hex').slice(0, 8);
|
|
20
|
+
}
|
|
21
|
+
/** Build a unique key for a violation so additions/resolutions can be detected. */
|
|
22
|
+
function violationKey(v) {
|
|
23
|
+
const rule_id = String(v['rule_id'] ?? '');
|
|
24
|
+
const applies_to = Array.isArray(v['applies_to'])
|
|
25
|
+
? v['applies_to'].join(',')
|
|
26
|
+
: String(v['applies_to'] ?? '');
|
|
27
|
+
const message = String(v['message'] ?? '');
|
|
28
|
+
return `${rule_id}||${applies_to}||${message}`;
|
|
29
|
+
}
|
|
30
|
+
function loadIndex(storageDir) {
|
|
31
|
+
const indexPath = path.join(storageDir, INDEX_FILE);
|
|
32
|
+
try {
|
|
33
|
+
return JSON.parse(fs.readFileSync(indexPath, 'utf-8'));
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return { runs: [] };
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function saveIndex(storageDir, index) {
|
|
40
|
+
const indexPath = path.join(storageDir, INDEX_FILE);
|
|
41
|
+
fs.writeFileSync(indexPath, JSON.stringify(index, null, 2), 'utf-8');
|
|
42
|
+
}
|
|
43
|
+
// ── Public API ────────────────────────────────────────────────────────────────
|
|
44
|
+
/**
|
|
45
|
+
* Compute a stable 8-char context hash for a tool + context pair. Used to
|
|
46
|
+
* scope baseline run lookups so that a run_id from context A cannot be diffed
|
|
47
|
+
* against context B (different project, different suite, different file).
|
|
48
|
+
*/
|
|
49
|
+
export function computeContextHash(toolTag, context) {
|
|
50
|
+
return shortHash(`${toolTag}|${context}`);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Resolve the validation storage root for a given tool subdir. Honors the
|
|
54
|
+
* PROVAR_MCP_VALIDATION_DIR env var when set; otherwise falls back to
|
|
55
|
+
* `~/.provardx/validation/<subdir>`. The env override is useful for restricted
|
|
56
|
+
* CI/dev environments where the home directory is read-only or shared.
|
|
57
|
+
*/
|
|
58
|
+
export function resolveValidationDir(subdir) {
|
|
59
|
+
const override = process.env['PROVAR_MCP_VALIDATION_DIR']?.trim();
|
|
60
|
+
if (override)
|
|
61
|
+
return path.join(override, subdir);
|
|
62
|
+
return path.join(os.homedir(), DEFAULT_ROOT_NAME, VALIDATION_SUBDIR, subdir);
|
|
63
|
+
}
|
|
64
|
+
/** Generate a run ID from a context string (e.g. project path or suite name). */
|
|
65
|
+
export function generateRunId(context) {
|
|
66
|
+
const rand = Math.random().toString(36).slice(2, 6);
|
|
67
|
+
return `${Date.now()}-${shortHash(context)}-${rand}`;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Check whether any prior runs exist in the given storage directory.
|
|
71
|
+
* Used by calcNextAction to determine the first-run heuristic.
|
|
72
|
+
*/
|
|
73
|
+
export function hasAnyRun(storageDir) {
|
|
74
|
+
const index = loadIndex(storageDir);
|
|
75
|
+
return index.runs.length > 0;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Save the current violations as a new run in the storage directory.
|
|
79
|
+
* Caps the index at MAX_RUNS by evicting the oldest entry when full.
|
|
80
|
+
* Returns the generated run_id.
|
|
81
|
+
*
|
|
82
|
+
* When `contextHash` is provided, it is recorded alongside the run so that
|
|
83
|
+
* `loadBaselineViolations` can reject a baseline_run_id whose context does
|
|
84
|
+
* not match the calling context (prevents cross-context diffs).
|
|
85
|
+
*/
|
|
86
|
+
export function saveRun(storageDir, runId, violations, contextHash) {
|
|
87
|
+
fs.mkdirSync(storageDir, { recursive: true });
|
|
88
|
+
const filename = `${runId}.json`;
|
|
89
|
+
fs.writeFileSync(path.join(storageDir, filename), JSON.stringify(violations), 'utf-8');
|
|
90
|
+
const index = loadIndex(storageDir);
|
|
91
|
+
index.runs.push({
|
|
92
|
+
run_id: runId,
|
|
93
|
+
timestamp: Date.now(),
|
|
94
|
+
filename,
|
|
95
|
+
...(contextHash ? { context_hash: contextHash } : {}),
|
|
96
|
+
});
|
|
97
|
+
// Evict oldest entries when over the cap
|
|
98
|
+
while (index.runs.length > MAX_RUNS) {
|
|
99
|
+
const evicted = index.runs.shift();
|
|
100
|
+
if (evicted) {
|
|
101
|
+
try {
|
|
102
|
+
fs.unlinkSync(path.join(storageDir, evicted.filename));
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
/* best-effort eviction */
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
saveIndex(storageDir, index);
|
|
110
|
+
return runId;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Load the violations array for a given baseline run ID.
|
|
114
|
+
* Returns null if the run is not found in the index (BASELINE_NOT_FOUND).
|
|
115
|
+
* The filename is looked up from the index only — the run_id itself is never
|
|
116
|
+
* used to construct a file path, preventing path traversal.
|
|
117
|
+
*
|
|
118
|
+
* When `expectedContextHash` is provided, the record's `context_hash` must
|
|
119
|
+
* match. Records without a `context_hash` (written by older versions before
|
|
120
|
+
* H3) are treated as a mismatch and are effectively retired within one or
|
|
121
|
+
* two new runs as the FIFO cap evicts them. This guard prevents diffing a
|
|
122
|
+
* baseline from a different file/suite/project against the current context.
|
|
123
|
+
*/
|
|
124
|
+
export function loadBaselineViolations(storageDir, baselineRunId, expectedContextHash) {
|
|
125
|
+
const index = loadIndex(storageDir);
|
|
126
|
+
const record = index.runs.find((r) => r.run_id === baselineRunId);
|
|
127
|
+
if (!record)
|
|
128
|
+
return null;
|
|
129
|
+
if (expectedContextHash !== undefined && record.context_hash !== expectedContextHash) {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
// Use the filename from the index, not the run_id
|
|
133
|
+
try {
|
|
134
|
+
const content = fs.readFileSync(path.join(storageDir, record.filename), 'utf-8');
|
|
135
|
+
return JSON.parse(content);
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Compute the diff between a baseline and current violations array.
|
|
143
|
+
* Uses (rule_id + applies_to + full message) as the unique key.
|
|
144
|
+
* Duplicate violations (same key, multiple occurrences) are treated as
|
|
145
|
+
* distinct entries — each occurrence is counted separately (multiset semantics).
|
|
146
|
+
*/
|
|
147
|
+
export function computeDiff(baseline, current) {
|
|
148
|
+
// Build multiset counts keyed by violation identity
|
|
149
|
+
const baselineCounts = new Map();
|
|
150
|
+
for (const v of baseline) {
|
|
151
|
+
const key = violationKey(v);
|
|
152
|
+
const entry = baselineCounts.get(key);
|
|
153
|
+
if (entry) {
|
|
154
|
+
entry.count++;
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
baselineCounts.set(key, { count: 1, sample: v });
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
const currentCounts = new Map();
|
|
161
|
+
for (const v of current) {
|
|
162
|
+
const key = violationKey(v);
|
|
163
|
+
const entry = currentCounts.get(key);
|
|
164
|
+
if (entry) {
|
|
165
|
+
entry.count++;
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
currentCounts.set(key, { count: 1, sample: v });
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
const added = [];
|
|
172
|
+
const resolved = [];
|
|
173
|
+
let unchanged_count = 0;
|
|
174
|
+
// Tally additions: occurrences in current that exceed baseline count
|
|
175
|
+
for (const [key, { count: curr, sample }] of currentCounts) {
|
|
176
|
+
const base = baselineCounts.get(key)?.count ?? 0;
|
|
177
|
+
unchanged_count += Math.min(base, curr);
|
|
178
|
+
const addedCount = curr - base;
|
|
179
|
+
for (let i = 0; i < addedCount; i++)
|
|
180
|
+
added.push(sample);
|
|
181
|
+
}
|
|
182
|
+
// Tally resolutions: occurrences in baseline that exceed current count
|
|
183
|
+
for (const [key, { count: base, sample }] of baselineCounts) {
|
|
184
|
+
const curr = currentCounts.get(key)?.count ?? 0;
|
|
185
|
+
const resolvedCount = base - Math.min(base, curr);
|
|
186
|
+
for (let i = 0; i < resolvedCount; i++)
|
|
187
|
+
resolved.push(sample);
|
|
188
|
+
}
|
|
189
|
+
return { added, resolved, unchanged_count };
|
|
190
|
+
}
|
|
191
|
+
//# sourceMappingURL=validationDiff.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validationDiff.js","sourceRoot":"","sources":["../../../src/mcp/utils/validationDiff.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type NextAction = 'stop' | 'fix_and_revalidate' | 'inspect_failures';
|
|
2
|
+
/** Fraction of passing tests expressed as 0–100 integer. Returns 0 when total is 0. */
|
|
3
|
+
export declare function calcCompletenessScore(passing: number, total: number): number;
|
|
4
|
+
/**
|
|
5
|
+
* Recommend what the agent should do next based on the completeness score,
|
|
6
|
+
* remaining violation count, and whether any prior runs exist on disk.
|
|
7
|
+
*
|
|
8
|
+
* - `stop` → score is 100 AND no violations remain
|
|
9
|
+
* - `inspect_failures` → first run (no baseline on disk) — review what's failing before trying to fix
|
|
10
|
+
* - `fix_and_revalidate`→ subsequent run — agent knows the failure set, should fix and re-run
|
|
11
|
+
*
|
|
12
|
+
* The secondary `remainingViolationCount` check prevents `stop` from firing when all
|
|
13
|
+
* tests pass but quality or best-practice violations are still present.
|
|
14
|
+
*/
|
|
15
|
+
export declare function calcNextAction(score: number, hasBaseline: boolean, remainingViolationCount?: number): NextAction;
|
|
@@ -0,0 +1,31 @@
|
|
|
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
|
+
/** Fraction of passing tests expressed as 0–100 integer. Returns 0 when total is 0. */
|
|
8
|
+
export function calcCompletenessScore(passing, total) {
|
|
9
|
+
if (total === 0)
|
|
10
|
+
return 0;
|
|
11
|
+
return Math.round((passing / total) * 100);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Recommend what the agent should do next based on the completeness score,
|
|
15
|
+
* remaining violation count, and whether any prior runs exist on disk.
|
|
16
|
+
*
|
|
17
|
+
* - `stop` → score is 100 AND no violations remain
|
|
18
|
+
* - `inspect_failures` → first run (no baseline on disk) — review what's failing before trying to fix
|
|
19
|
+
* - `fix_and_revalidate`→ subsequent run — agent knows the failure set, should fix and re-run
|
|
20
|
+
*
|
|
21
|
+
* The secondary `remainingViolationCount` check prevents `stop` from firing when all
|
|
22
|
+
* tests pass but quality or best-practice violations are still present.
|
|
23
|
+
*/
|
|
24
|
+
export function calcNextAction(score, hasBaseline, remainingViolationCount = 0) {
|
|
25
|
+
if (score === 100 && remainingViolationCount === 0)
|
|
26
|
+
return 'stop';
|
|
27
|
+
if (!hasBaseline)
|
|
28
|
+
return 'inspect_failures';
|
|
29
|
+
return 'fix_and_revalidate';
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=validationScore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validationScore.js","sourceRoot":"","sources":["../../../src/mcp/utils/validationScore.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,uFAAuF;AACvF,MAAM,UAAU,qBAAqB,CAAC,OAAe,EAAE,KAAa;IAClE,IAAI,KAAK,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IAC1B,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,GAAG,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC;AAC7C,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,cAAc,CAAC,KAAa,EAAE,WAAoB,EAAE,uBAAuB,GAAG,CAAC;IAC7F,IAAI,KAAK,KAAK,GAAG,IAAI,uBAAuB,KAAK,CAAC;QAAE,OAAO,MAAM,CAAC;IAClE,IAAI,CAAC,WAAW;QAAE,OAAO,kBAAkB,CAAC;IAC5C,OAAO,oBAAoB,CAAC;AAC9B,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface StoredCredentials {
|
|
2
|
+
api_key: string;
|
|
3
|
+
prefix: string;
|
|
4
|
+
set_at: string;
|
|
5
|
+
source: 'manual' | 'cognito' | 'salesforce';
|
|
6
|
+
username?: string;
|
|
7
|
+
tier?: string;
|
|
8
|
+
expires_at?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function getCredentialsPath(): string;
|
|
11
|
+
export declare function readStoredCredentials(): StoredCredentials | null;
|
|
12
|
+
export declare function writeCredentials(key: string, prefix: string, source: StoredCredentials['source'], extra?: {
|
|
13
|
+
username?: string;
|
|
14
|
+
tier?: string;
|
|
15
|
+
expires_at?: string;
|
|
16
|
+
}): void;
|
|
17
|
+
export declare function clearCredentials(): void;
|
|
18
|
+
export declare function resolveApiKey(): string | null;
|
|
19
|
+
export declare const credentialsService: {
|
|
20
|
+
resolveApiKey: typeof resolveApiKey;
|
|
21
|
+
};
|
|
@@ -0,0 +1,75 @@
|
|
|
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 fs from 'node:fs';
|
|
9
|
+
import os from 'node:os';
|
|
10
|
+
import path from 'node:path';
|
|
11
|
+
const KEY_PREFIX = 'pv_k_';
|
|
12
|
+
export function getCredentialsPath() {
|
|
13
|
+
return path.join(os.homedir(), '.provar', 'credentials.json');
|
|
14
|
+
}
|
|
15
|
+
export function readStoredCredentials() {
|
|
16
|
+
try {
|
|
17
|
+
const p = getCredentialsPath();
|
|
18
|
+
if (!fs.existsSync(p))
|
|
19
|
+
return null;
|
|
20
|
+
const raw = fs.readFileSync(p, 'utf-8');
|
|
21
|
+
return JSON.parse(raw);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export function writeCredentials(key, prefix, source, extra) {
|
|
28
|
+
if (!key.startsWith(KEY_PREFIX)) {
|
|
29
|
+
throw new Error(`Invalid API key format. Keys must start with "${KEY_PREFIX}".`);
|
|
30
|
+
}
|
|
31
|
+
const p = getCredentialsPath();
|
|
32
|
+
fs.mkdirSync(path.dirname(p), { recursive: true });
|
|
33
|
+
const data = {
|
|
34
|
+
api_key: key,
|
|
35
|
+
prefix,
|
|
36
|
+
set_at: new Date().toISOString(),
|
|
37
|
+
source,
|
|
38
|
+
...(extra?.username ? { username: extra.username } : {}),
|
|
39
|
+
...(extra?.tier ? { tier: extra.tier } : {}),
|
|
40
|
+
...(extra?.expires_at ? { expires_at: extra.expires_at } : {}),
|
|
41
|
+
};
|
|
42
|
+
// mode: 0o600 sets permissions atomically on file creation (POSIX).
|
|
43
|
+
// chmodSync handles re-runs on existing files. Both are no-ops on Windows.
|
|
44
|
+
fs.writeFileSync(p, JSON.stringify(data, null, 2), { encoding: 'utf-8', mode: 0o600 });
|
|
45
|
+
try {
|
|
46
|
+
fs.chmodSync(p, 0o600);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
/* Windows: no file permission model */
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
export function clearCredentials() {
|
|
53
|
+
const p = getCredentialsPath();
|
|
54
|
+
try {
|
|
55
|
+
fs.rmSync(p);
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
const e = err;
|
|
59
|
+
if (e.code !== 'ENOENT')
|
|
60
|
+
throw err;
|
|
61
|
+
// file did not exist — nothing to clear, not an error
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
export function resolveApiKey() {
|
|
65
|
+
const envKey = process.env.PROVAR_API_KEY?.trim();
|
|
66
|
+
if (envKey?.startsWith(KEY_PREFIX))
|
|
67
|
+
return envKey;
|
|
68
|
+
const creds = readStoredCredentials();
|
|
69
|
+
const storedKey = typeof creds?.api_key === 'string' ? creds.api_key.trim() : null;
|
|
70
|
+
if (storedKey?.startsWith(KEY_PREFIX))
|
|
71
|
+
return storedKey;
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
export const credentialsService = { resolveApiKey };
|
|
75
|
+
//# sourceMappingURL=credentials.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credentials.js","sourceRoot":"","sources":["../../../src/services/auth/credentials.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/// <reference types="node" resolution-mode="require"/>
|
|
2
|
+
export declare const CALLBACK_PORTS: number[];
|
|
3
|
+
/**
|
|
4
|
+
* Generate a PKCE code_verifier / code_challenge pair (S256 method, as required by Cognito).
|
|
5
|
+
*/
|
|
6
|
+
export declare function generatePkce(): {
|
|
7
|
+
verifier: string;
|
|
8
|
+
challenge: string;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Generate a random nonce for OIDC replay-attack prevention.
|
|
12
|
+
* Required by the OpenID Connect spec when requesting an id_token.
|
|
13
|
+
*/
|
|
14
|
+
export declare function generateNonce(): string;
|
|
15
|
+
/**
|
|
16
|
+
* Generate a random state value for CSRF protection.
|
|
17
|
+
* Required by Cognito Managed Login even though it is optional per the OAuth 2.0 spec.
|
|
18
|
+
*/
|
|
19
|
+
export declare function generateState(): string;
|
|
20
|
+
/**
|
|
21
|
+
* Try each registered callback port in order; return the first that is free.
|
|
22
|
+
*/
|
|
23
|
+
export declare function findAvailablePort(): Promise<number>;
|
|
24
|
+
/**
|
|
25
|
+
* Return the platform-specific command and argument list for opening a URL
|
|
26
|
+
* in the system browser. The URL is passed as an argument — never interpolated
|
|
27
|
+
* into a shell string — to avoid command injection. Exported so tests can
|
|
28
|
+
* assert the correct command is chosen without actually spawning a process.
|
|
29
|
+
*/
|
|
30
|
+
export declare function getBrowserCommand(url: string, platform?: NodeJS.Platform): {
|
|
31
|
+
cmd: string;
|
|
32
|
+
args: string[];
|
|
33
|
+
};
|
|
34
|
+
export declare function openBrowser(url: string): void;
|
|
35
|
+
/**
|
|
36
|
+
* Spin up a temporary localhost HTTP server that accepts exactly one callback
|
|
37
|
+
* from Cognito's Hosted UI, extracts the auth code, and shuts down.
|
|
38
|
+
*/
|
|
39
|
+
export declare function listenForCallback(port: number, expectedState?: string): Promise<string>;
|
|
40
|
+
export interface CognitoTokens {
|
|
41
|
+
access_token: string;
|
|
42
|
+
id_token: string;
|
|
43
|
+
token_type: string;
|
|
44
|
+
expires_in: number;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Exchange a PKCE auth code for Cognito tokens via the standard token endpoint.
|
|
48
|
+
* Uses the Authorization Code + PKCE grant — no client secret required.
|
|
49
|
+
*/
|
|
50
|
+
export declare function exchangeCodeForTokens(opts: {
|
|
51
|
+
code: string;
|
|
52
|
+
redirectUri: string;
|
|
53
|
+
clientId: string;
|
|
54
|
+
verifier: string;
|
|
55
|
+
tokenEndpoint: string;
|
|
56
|
+
}): Promise<CognitoTokens>;
|
|
57
|
+
/**
|
|
58
|
+
* The login command calls loginFlowClient.X() so tests can replace properties with stubs.
|
|
59
|
+
*/
|
|
60
|
+
export declare const loginFlowClient: {
|
|
61
|
+
generatePkce: typeof generatePkce;
|
|
62
|
+
generateNonce: typeof generateNonce;
|
|
63
|
+
generateState: typeof generateState;
|
|
64
|
+
findAvailablePort: typeof findAvailablePort;
|
|
65
|
+
openBrowser: typeof openBrowser;
|
|
66
|
+
listenForCallback: (port: number, expectedState?: string) => Promise<string>;
|
|
67
|
+
exchangeCodeForTokens: typeof exchangeCodeForTokens;
|
|
68
|
+
};
|
|
@@ -0,0 +1,216 @@
|
|
|
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 crypto from 'node:crypto';
|
|
9
|
+
import http from 'node:http';
|
|
10
|
+
import https from 'node:https';
|
|
11
|
+
import { spawn } from 'node:child_process';
|
|
12
|
+
import { URL } from 'node:url';
|
|
13
|
+
// All three ports must be pre-registered in both the Cognito App Client and the Salesforce External Client Application (SF ECA).
|
|
14
|
+
// Both providers require redirect_uri to exactly match a registered callback URL — no wildcards.
|
|
15
|
+
export const CALLBACK_PORTS = [1717, 7890, 8080];
|
|
16
|
+
// ── PKCE ─────────────────────────────────────────────────────────────────────
|
|
17
|
+
/**
|
|
18
|
+
* Generate a PKCE code_verifier / code_challenge pair (S256 method, as required by Cognito).
|
|
19
|
+
*/
|
|
20
|
+
export function generatePkce() {
|
|
21
|
+
const verifier = crypto.randomBytes(32).toString('base64url');
|
|
22
|
+
const challenge = crypto.createHash('sha256').update(verifier).digest('base64url');
|
|
23
|
+
return { verifier, challenge };
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Generate a random nonce for OIDC replay-attack prevention.
|
|
27
|
+
* Required by the OpenID Connect spec when requesting an id_token.
|
|
28
|
+
*/
|
|
29
|
+
export function generateNonce() {
|
|
30
|
+
return crypto.randomBytes(16).toString('base64url');
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Generate a random state value for CSRF protection.
|
|
34
|
+
* Required by Cognito Managed Login even though it is optional per the OAuth 2.0 spec.
|
|
35
|
+
*/
|
|
36
|
+
export function generateState() {
|
|
37
|
+
return crypto.randomBytes(16).toString('base64url');
|
|
38
|
+
}
|
|
39
|
+
// ── Port selection ────────────────────────────────────────────────────────────
|
|
40
|
+
/**
|
|
41
|
+
* Try each registered callback port in order; return the first that is free.
|
|
42
|
+
*/
|
|
43
|
+
export async function findAvailablePort() {
|
|
44
|
+
for (const port of CALLBACK_PORTS) {
|
|
45
|
+
// Sequential by design — we need the first free registered port, not all of them.
|
|
46
|
+
// eslint-disable-next-line no-await-in-loop
|
|
47
|
+
if (await isPortFree(port))
|
|
48
|
+
return port;
|
|
49
|
+
}
|
|
50
|
+
throw new Error('Could not bind to any registered callback port (1717, 7890, 8080). ' +
|
|
51
|
+
'Check that no other process is using these ports and try again.');
|
|
52
|
+
}
|
|
53
|
+
function isPortFree(port) {
|
|
54
|
+
return new Promise((resolve) => {
|
|
55
|
+
const probe = http.createServer();
|
|
56
|
+
probe.once('error', () => resolve(false));
|
|
57
|
+
probe.listen(port, '127.0.0.1', () => {
|
|
58
|
+
probe.close(() => resolve(true));
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
// ── Browser open ──────────────────────────────────────────────────────────────
|
|
63
|
+
/**
|
|
64
|
+
* Return the platform-specific command and argument list for opening a URL
|
|
65
|
+
* in the system browser. The URL is passed as an argument — never interpolated
|
|
66
|
+
* into a shell string — to avoid command injection. Exported so tests can
|
|
67
|
+
* assert the correct command is chosen without actually spawning a process.
|
|
68
|
+
*/
|
|
69
|
+
export function getBrowserCommand(url, platform = process.platform) {
|
|
70
|
+
switch (platform) {
|
|
71
|
+
case 'darwin':
|
|
72
|
+
return { cmd: 'open', args: [url] };
|
|
73
|
+
case 'win32':
|
|
74
|
+
// Pass the URL via $args[0] so it is never interpolated into the -Command
|
|
75
|
+
// string — avoids quote-breaking and injection risk from special characters.
|
|
76
|
+
return { cmd: 'powershell.exe', args: ['-NoProfile', '-Command', 'Start-Process $args[0]', '-args', url] };
|
|
77
|
+
default:
|
|
78
|
+
return { cmd: 'xdg-open', args: [url] };
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
export function openBrowser(url) {
|
|
82
|
+
// detached:true + stdio:'ignore' + unref() is the standard Node.js pattern for
|
|
83
|
+
// fire-and-forget child processes — the event loop will not wait for them to exit.
|
|
84
|
+
const { cmd, args } = getBrowserCommand(url);
|
|
85
|
+
const child = spawn(cmd, args, { detached: true, stdio: 'ignore' });
|
|
86
|
+
// Suppress unhandled-error crashes if the browser executable is not found.
|
|
87
|
+
// The login URL is already printed to the terminal so the user can open it manually.
|
|
88
|
+
child.on('error', () => {
|
|
89
|
+
/* intentional no-op */
|
|
90
|
+
});
|
|
91
|
+
child.unref();
|
|
92
|
+
}
|
|
93
|
+
// ── Localhost callback server ─────────────────────────────────────────────────
|
|
94
|
+
/**
|
|
95
|
+
* Spin up a temporary localhost HTTP server that accepts exactly one callback
|
|
96
|
+
* from Cognito's Hosted UI, extracts the auth code, and shuts down.
|
|
97
|
+
*/
|
|
98
|
+
export function listenForCallback(port, expectedState) {
|
|
99
|
+
return new Promise((resolve, reject) => {
|
|
100
|
+
// Track open sockets so we can forcibly destroy them on shutdown.
|
|
101
|
+
// This is a Node 18.0/18.1 fallback for server.closeAllConnections(), which
|
|
102
|
+
// was added in Node 18.2. Without it a browser that ignores Connection:close
|
|
103
|
+
// could keep the event loop alive after server.close() returns.
|
|
104
|
+
const openSockets = new Set();
|
|
105
|
+
const closeServer = (srv) => {
|
|
106
|
+
srv.close();
|
|
107
|
+
if (typeof srv.closeAllConnections === 'function') {
|
|
108
|
+
srv.closeAllConnections();
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
openSockets.forEach((s) => s.destroy());
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
const server = http.createServer((req, res) => {
|
|
115
|
+
const parsed = new URL(req.url ?? '/', `http://localhost:${port}`);
|
|
116
|
+
const code = parsed.searchParams.get('code');
|
|
117
|
+
const error = parsed.searchParams.get('error');
|
|
118
|
+
const description = parsed.searchParams.get('error_description');
|
|
119
|
+
const callbackState = parsed.searchParams.get('state');
|
|
120
|
+
if (expectedState && callbackState !== expectedState) {
|
|
121
|
+
res.writeHead(400, { 'Content-Type': 'text/html; charset=utf-8', Connection: 'close' });
|
|
122
|
+
res.end('<html><body style="font-family:sans-serif;padding:2rem;max-width:480px">' +
|
|
123
|
+
'<h2 style="color:#c23934">Authentication failed</h2>' +
|
|
124
|
+
'<p>Invalid state parameter — possible CSRF attack. Please try again.</p>' +
|
|
125
|
+
'</body></html>');
|
|
126
|
+
closeServer(server);
|
|
127
|
+
reject(new Error('OAuth callback state mismatch — possible CSRF. Try again.'));
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
// 'Connection: close' tells the browser to close the TCP connection after
|
|
131
|
+
// this response so server.close() has no lingering keep-alive sockets to
|
|
132
|
+
// wait for, allowing the Node.js event loop to exit promptly.
|
|
133
|
+
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8', Connection: 'close' });
|
|
134
|
+
res.end('<html><body style="font-family:sans-serif;padding:2rem;max-width:480px">' +
|
|
135
|
+
'<h2 style="color:#0070d2">Authentication complete</h2>' +
|
|
136
|
+
'<p>You can close this tab and return to the terminal.</p>' +
|
|
137
|
+
'</body></html>');
|
|
138
|
+
closeServer(server);
|
|
139
|
+
if (code) {
|
|
140
|
+
resolve(code);
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
reject(new Error(description ?? error ?? 'No authorisation code received from identity provider'));
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
server.on('connection', (socket) => {
|
|
147
|
+
openSockets.add(socket);
|
|
148
|
+
socket.once('close', () => openSockets.delete(socket));
|
|
149
|
+
});
|
|
150
|
+
server.listen(port, '127.0.0.1');
|
|
151
|
+
server.on('error', (err) => reject(err));
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Exchange a PKCE auth code for Cognito tokens via the standard token endpoint.
|
|
156
|
+
* Uses the Authorization Code + PKCE grant — no client secret required.
|
|
157
|
+
*/
|
|
158
|
+
export async function exchangeCodeForTokens(opts) {
|
|
159
|
+
const body = new URLSearchParams({
|
|
160
|
+
grant_type: 'authorization_code',
|
|
161
|
+
code: opts.code,
|
|
162
|
+
redirect_uri: opts.redirectUri,
|
|
163
|
+
client_id: opts.clientId,
|
|
164
|
+
code_verifier: opts.verifier,
|
|
165
|
+
}).toString();
|
|
166
|
+
const { status, responseBody } = await httpsPost(opts.tokenEndpoint, body, {
|
|
167
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
168
|
+
});
|
|
169
|
+
if (status !== 200) {
|
|
170
|
+
throw new Error(`Token exchange failed (${status}): ${responseBody}`);
|
|
171
|
+
}
|
|
172
|
+
return JSON.parse(responseBody);
|
|
173
|
+
}
|
|
174
|
+
// ── Internal HTTPS helper ─────────────────────────────────────────────────────
|
|
175
|
+
const REQUEST_TIMEOUT_MS = 30000;
|
|
176
|
+
function httpsPost(url, body, headers) {
|
|
177
|
+
return new Promise((resolve, reject) => {
|
|
178
|
+
const parsed = new URL(url);
|
|
179
|
+
const req = https.request({
|
|
180
|
+
hostname: parsed.hostname,
|
|
181
|
+
port: parsed.port || undefined,
|
|
182
|
+
path: parsed.pathname + parsed.search,
|
|
183
|
+
method: 'POST',
|
|
184
|
+
headers: {
|
|
185
|
+
...headers,
|
|
186
|
+
'Content-Length': Buffer.byteLength(body).toString(),
|
|
187
|
+
},
|
|
188
|
+
}, (res) => {
|
|
189
|
+
let data = '';
|
|
190
|
+
res.on('data', (chunk) => {
|
|
191
|
+
data += chunk.toString('utf-8');
|
|
192
|
+
});
|
|
193
|
+
res.on('end', () => resolve({ status: res.statusCode ?? 0, responseBody: data }));
|
|
194
|
+
});
|
|
195
|
+
req.setTimeout(REQUEST_TIMEOUT_MS, () => {
|
|
196
|
+
req.destroy(new Error(`Token exchange timed out after ${REQUEST_TIMEOUT_MS / 1000}s`));
|
|
197
|
+
});
|
|
198
|
+
req.on('error', reject);
|
|
199
|
+
req.write(body);
|
|
200
|
+
req.end();
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
// ── Indirection object (sinon-stubbable) ──────────────────────────────────────
|
|
204
|
+
/**
|
|
205
|
+
* The login command calls loginFlowClient.X() so tests can replace properties with stubs.
|
|
206
|
+
*/
|
|
207
|
+
export const loginFlowClient = {
|
|
208
|
+
generatePkce,
|
|
209
|
+
generateNonce,
|
|
210
|
+
generateState,
|
|
211
|
+
findAvailablePort,
|
|
212
|
+
openBrowser,
|
|
213
|
+
listenForCallback: listenForCallback,
|
|
214
|
+
exchangeCodeForTokens,
|
|
215
|
+
};
|
|
216
|
+
//# sourceMappingURL=loginFlow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loginFlow.js","sourceRoot":"","sources":["../../../src/services/auth/loginFlow.ts"],"names":[],"mappings":"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"}
|