fraim-framework 2.0.26 â 2.0.30
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/.github/workflows/deploy-fraim.yml +1 -1
- package/dist/registry/scripts/build-scripts-generator.js +205 -0
- package/dist/registry/scripts/cleanup-branch.js +258 -0
- package/dist/registry/scripts/evaluate-code-quality.js +66 -0
- package/dist/registry/scripts/exec-with-timeout.js +142 -0
- package/dist/registry/scripts/fraim-config.js +61 -0
- package/dist/registry/scripts/generate-engagement-emails.js +630 -0
- package/dist/registry/scripts/generic-issues-api.js +100 -0
- package/dist/registry/scripts/newsletter-helpers.js +731 -0
- package/dist/registry/scripts/openapi-generator.js +664 -0
- package/dist/registry/scripts/performance/profile-server.js +390 -0
- package/dist/registry/scripts/run-thank-you-workflow.js +92 -0
- package/dist/registry/scripts/send-newsletter-simple.js +85 -0
- package/dist/registry/scripts/send-thank-you-emails.js +54 -0
- package/dist/registry/scripts/validate-openapi-limits.js +311 -0
- package/dist/registry/scripts/validate-test-coverage.js +262 -0
- package/dist/registry/scripts/verify-test-coverage.js +66 -0
- package/dist/src/cli/commands/init.js +14 -12
- package/dist/src/cli/commands/sync.js +19 -2
- package/dist/src/cli/fraim.js +24 -22
- package/dist/src/cli/setup/first-run.js +13 -6
- package/dist/src/fraim/config-loader.js +0 -8
- package/dist/src/fraim/db-service.js +26 -15
- package/dist/src/fraim/issues.js +67 -0
- package/dist/src/fraim/setup-wizard.js +1 -69
- package/dist/src/fraim/types.js +0 -11
- package/dist/src/fraim-mcp-server.js +272 -18
- package/dist/src/utils/git-utils.js +1 -1
- package/dist/src/utils/version-utils.js +32 -0
- package/dist/tests/debug-tools.js +79 -0
- package/dist/tests/esm-compat.js +11 -0
- package/dist/tests/test-chalk-esm-issue.js +159 -0
- package/dist/tests/test-chalk-real-world.js +265 -0
- package/dist/tests/test-chalk-regression.js +327 -0
- package/dist/tests/test-chalk-resolution-issue.js +304 -0
- package/dist/tests/test-cli.js +0 -2
- package/dist/tests/test-fraim-install-chalk-issue.js +254 -0
- package/dist/tests/test-fraim-issues.js +59 -0
- package/dist/tests/test-genericization.js +1 -3
- package/dist/tests/test-mcp-connection.js +166 -0
- package/dist/tests/test-mcp-issue-integration.js +144 -0
- package/dist/tests/test-mcp-lifecycle-methods.js +312 -0
- package/dist/tests/test-node-compatibility.js +71 -0
- package/dist/tests/test-npm-install.js +66 -0
- package/dist/tests/test-npm-resolution-diagnostic.js +140 -0
- package/dist/tests/test-session-rehydration.js +145 -0
- package/dist/tests/test-standalone.js +2 -8
- package/dist/tests/test-sync-version-update.js +93 -0
- package/dist/tests/test-telemetry.js +190 -0
- package/package.json +10 -8
- package/registry/agent-guardrails.md +62 -54
- package/registry/rules/agent-success-criteria.md +52 -0
- package/registry/rules/agent-testing-guidelines.md +502 -502
- package/registry/rules/communication.md +121 -121
- package/registry/rules/continuous-learning.md +54 -54
- package/registry/rules/ephemeral-execution.md +10 -5
- package/registry/rules/hitl-ppe-record-analysis.md +302 -302
- package/registry/rules/local-development.md +251 -251
- package/registry/rules/software-development-lifecycle.md +104 -104
- package/registry/rules/successful-debugging-patterns.md +482 -478
- package/registry/rules/telemetry.md +67 -0
- package/registry/scripts/build-scripts-generator.ts +216 -215
- package/registry/scripts/cleanup-branch.ts +303 -284
- package/registry/scripts/code-quality-check.sh +559 -559
- package/registry/scripts/detect-tautological-tests.sh +38 -38
- package/registry/scripts/evaluate-code-quality.ts +1 -1
- package/registry/scripts/generate-engagement-emails.ts +744 -744
- package/registry/scripts/generic-issues-api.ts +110 -150
- package/registry/scripts/newsletter-helpers.ts +874 -874
- package/registry/scripts/openapi-generator.ts +695 -693
- package/registry/scripts/performance/profile-server.ts +5 -3
- package/registry/scripts/prep-issue.sh +468 -455
- package/registry/scripts/validate-openapi-limits.ts +366 -365
- package/registry/scripts/validate-test-coverage.ts +280 -280
- package/registry/scripts/verify-pr-comments.sh +70 -70
- package/registry/scripts/verify-test-coverage.ts +1 -1
- package/registry/templates/bootstrap/ARCHITECTURE-TEMPLATE.md +53 -53
- package/registry/templates/evidence/Implementation-BugEvidence.md +85 -85
- package/registry/templates/evidence/Implementation-FeatureEvidence.md +120 -120
- package/registry/templates/marketing/HBR-ARTICLE-TEMPLATE.md +66 -0
- package/registry/workflows/bootstrap/create-architecture.md +2 -2
- package/registry/workflows/bootstrap/evaluate-code-quality.md +3 -3
- package/registry/workflows/bootstrap/verify-test-coverage.md +2 -2
- package/registry/workflows/customer-development/insight-analysis.md +156 -156
- package/registry/workflows/customer-development/interview-preparation.md +421 -421
- package/registry/workflows/customer-development/strategic-brainstorming.md +146 -146
- package/registry/workflows/customer-development/thank-customers.md +193 -191
- package/registry/workflows/customer-development/weekly-newsletter.md +362 -352
- package/registry/workflows/improve-fraim/contribute.md +32 -0
- package/registry/workflows/improve-fraim/file-issue.md +32 -0
- package/registry/workflows/marketing/hbr-article.md +73 -0
- package/registry/workflows/performance/analyze-performance.md +63 -59
- package/registry/workflows/product-building/design.md +3 -2
- package/registry/workflows/product-building/implement.md +4 -3
- package/registry/workflows/product-building/prep-issue.md +28 -17
- package/registry/workflows/product-building/resolve.md +3 -2
- package/registry/workflows/product-building/retrospect.md +3 -2
- package/registry/workflows/product-building/spec.md +5 -4
- package/registry/workflows/product-building/test.md +3 -2
- package/registry/workflows/quality-assurance/iterative-improvement-cycle.md +562 -562
- package/registry/workflows/replicate/website-discovery-analysis.md +3 -3
- package/registry/workflows/reviewer/review-implementation-vs-design-spec.md +632 -632
- package/registry/workflows/reviewer/review-implementation-vs-feature-spec.md +669 -669
- package/tsconfig.json +2 -1
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const node_child_process_1 = require("node:child_process");
|
|
7
|
+
const test_utils_1 = require("./test-utils");
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const os_1 = __importDefault(require("os"));
|
|
11
|
+
async function testInitOnNodeVersion(version) {
|
|
12
|
+
console.log(` đ§Ē Testing Node v${version}...`);
|
|
13
|
+
// Create a temp directory for each version test
|
|
14
|
+
const tempDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), `fraim-node-${version}-`));
|
|
15
|
+
try {
|
|
16
|
+
const platform = process.platform;
|
|
17
|
+
const npx = platform === 'win32' ? 'npx.cmd' : 'npx';
|
|
18
|
+
const binPath = path_1.default.resolve(__dirname, '../bin/fraim.js');
|
|
19
|
+
// Run: npx -y -p node@<version> node bin/fraim.js init
|
|
20
|
+
// We use -y to skip prompt, and -p node@version to ensure that specific node binary is used
|
|
21
|
+
const execNode = (args) => {
|
|
22
|
+
return new Promise((resolve) => {
|
|
23
|
+
const ps = (0, node_child_process_1.spawn)(npx, ['-y', '-p', `node@${version}`, 'node', `"${binPath}"`, ...args], {
|
|
24
|
+
cwd: tempDir,
|
|
25
|
+
env: { ...process.env, TEST_MODE: 'true' },
|
|
26
|
+
shell: true
|
|
27
|
+
});
|
|
28
|
+
let stdout = '';
|
|
29
|
+
let stderr = '';
|
|
30
|
+
ps.stdout.on('data', d => stdout += d.toString());
|
|
31
|
+
ps.stderr.on('data', d => stderr += d.toString());
|
|
32
|
+
ps.on('close', (code) => resolve({ stdout, stderr, code }));
|
|
33
|
+
});
|
|
34
|
+
};
|
|
35
|
+
console.log(` Running "fraim init" on Node ${version}...`);
|
|
36
|
+
const result = await execNode(['init']);
|
|
37
|
+
if (result.code !== 0) {
|
|
38
|
+
console.error(` â Failed on Node ${version}`);
|
|
39
|
+
console.error(` Error: ${result.stderr}`);
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
const configPath = path_1.default.join(tempDir, '.fraim', 'config.json');
|
|
43
|
+
if (fs_1.default.existsSync(configPath)) {
|
|
44
|
+
console.log(` â
Success on Node ${version}`);
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
console.error(` â Config not found on Node ${version}`);
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
catch (e) {
|
|
53
|
+
console.error(` â Test Logic Error on Node ${version}:`, e.message);
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
finally {
|
|
57
|
+
try {
|
|
58
|
+
fs_1.default.rmSync(tempDir, { recursive: true, force: true });
|
|
59
|
+
}
|
|
60
|
+
catch (e) { }
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const nodeVersions = ['16', '18', '20', '22'];
|
|
64
|
+
const testCases = nodeVersions.map(v => ({
|
|
65
|
+
name: `Node v${v} Compatibility`,
|
|
66
|
+
version: v,
|
|
67
|
+
tags: ['node', 'compatibility']
|
|
68
|
+
}));
|
|
69
|
+
(0, test_utils_1.runTests)(testCases, async (t) => await testInitOnNodeVersion(t.version), 'Node Compatibility Matrix')
|
|
70
|
+
.then(() => process.exit(0))
|
|
71
|
+
.catch(() => process.exit(1));
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const node_child_process_1 = require("node:child_process");
|
|
7
|
+
const test_utils_1 = require("./test-utils");
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const os_1 = __importDefault(require("os"));
|
|
11
|
+
async function testCleanNpmInstall(version) {
|
|
12
|
+
console.log(` đ§Ē Testing NPM Install on Node v${version}...`);
|
|
13
|
+
// Create a temp directory for the install test
|
|
14
|
+
const tempDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), `fraim-npm-${version}-`));
|
|
15
|
+
try {
|
|
16
|
+
const platform = process.platform;
|
|
17
|
+
const npx = platform === 'win32' ? 'npx.cmd' : 'npx';
|
|
18
|
+
// Copy package.json to temp dir
|
|
19
|
+
const packageJsonPath = path_1.default.resolve(__dirname, '../package.json');
|
|
20
|
+
fs_1.default.copyFileSync(packageJsonPath, path_1.default.join(tempDir, 'package.json'));
|
|
21
|
+
// Run: npx -p node@<version> npm install --package-lock-only
|
|
22
|
+
// This uses the npm bundled with that specific Node version
|
|
23
|
+
const execNpm = () => {
|
|
24
|
+
return new Promise((resolve) => {
|
|
25
|
+
const ps = (0, node_child_process_1.spawn)(npx, ['-y', '-p', `node@${version}`, 'npm', 'install', '--package-lock-only', '--quiet', '--no-audit'], {
|
|
26
|
+
cwd: tempDir,
|
|
27
|
+
env: { ...process.env },
|
|
28
|
+
shell: true
|
|
29
|
+
});
|
|
30
|
+
let stdout = '';
|
|
31
|
+
let stderr = '';
|
|
32
|
+
ps.stdout.on('data', d => stdout += d.toString());
|
|
33
|
+
ps.stderr.on('data', d => stderr += d.toString());
|
|
34
|
+
ps.on('close', (code) => resolve({ stdout, stderr, code }));
|
|
35
|
+
});
|
|
36
|
+
};
|
|
37
|
+
console.log(` Running "npm install" with Node ${version}...`);
|
|
38
|
+
const result = await execNpm();
|
|
39
|
+
if (result.code !== 0) {
|
|
40
|
+
console.error(` â NPM Install Failed on Node ${version}`);
|
|
41
|
+
console.error(` Stderr: ${result.stderr}`);
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
console.log(` â
NPM Resolution Success on Node ${version}`);
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
catch (e) {
|
|
48
|
+
console.error(` â Test Logic Error on Node ${version}:`, e.message);
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
finally {
|
|
52
|
+
try {
|
|
53
|
+
fs_1.default.rmSync(tempDir, { recursive: true, force: true });
|
|
54
|
+
}
|
|
55
|
+
catch (e) { }
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const nodeVersions = ['16', '18', '20', '22'];
|
|
59
|
+
const testCases = nodeVersions.map(v => ({
|
|
60
|
+
name: `NPM Compatibility (Node v${v})`,
|
|
61
|
+
version: v,
|
|
62
|
+
tags: ['npm', 'compatibility']
|
|
63
|
+
}));
|
|
64
|
+
(0, test_utils_1.runTests)(testCases, async (t) => await testCleanNpmInstall(t.version), 'NPM Configuration Matrix')
|
|
65
|
+
.then(() => process.exit(0))
|
|
66
|
+
.catch(() => process.exit(1));
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Diagnostic test to understand npm's chalk resolution behavior
|
|
4
|
+
*
|
|
5
|
+
* This test will show us exactly what npm does when there's a version conflict
|
|
6
|
+
*/
|
|
7
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
8
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
9
|
+
};
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
const node_child_process_1 = require("node:child_process");
|
|
12
|
+
const fs_1 = __importDefault(require("fs"));
|
|
13
|
+
const path_1 = __importDefault(require("path"));
|
|
14
|
+
const os_1 = __importDefault(require("os"));
|
|
15
|
+
async function main() {
|
|
16
|
+
console.log('đ NPM Chalk Resolution Diagnostic\n');
|
|
17
|
+
const tempDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'npm-diagnostic-'));
|
|
18
|
+
console.log(`đ Temp dir: ${tempDir}\n`);
|
|
19
|
+
try {
|
|
20
|
+
// 1. Pack fraim-framework
|
|
21
|
+
console.log('đĻ Packing fraim-framework...');
|
|
22
|
+
const projectRoot = process.cwd();
|
|
23
|
+
const packResult = (0, node_child_process_1.execSync)('npm pack', {
|
|
24
|
+
cwd: projectRoot,
|
|
25
|
+
encoding: 'utf-8'
|
|
26
|
+
});
|
|
27
|
+
const tarballName = packResult.trim().split('\n').pop()?.trim();
|
|
28
|
+
if (!tarballName) {
|
|
29
|
+
console.log('â Failed to pack');
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const tarballPath = path_1.default.join(projectRoot, tarballName);
|
|
33
|
+
console.log(`â
Created: ${tarballName}\n`);
|
|
34
|
+
// 2. Create test project with chalk v5
|
|
35
|
+
console.log('đ Creating test project with chalk v5 dependency...');
|
|
36
|
+
const packageJson = {
|
|
37
|
+
name: 'diagnostic-test',
|
|
38
|
+
version: '1.0.0',
|
|
39
|
+
dependencies: {
|
|
40
|
+
'chalk': '^5.0.0',
|
|
41
|
+
'fraim-framework': `file:${tarballPath}`
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
fs_1.default.writeFileSync(path_1.default.join(tempDir, 'package.json'), JSON.stringify(packageJson, null, 2));
|
|
45
|
+
// 3. Install
|
|
46
|
+
console.log('đĨ Running npm install...\n');
|
|
47
|
+
try {
|
|
48
|
+
const installOutput = (0, node_child_process_1.execSync)('npm install', {
|
|
49
|
+
cwd: tempDir,
|
|
50
|
+
encoding: 'utf-8',
|
|
51
|
+
stdio: 'pipe'
|
|
52
|
+
});
|
|
53
|
+
console.log('â
Install completed\n');
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
console.log('â ī¸ Install had issues:\n', error.stdout || error.message);
|
|
57
|
+
}
|
|
58
|
+
// 4. Check what was installed
|
|
59
|
+
console.log('đ Checking installed chalk versions...\n');
|
|
60
|
+
// Root chalk
|
|
61
|
+
const rootChalkPath = path_1.default.join(tempDir, 'node_modules', 'chalk', 'package.json');
|
|
62
|
+
if (fs_1.default.existsSync(rootChalkPath)) {
|
|
63
|
+
const pkg = JSON.parse(fs_1.default.readFileSync(rootChalkPath, 'utf-8'));
|
|
64
|
+
console.log(` Root chalk: ${pkg.version}`);
|
|
65
|
+
console.log(` Type: ${pkg.type || 'commonjs'}`);
|
|
66
|
+
console.log(` Main: ${pkg.main || 'N/A'}`);
|
|
67
|
+
console.log(` Exports: ${pkg.exports ? 'Yes' : 'No'}\n`);
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
console.log(' â No root chalk found\n');
|
|
71
|
+
}
|
|
72
|
+
// Fraim's chalk
|
|
73
|
+
const fraimChalkPath = path_1.default.join(tempDir, 'node_modules', 'fraim-framework', 'node_modules', 'chalk', 'package.json');
|
|
74
|
+
if (fs_1.default.existsSync(fraimChalkPath)) {
|
|
75
|
+
const pkg = JSON.parse(fs_1.default.readFileSync(fraimChalkPath, 'utf-8'));
|
|
76
|
+
console.log(` fraim-framework's chalk: ${pkg.version}`);
|
|
77
|
+
console.log(` Type: ${pkg.type || 'commonjs'}`);
|
|
78
|
+
console.log(` Main: ${pkg.main || 'N/A'}\n`);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
console.log(' âšī¸ fraim-framework has no separate chalk (using root)\n');
|
|
82
|
+
}
|
|
83
|
+
// 5. Run npm ls to see the dependency tree
|
|
84
|
+
console.log('đŗ Dependency tree (npm ls chalk):\n');
|
|
85
|
+
try {
|
|
86
|
+
const lsOutput = (0, node_child_process_1.execSync)('npm ls chalk', {
|
|
87
|
+
cwd: tempDir,
|
|
88
|
+
encoding: 'utf-8'
|
|
89
|
+
});
|
|
90
|
+
console.log(lsOutput);
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
console.log(error.stdout || error.message);
|
|
94
|
+
}
|
|
95
|
+
// 6. Check fraim-framework's package.json in node_modules
|
|
96
|
+
console.log('\nđ fraim-framework\'s chalk dependency in node_modules:\n');
|
|
97
|
+
const fraimPkgPath = path_1.default.join(tempDir, 'node_modules', 'fraim-framework', 'package.json');
|
|
98
|
+
if (fs_1.default.existsSync(fraimPkgPath)) {
|
|
99
|
+
const fraimPkg = JSON.parse(fs_1.default.readFileSync(fraimPkgPath, 'utf-8'));
|
|
100
|
+
console.log(` Chalk version in package.json: ${fraimPkg.dependencies?.chalk || 'N/A'}\n`);
|
|
101
|
+
}
|
|
102
|
+
// 7. Try to load fraim CLI
|
|
103
|
+
console.log('đ Testing fraim CLI load...\n');
|
|
104
|
+
const testScript = `
|
|
105
|
+
try {
|
|
106
|
+
const fraimCli = require('./node_modules/fraim-framework/dist/src/cli/fraim.js');
|
|
107
|
+
console.log('â
SUCCESS: fraim CLI loaded');
|
|
108
|
+
} catch (error) {
|
|
109
|
+
console.log('â ERROR:', error.code || 'Unknown');
|
|
110
|
+
console.log('Message:', error.message.substring(0, 200));
|
|
111
|
+
}
|
|
112
|
+
`;
|
|
113
|
+
fs_1.default.writeFileSync(path_1.default.join(tempDir, 'test.js'), testScript);
|
|
114
|
+
try {
|
|
115
|
+
const testOutput = (0, node_child_process_1.execSync)('node test.js', {
|
|
116
|
+
cwd: tempDir,
|
|
117
|
+
encoding: 'utf-8'
|
|
118
|
+
});
|
|
119
|
+
console.log(testOutput);
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
console.log(error.stdout || error.message);
|
|
123
|
+
}
|
|
124
|
+
// Cleanup
|
|
125
|
+
fs_1.default.unlinkSync(tarballPath);
|
|
126
|
+
console.log('\nâ
Diagnostic complete');
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
console.error('\nâ Diagnostic failed:', error);
|
|
130
|
+
}
|
|
131
|
+
finally {
|
|
132
|
+
try {
|
|
133
|
+
fs_1.default.rmSync(tempDir, { recursive: true, force: true });
|
|
134
|
+
}
|
|
135
|
+
catch (e) {
|
|
136
|
+
console.log('\nâ ī¸ Could not clean up temp directory');
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
main();
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const node_child_process_1 = require("node:child_process");
|
|
7
|
+
const axios_1 = __importDefault(require("axios"));
|
|
8
|
+
const db_service_js_1 = require("../src/fraim/db-service.js");
|
|
9
|
+
const test_utils_1 = require("./test-utils");
|
|
10
|
+
const node_assert_1 = __importDefault(require("node:assert"));
|
|
11
|
+
const tree_kill_1 = __importDefault(require("tree-kill"));
|
|
12
|
+
const path_1 = __importDefault(require("path"));
|
|
13
|
+
async function testRehydrationAndExemptions() {
|
|
14
|
+
console.log(' đ Testing Session Re-hydration and Initialization Exemptions...');
|
|
15
|
+
let fraimProcess;
|
|
16
|
+
let dbService;
|
|
17
|
+
const PORT = Math.floor(Math.random() * 1000) + 12000;
|
|
18
|
+
const TEST_API_KEY = 'test-rehydration-key';
|
|
19
|
+
const TEST_ADMIN_KEY = 'test-admin-key';
|
|
20
|
+
const BASE_URL = `http://localhost:${PORT}`;
|
|
21
|
+
const startServer = async () => {
|
|
22
|
+
console.log(` Starting server on port ${PORT}...`);
|
|
23
|
+
const npxCommand = process.platform === 'win32' ? 'npx.cmd' : 'npx';
|
|
24
|
+
const serverScript = path_1.default.resolve(__dirname, '../src/fraim-mcp-server.ts');
|
|
25
|
+
const proc = (0, node_child_process_1.spawn)(npxCommand, ['tsx', `"${serverScript}"`], {
|
|
26
|
+
env: {
|
|
27
|
+
...process.env,
|
|
28
|
+
FRAIM_MCP_PORT: PORT.toString(),
|
|
29
|
+
FRAIM_ADMIN_KEY: TEST_ADMIN_KEY,
|
|
30
|
+
FRAIM_SKIP_INDEX_ON_START: 'true'
|
|
31
|
+
},
|
|
32
|
+
stdio: 'inherit',
|
|
33
|
+
shell: true
|
|
34
|
+
});
|
|
35
|
+
let started = false;
|
|
36
|
+
for (let i = 0; i < 15; i++) {
|
|
37
|
+
try {
|
|
38
|
+
await axios_1.default.get(`${BASE_URL}/health`, { timeout: 500 });
|
|
39
|
+
started = true;
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
catch (e) {
|
|
43
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (!started)
|
|
47
|
+
throw new Error('Server failed to start');
|
|
48
|
+
return proc;
|
|
49
|
+
};
|
|
50
|
+
const stopServer = async (proc) => {
|
|
51
|
+
if (proc && proc.pid) {
|
|
52
|
+
await new Promise(resolve => (0, tree_kill_1.default)(proc.pid, 'SIGKILL', () => resolve()));
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
try {
|
|
56
|
+
// 1. Setup DB and Key
|
|
57
|
+
dbService = new db_service_js_1.FraimDbService();
|
|
58
|
+
await dbService.connect();
|
|
59
|
+
const db = dbService.db;
|
|
60
|
+
await db.collection('fraim_api_keys').deleteOne({ key: TEST_API_KEY });
|
|
61
|
+
await db.collection('fraim_telemetry_sessions').deleteMany({ userId: 'test-user-recalc' });
|
|
62
|
+
await db.collection('fraim_api_keys').insertOne({
|
|
63
|
+
key: TEST_API_KEY,
|
|
64
|
+
userId: 'test-user-recalc',
|
|
65
|
+
orgId: 'test-org',
|
|
66
|
+
isActive: true,
|
|
67
|
+
createdAt: new Date()
|
|
68
|
+
});
|
|
69
|
+
// 2. Start Server (Instance 1)
|
|
70
|
+
fraimProcess = await startServer();
|
|
71
|
+
// 3. Verify Initialization Exemptions
|
|
72
|
+
console.log(' Testing standard MCP exemptions (initialize, tools/list)...');
|
|
73
|
+
const initRes = await axios_1.default.post(`${BASE_URL}/mcp`, {
|
|
74
|
+
jsonrpc: '2.0', id: 1, method: 'initialize', params: {}
|
|
75
|
+
}, { headers: { 'x-api-key': TEST_API_KEY } });
|
|
76
|
+
node_assert_1.default.strictEqual(initRes.status, 200, 'initialize should be exempt');
|
|
77
|
+
const listRes = await axios_1.default.post(`${BASE_URL}/mcp`, {
|
|
78
|
+
jsonrpc: '2.0', id: 2, method: 'tools/list', params: {}
|
|
79
|
+
}, { headers: { 'x-api-key': TEST_API_KEY } });
|
|
80
|
+
node_assert_1.default.strictEqual(listRes.status, 200, 'tools/list should be exempt');
|
|
81
|
+
console.log(' Testing discovery tools exemptions (get_fraim_init)...');
|
|
82
|
+
const discoveryRes = await axios_1.default.post(`${BASE_URL}/mcp`, {
|
|
83
|
+
jsonrpc: '2.0', id: 3, method: 'tools/call',
|
|
84
|
+
params: { name: 'get_fraim_init', arguments: {} }
|
|
85
|
+
}, { headers: { 'x-api-key': TEST_API_KEY } });
|
|
86
|
+
node_assert_1.default.strictEqual(discoveryRes.status, 200, 'get_fraim_init should be exempt');
|
|
87
|
+
console.log(' â
Exemptions verified.');
|
|
88
|
+
// 4. Start Session
|
|
89
|
+
console.log(' Starting session...');
|
|
90
|
+
await axios_1.default.post(`${BASE_URL}/mcp`, {
|
|
91
|
+
jsonrpc: '2.0', id: 4, method: 'tools/call',
|
|
92
|
+
params: {
|
|
93
|
+
name: 'fraim_connect',
|
|
94
|
+
arguments: {
|
|
95
|
+
machine: { hostname: 'repro-host' },
|
|
96
|
+
repo: { url: 'http://github.com/repro' }
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}, { headers: { 'x-api-key': TEST_API_KEY } });
|
|
100
|
+
// 5. Restart Server (Instance 2)
|
|
101
|
+
console.log(' âģī¸ Swapping server instances (Simulating restart)...');
|
|
102
|
+
await stopServer(fraimProcess);
|
|
103
|
+
fraimProcess = await startServer();
|
|
104
|
+
// 6. Verify Re-hydration
|
|
105
|
+
console.log(' Checking if session re-hydrated from DB...');
|
|
106
|
+
const secureRes = await axios_1.default.post(`${BASE_URL}/mcp`, {
|
|
107
|
+
jsonrpc: '2.0', id: 5, method: 'tools/call',
|
|
108
|
+
params: { name: 'get_fraim_workflow', arguments: { workflow: 'test' } }
|
|
109
|
+
}, { headers: { 'x-api-key': TEST_API_KEY } });
|
|
110
|
+
node_assert_1.default.strictEqual(secureRes.status, 200, 'Request should be authorized via re-hydrated session');
|
|
111
|
+
console.log(' â
Re-hydration successful!');
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
console.error(' â Test failed:', error.message);
|
|
116
|
+
if (error.response?.data) {
|
|
117
|
+
console.error(' đ Server Error Detail:', JSON.stringify(error.response.data, null, 2));
|
|
118
|
+
}
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
finally {
|
|
122
|
+
if (dbService) {
|
|
123
|
+
const db = dbService.db;
|
|
124
|
+
await db.collection('fraim_api_keys').deleteOne({ key: TEST_API_KEY }).catch(() => { });
|
|
125
|
+
await db.collection('fraim_telemetry_sessions').deleteMany({ userId: 'test-user-recalc' }).catch(() => { });
|
|
126
|
+
await dbService.close();
|
|
127
|
+
}
|
|
128
|
+
if (fraimProcess)
|
|
129
|
+
await stopServer(fraimProcess);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
const testCases = [
|
|
133
|
+
{
|
|
134
|
+
name: 'MCP Session Re-hydration and Initialization',
|
|
135
|
+
description: 'Verifies re-hydration from DB after restart and initialization exemptions',
|
|
136
|
+
testFunction: testRehydrationAndExemptions,
|
|
137
|
+
tags: ['session', 'rehydration']
|
|
138
|
+
}
|
|
139
|
+
];
|
|
140
|
+
(0, test_utils_1.runTests)(testCases, async (t) => t.testFunction(), 'Fraim Session System')
|
|
141
|
+
.then(() => process.exit(0))
|
|
142
|
+
.catch((err) => {
|
|
143
|
+
console.error('Test runner failed:', err);
|
|
144
|
+
process.exit(1);
|
|
145
|
+
});
|
|
@@ -98,13 +98,7 @@ async function testServerStartsAndResponds() {
|
|
|
98
98
|
});
|
|
99
99
|
node_assert_1.default.strictEqual(mcpResponse.status, 200);
|
|
100
100
|
node_assert_1.default.ok(mcpResponse.data.result.tools.length > 0);
|
|
101
|
-
// 7.
|
|
102
|
-
console.log(' Verifying usage logging...');
|
|
103
|
-
await new Promise(resolve => setTimeout(resolve, 1500));
|
|
104
|
-
const log = await db.collection('fraim_usage_logs').findOne({ keyId: TEST_API_KEY });
|
|
105
|
-
node_assert_1.default.ok(log, 'Usage log should have been created');
|
|
106
|
-
node_assert_1.default.strictEqual(log.userId, 'test-user@ashley.ai');
|
|
107
|
-
// 8. Test Admin API - List Keys
|
|
101
|
+
// 7. Test Admin API - List Keys
|
|
108
102
|
console.log(' Testing Admin API - List Keys...');
|
|
109
103
|
const listKeysRes = await axios_1.default.get(`${BASE_URL}/admin/keys`, {
|
|
110
104
|
headers: TEST_ADMIN_HEADER,
|
|
@@ -141,7 +135,7 @@ async function runFraimTest(testCase) {
|
|
|
141
135
|
const testCases = [
|
|
142
136
|
{
|
|
143
137
|
name: 'Fraim Standalone Server Integration',
|
|
144
|
-
description: 'Tests server startup, public health check, authenticated MCP access,
|
|
138
|
+
description: 'Tests server startup, public health check, authenticated MCP access, and admin API',
|
|
145
139
|
testFunction: testServerStartsAndResponds,
|
|
146
140
|
tags: ['smoke', 'fraim']
|
|
147
141
|
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const node_child_process_1 = require("node:child_process");
|
|
7
|
+
const test_utils_1 = require("./test-utils");
|
|
8
|
+
const node_assert_1 = __importDefault(require("node:assert"));
|
|
9
|
+
const fs_1 = __importDefault(require("fs"));
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
11
|
+
const os_1 = __importDefault(require("os"));
|
|
12
|
+
const version_utils_1 = require("../src/utils/version-utils");
|
|
13
|
+
const types_1 = require("../src/fraim/types");
|
|
14
|
+
async function testSyncUpdateVersion() {
|
|
15
|
+
console.log(' đ Testing "fraim sync" version update...');
|
|
16
|
+
// Create a temp directory for the test project
|
|
17
|
+
const tempDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'fraim-test-sync-'));
|
|
18
|
+
console.log(` đ Created temp dir: ${tempDir}`);
|
|
19
|
+
try {
|
|
20
|
+
const platform = process.platform;
|
|
21
|
+
const npx = platform === 'win32' ? 'npx.cmd' : 'npx';
|
|
22
|
+
const cliScript = path_1.default.resolve(__dirname, '../src/cli/fraim.ts');
|
|
23
|
+
// Setup .fraim/config.json with an OLD version
|
|
24
|
+
const fraimDir = path_1.default.join(tempDir, '.fraim');
|
|
25
|
+
fs_1.default.mkdirSync(fraimDir, { recursive: true });
|
|
26
|
+
const oldVersion = '0.0.0';
|
|
27
|
+
const initialConfig = {
|
|
28
|
+
...types_1.DEFAULT_FRAIM_CONFIG,
|
|
29
|
+
version: oldVersion,
|
|
30
|
+
project: { name: 'test-project' },
|
|
31
|
+
git: { repoOwner: 'test', repoName: 'test' }
|
|
32
|
+
};
|
|
33
|
+
fs_1.default.writeFileSync(path_1.default.join(fraimDir, 'config.json'), JSON.stringify(initialConfig, null, 2));
|
|
34
|
+
// Create a fake registry so sync doesn't fail hard
|
|
35
|
+
const registryDir = path_1.default.join(tempDir, 'registry', 'workflows');
|
|
36
|
+
fs_1.default.mkdirSync(registryDir, { recursive: true });
|
|
37
|
+
fs_1.default.writeFileSync(path_1.default.join(registryDir, 'dummy.md'), '---\nintent: test\n---\n# Dummy');
|
|
38
|
+
// Helper to run CLI commands
|
|
39
|
+
const runFraim = (args) => {
|
|
40
|
+
return new Promise((resolve) => {
|
|
41
|
+
const ps = (0, node_child_process_1.spawn)(npx, ['tsx', `"${cliScript}"`, ...args], {
|
|
42
|
+
cwd: tempDir,
|
|
43
|
+
env: { ...process.env, TEST_MODE: 'true' },
|
|
44
|
+
shell: true
|
|
45
|
+
});
|
|
46
|
+
let stdout = '';
|
|
47
|
+
let stderr = '';
|
|
48
|
+
ps.stdout.on('data', d => stdout += d.toString());
|
|
49
|
+
ps.stderr.on('data', d => stderr += d.toString());
|
|
50
|
+
ps.on('close', (code) => resolve({ stdout, stderr, code }));
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
// Run `fraim sync`
|
|
54
|
+
console.log(' Running "fraim sync"...');
|
|
55
|
+
const res = await runFraim(['sync']);
|
|
56
|
+
if (res.code !== 0) {
|
|
57
|
+
console.error(' â Sync failed:', res.stderr);
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
// Verify config.json has the NEW/Current version
|
|
61
|
+
const updatedConfig = JSON.parse(fs_1.default.readFileSync(path_1.default.join(fraimDir, 'config.json'), 'utf-8'));
|
|
62
|
+
const currentPackageVersion = (0, version_utils_1.getFraimVersion)();
|
|
63
|
+
console.log(` âšī¸ Old Version: ${oldVersion}`);
|
|
64
|
+
console.log(` âšī¸ New Version in Config: ${updatedConfig.version}`);
|
|
65
|
+
console.log(` âšī¸ Current Package Version: ${currentPackageVersion}`);
|
|
66
|
+
node_assert_1.default.strictEqual(updatedConfig.version, currentPackageVersion, 'Config version should match package version after sync');
|
|
67
|
+
node_assert_1.default.notStrictEqual(updatedConfig.version, oldVersion, 'Config version should have been updated from 0.0.0');
|
|
68
|
+
console.log(' â
Validated: Version was updated correctly.');
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
console.error(' â Test failed:', error);
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
finally {
|
|
76
|
+
try {
|
|
77
|
+
fs_1.default.rmSync(tempDir, { recursive: true, force: true });
|
|
78
|
+
}
|
|
79
|
+
catch (e) { }
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
async function runTest(testCase) {
|
|
83
|
+
return await testCase.testFunction();
|
|
84
|
+
}
|
|
85
|
+
const testCases = [
|
|
86
|
+
{
|
|
87
|
+
name: 'Sync Version Update',
|
|
88
|
+
description: 'Verifies that fraim sync updates the version in config.json',
|
|
89
|
+
testFunction: testSyncUpdateVersion,
|
|
90
|
+
tags: ['cli', 'sync']
|
|
91
|
+
},
|
|
92
|
+
];
|
|
93
|
+
(0, test_utils_1.runTests)(testCases, runTest, 'Fraim Sync Update Tests');
|