fraim 2.0.326 → 2.0.328
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/dist/src/api/payment/webhook.js +7 -6
- package/dist/src/cli/commands/first-run.js +1 -1
- package/dist/src/cli/commands/sync.js +11 -2
- package/dist/src/cli/doctor/checks/fraim-mcp-diagnostics.js +17 -10
- package/dist/src/cli/doctor/checks/mcp-connectivity-checks.js +20 -2
- package/dist/src/cli/mcp/fraim-mcp-latest-launcher.js +200 -130
- package/dist/src/cli/mcp/mcp-server-registry.js +1 -6
- package/dist/src/cli/utils/managed-agent-install.js +5 -1
- package/dist/src/cli/utils/managed-agent-paths.js +14 -3
- package/dist/src/cli/utils/managed-node-runtime.js +22 -19
- package/dist/src/cli/utils/persist-shell-path.js +121 -0
- package/dist/src/core/utils/redact-secrets.js +89 -0
- package/dist/src/first-run/session-service.js +25 -145
- package/dist/src/local-mcp-server/stdio-server.js +19 -6
- package/dist/src/middleware/auth.js +4 -3
- package/dist/src/middleware/logger.js +7 -24
- package/dist/src/middleware/telemetry.js +2 -1
- package/dist/src/services/installer-service.js +5 -47
- package/dist/src/services/session-service.js +2 -1
- package/package.json +1 -1
|
@@ -62,6 +62,7 @@ const crypto_1 = require("crypto");
|
|
|
62
62
|
const axios_1 = __importDefault(require("axios"));
|
|
63
63
|
const provider_utils_1 = require("../core/utils/provider-utils");
|
|
64
64
|
const object_utils_1 = require("../core/utils/object-utils");
|
|
65
|
+
const redact_secrets_1 = require("../core/utils/redact-secrets");
|
|
65
66
|
const pack_home_1 = require("../cli/utils/pack-home");
|
|
66
67
|
const local_registry_resolver_1 = require("../core/utils/local-registry-resolver");
|
|
67
68
|
const ai_mentor_1 = require("../core/ai-mentor");
|
|
@@ -553,7 +554,7 @@ class FraimLocalMCPServer {
|
|
|
553
554
|
}
|
|
554
555
|
this.log('🚀 FRAIM Local MCP Server starting... [DEBUG-PROXY-V3]');
|
|
555
556
|
this.log(`📡 Remote server: ${this.remoteUrl}`);
|
|
556
|
-
this.log(`🔑 API key: ${
|
|
557
|
+
this.log(`🔑 API key: ${(0, redact_secrets_1.redactApiKey)(this.apiKey)}`);
|
|
557
558
|
this.log(`Local MCP version: ${this.localVersion}`);
|
|
558
559
|
this.log(`🔍 DEBUG BUILD: Machine detection v2 active`);
|
|
559
560
|
// Initialize usage collector
|
|
@@ -626,23 +627,35 @@ class FraimLocalMCPServer {
|
|
|
626
627
|
this.log(`Could not persist userEmail: ${e instanceof Error ? e.message : String(e)}`);
|
|
627
628
|
}
|
|
628
629
|
}
|
|
630
|
+
/**
|
|
631
|
+
* Non-reversible reference for this proxy's key (issue #1701). Every line the
|
|
632
|
+
* proxy writes carries it, and the proxy mirrors those lines into a temp file
|
|
633
|
+
* that survives the process, so the key itself must never appear here. The
|
|
634
|
+
* reference matches the one the server logs for the same key, so a user's local
|
|
635
|
+
* log still correlates with the server-side log.
|
|
636
|
+
*/
|
|
637
|
+
keyRef() {
|
|
638
|
+
return this.apiKey ? (0, redact_secrets_1.redactApiKey)(this.apiKey) : 'MISSING_API_KEY';
|
|
639
|
+
}
|
|
629
640
|
log(message) {
|
|
630
641
|
// Log to stderr (stdout is reserved for MCP protocol)
|
|
631
|
-
const
|
|
632
|
-
|
|
642
|
+
const keyRef = this.keyRef();
|
|
643
|
+
// Messages are assembled from remote responses and caught errors, either of
|
|
644
|
+
// which can carry a credential this proxy did not author.
|
|
645
|
+
const safeMessage = (0, redact_secrets_1.redactSecretsInText)(message);
|
|
646
|
+
console.error(`[FRAIM key:${keyRef}] ${safeMessage}`);
|
|
633
647
|
// Also log to file for debugging
|
|
634
648
|
try {
|
|
635
649
|
const fs = require('fs');
|
|
636
650
|
const logFile = require('path').join(require('os').tmpdir(), 'fraim-mcp-proxy.log');
|
|
637
|
-
fs.appendFileSync(logFile, `${new Date().toISOString()} [${
|
|
651
|
+
fs.appendFileSync(logFile, `${new Date().toISOString()} [${keyRef}] ${safeMessage}\n`);
|
|
638
652
|
}
|
|
639
653
|
catch (e) {
|
|
640
654
|
// Ignore file logging errors
|
|
641
655
|
}
|
|
642
656
|
}
|
|
643
657
|
logError(message) {
|
|
644
|
-
|
|
645
|
-
console.error(`[FRAIM ERROR key:${key}] ${message}`);
|
|
658
|
+
console.error(`[FRAIM ERROR key:${this.keyRef()}] ${(0, redact_secrets_1.redactSecretsInText)(message)}`);
|
|
646
659
|
}
|
|
647
660
|
detectLocalVersion() {
|
|
648
661
|
const candidates = [
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.AuthMiddleware = void 0;
|
|
4
|
+
const redact_secrets_1 = require("../core/utils/redact-secrets");
|
|
4
5
|
const fs_1 = require("fs");
|
|
5
6
|
const path_1 = require("path");
|
|
6
7
|
const cookie_service_1 = require("../services/cookie-service");
|
|
@@ -219,7 +220,7 @@ class AuthMiddleware {
|
|
|
219
220
|
try {
|
|
220
221
|
const apiKeyData = await this.dbService.verifyApiKey(apiKey);
|
|
221
222
|
if (!apiKeyData) {
|
|
222
|
-
console.error(`❌ FRAIM AUTH: Invalid API key: ${apiKey}`);
|
|
223
|
+
console.error(`❌ FRAIM AUTH: Invalid API key: ${(0, redact_secrets_1.redactApiKey)(apiKey)}`);
|
|
223
224
|
setMcpAuthenticateChallenge();
|
|
224
225
|
return res.status(401).json({
|
|
225
226
|
jsonrpc: '2.0',
|
|
@@ -230,7 +231,7 @@ class AuthMiddleware {
|
|
|
230
231
|
// Check if key is suspended
|
|
231
232
|
if (apiKeyData.status === 'suspended') {
|
|
232
233
|
const reason = apiKeyData.suspensionReason || 'payment failure';
|
|
233
|
-
console.error(`❌ FRAIM AUTH: Suspended API key: ${apiKey} (reason: ${reason})`);
|
|
234
|
+
console.error(`❌ FRAIM AUTH: Suspended API key: ${(0, redact_secrets_1.redactApiKey)(apiKey)} (reason: ${reason})`);
|
|
234
235
|
return res.status(402).json({
|
|
235
236
|
jsonrpc: '2.0',
|
|
236
237
|
error: {
|
|
@@ -255,7 +256,7 @@ class AuthMiddleware {
|
|
|
255
256
|
const message = tier === 'trial'
|
|
256
257
|
? 'Your 14-day free trial has expired. Please upgrade to continue using FRAIM.'
|
|
257
258
|
: 'Your subscription has expired. Please renew to continue using FRAIM.';
|
|
258
|
-
console.error(`❌ FRAIM AUTH: Expired API key: ${apiKey} (tier: ${tier})`);
|
|
259
|
+
console.error(`❌ FRAIM AUTH: Expired API key: ${(0, redact_secrets_1.redactApiKey)(apiKey)} (tier: ${tier})`);
|
|
259
260
|
return res.status(402).json({
|
|
260
261
|
jsonrpc: '2.0',
|
|
261
262
|
error: {
|
|
@@ -35,38 +35,21 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.requestLogger = requestLogger;
|
|
37
37
|
const crypto_1 = require("crypto");
|
|
38
|
+
const redact_secrets_1 = require("../core/utils/redact-secrets");
|
|
38
39
|
const fs = __importStar(require("fs"));
|
|
39
40
|
const path = __importStar(require("path"));
|
|
40
41
|
const LOG_FILE = path.join(process.cwd(), 'server-direct.log');
|
|
41
|
-
/**
|
|
42
|
-
* Extracts API key from request for logging purposes
|
|
43
|
-
*/
|
|
44
|
-
function getApiKeyForLogging(req) {
|
|
45
|
-
const fromContext = req.apiKeyData?.key;
|
|
46
|
-
if (typeof fromContext === 'string' && fromContext.trim().length > 0) {
|
|
47
|
-
return fromContext;
|
|
48
|
-
}
|
|
49
|
-
const fromHeader = Array.isArray(req.headers['x-api-key'])
|
|
50
|
-
? req.headers['x-api-key'][0]
|
|
51
|
-
: req.headers['x-api-key'];
|
|
52
|
-
if (typeof fromHeader === 'string' && fromHeader.trim().length > 0) {
|
|
53
|
-
return fromHeader;
|
|
54
|
-
}
|
|
55
|
-
const fromQuery = req.query?.['api-key'];
|
|
56
|
-
if (typeof fromQuery === 'string' && fromQuery.trim().length > 0) {
|
|
57
|
-
return fromQuery;
|
|
58
|
-
}
|
|
59
|
-
return 'ANONYMOUS';
|
|
60
|
-
}
|
|
61
42
|
function getBodyForLogging(req) {
|
|
62
43
|
if (req.path === '/api/ai-hub/brand' && req.body && typeof req.body === 'object') {
|
|
63
44
|
const body = { ...req.body };
|
|
64
45
|
if (typeof body.logo === 'string' && body.logo.length > 0) {
|
|
65
46
|
body.logo = `[redacted logo payload, ${body.logo.length} chars]`;
|
|
66
47
|
}
|
|
67
|
-
return JSON.stringify(body);
|
|
48
|
+
return (0, redact_secrets_1.redactSecretsInText)(JSON.stringify(body));
|
|
68
49
|
}
|
|
69
|
-
|
|
50
|
+
// Bodies are logged verbatim; a caller that puts a credential in one must not
|
|
51
|
+
// have it land in the log in the clear (issue #1701).
|
|
52
|
+
return (0, redact_secrets_1.redactSecretsInText)(JSON.stringify(req.body));
|
|
70
53
|
}
|
|
71
54
|
/**
|
|
72
55
|
* Middleware to log all requests
|
|
@@ -118,8 +101,8 @@ function requestLogger(req, res, next) {
|
|
|
118
101
|
res.on('finish', () => {
|
|
119
102
|
const duration = Date.now() - start;
|
|
120
103
|
const status = res.statusCode;
|
|
121
|
-
const
|
|
122
|
-
const logMsg = `[req:${requestId}] [local:${localMcpVersion}] ${
|
|
104
|
+
const keyRef = (0, redact_secrets_1.apiKeyRefFromRequest)(req);
|
|
105
|
+
const logMsg = `[req:${requestId}] [local:${localMcpVersion}] ${keyRef} END ${method} ${path} ${status} ${duration}ms`;
|
|
123
106
|
logEntry(logMsg);
|
|
124
107
|
});
|
|
125
108
|
next();
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.TelemetryMiddleware = void 0;
|
|
4
|
+
const redact_secrets_1 = require("../core/utils/redact-secrets");
|
|
4
5
|
class TelemetryMiddleware {
|
|
5
6
|
constructor(sessionManager) {
|
|
6
7
|
this.sessionManager = sessionManager;
|
|
@@ -125,7 +126,7 @@ After successful fraim_connect, all other FRAIM tools will work. This is require
|
|
|
125
126
|
id: req.body?.id || null
|
|
126
127
|
});
|
|
127
128
|
}
|
|
128
|
-
console.log(`Telemetry active for ${apiKey}`);
|
|
129
|
+
console.log(`Telemetry active for ${(0, redact_secrets_1.redactApiKey)(apiKey)}`);
|
|
129
130
|
return next();
|
|
130
131
|
}
|
|
131
132
|
catch (error) {
|
|
@@ -1,26 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* InstallerService - Generates
|
|
4
|
-
* Scripts are loaded from templates in scripts/installer/ and populated with token/baseUrl.
|
|
3
|
+
* InstallerService - Generates dashboard HTML for the Get Started flow.
|
|
5
4
|
*/
|
|
6
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
6
|
exports.InstallerService = void 0;
|
|
8
|
-
const fs_1 = require("fs");
|
|
9
|
-
const path_1 = require("path");
|
|
10
|
-
function getScriptsDir() {
|
|
11
|
-
// When running from dist: dist/services/ -> dist/../scripts = scripts
|
|
12
|
-
// When running from src with tsx: src/services/ -> src/../scripts = scripts
|
|
13
|
-
const candidates = [
|
|
14
|
-
(0, path_1.join)(process.cwd(), 'scripts', 'installer'),
|
|
15
|
-
(0, path_1.join)(__dirname, '..', '..', 'scripts', 'installer'),
|
|
16
|
-
(0, path_1.join)(__dirname, '..', '..', '..', 'scripts', 'installer')
|
|
17
|
-
];
|
|
18
|
-
for (const dir of candidates) {
|
|
19
|
-
if ((0, fs_1.existsSync)(dir))
|
|
20
|
-
return dir;
|
|
21
|
-
}
|
|
22
|
-
return null;
|
|
23
|
-
}
|
|
24
7
|
/**
|
|
25
8
|
* Default marketing site base URL. All asset references and nav links in the
|
|
26
9
|
* layout shell resolve against this host. Overridable via FRAIM_MARKETING_URL.
|
|
@@ -55,15 +38,6 @@ function buildLayoutShell(marketingBaseUrl) {
|
|
|
55
38
|
<script src="${m}/js/script.js"></script></body></html>`;
|
|
56
39
|
}
|
|
57
40
|
class InstallerService {
|
|
58
|
-
constructor() {
|
|
59
|
-
this.scriptsDir = getScriptsDir();
|
|
60
|
-
}
|
|
61
|
-
requireScriptsDir() {
|
|
62
|
-
if (!this.scriptsDir) {
|
|
63
|
-
throw new Error('Installer scripts directory not found. Installer download is unavailable.');
|
|
64
|
-
}
|
|
65
|
-
return this.scriptsDir;
|
|
66
|
-
}
|
|
67
41
|
/**
|
|
68
42
|
* Build the HTML layout shell (nav, styles, theme) wrapping body content.
|
|
69
43
|
*
|
|
@@ -105,27 +79,11 @@ class InstallerService {
|
|
|
105
79
|
<h1 style="color:var(--error-color);margin-bottom:12px">${title}</h1><p style="color:var(--text-secondary)">${message}</p>
|
|
106
80
|
<p style="margin-top:16px"><a href="${linkHref}" class="btn-primary">${linkText}</a></p></div></div>`;
|
|
107
81
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
*/
|
|
111
|
-
generateMacScript(key, _baseUrl) {
|
|
112
|
-
const fraimCmd = `npx -y fraim@latest first-run --key=${key}`;
|
|
113
|
-
const templatePath = (0, path_1.join)(this.requireScriptsDir(), 'fraim-install-mac.template.sh');
|
|
114
|
-
const template = (0, fs_1.readFileSync)(templatePath, 'utf-8');
|
|
115
|
-
return template
|
|
116
|
-
.replace('{{FRAIM_CMD}}', fraimCmd)
|
|
117
|
-
.split('{{KEY}}').join(key);
|
|
82
|
+
generateMacScript(_key, _baseUrl) {
|
|
83
|
+
throw new Error('Shell installer fallbacks are retired; download FRAIM-Setup.dmg.');
|
|
118
84
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
*/
|
|
122
|
-
generateWindowsScript(key, _baseUrl) {
|
|
123
|
-
const fraimCmd = `npx -y fraim@latest first-run --key=${key}`;
|
|
124
|
-
const templatePath = (0, path_1.join)(this.requireScriptsDir(), 'fraim-install-win.template.cmd');
|
|
125
|
-
const template = (0, fs_1.readFileSync)(templatePath, 'utf-8');
|
|
126
|
-
return template
|
|
127
|
-
.replace('{{FRAIM_CMD}}', fraimCmd)
|
|
128
|
-
.split('{{KEY}}').join(key);
|
|
85
|
+
generateWindowsScript(_key, _baseUrl) {
|
|
86
|
+
throw new Error('Command installer fallbacks are retired; download FRAIM-Setup.exe.');
|
|
129
87
|
}
|
|
130
88
|
}
|
|
131
89
|
exports.InstallerService = InstallerService;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.SessionManager = void 0;
|
|
4
|
+
const redact_secrets_1 = require("../core/utils/redact-secrets");
|
|
4
5
|
class SessionManager {
|
|
5
6
|
constructor(dbService) {
|
|
6
7
|
this.sessions = new Map();
|
|
@@ -42,7 +43,7 @@ class SessionManager {
|
|
|
42
43
|
: await this.dbService.getActiveSessionByApiKey(apiKey);
|
|
43
44
|
if (dbSession) {
|
|
44
45
|
sessionKey = this.getSessionKey(apiKey, dbSession.sessionId);
|
|
45
|
-
console.log(`Re-hydrating session for API Key: ${apiKey}, sessionId: ${dbSession.sessionId}`);
|
|
46
|
+
console.log(`Re-hydrating session for API Key: ${(0, redact_secrets_1.redactApiKey)(apiKey)}, sessionId: ${dbSession.sessionId}`);
|
|
46
47
|
session = {
|
|
47
48
|
sessionId: dbSession.sessionId,
|
|
48
49
|
lastWrite: now,
|