@provartesting/provardx-cli 1.4.6 → 1.5.0-dev
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 +292 -8
- package/lib/commands/provar/automation/config/validate.js.map +1 -1
- package/lib/commands/provar/automation/project/validate.d.ts +14 -0
- package/lib/commands/provar/automation/project/validate.js +69 -0
- package/lib/commands/provar/automation/project/validate.js.map +1 -0
- package/lib/commands/provar/mcp/start.d.ts +16 -0
- package/lib/commands/provar/mcp/start.js +62 -0
- package/lib/commands/provar/mcp/start.js.map +1 -0
- package/lib/commands/provar/quality-hub/connect.d.ts +5 -0
- package/lib/commands/provar/quality-hub/connect.js +12 -0
- package/lib/commands/provar/quality-hub/connect.js.map +1 -0
- package/lib/commands/provar/quality-hub/display.d.ts +5 -0
- package/lib/commands/provar/quality-hub/display.js +12 -0
- package/lib/commands/provar/quality-hub/display.js.map +1 -0
- package/lib/commands/provar/quality-hub/open.d.ts +5 -0
- package/lib/commands/provar/quality-hub/open.js +12 -0
- package/lib/commands/provar/quality-hub/open.js.map +1 -0
- package/lib/commands/provar/quality-hub/test/run/abort.d.ts +5 -0
- package/lib/commands/provar/quality-hub/test/run/abort.js +12 -0
- package/lib/commands/provar/quality-hub/test/run/abort.js.map +1 -0
- package/lib/commands/provar/quality-hub/test/run/report.d.ts +5 -0
- package/lib/commands/provar/quality-hub/test/run/report.js +12 -0
- package/lib/commands/provar/quality-hub/test/run/report.js.map +1 -0
- package/lib/commands/provar/quality-hub/test/run.d.ts +5 -0
- package/lib/commands/provar/quality-hub/test/run.js +12 -0
- package/lib/commands/provar/quality-hub/test/run.js.map +1 -0
- package/lib/commands/provar/quality-hub/testcase/retrieve.d.ts +5 -0
- package/lib/commands/provar/quality-hub/testcase/retrieve.js +12 -0
- package/lib/commands/provar/quality-hub/testcase/retrieve.js.map +1 -0
- package/lib/mcp/licensing/algasClient.d.ts +19 -0
- package/lib/mcp/licensing/algasClient.js +144 -0
- package/lib/mcp/licensing/algasClient.js.map +1 -0
- package/lib/mcp/licensing/ideDetection.d.ts +34 -0
- package/lib/mcp/licensing/ideDetection.js +179 -0
- package/lib/mcp/licensing/ideDetection.js.map +1 -0
- package/lib/mcp/licensing/index.d.ts +5 -0
- package/lib/mcp/licensing/index.js +10 -0
- package/lib/mcp/licensing/index.js.map +1 -0
- package/lib/mcp/licensing/licenseCache.d.ts +20 -0
- package/lib/mcp/licensing/licenseCache.js +79 -0
- package/lib/mcp/licensing/licenseCache.js.map +1 -0
- package/lib/mcp/licensing/licenseError.d.ts +4 -0
- package/lib/mcp/licensing/licenseError.js +15 -0
- package/lib/mcp/licensing/licenseError.js.map +1 -0
- package/lib/mcp/licensing/licenseValidator.d.ts +33 -0
- package/lib/mcp/licensing/licenseValidator.js +103 -0
- package/lib/mcp/licensing/licenseValidator.js.map +1 -0
- package/lib/mcp/logging/logger.d.ts +7 -0
- package/lib/mcp/logging/logger.js +22 -0
- package/lib/mcp/logging/logger.js.map +1 -0
- package/lib/mcp/rules/page_object_validation_rules.json +344 -0
- package/lib/mcp/rules/provar_best_practices_rules.json +3192 -0
- package/lib/mcp/schemas/common.d.ts +20 -0
- package/lib/mcp/schemas/common.js +16 -0
- package/lib/mcp/schemas/common.js.map +1 -0
- package/lib/mcp/security/pathPolicy.d.ts +14 -0
- package/lib/mcp/security/pathPolicy.js +38 -0
- package/lib/mcp/security/pathPolicy.js.map +1 -0
- package/lib/mcp/server.d.ts +5 -0
- package/lib/mcp/server.js +59 -0
- package/lib/mcp/server.js.map +1 -0
- package/lib/mcp/tools/antTools.d.ts +21 -0
- package/lib/mcp/tools/antTools.js +602 -0
- package/lib/mcp/tools/antTools.js.map +1 -0
- package/lib/mcp/tools/automationTools.d.ts +14 -0
- package/lib/mcp/tools/automationTools.js +386 -0
- package/lib/mcp/tools/automationTools.js.map +1 -0
- package/lib/mcp/tools/bestPracticesEngine.d.ts +30 -0
- package/lib/mcp/tools/bestPracticesEngine.js +632 -0
- package/lib/mcp/tools/bestPracticesEngine.js.map +1 -0
- package/lib/mcp/tools/defectTools.d.ts +15 -0
- package/lib/mcp/tools/defectTools.js +199 -0
- package/lib/mcp/tools/defectTools.js.map +1 -0
- package/lib/mcp/tools/hierarchyValidate.d.ts +139 -0
- package/lib/mcp/tools/hierarchyValidate.js +540 -0
- package/lib/mcp/tools/hierarchyValidate.js.map +1 -0
- package/lib/mcp/tools/pageObjectGenerate.d.ts +3 -0
- package/lib/mcp/tools/pageObjectGenerate.js +153 -0
- package/lib/mcp/tools/pageObjectGenerate.js.map +1 -0
- package/lib/mcp/tools/pageObjectValidate.d.ts +18 -0
- package/lib/mcp/tools/pageObjectValidate.js +420 -0
- package/lib/mcp/tools/pageObjectValidate.js.map +1 -0
- package/lib/mcp/tools/projectInspect.d.ts +3 -0
- package/lib/mcp/tools/projectInspect.js +694 -0
- package/lib/mcp/tools/projectInspect.js.map +1 -0
- package/lib/mcp/tools/projectValidateFromPath.d.ts +3 -0
- package/lib/mcp/tools/projectValidateFromPath.js +153 -0
- package/lib/mcp/tools/projectValidateFromPath.js.map +1 -0
- package/lib/mcp/tools/propertiesTools.d.ts +7 -0
- package/lib/mcp/tools/propertiesTools.js +314 -0
- package/lib/mcp/tools/propertiesTools.js.map +1 -0
- package/lib/mcp/tools/qualityHubTools.d.ts +8 -0
- package/lib/mcp/tools/qualityHubTools.js +178 -0
- package/lib/mcp/tools/qualityHubTools.js.map +1 -0
- package/lib/mcp/tools/rcaTools.d.ts +4 -0
- package/lib/mcp/tools/rcaTools.js +620 -0
- package/lib/mcp/tools/rcaTools.js.map +1 -0
- package/lib/mcp/tools/sfSpawn.d.ts +28 -0
- package/lib/mcp/tools/sfSpawn.js +50 -0
- package/lib/mcp/tools/sfSpawn.js.map +1 -0
- package/lib/mcp/tools/testCaseGenerate.d.ts +3 -0
- package/lib/mcp/tools/testCaseGenerate.js +221 -0
- package/lib/mcp/tools/testCaseGenerate.js.map +1 -0
- package/lib/mcp/tools/testCaseValidate.d.ts +20 -0
- package/lib/mcp/tools/testCaseValidate.js +227 -0
- package/lib/mcp/tools/testCaseValidate.js.map +1 -0
- package/lib/mcp/tools/testPlanTools.d.ts +6 -0
- package/lib/mcp/tools/testPlanTools.js +311 -0
- package/lib/mcp/tools/testPlanTools.js.map +1 -0
- package/lib/mcp/tools/testPlanValidate.d.ts +2 -0
- package/lib/mcp/tools/testPlanValidate.js +75 -0
- package/lib/mcp/tools/testPlanValidate.js.map +1 -0
- package/lib/mcp/tools/testSuiteValidate.d.ts +2 -0
- package/lib/mcp/tools/testSuiteValidate.js +63 -0
- package/lib/mcp/tools/testSuiteValidate.js.map +1 -0
- package/lib/services/projectValidation.d.ts +119 -0
- package/lib/services/projectValidation.js +678 -0
- package/lib/services/projectValidation.js.map +1 -0
- package/messages/sf.provar.automation.project.validate.md +52 -0
- package/messages/sf.provar.mcp.start.md +74 -0
- package/oclif.manifest.json +1298 -1
- package/package.json +29 -15
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report.js","sourceRoot":"","sources":["../../../../../../src/commands/provar/quality-hub/test/run/report.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,oBAAoB,MAAM,wFAAwF,CAAC;AAE1H,MAAM,CAAC,OAAO,OAAO,+BAAgC,SAAQ,oBAAoB;IACxE,MAAM,CAAU,OAAO,GAAG,CAAC,gCAAgC,CAAC,CAAC;IAC7D,MAAM,CAAU,gBAAgB,GAAG,IAAI,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
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 ManagerTestRun from '@provartesting/provardx-plugins-manager/lib/commands/provar/manager/test/run.js';
|
|
8
|
+
export default class SfProvarQualityHubTestRun extends ManagerTestRun {
|
|
9
|
+
static aliases = ['provar:manager:test:run'];
|
|
10
|
+
static deprecateAliases = true;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=run.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../../../../src/commands/provar/quality-hub/test/run.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,cAAc,MAAM,iFAAiF,CAAC;AAE7G,MAAM,CAAC,OAAO,OAAO,yBAA0B,SAAQ,cAAc;IAC5D,MAAM,CAAU,OAAO,GAAG,CAAC,yBAAyB,CAAC,CAAC;IACtD,MAAM,CAAU,gBAAgB,GAAG,IAAI,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import ManagerTestcaseRetrieve from '@provartesting/provardx-plugins-manager/lib/commands/provar/manager/testcase/retrieve.js';
|
|
2
|
+
export default class SfProvarQualityHubTestcaseRetrieve extends ManagerTestcaseRetrieve {
|
|
3
|
+
static readonly aliases: string[];
|
|
4
|
+
static readonly deprecateAliases = true;
|
|
5
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
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 ManagerTestcaseRetrieve from '@provartesting/provardx-plugins-manager/lib/commands/provar/manager/testcase/retrieve.js';
|
|
8
|
+
export default class SfProvarQualityHubTestcaseRetrieve extends ManagerTestcaseRetrieve {
|
|
9
|
+
static aliases = ['provar:manager:testcase:retrieve'];
|
|
10
|
+
static deprecateAliases = true;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=retrieve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retrieve.js","sourceRoot":"","sources":["../../../../../src/commands/provar/quality-hub/testcase/retrieve.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,uBAAuB,MAAM,0FAA0F,CAAC;AAE/H,MAAM,CAAC,OAAO,OAAO,kCAAmC,SAAQ,uBAAuB;IAC9E,MAAM,CAAU,OAAO,GAAG,CAAC,kCAAkC,CAAC,CAAC;IAC/D,MAAM,CAAU,gBAAgB,GAAG,IAAI,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { LicenseType } from './licenseCache.js';
|
|
2
|
+
export interface AlgasResult {
|
|
3
|
+
valid: boolean;
|
|
4
|
+
licenseType: LicenseType;
|
|
5
|
+
expiresAt?: number;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Validate a license key against the Provar tag API.
|
|
9
|
+
*
|
|
10
|
+
* Returns { valid: true, licenseType: 'None' } when the key exists and is not deleted.
|
|
11
|
+
* The licenseType is 'None' because the tag endpoint does not expose the license type;
|
|
12
|
+
* licenseValidator.ts enriches the type via the IDE cross-reference step when possible.
|
|
13
|
+
*/
|
|
14
|
+
export declare function validateKeyWithAlgas(licenseKey: string): Promise<AlgasResult>;
|
|
15
|
+
/**
|
|
16
|
+
* Exposed for testing only — resets the in-memory token cache.
|
|
17
|
+
* Do not call in production code.
|
|
18
|
+
*/
|
|
19
|
+
export declare function resetTokenCacheForTest(): void;
|
|
@@ -0,0 +1,144 @@
|
|
|
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
|
+
/**
|
|
8
|
+
* algasClient.ts — Provar licensing API client.
|
|
9
|
+
*
|
|
10
|
+
* Validates a license key via the Provar tag API:
|
|
11
|
+
* 1. Obtain a short-lived Cognito Bearer token (client credentials flow, cached 50 min).
|
|
12
|
+
* 2. GET /studio/licensing/{licenseKey} — returns tag metadata including `deleted` flag.
|
|
13
|
+
*
|
|
14
|
+
* Validation semantics:
|
|
15
|
+
* - `deleted === false` → key exists in the Provar system and has not been revoked → valid.
|
|
16
|
+
* - `deleted === true` → key has been revoked → invalid.
|
|
17
|
+
* - HTTP 404 → key does not exist in the Provar system → invalid.
|
|
18
|
+
*
|
|
19
|
+
* Note: `validityPeriod` in the tag response is measured from the key's generation /
|
|
20
|
+
* modification date, NOT from now. It cannot be used to determine remaining validity.
|
|
21
|
+
* We rely on the tag's `deleted` flag only.
|
|
22
|
+
*
|
|
23
|
+
* The licenseType returned is 'None' when only the tag API is consulted (the tag endpoint
|
|
24
|
+
* does not expose the type). The IDE cross-reference step in licenseValidator.ts can
|
|
25
|
+
* supply a richer type when the key matches a locally-activated IDE license.
|
|
26
|
+
*/
|
|
27
|
+
import { LicenseError } from './licenseError.js';
|
|
28
|
+
const TOKEN_URL = 'https://prod.provar.cloud/studio/licensingauth/oauth2/token';
|
|
29
|
+
const TAG_BASE_URL = 'https://prod.provar.cloud/studio/licensing';
|
|
30
|
+
/** Cognito tokens expire at 1h; cache for 50 min to allow a margin for clock skew. */
|
|
31
|
+
const TOKEN_TTL_MS = 50 * 60 * 1000;
|
|
32
|
+
const TIMEOUT_MS = 15000;
|
|
33
|
+
function getOAuthCredentials() {
|
|
34
|
+
const clientId = process.env['PROVAR_OAUTH_CLIENT_ID'];
|
|
35
|
+
const clientSecret = process.env['PROVAR_OAUTH_CLIENT_SECRET'];
|
|
36
|
+
if (!clientId || !clientSecret) {
|
|
37
|
+
throw new LicenseError('ALGAS_UNREACHABLE', 'Provar OAuth credentials are not configured.\n' +
|
|
38
|
+
'Set PROVAR_OAUTH_CLIENT_ID and PROVAR_OAUTH_CLIENT_SECRET (see .env.example).');
|
|
39
|
+
}
|
|
40
|
+
return { clientId, clientSecret };
|
|
41
|
+
}
|
|
42
|
+
let cachedToken = null;
|
|
43
|
+
let tokenExpiresAt = 0;
|
|
44
|
+
async function getCognitoToken() {
|
|
45
|
+
if (cachedToken && Date.now() < tokenExpiresAt)
|
|
46
|
+
return cachedToken;
|
|
47
|
+
const { clientId, clientSecret } = getOAuthCredentials();
|
|
48
|
+
const credentials = Buffer.from(`${clientId}:${clientSecret}`).toString('base64');
|
|
49
|
+
const controller = new AbortController();
|
|
50
|
+
const timeout = setTimeout(() => controller.abort(), TIMEOUT_MS);
|
|
51
|
+
try {
|
|
52
|
+
const res = await fetch(TOKEN_URL, {
|
|
53
|
+
method: 'POST',
|
|
54
|
+
headers: {
|
|
55
|
+
Authorization: `Basic ${credentials}`,
|
|
56
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
57
|
+
},
|
|
58
|
+
body: 'grant_type=client_credentials&scope=studio-licensing-api%2Fread',
|
|
59
|
+
signal: controller.signal,
|
|
60
|
+
});
|
|
61
|
+
if (!res.ok) {
|
|
62
|
+
throw new LicenseError('ALGAS_HTTP_ERROR', `Provar licensing auth returned HTTP ${res.status}: ${res.statusText}`);
|
|
63
|
+
}
|
|
64
|
+
const json = (await res.json());
|
|
65
|
+
const token = String(json['access_token'] ?? '');
|
|
66
|
+
if (!token) {
|
|
67
|
+
throw new LicenseError('ALGAS_HTTP_ERROR', 'Empty access_token in Cognito response');
|
|
68
|
+
}
|
|
69
|
+
cachedToken = token;
|
|
70
|
+
tokenExpiresAt = Date.now() + TOKEN_TTL_MS;
|
|
71
|
+
return token;
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
if (err.name === 'AbortError') {
|
|
75
|
+
throw new LicenseError('ALGAS_TIMEOUT', 'Provar licensing auth did not respond within 15s');
|
|
76
|
+
}
|
|
77
|
+
if (err instanceof LicenseError)
|
|
78
|
+
throw err;
|
|
79
|
+
throw new LicenseError('ALGAS_UNREACHABLE', `Could not reach Provar licensing auth: ${err.message}`);
|
|
80
|
+
}
|
|
81
|
+
finally {
|
|
82
|
+
clearTimeout(timeout);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Validate a license key against the Provar tag API.
|
|
87
|
+
*
|
|
88
|
+
* Returns { valid: true, licenseType: 'None' } when the key exists and is not deleted.
|
|
89
|
+
* The licenseType is 'None' because the tag endpoint does not expose the license type;
|
|
90
|
+
* licenseValidator.ts enriches the type via the IDE cross-reference step when possible.
|
|
91
|
+
*/
|
|
92
|
+
export async function validateKeyWithAlgas(licenseKey) {
|
|
93
|
+
const controller = new AbortController();
|
|
94
|
+
const timeout = setTimeout(() => controller.abort(), TIMEOUT_MS);
|
|
95
|
+
try {
|
|
96
|
+
const token = await getCognitoToken();
|
|
97
|
+
let res = await fetch(`${TAG_BASE_URL}/${encodeURIComponent(licenseKey)}`, {
|
|
98
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
99
|
+
signal: controller.signal,
|
|
100
|
+
});
|
|
101
|
+
// 401 means the Cognito token was rotated mid-session — clear the module-level cache
|
|
102
|
+
// and retry exactly once with a fresh token before surfacing an error.
|
|
103
|
+
if (res.status === 401) {
|
|
104
|
+
cachedToken = null;
|
|
105
|
+
tokenExpiresAt = 0;
|
|
106
|
+
const refreshedToken = await getCognitoToken();
|
|
107
|
+
res = await fetch(`${TAG_BASE_URL}/${encodeURIComponent(licenseKey)}`, {
|
|
108
|
+
headers: { Authorization: `Bearer ${refreshedToken}` },
|
|
109
|
+
signal: controller.signal,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
if (res.status === 404) {
|
|
113
|
+
// Key not found in the Provar system — definitively invalid
|
|
114
|
+
return { valid: false, licenseType: 'None' };
|
|
115
|
+
}
|
|
116
|
+
if (!res.ok) {
|
|
117
|
+
throw new LicenseError('ALGAS_HTTP_ERROR', `Provar licensing tag API returned HTTP ${res.status}: ${res.statusText}`);
|
|
118
|
+
}
|
|
119
|
+
const body = (await res.json());
|
|
120
|
+
const deleted = body['deleted'] === true;
|
|
121
|
+
// !deleted = key is present in the system and not revoked
|
|
122
|
+
return { valid: !deleted, licenseType: 'None' };
|
|
123
|
+
}
|
|
124
|
+
catch (err) {
|
|
125
|
+
if (err.name === 'AbortError') {
|
|
126
|
+
throw new LicenseError('ALGAS_TIMEOUT', 'Provar licensing tag API did not respond within 15s');
|
|
127
|
+
}
|
|
128
|
+
if (err instanceof LicenseError)
|
|
129
|
+
throw err;
|
|
130
|
+
throw new LicenseError('ALGAS_UNREACHABLE', `Could not reach Provar licensing API: ${err.message}`);
|
|
131
|
+
}
|
|
132
|
+
finally {
|
|
133
|
+
clearTimeout(timeout);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Exposed for testing only — resets the in-memory token cache.
|
|
138
|
+
* Do not call in production code.
|
|
139
|
+
*/
|
|
140
|
+
export function resetTokenCacheForTest() {
|
|
141
|
+
cachedToken = null;
|
|
142
|
+
tokenExpiresAt = 0;
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=algasClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"algasClient.js","sourceRoot":"","sources":["../../../src/mcp/licensing/algasClient.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { LicenseType } from './licenseCache.js';
|
|
2
|
+
export interface IdeLicenseState {
|
|
3
|
+
/** Name of the .properties file (without extension). */
|
|
4
|
+
name: string;
|
|
5
|
+
licenseType: LicenseType;
|
|
6
|
+
activated: boolean;
|
|
7
|
+
/** When the IDE last checked this license online against ALGAS (Unix ms). */
|
|
8
|
+
lastOnlineCheckMs: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Read all .properties files from the Provar IDE license folder and return
|
|
12
|
+
* the activation state of each. Returns an empty array when the folder does
|
|
13
|
+
* not exist or cannot be read.
|
|
14
|
+
*/
|
|
15
|
+
export declare function readIdeLicenses(): IdeLicenseState[];
|
|
16
|
+
/**
|
|
17
|
+
* Find the best activated IDE license, if any.
|
|
18
|
+
*
|
|
19
|
+
* Priority: most recently validated activated license.
|
|
20
|
+
* Returns null when no activated license is present.
|
|
21
|
+
*/
|
|
22
|
+
export declare function findActivatedIdeLicense(): IdeLicenseState | null;
|
|
23
|
+
/**
|
|
24
|
+
* Search all IDE .properties files for one whose decrypted licenseKey matches
|
|
25
|
+
* the supplied raw key string.
|
|
26
|
+
*
|
|
27
|
+
* Used to cross-reference an explicit --license-key against the IDE's already-
|
|
28
|
+
* validated activation state without making a fresh API call.
|
|
29
|
+
*
|
|
30
|
+
* Returns the matching IdeLicenseState (which may have activated=false if the
|
|
31
|
+
* IDE recorded the key but it isn't currently activated), or null when no file
|
|
32
|
+
* decrypts to the given key.
|
|
33
|
+
*/
|
|
34
|
+
export declare function findLicenseByDecryptedKey(rawKey: string): IdeLicenseState | null;
|
|
@@ -0,0 +1,179 @@
|
|
|
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
|
+
/**
|
|
8
|
+
* ideDetection.ts — Read activated Provar IDE license from disk.
|
|
9
|
+
*
|
|
10
|
+
* The Provar IDE stores license state in ~/Provar/.licenses/{name}.properties
|
|
11
|
+
*
|
|
12
|
+
* Fields we care about:
|
|
13
|
+
* - licenseStatus — Activated | NotActivated | Expired | Invalid | QuotaReached
|
|
14
|
+
* - licenseType — Fixed Seat | Floating | Trial | None
|
|
15
|
+
* - lastOnlineAvailabilityCheckUtc — epoch ms of last ALGAS check by the IDE
|
|
16
|
+
* - licenseKey — AES-128-ECB encrypted with key "provarautomation", Base64-encoded
|
|
17
|
+
*
|
|
18
|
+
* The licenseKey field is AES-128-ECB encrypted (LicenseSupport.KEY = "provarautomation").
|
|
19
|
+
* We decrypt it to allow cross-referencing an explicitly supplied --license-key against
|
|
20
|
+
* the IDE's already-validated license state without re-calling the licensing API.
|
|
21
|
+
*/
|
|
22
|
+
import fs from 'node:fs';
|
|
23
|
+
import path from 'node:path';
|
|
24
|
+
import os from 'node:os';
|
|
25
|
+
import { createDecipheriv } from 'node:crypto';
|
|
26
|
+
/** AES-128-ECB key used by Provar IDE to encrypt the licenseKey field. */
|
|
27
|
+
const AES_KEY = Buffer.from('provarautomation'); // 16 bytes ASCII
|
|
28
|
+
/**
|
|
29
|
+
* Decrypt an AES-128-ECB + PKCS5 encrypted, Base64-encoded licenseKey field.
|
|
30
|
+
* Returns the plaintext string, or null if decryption fails (wrong key / corrupt).
|
|
31
|
+
*/
|
|
32
|
+
function decryptLicenseKeyField(encryptedBase64) {
|
|
33
|
+
try {
|
|
34
|
+
const decipher = createDecipheriv('aes-128-ecb', AES_KEY, null);
|
|
35
|
+
const buf = Buffer.from(encryptedBase64, 'base64');
|
|
36
|
+
return Buffer.concat([decipher.update(buf), decipher.final()]).toString('utf-8');
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/** Mirrors License4J's DEFAUT_LICENSE_FOLDER_PATH + PROVAR_USER_HOME. */
|
|
43
|
+
function provarLicensesDir() {
|
|
44
|
+
const provarHome = process.env['PROVAR_HOME'] ?? path.join(os.homedir(), 'Provar');
|
|
45
|
+
return path.join(provarHome, '.licenses');
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Parse a Java .properties file into a key→value map.
|
|
49
|
+
* Only handles simple `key=value` lines; ignores comments and blank lines.
|
|
50
|
+
*/
|
|
51
|
+
function parseProperties(content) {
|
|
52
|
+
const map = new Map();
|
|
53
|
+
for (const line of content.split('\n')) {
|
|
54
|
+
const trimmed = line.trim();
|
|
55
|
+
if (!trimmed || trimmed.startsWith('#') || trimmed.startsWith('!'))
|
|
56
|
+
continue;
|
|
57
|
+
const eqIdx = trimmed.indexOf('=');
|
|
58
|
+
if (eqIdx < 0)
|
|
59
|
+
continue;
|
|
60
|
+
const k = trimmed.slice(0, eqIdx).trim();
|
|
61
|
+
const v = trimmed.slice(eqIdx + 1).trim();
|
|
62
|
+
map.set(k, v);
|
|
63
|
+
}
|
|
64
|
+
return map;
|
|
65
|
+
}
|
|
66
|
+
function parseLicenseType(raw) {
|
|
67
|
+
// Java stores LicenseType.name() = "FixedSeat"; "Fixed Seat" kept for defensive compat.
|
|
68
|
+
if (raw === 'FixedSeat' || raw === 'Fixed Seat')
|
|
69
|
+
return 'FixedSeat';
|
|
70
|
+
if (raw === 'Floating')
|
|
71
|
+
return 'Floating';
|
|
72
|
+
if (raw === 'Trial')
|
|
73
|
+
return 'Trial';
|
|
74
|
+
return 'None';
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Read all .properties files from the Provar IDE license folder and return
|
|
78
|
+
* the activation state of each. Returns an empty array when the folder does
|
|
79
|
+
* not exist or cannot be read.
|
|
80
|
+
*/
|
|
81
|
+
export function readIdeLicenses() {
|
|
82
|
+
const dir = provarLicensesDir();
|
|
83
|
+
if (!fs.existsSync(dir))
|
|
84
|
+
return [];
|
|
85
|
+
const results = [];
|
|
86
|
+
let entries;
|
|
87
|
+
try {
|
|
88
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
return [];
|
|
92
|
+
}
|
|
93
|
+
for (const entry of entries) {
|
|
94
|
+
if (!entry.isFile() || !entry.name.endsWith('.properties'))
|
|
95
|
+
continue;
|
|
96
|
+
try {
|
|
97
|
+
const content = fs.readFileSync(path.join(dir, entry.name), 'utf-8');
|
|
98
|
+
const props = parseProperties(content);
|
|
99
|
+
const licenseStatus = props.get('licenseStatus') ?? '';
|
|
100
|
+
const licenseType = parseLicenseType(props.get('licenseType') ?? '');
|
|
101
|
+
const lastCheck = parseInt(props.get('lastOnlineAvailabilityCheckUtc') ?? '0', 10);
|
|
102
|
+
results.push({
|
|
103
|
+
name: entry.name.slice(0, -'.properties'.length),
|
|
104
|
+
licenseType,
|
|
105
|
+
activated: licenseStatus === 'Activated',
|
|
106
|
+
lastOnlineCheckMs: isNaN(lastCheck) ? 0 : lastCheck,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
// Unreadable file — skip
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return results;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Find the best activated IDE license, if any.
|
|
117
|
+
*
|
|
118
|
+
* Priority: most recently validated activated license.
|
|
119
|
+
* Returns null when no activated license is present.
|
|
120
|
+
*/
|
|
121
|
+
export function findActivatedIdeLicense() {
|
|
122
|
+
const all = readIdeLicenses();
|
|
123
|
+
const activated = all.filter((l) => l.activated);
|
|
124
|
+
if (activated.length === 0)
|
|
125
|
+
return null;
|
|
126
|
+
// Return the one with the most recent online check
|
|
127
|
+
return activated.sort((a, b) => b.lastOnlineCheckMs - a.lastOnlineCheckMs)[0];
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Search all IDE .properties files for one whose decrypted licenseKey matches
|
|
131
|
+
* the supplied raw key string.
|
|
132
|
+
*
|
|
133
|
+
* Used to cross-reference an explicit --license-key against the IDE's already-
|
|
134
|
+
* validated activation state without making a fresh API call.
|
|
135
|
+
*
|
|
136
|
+
* Returns the matching IdeLicenseState (which may have activated=false if the
|
|
137
|
+
* IDE recorded the key but it isn't currently activated), or null when no file
|
|
138
|
+
* decrypts to the given key.
|
|
139
|
+
*/
|
|
140
|
+
export function findLicenseByDecryptedKey(rawKey) {
|
|
141
|
+
const dir = provarLicensesDir();
|
|
142
|
+
if (!fs.existsSync(dir))
|
|
143
|
+
return null;
|
|
144
|
+
let entries;
|
|
145
|
+
try {
|
|
146
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
for (const entry of entries) {
|
|
152
|
+
if (!entry.isFile() || !entry.name.endsWith('.properties'))
|
|
153
|
+
continue;
|
|
154
|
+
try {
|
|
155
|
+
const content = fs.readFileSync(path.join(dir, entry.name), 'utf-8');
|
|
156
|
+
const props = parseProperties(content);
|
|
157
|
+
const encryptedKey = props.get('licenseKey');
|
|
158
|
+
if (!encryptedKey)
|
|
159
|
+
continue;
|
|
160
|
+
const decrypted = decryptLicenseKeyField(encryptedKey);
|
|
161
|
+
if (decrypted !== rawKey)
|
|
162
|
+
continue;
|
|
163
|
+
const licenseStatus = props.get('licenseStatus') ?? '';
|
|
164
|
+
const licenseType = parseLicenseType(props.get('licenseType') ?? '');
|
|
165
|
+
const lastCheck = parseInt(props.get('lastOnlineAvailabilityCheckUtc') ?? '0', 10);
|
|
166
|
+
return {
|
|
167
|
+
name: entry.name.slice(0, -'.properties'.length),
|
|
168
|
+
licenseType,
|
|
169
|
+
activated: licenseStatus === 'Activated',
|
|
170
|
+
lastOnlineCheckMs: isNaN(lastCheck) ? 0 : lastCheck,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
// Unreadable or corrupt file — skip
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
//# sourceMappingURL=ideDetection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ideDetection.js","sourceRoot":"","sources":["../../../src/mcp/licensing/ideDetection.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { LicenseError } from './licenseError.js';
|
|
2
|
+
export { validateLicense } from './licenseValidator.js';
|
|
3
|
+
export type { LicenseValidationResult } from './licenseValidator.js';
|
|
4
|
+
export { findActivatedIdeLicense, readIdeLicenses } from './ideDetection.js';
|
|
5
|
+
export type { IdeLicenseState } from './ideDetection.js';
|
|
@@ -0,0 +1,10 @@
|
|
|
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
|
+
export { LicenseError } from './licenseError.js';
|
|
8
|
+
export { validateLicense } from './licenseValidator.js';
|
|
9
|
+
export { findActivatedIdeLicense, readIdeLicenses } from './ideDetection.js';
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/mcp/licensing/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExD,OAAO,EAAE,uBAAuB,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type LicenseType = 'Trial' | 'Floating' | 'FixedSeat' | 'None' | 'Whitelisted';
|
|
2
|
+
export interface CacheEntry {
|
|
3
|
+
keyHash: string;
|
|
4
|
+
valid: boolean;
|
|
5
|
+
licenseType: LicenseType;
|
|
6
|
+
checkedAt: number;
|
|
7
|
+
expiresAt?: number;
|
|
8
|
+
}
|
|
9
|
+
/** Re-validate against ALGAS after 2 hours — mirrors License4J's online check interval. */
|
|
10
|
+
export declare const ONLINE_TTL_MS: number;
|
|
11
|
+
/** Allow cached result for up to 48 hours when ALGAS is unreachable. */
|
|
12
|
+
export declare const OFFLINE_GRACE_MS: number;
|
|
13
|
+
/** Hash the raw license key so we never store it on disk. */
|
|
14
|
+
export declare function hashKey(licenseKey: string): string;
|
|
15
|
+
export declare function readCacheEntry(keyHash: string): CacheEntry | null;
|
|
16
|
+
export declare function writeCacheEntry(entry: CacheEntry): void;
|
|
17
|
+
/** True when the cached entry is fresh enough to skip the next ALGAS call. */
|
|
18
|
+
export declare function isCacheEntryFresh(entry: CacheEntry): boolean;
|
|
19
|
+
/** True when the cached entry is within the 48-hour offline grace window. */
|
|
20
|
+
export declare function isCacheEntryWithinGrace(entry: CacheEntry): boolean;
|
|
@@ -0,0 +1,79 @@
|
|
|
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 fs from 'node:fs';
|
|
8
|
+
import path from 'node:path';
|
|
9
|
+
import os from 'node:os';
|
|
10
|
+
import { createHash } from 'node:crypto';
|
|
11
|
+
/**
|
|
12
|
+
* Store the MCP license cache alongside the Provar IDE's own license folder
|
|
13
|
+
* (~/Provar/.licenses/) so there is a single authoritative location for all
|
|
14
|
+
* Provar license state on the machine. This also enables auto-detection when
|
|
15
|
+
* a user switches from Provar Automation IDE to Claude + MCP.
|
|
16
|
+
*
|
|
17
|
+
* Computed lazily so tests can redirect HOME/USERPROFILE before calling any
|
|
18
|
+
* cache function without the path being baked in at module-load time.
|
|
19
|
+
*/
|
|
20
|
+
function cacheDir() {
|
|
21
|
+
const provarHome = process.env['PROVAR_HOME'] ?? path.join(os.homedir(), 'Provar');
|
|
22
|
+
return path.join(provarHome, '.licenses');
|
|
23
|
+
}
|
|
24
|
+
function cacheFile() {
|
|
25
|
+
return path.join(cacheDir(), '.mcp-license-cache.json');
|
|
26
|
+
}
|
|
27
|
+
/** Re-validate against ALGAS after 2 hours — mirrors License4J's online check interval. */
|
|
28
|
+
export const ONLINE_TTL_MS = 2 * 60 * 60 * 1000;
|
|
29
|
+
/** Allow cached result for up to 48 hours when ALGAS is unreachable. */
|
|
30
|
+
export const OFFLINE_GRACE_MS = 48 * 60 * 60 * 1000;
|
|
31
|
+
/** Hash the raw license key so we never store it on disk. */
|
|
32
|
+
export function hashKey(licenseKey) {
|
|
33
|
+
return createHash('sha256').update(licenseKey).digest('hex');
|
|
34
|
+
}
|
|
35
|
+
export function readCacheEntry(keyHash) {
|
|
36
|
+
try {
|
|
37
|
+
const file = cacheFile();
|
|
38
|
+
if (!fs.existsSync(file))
|
|
39
|
+
return null;
|
|
40
|
+
const raw = fs.readFileSync(file, 'utf-8');
|
|
41
|
+
const cache = JSON.parse(raw);
|
|
42
|
+
return cache[keyHash] ?? null;
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
export function writeCacheEntry(entry) {
|
|
49
|
+
try {
|
|
50
|
+
const dir = cacheDir();
|
|
51
|
+
const file = cacheFile();
|
|
52
|
+
if (!fs.existsSync(dir)) {
|
|
53
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
54
|
+
}
|
|
55
|
+
let cache = {};
|
|
56
|
+
if (fs.existsSync(file)) {
|
|
57
|
+
try {
|
|
58
|
+
cache = JSON.parse(fs.readFileSync(file, 'utf-8'));
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
cache = {};
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
cache[entry.keyHash] = entry;
|
|
65
|
+
fs.writeFileSync(file, JSON.stringify(cache, null, 2), 'utf-8');
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
// Cache write failure is non-fatal; validation result still returned to caller.
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/** True when the cached entry is fresh enough to skip the next ALGAS call. */
|
|
72
|
+
export function isCacheEntryFresh(entry) {
|
|
73
|
+
return Date.now() - entry.checkedAt < ONLINE_TTL_MS;
|
|
74
|
+
}
|
|
75
|
+
/** True when the cached entry is within the 48-hour offline grace window. */
|
|
76
|
+
export function isCacheEntryWithinGrace(entry) {
|
|
77
|
+
return Date.now() - entry.checkedAt < OFFLINE_GRACE_MS;
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=licenseCache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"licenseCache.js","sourceRoot":"","sources":["../../../src/mcp/licensing/licenseCache.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
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
|
+
export class LicenseError extends Error {
|
|
8
|
+
code;
|
|
9
|
+
constructor(code, message) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = 'LicenseError';
|
|
12
|
+
this.code = code;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=licenseError.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"licenseError.js","sourceRoot":"","sources":["../../../src/mcp/licensing/licenseError.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,OAAO,YAAa,SAAQ,KAAK;IACrB,IAAI,CAAS;IAE7B,YAAmB,IAAY,EAAE,OAAe;QAC9C,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;QAC3B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type LicenseType } from './licenseCache.js';
|
|
2
|
+
export interface LicenseValidationResult {
|
|
3
|
+
valid: boolean;
|
|
4
|
+
licenseType: LicenseType;
|
|
5
|
+
/** True when the result came from disk cache rather than a live ALGAS call. */
|
|
6
|
+
fromCache: boolean;
|
|
7
|
+
/** True when ALGAS was unreachable and we fell back to the offline grace window. */
|
|
8
|
+
offlineGrace: boolean;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Validate the Provar license before starting the MCP server.
|
|
12
|
+
*
|
|
13
|
+
* Requires Provar Automation IDE to be installed with an activated licence.
|
|
14
|
+
* We trust the IDE's own licenseStatus field — if it says "Activated" we
|
|
15
|
+
* accept it. The IDE is responsible for setting licenseStatus to "Expired"
|
|
16
|
+
* or "Invalid" when a licence lapses; we do not re-check timing ourselves.
|
|
17
|
+
*
|
|
18
|
+
* The result is cached so repeated starts within 2 hours skip the IDE file
|
|
19
|
+
* read entirely. The 48-hour grace window lets the MCP server keep running
|
|
20
|
+
* when the IDE files become temporarily inaccessible (e.g. network share,
|
|
21
|
+
* permission change) after a successful read.
|
|
22
|
+
*
|
|
23
|
+
* Validation flow:
|
|
24
|
+
* 1. MCP cache fresh (< 2h) → serve from cache, skip IDE read
|
|
25
|
+
* 2. Scan ~/Provar/.licenses/*.properties for an Activated licence
|
|
26
|
+
* 3. Found → write cache, accept (offlineGrace=false)
|
|
27
|
+
* 4. Not found but MCP cache within 48h grace → serve, offlineGrace=true
|
|
28
|
+
* 5. Not found, no usable cache → throw LICENSE_NOT_FOUND
|
|
29
|
+
*
|
|
30
|
+
* When NODE_ENV=test this function returns immediately so unit tests never
|
|
31
|
+
* touch the filesystem.
|
|
32
|
+
*/
|
|
33
|
+
export declare function validateLicense(): Promise<LicenseValidationResult>;
|