fraim 2.0.280 → 2.0.283
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 +467 -467
- package/bin/fraim.js +12 -12
- package/dist/src/cli/mcp/fraim-mcp-latest-launcher.js +266 -182
- package/dist/src/cli/mcp/mcp-server-registry.js +11 -3
- package/dist/src/cli/setup/ide-invocation-surfaces.js +64 -64
- package/dist/src/cli/utils/agent-adapters.js +61 -61
- package/dist/src/core/ai-mentor.js +35 -0
- package/dist/src/core/handoff-contracts.js +73 -46
- package/dist/src/core/resolve-phase-edge.js +33 -0
- package/dist/src/core/utils/stub-generator.js +53 -53
- package/dist/src/first-run/server.js +5 -1
- package/dist/src/first-run/session-service.js +48 -12
- package/dist/src/fraim/issues.js +4 -4
- package/dist/src/local-mcp-server/stdio-server.js +43 -4
- package/dist/src/mcp/tool-schemas.js +40 -40
- package/dist/src/middleware/telemetry.js +21 -21
- package/dist/src/services/email-service.js +623 -623
- package/dist/src/services/installer-service.js +22 -22
- package/index.js +83 -83
- package/package.json +59 -59
- package/public/first-run/error-frame.js +100 -100
- package/public/first-run/index.html +35 -35
- package/public/first-run/script.js +747 -742
- package/public/first-run/styles.css +929 -929
- package/dist/src/cli/commands/learning-usage.js +0 -412
- package/dist/src/cli/commands/test-mcp.js +0 -171
- package/dist/src/cli/setup/first-run.js +0 -242
- package/dist/src/core/config-writer.js +0 -75
- package/dist/src/core/utils/job-aliases.js +0 -47
- package/dist/src/core/utils/workflow-parser.js +0 -174
- package/dist/src/services/email-service-clean.js +0 -782
|
@@ -40,18 +40,18 @@ function getMarketingBaseUrl() {
|
|
|
40
40
|
}
|
|
41
41
|
function buildLayoutShell(marketingBaseUrl) {
|
|
42
42
|
const m = marketingBaseUrl;
|
|
43
|
-
return `<!DOCTYPE html>
|
|
44
|
-
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=5.0,user-scalable=yes">
|
|
45
|
-
<script>(function(){var t=localStorage.getItem('theme')||(window.matchMedia('(prefers-color-scheme: dark)').matches?'dark':'light');document.documentElement.setAttribute('data-theme',t);})();</script>
|
|
46
|
-
<title>{{TITLE}}</title><meta name="theme-color" content="#6366f1">
|
|
47
|
-
<link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
48
|
-
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
|
49
|
-
<link rel="stylesheet" href="${m}/css/styles.css"></head><body>
|
|
50
|
-
<nav class="nav"><div class="nav-container"><div class="nav-logo"><a href="${m}/"><img src="${m}/images/fraim-logo.png" alt="FRAIM" class="logo-icon"/><span class="logo-text">FRAIM</span></a></div>
|
|
51
|
-
<div class="nav-links"><a href="${m}/#features">Features</a><a href="${m}/#how-it-works">How it Works</a><a href="${m}/pricing.html">Pricing</a><a href="${m}/#results">Results</a>
|
|
52
|
-
<button class="theme-toggle" id="theme-toggle" aria-label="Toggle dark mode"><span class="theme-icon">🌙</span></button>
|
|
53
|
-
<a href="${m}/" class="nav-cta join-waitlist-btn">Get Started</a></div></div></nav>
|
|
54
|
-
<main style="min-height:100vh;display:flex;align-items:center;justify-content:center;padding:80px 20px 40px">{{BODY}}</main>
|
|
43
|
+
return `<!DOCTYPE html>
|
|
44
|
+
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=5.0,user-scalable=yes">
|
|
45
|
+
<script>(function(){var t=localStorage.getItem('theme')||(window.matchMedia('(prefers-color-scheme: dark)').matches?'dark':'light');document.documentElement.setAttribute('data-theme',t);})();</script>
|
|
46
|
+
<title>{{TITLE}}</title><meta name="theme-color" content="#6366f1">
|
|
47
|
+
<link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
48
|
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
|
49
|
+
<link rel="stylesheet" href="${m}/css/styles.css"></head><body>
|
|
50
|
+
<nav class="nav"><div class="nav-container"><div class="nav-logo"><a href="${m}/"><img src="${m}/images/fraim-logo.png" alt="FRAIM" class="logo-icon"/><span class="logo-text">FRAIM</span></a></div>
|
|
51
|
+
<div class="nav-links"><a href="${m}/#features">Features</a><a href="${m}/#how-it-works">How it Works</a><a href="${m}/pricing.html">Pricing</a><a href="${m}/#results">Results</a>
|
|
52
|
+
<button class="theme-toggle" id="theme-toggle" aria-label="Toggle dark mode"><span class="theme-icon">🌙</span></button>
|
|
53
|
+
<a href="${m}/" class="nav-cta join-waitlist-btn">Get Started</a></div></div></nav>
|
|
54
|
+
<main style="min-height:100vh;display:flex;align-items:center;justify-content:center;padding:80px 20px 40px">{{BODY}}</main>
|
|
55
55
|
<script src="${m}/js/script.js"></script></body></html>`;
|
|
56
56
|
}
|
|
57
57
|
class InstallerService {
|
|
@@ -87,22 +87,22 @@ class InstallerService {
|
|
|
87
87
|
* bytes through curl avoids the quarantine attribute entirely.
|
|
88
88
|
*/
|
|
89
89
|
generateDashboardCard(_data, _baseUrl) {
|
|
90
|
-
return `<div class="container" style="max-width:520px;width:100%">
|
|
91
|
-
<div class="card" style="background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:16px;max-width:520px;width:100%;box-shadow:var(--shadow-lg);overflow:hidden">
|
|
92
|
-
<div class="card-header" style="padding:24px;background:linear-gradient(135deg,#22c55e 0%,#16a34a 100%);color:#fff;text-align:center;border-radius:16px 16px 0 0">
|
|
93
|
-
<div style="font-size:48px;margin-bottom:8px">✅</div><h2 style="font-size:20px;font-weight:700">Open Account to view your FRAIM key</h2><p style="font-size:14px;opacity:.9;margin-top:4px">Your key and install commands now live in Account.</p></div>
|
|
94
|
-
<div style="padding:24px">
|
|
95
|
-
<p style="font-size:14px;color:var(--text-muted);line-height:1.6;margin:0 0 20px">For security, FRAIM no longer displays API keys on this standalone page. Continue to Account, reveal the key when you need it, and generate fresh install commands there.</p>
|
|
96
|
-
<a href="/#account" class="btn-primary" style="display:block;padding:14px 20px;border-radius:10px;font-size:14px;font-weight:600;text-align:center;text-decoration:none">Open Account</a>
|
|
90
|
+
return `<div class="container" style="max-width:520px;width:100%">
|
|
91
|
+
<div class="card" style="background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:16px;max-width:520px;width:100%;box-shadow:var(--shadow-lg);overflow:hidden">
|
|
92
|
+
<div class="card-header" style="padding:24px;background:linear-gradient(135deg,#22c55e 0%,#16a34a 100%);color:#fff;text-align:center;border-radius:16px 16px 0 0">
|
|
93
|
+
<div style="font-size:48px;margin-bottom:8px">✅</div><h2 style="font-size:20px;font-weight:700">Open Account to view your FRAIM key</h2><p style="font-size:14px;opacity:.9;margin-top:4px">Your key and install commands now live in Account.</p></div>
|
|
94
|
+
<div style="padding:24px">
|
|
95
|
+
<p style="font-size:14px;color:var(--text-muted);line-height:1.6;margin:0 0 20px">For security, FRAIM no longer displays API keys on this standalone page. Continue to Account, reveal the key when you need it, and generate fresh install commands there.</p>
|
|
96
|
+
<a href="/#account" class="btn-primary" style="display:block;padding:14px 20px;border-radius:10px;font-size:14px;font-weight:600;text-align:center;text-decoration:none">Open Account</a>
|
|
97
97
|
</div></div></div>`;
|
|
98
98
|
}
|
|
99
99
|
/**
|
|
100
100
|
* Generate an error page card (missing token, invalid token, etc.).
|
|
101
101
|
*/
|
|
102
102
|
generateErrorCard(title, message, linkText, linkHref) {
|
|
103
|
-
return `<div class="container" style="text-align:center;max-width:400px">
|
|
104
|
-
<div class="card" style="background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:16px;padding:32px">
|
|
105
|
-
<h1 style="color:var(--error-color);margin-bottom:12px">${title}</h1><p style="color:var(--text-secondary)">${message}</p>
|
|
103
|
+
return `<div class="container" style="text-align:center;max-width:400px">
|
|
104
|
+
<div class="card" style="background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:16px;padding:32px">
|
|
105
|
+
<h1 style="color:var(--error-color);margin-bottom:12px">${title}</h1><p style="color:var(--text-secondary)">${message}</p>
|
|
106
106
|
<p style="margin-top:16px"><a href="${linkHref}" class="btn-primary">${linkText}</a></p></div></div>`;
|
|
107
107
|
}
|
|
108
108
|
/**
|
package/index.js
CHANGED
|
@@ -1,85 +1,85 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* FRAIM Framework - Smart Entry Point
|
|
5
|
-
* This file handles both production (dist/) and development (src/) environments.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
const path = require('path');
|
|
9
|
-
const fs = require('fs');
|
|
10
|
-
const { spawnSync } = require('child_process');
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Runs the CLI using either the compiled JS or the source TS via tsx
|
|
14
|
-
*/
|
|
15
|
-
function runCLI() {
|
|
16
|
-
const distPath = path.join(__dirname, 'dist', 'src', 'cli', 'fraim.js');
|
|
17
|
-
const srcPath = path.join(__dirname, 'src', 'cli', 'fraim.ts');
|
|
18
|
-
|
|
19
|
-
// 1. Check if we have a compiled version (Production / CI)
|
|
20
|
-
if (fs.existsSync(distPath)) {
|
|
21
|
-
require(distPath);
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// 2. Explicitly fail in production if dist/ is missing
|
|
26
|
-
if (process.env.NODE_ENV === 'production') {
|
|
27
|
-
console.error('❌ FRAIM Error: Production build (dist/) not found.');
|
|
28
|
-
console.error('In production environments, you must run the compiled version.');
|
|
29
|
-
console.error(`Expected: ${distPath}`);
|
|
30
|
-
process.exit(1);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// 3. Fallback to source version using tsx (Development)
|
|
34
|
-
if (fs.existsSync(srcPath)) {
|
|
35
|
-
// We use spawnSync to run tsx so we don't have to require it in memory
|
|
36
|
-
// if it's not needed, and it handles the process arguments correctly.
|
|
37
|
-
//
|
|
38
|
-
// IMPORTANT FIX: Directory names with spaces and dashes (e.g., "FRAIM - Issue 166")
|
|
39
|
-
// cause argument parsing issues on Windows when shell: true is used.
|
|
40
|
-
// Without quoting, a path like "C:\...\FRAIM - Issue 166\src\cli\fraim.ts" gets
|
|
41
|
-
// split into multiple arguments, with the dash interpreted as a command flag,
|
|
42
|
-
// resulting in "error: unknown command '-'".
|
|
43
|
-
//
|
|
44
|
-
// Solution: On Windows with shell: true, quote paths containing spaces.
|
|
45
|
-
// On Unix with shell: false, pass the path unquoted (spawnSync handles it correctly).
|
|
46
|
-
const isWindows = process.platform === 'win32';
|
|
47
|
-
|
|
48
|
-
// On Windows with shell, quote paths with spaces to prevent shell misinterpretation
|
|
49
|
-
// On Unix without shell, pass path as-is (spawnSync handles spaces correctly)
|
|
50
|
-
const processedSrcPath = (isWindows && srcPath.includes(' '))
|
|
51
|
-
? `"${srcPath}"`
|
|
52
|
-
: srcPath;
|
|
53
|
-
|
|
54
|
-
const result = spawnSync(
|
|
55
|
-
'npx',
|
|
56
|
-
['tsx', processedSrcPath, ...process.argv.slice(2)],
|
|
57
|
-
{
|
|
58
|
-
stdio: 'inherit',
|
|
59
|
-
shell: isWindows, // Windows needs shell for npx, Unix doesn't
|
|
60
|
-
windowsHide: true
|
|
61
|
-
}
|
|
62
|
-
);
|
|
63
|
-
process.exit(result.status || 0);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
console.error('❌ FRAIM Error: Could not find CLI entry point.');
|
|
67
|
-
console.error('Expected one of:');
|
|
68
|
-
console.error(` - ${distPath}`);
|
|
69
|
-
console.error(` - ${srcPath}`);
|
|
70
|
-
process.exit(1);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Global programmatic exports
|
|
74
|
-
module.exports = {
|
|
75
|
-
FRAIM_INFO: {
|
|
76
|
-
name: 'FRAIM',
|
|
77
|
-
version: '2.0.98',
|
|
78
|
-
repository: 'https://github.com/mathursrus/FRAIM'
|
|
79
|
-
}
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
// If this file is run directly (via npx or global link), run the CLI
|
|
83
|
-
if (require.main === module) {
|
|
84
|
-
runCLI();
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* FRAIM Framework - Smart Entry Point
|
|
5
|
+
* This file handles both production (dist/) and development (src/) environments.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const { spawnSync } = require('child_process');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Runs the CLI using either the compiled JS or the source TS via tsx
|
|
14
|
+
*/
|
|
15
|
+
function runCLI() {
|
|
16
|
+
const distPath = path.join(__dirname, 'dist', 'src', 'cli', 'fraim.js');
|
|
17
|
+
const srcPath = path.join(__dirname, 'src', 'cli', 'fraim.ts');
|
|
18
|
+
|
|
19
|
+
// 1. Check if we have a compiled version (Production / CI)
|
|
20
|
+
if (fs.existsSync(distPath)) {
|
|
21
|
+
require(distPath);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// 2. Explicitly fail in production if dist/ is missing
|
|
26
|
+
if (process.env.NODE_ENV === 'production') {
|
|
27
|
+
console.error('❌ FRAIM Error: Production build (dist/) not found.');
|
|
28
|
+
console.error('In production environments, you must run the compiled version.');
|
|
29
|
+
console.error(`Expected: ${distPath}`);
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// 3. Fallback to source version using tsx (Development)
|
|
34
|
+
if (fs.existsSync(srcPath)) {
|
|
35
|
+
// We use spawnSync to run tsx so we don't have to require it in memory
|
|
36
|
+
// if it's not needed, and it handles the process arguments correctly.
|
|
37
|
+
//
|
|
38
|
+
// IMPORTANT FIX: Directory names with spaces and dashes (e.g., "FRAIM - Issue 166")
|
|
39
|
+
// cause argument parsing issues on Windows when shell: true is used.
|
|
40
|
+
// Without quoting, a path like "C:\...\FRAIM - Issue 166\src\cli\fraim.ts" gets
|
|
41
|
+
// split into multiple arguments, with the dash interpreted as a command flag,
|
|
42
|
+
// resulting in "error: unknown command '-'".
|
|
43
|
+
//
|
|
44
|
+
// Solution: On Windows with shell: true, quote paths containing spaces.
|
|
45
|
+
// On Unix with shell: false, pass the path unquoted (spawnSync handles it correctly).
|
|
46
|
+
const isWindows = process.platform === 'win32';
|
|
47
|
+
|
|
48
|
+
// On Windows with shell, quote paths with spaces to prevent shell misinterpretation
|
|
49
|
+
// On Unix without shell, pass path as-is (spawnSync handles spaces correctly)
|
|
50
|
+
const processedSrcPath = (isWindows && srcPath.includes(' '))
|
|
51
|
+
? `"${srcPath}"`
|
|
52
|
+
: srcPath;
|
|
53
|
+
|
|
54
|
+
const result = spawnSync(
|
|
55
|
+
'npx',
|
|
56
|
+
['tsx', processedSrcPath, ...process.argv.slice(2)],
|
|
57
|
+
{
|
|
58
|
+
stdio: 'inherit',
|
|
59
|
+
shell: isWindows, // Windows needs shell for npx, Unix doesn't
|
|
60
|
+
windowsHide: true
|
|
61
|
+
}
|
|
62
|
+
);
|
|
63
|
+
process.exit(result.status || 0);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
console.error('❌ FRAIM Error: Could not find CLI entry point.');
|
|
67
|
+
console.error('Expected one of:');
|
|
68
|
+
console.error(` - ${distPath}`);
|
|
69
|
+
console.error(` - ${srcPath}`);
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Global programmatic exports
|
|
74
|
+
module.exports = {
|
|
75
|
+
FRAIM_INFO: {
|
|
76
|
+
name: 'FRAIM',
|
|
77
|
+
version: '2.0.98',
|
|
78
|
+
repository: 'https://github.com/mathursrus/FRAIM'
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// If this file is run directly (via npx or global link), run the CLI
|
|
83
|
+
if (require.main === module) {
|
|
84
|
+
runCLI();
|
|
85
85
|
}
|
package/package.json
CHANGED
|
@@ -1,62 +1,62 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "fraim",
|
|
3
|
-
"version": "2.0.
|
|
4
|
-
"description": "FRAIM core CLI and MCP package.",
|
|
5
|
-
"main": "index.js",
|
|
6
|
-
"bin": {
|
|
7
|
-
"fraim": "bin/fraim.js"
|
|
8
|
-
},
|
|
9
|
-
"files": [
|
|
10
|
-
"dist/src/local-mcp-server/",
|
|
11
|
-
"dist/src/cli/",
|
|
12
|
-
"!dist/src/cli/fraim-hub.js",
|
|
13
|
-
"dist/src/first-run/",
|
|
14
|
-
"dist/src/core/",
|
|
15
|
-
"dist/src/config/",
|
|
16
|
-
"dist/src/fraim/",
|
|
17
|
-
"dist/src/db/",
|
|
18
|
-
"dist/src/routes/",
|
|
19
|
-
"dist/src/services/",
|
|
20
|
-
"dist/src/api/",
|
|
21
|
-
"dist/src/middleware/",
|
|
1
|
+
{
|
|
2
|
+
"name": "fraim",
|
|
3
|
+
"version": "2.0.283",
|
|
4
|
+
"description": "FRAIM core CLI and MCP package.",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"fraim": "bin/fraim.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist/src/local-mcp-server/",
|
|
11
|
+
"dist/src/cli/",
|
|
12
|
+
"!dist/src/cli/fraim-hub.js",
|
|
13
|
+
"dist/src/first-run/",
|
|
14
|
+
"dist/src/core/",
|
|
15
|
+
"dist/src/config/",
|
|
16
|
+
"dist/src/fraim/",
|
|
17
|
+
"dist/src/db/",
|
|
18
|
+
"dist/src/routes/",
|
|
19
|
+
"dist/src/services/",
|
|
20
|
+
"dist/src/api/",
|
|
21
|
+
"dist/src/middleware/",
|
|
22
22
|
"dist/src/models/",
|
|
23
23
|
"dist/src/mcp/",
|
|
24
24
|
"dist/src/types/",
|
|
25
|
-
"dist/src/utils/",
|
|
26
|
-
"bin/fraim.js",
|
|
27
|
-
"public/first-run/",
|
|
28
|
-
"index.js",
|
|
29
|
-
"README.md",
|
|
30
|
-
"CHANGELOG.md",
|
|
31
|
-
"LICENSE",
|
|
32
|
-
"package.json"
|
|
33
|
-
],
|
|
34
|
-
"publishConfig": {
|
|
35
|
-
"access": "public"
|
|
36
|
-
},
|
|
37
|
-
"engines": {
|
|
38
|
-
"node": ">=16.0.0"
|
|
39
|
-
},
|
|
40
|
-
"dependencies": {
|
|
41
|
-
"@octokit/rest": "^22.0.1",
|
|
42
|
-
"adm-zip": "^0.6.0",
|
|
43
|
-
"axios": "^1.7.0",
|
|
44
|
-
"chalk": "4.1.2",
|
|
45
|
-
"commander": "^14.0.2",
|
|
46
|
-
"cors": "^2.8.5",
|
|
47
|
-
"dotenv": "^16.4.7",
|
|
48
|
-
"express": "^5.2.1",
|
|
49
|
-
"mongodb": "^7.0.0",
|
|
50
|
-
"node-cron": "4.2.1",
|
|
51
|
-
"node-edge-tts": "^1.2.10",
|
|
52
|
-
"nodemailer": "^8.0.3",
|
|
53
|
-
"prompts": "^2.4.2",
|
|
54
|
-
"resend": "^6.9.3",
|
|
55
|
-
"selfsigned": "^5.5.0",
|
|
56
|
-
"semver": "^7.7.4",
|
|
57
|
-
"stripe": "^20.3.1",
|
|
58
|
-
"toml": "^3.0.0",
|
|
59
|
-
"tree-kill": "^1.2.2",
|
|
60
|
-
"xml2js": "^0.6.2"
|
|
61
|
-
}
|
|
62
|
-
}
|
|
25
|
+
"dist/src/utils/",
|
|
26
|
+
"bin/fraim.js",
|
|
27
|
+
"public/first-run/",
|
|
28
|
+
"index.js",
|
|
29
|
+
"README.md",
|
|
30
|
+
"CHANGELOG.md",
|
|
31
|
+
"LICENSE",
|
|
32
|
+
"package.json"
|
|
33
|
+
],
|
|
34
|
+
"publishConfig": {
|
|
35
|
+
"access": "public"
|
|
36
|
+
},
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=16.0.0"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@octokit/rest": "^22.0.1",
|
|
42
|
+
"adm-zip": "^0.6.0",
|
|
43
|
+
"axios": "^1.7.0",
|
|
44
|
+
"chalk": "4.1.2",
|
|
45
|
+
"commander": "^14.0.2",
|
|
46
|
+
"cors": "^2.8.5",
|
|
47
|
+
"dotenv": "^16.4.7",
|
|
48
|
+
"express": "^5.2.1",
|
|
49
|
+
"mongodb": "^7.0.0",
|
|
50
|
+
"node-cron": "4.2.1",
|
|
51
|
+
"node-edge-tts": "^1.2.10",
|
|
52
|
+
"nodemailer": "^8.0.3",
|
|
53
|
+
"prompts": "^2.4.2",
|
|
54
|
+
"resend": "^6.9.3",
|
|
55
|
+
"selfsigned": "^5.5.0",
|
|
56
|
+
"semver": "^7.7.4",
|
|
57
|
+
"stripe": "^20.3.1",
|
|
58
|
+
"toml": "^3.0.0",
|
|
59
|
+
"tree-kill": "^1.2.2",
|
|
60
|
+
"xml2js": "^0.6.2"
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -1,100 +1,100 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* R6.1 — unified error-frame renderer.
|
|
3
|
-
*
|
|
4
|
-
* Exposes a single function `renderErrorFrame(frame, onAction)` which returns
|
|
5
|
-
* a DOM Node containing exactly:
|
|
6
|
-
* - "What we tried" sentence
|
|
7
|
-
* - "What happened" verbatim stderr (last 12 lines, with a Show-full toggle
|
|
8
|
-
* that surfaces the rest on demand)
|
|
9
|
-
* - Up to three actions in fixed order (Retry / Try alternative / Skip and continue)
|
|
10
|
-
*
|
|
11
|
-
* The same renderer must be lifted into public/ai-hub/ in the v2 follow-up
|
|
12
|
-
* (issue #355, "Hub-side error-frame adoption") so bootstrap and Hub share
|
|
13
|
-
* one frame across surfaces.
|
|
14
|
-
*/
|
|
15
|
-
(function () {
|
|
16
|
-
'use strict';
|
|
17
|
-
|
|
18
|
-
function escapeText(text) {
|
|
19
|
-
if (typeof text !== 'string') return '';
|
|
20
|
-
return text;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function truncateToLastLines(text, lineCount) {
|
|
24
|
-
if (typeof text !== 'string') return { visible: '', hidden: '' };
|
|
25
|
-
const lines = text.split(/\r?\n/);
|
|
26
|
-
if (lines.length <= lineCount) {
|
|
27
|
-
return { visible: text, hidden: '' };
|
|
28
|
-
}
|
|
29
|
-
const visible = lines.slice(-lineCount).join('\n');
|
|
30
|
-
const hidden = lines.slice(0, -lineCount).join('\n');
|
|
31
|
-
return { visible, hidden };
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function renderErrorFrame(frame, onAction) {
|
|
35
|
-
const root = document.createElement('div');
|
|
36
|
-
root.className = 'error-frame';
|
|
37
|
-
root.setAttribute('data-testid', 'error-frame');
|
|
38
|
-
root.setAttribute('role', 'alert');
|
|
39
|
-
|
|
40
|
-
const whatTried = document.createElement('div');
|
|
41
|
-
whatTried.className = 'what-tried';
|
|
42
|
-
whatTried.setAttribute('data-testid', 'error-what-tried');
|
|
43
|
-
whatTried.textContent = escapeText(frame.whatTried || '');
|
|
44
|
-
root.appendChild(whatTried);
|
|
45
|
-
|
|
46
|
-
// Optional plain-language hint rendered ABOVE the verbatim stderr so
|
|
47
|
-
// a non-tech user reads "your install key was rejected — get a new
|
|
48
|
-
// one" before they parse `Request failed with status code 401`.
|
|
49
|
-
if (frame.hint) {
|
|
50
|
-
const hint = document.createElement('div');
|
|
51
|
-
hint.className = 'error-hint';
|
|
52
|
-
hint.setAttribute('data-testid', 'error-hint');
|
|
53
|
-
hint.textContent = escapeText(frame.hint);
|
|
54
|
-
root.appendChild(hint);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const { visible, hidden } = truncateToLastLines(frame.whatHappened || '', 12);
|
|
58
|
-
const whatHappened = document.createElement('pre');
|
|
59
|
-
whatHappened.className = 'what-happened';
|
|
60
|
-
whatHappened.setAttribute('data-testid', 'error-what-happened');
|
|
61
|
-
whatHappened.textContent = visible;
|
|
62
|
-
root.appendChild(whatHappened);
|
|
63
|
-
|
|
64
|
-
if (hidden) {
|
|
65
|
-
const showFull = document.createElement('button');
|
|
66
|
-
showFull.type = 'button';
|
|
67
|
-
showFull.className = 'show-full';
|
|
68
|
-
showFull.setAttribute('data-testid', 'error-show-full');
|
|
69
|
-
showFull.textContent = 'Show full output';
|
|
70
|
-
showFull.addEventListener('click', () => {
|
|
71
|
-
whatHappened.textContent = (hidden + '\n' + visible).trimEnd();
|
|
72
|
-
showFull.remove();
|
|
73
|
-
});
|
|
74
|
-
root.appendChild(showFull);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const actions = Array.isArray(frame.actions) ? frame.actions.slice(0, 3) : [];
|
|
78
|
-
const actionRow = document.createElement('div');
|
|
79
|
-
actionRow.className = 'actions';
|
|
80
|
-
for (const action of actions) {
|
|
81
|
-
const btn = document.createElement('button');
|
|
82
|
-
btn.type = 'button';
|
|
83
|
-
btn.className = 'action';
|
|
84
|
-
btn.setAttribute('data-testid', 'error-action');
|
|
85
|
-
btn.setAttribute('data-action-id', action.id);
|
|
86
|
-
btn.setAttribute('data-variant', action.variant || 'secondary');
|
|
87
|
-
btn.textContent = action.label || action.id;
|
|
88
|
-
btn.addEventListener('click', () => {
|
|
89
|
-
if (typeof onAction === 'function') {
|
|
90
|
-
onAction(action);
|
|
91
|
-
}
|
|
92
|
-
});
|
|
93
|
-
actionRow.appendChild(btn);
|
|
94
|
-
}
|
|
95
|
-
root.appendChild(actionRow);
|
|
96
|
-
return root;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
window.FraimErrorFrame = { render: renderErrorFrame };
|
|
100
|
-
})();
|
|
1
|
+
/**
|
|
2
|
+
* R6.1 — unified error-frame renderer.
|
|
3
|
+
*
|
|
4
|
+
* Exposes a single function `renderErrorFrame(frame, onAction)` which returns
|
|
5
|
+
* a DOM Node containing exactly:
|
|
6
|
+
* - "What we tried" sentence
|
|
7
|
+
* - "What happened" verbatim stderr (last 12 lines, with a Show-full toggle
|
|
8
|
+
* that surfaces the rest on demand)
|
|
9
|
+
* - Up to three actions in fixed order (Retry / Try alternative / Skip and continue)
|
|
10
|
+
*
|
|
11
|
+
* The same renderer must be lifted into public/ai-hub/ in the v2 follow-up
|
|
12
|
+
* (issue #355, "Hub-side error-frame adoption") so bootstrap and Hub share
|
|
13
|
+
* one frame across surfaces.
|
|
14
|
+
*/
|
|
15
|
+
(function () {
|
|
16
|
+
'use strict';
|
|
17
|
+
|
|
18
|
+
function escapeText(text) {
|
|
19
|
+
if (typeof text !== 'string') return '';
|
|
20
|
+
return text;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function truncateToLastLines(text, lineCount) {
|
|
24
|
+
if (typeof text !== 'string') return { visible: '', hidden: '' };
|
|
25
|
+
const lines = text.split(/\r?\n/);
|
|
26
|
+
if (lines.length <= lineCount) {
|
|
27
|
+
return { visible: text, hidden: '' };
|
|
28
|
+
}
|
|
29
|
+
const visible = lines.slice(-lineCount).join('\n');
|
|
30
|
+
const hidden = lines.slice(0, -lineCount).join('\n');
|
|
31
|
+
return { visible, hidden };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function renderErrorFrame(frame, onAction) {
|
|
35
|
+
const root = document.createElement('div');
|
|
36
|
+
root.className = 'error-frame';
|
|
37
|
+
root.setAttribute('data-testid', 'error-frame');
|
|
38
|
+
root.setAttribute('role', 'alert');
|
|
39
|
+
|
|
40
|
+
const whatTried = document.createElement('div');
|
|
41
|
+
whatTried.className = 'what-tried';
|
|
42
|
+
whatTried.setAttribute('data-testid', 'error-what-tried');
|
|
43
|
+
whatTried.textContent = escapeText(frame.whatTried || '');
|
|
44
|
+
root.appendChild(whatTried);
|
|
45
|
+
|
|
46
|
+
// Optional plain-language hint rendered ABOVE the verbatim stderr so
|
|
47
|
+
// a non-tech user reads "your install key was rejected — get a new
|
|
48
|
+
// one" before they parse `Request failed with status code 401`.
|
|
49
|
+
if (frame.hint) {
|
|
50
|
+
const hint = document.createElement('div');
|
|
51
|
+
hint.className = 'error-hint';
|
|
52
|
+
hint.setAttribute('data-testid', 'error-hint');
|
|
53
|
+
hint.textContent = escapeText(frame.hint);
|
|
54
|
+
root.appendChild(hint);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const { visible, hidden } = truncateToLastLines(frame.whatHappened || '', 12);
|
|
58
|
+
const whatHappened = document.createElement('pre');
|
|
59
|
+
whatHappened.className = 'what-happened';
|
|
60
|
+
whatHappened.setAttribute('data-testid', 'error-what-happened');
|
|
61
|
+
whatHappened.textContent = visible;
|
|
62
|
+
root.appendChild(whatHappened);
|
|
63
|
+
|
|
64
|
+
if (hidden) {
|
|
65
|
+
const showFull = document.createElement('button');
|
|
66
|
+
showFull.type = 'button';
|
|
67
|
+
showFull.className = 'show-full';
|
|
68
|
+
showFull.setAttribute('data-testid', 'error-show-full');
|
|
69
|
+
showFull.textContent = 'Show full output';
|
|
70
|
+
showFull.addEventListener('click', () => {
|
|
71
|
+
whatHappened.textContent = (hidden + '\n' + visible).trimEnd();
|
|
72
|
+
showFull.remove();
|
|
73
|
+
});
|
|
74
|
+
root.appendChild(showFull);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const actions = Array.isArray(frame.actions) ? frame.actions.slice(0, 3) : [];
|
|
78
|
+
const actionRow = document.createElement('div');
|
|
79
|
+
actionRow.className = 'actions';
|
|
80
|
+
for (const action of actions) {
|
|
81
|
+
const btn = document.createElement('button');
|
|
82
|
+
btn.type = 'button';
|
|
83
|
+
btn.className = 'action';
|
|
84
|
+
btn.setAttribute('data-testid', 'error-action');
|
|
85
|
+
btn.setAttribute('data-action-id', action.id);
|
|
86
|
+
btn.setAttribute('data-variant', action.variant || 'secondary');
|
|
87
|
+
btn.textContent = action.label || action.id;
|
|
88
|
+
btn.addEventListener('click', () => {
|
|
89
|
+
if (typeof onAction === 'function') {
|
|
90
|
+
onAction(action);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
actionRow.appendChild(btn);
|
|
94
|
+
}
|
|
95
|
+
root.appendChild(actionRow);
|
|
96
|
+
return root;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
window.FraimErrorFrame = { render: renderErrorFrame };
|
|
100
|
+
})();
|
|
@@ -1,35 +1,35 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
-
<title>FRAIM Setup</title>
|
|
7
|
-
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 48 48'%3E%3Crect width='48' height='48' rx='10' fill='%230f2540'/%3E%3Ctext x='24' y='31' text-anchor='middle' font-family='Helvetica,Arial,sans-serif' font-size='22' font-weight='700' fill='%23f6efe4'%3EF%3C/text%3E%3C/svg%3E">
|
|
8
|
-
<link rel="stylesheet" href="/first-run/styles.css">
|
|
9
|
-
</head>
|
|
10
|
-
<body>
|
|
11
|
-
<main class="page">
|
|
12
|
-
<header class="page-header">
|
|
13
|
-
<h1>Set up FRAIM</h1>
|
|
14
|
-
<p class="lede" id="lede">We are getting your machine ready for FRAIM.</p>
|
|
15
|
-
</header>
|
|
16
|
-
|
|
17
|
-
<section class="card">
|
|
18
|
-
<ul class="checklist" id="checklist" data-testid="setup-checklist" aria-label="FRAIM setup checklist">
|
|
19
|
-
<!-- Skeleton rows shown until the async session-load completes.
|
|
20
|
-
Replaced wholesale by script.js once /api/first-run/session returns. -->
|
|
21
|
-
<li class="row skeleton-row" data-row-id="node" data-row-status="pending"><span class="icon" aria-hidden="true">.</span><span class="label">Node.js</span><span class="verb" data-testid="row-verb">checking...</span></li>
|
|
22
|
-
<li class="row skeleton-row" data-row-id="git" data-row-status="pending"><span class="icon" aria-hidden="true">.</span><span class="label">git</span><span class="verb" data-testid="row-verb">checking...</span></li>
|
|
23
|
-
<li class="row skeleton-row" data-row-id="agent" data-row-status="pending"><span class="icon" aria-hidden="true">.</span><span class="label">Locally installed AI agents</span><span class="verb" data-testid="row-verb">checking...</span></li>
|
|
24
|
-
<li class="row skeleton-row" data-row-id="fraim" data-row-status="pending"><span class="icon" aria-hidden="true">.</span><span class="label">FRAIM</span><span class="verb" data-testid="row-verb">checking...</span></li>
|
|
25
|
-
</ul>
|
|
26
|
-
<div class="actions">
|
|
27
|
-
<button id="primary-button" class="primary-button" data-testid="primary-button">Set up FRAIM</button>
|
|
28
|
-
</div>
|
|
29
|
-
<p class="status" id="status" role="status" aria-live="polite"></p>
|
|
30
|
-
</section>
|
|
31
|
-
</main>
|
|
32
|
-
<script src="/first-run/error-frame.js"></script>
|
|
33
|
-
<script src="/first-run/script.js"></script>
|
|
34
|
-
</body>
|
|
35
|
-
</html>
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>FRAIM Setup</title>
|
|
7
|
+
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 48 48'%3E%3Crect width='48' height='48' rx='10' fill='%230f2540'/%3E%3Ctext x='24' y='31' text-anchor='middle' font-family='Helvetica,Arial,sans-serif' font-size='22' font-weight='700' fill='%23f6efe4'%3EF%3C/text%3E%3C/svg%3E">
|
|
8
|
+
<link rel="stylesheet" href="/first-run/styles.css">
|
|
9
|
+
</head>
|
|
10
|
+
<body>
|
|
11
|
+
<main class="page">
|
|
12
|
+
<header class="page-header">
|
|
13
|
+
<h1>Set up FRAIM</h1>
|
|
14
|
+
<p class="lede" id="lede">We are getting your machine ready for FRAIM.</p>
|
|
15
|
+
</header>
|
|
16
|
+
|
|
17
|
+
<section class="card">
|
|
18
|
+
<ul class="checklist" id="checklist" data-testid="setup-checklist" aria-label="FRAIM setup checklist">
|
|
19
|
+
<!-- Skeleton rows shown until the async session-load completes.
|
|
20
|
+
Replaced wholesale by script.js once /api/first-run/session returns. -->
|
|
21
|
+
<li class="row skeleton-row" data-row-id="node" data-row-status="pending"><span class="icon" aria-hidden="true">.</span><span class="label">Node.js</span><span class="verb" data-testid="row-verb">checking...</span></li>
|
|
22
|
+
<li class="row skeleton-row" data-row-id="git" data-row-status="pending"><span class="icon" aria-hidden="true">.</span><span class="label">git</span><span class="verb" data-testid="row-verb">checking...</span></li>
|
|
23
|
+
<li class="row skeleton-row" data-row-id="agent" data-row-status="pending"><span class="icon" aria-hidden="true">.</span><span class="label">Locally installed AI agents</span><span class="verb" data-testid="row-verb">checking...</span></li>
|
|
24
|
+
<li class="row skeleton-row" data-row-id="fraim" data-row-status="pending"><span class="icon" aria-hidden="true">.</span><span class="label">FRAIM</span><span class="verb" data-testid="row-verb">checking...</span></li>
|
|
25
|
+
</ul>
|
|
26
|
+
<div class="actions">
|
|
27
|
+
<button id="primary-button" class="primary-button" data-testid="primary-button">Set up FRAIM</button>
|
|
28
|
+
</div>
|
|
29
|
+
<p class="status" id="status" role="status" aria-live="polite"></p>
|
|
30
|
+
</section>
|
|
31
|
+
</main>
|
|
32
|
+
<script src="/first-run/error-frame.js"></script>
|
|
33
|
+
<script src="/first-run/script.js"></script>
|
|
34
|
+
</body>
|
|
35
|
+
</html>
|