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.
Files changed (104) hide show
  1. package/.github/workflows/deploy-fraim.yml +1 -1
  2. package/dist/registry/scripts/build-scripts-generator.js +205 -0
  3. package/dist/registry/scripts/cleanup-branch.js +258 -0
  4. package/dist/registry/scripts/evaluate-code-quality.js +66 -0
  5. package/dist/registry/scripts/exec-with-timeout.js +142 -0
  6. package/dist/registry/scripts/fraim-config.js +61 -0
  7. package/dist/registry/scripts/generate-engagement-emails.js +630 -0
  8. package/dist/registry/scripts/generic-issues-api.js +100 -0
  9. package/dist/registry/scripts/newsletter-helpers.js +731 -0
  10. package/dist/registry/scripts/openapi-generator.js +664 -0
  11. package/dist/registry/scripts/performance/profile-server.js +390 -0
  12. package/dist/registry/scripts/run-thank-you-workflow.js +92 -0
  13. package/dist/registry/scripts/send-newsletter-simple.js +85 -0
  14. package/dist/registry/scripts/send-thank-you-emails.js +54 -0
  15. package/dist/registry/scripts/validate-openapi-limits.js +311 -0
  16. package/dist/registry/scripts/validate-test-coverage.js +262 -0
  17. package/dist/registry/scripts/verify-test-coverage.js +66 -0
  18. package/dist/src/cli/commands/init.js +14 -12
  19. package/dist/src/cli/commands/sync.js +19 -2
  20. package/dist/src/cli/fraim.js +24 -22
  21. package/dist/src/cli/setup/first-run.js +13 -6
  22. package/dist/src/fraim/config-loader.js +0 -8
  23. package/dist/src/fraim/db-service.js +26 -15
  24. package/dist/src/fraim/issues.js +67 -0
  25. package/dist/src/fraim/setup-wizard.js +1 -69
  26. package/dist/src/fraim/types.js +0 -11
  27. package/dist/src/fraim-mcp-server.js +272 -18
  28. package/dist/src/utils/git-utils.js +1 -1
  29. package/dist/src/utils/version-utils.js +32 -0
  30. package/dist/tests/debug-tools.js +79 -0
  31. package/dist/tests/esm-compat.js +11 -0
  32. package/dist/tests/test-chalk-esm-issue.js +159 -0
  33. package/dist/tests/test-chalk-real-world.js +265 -0
  34. package/dist/tests/test-chalk-regression.js +327 -0
  35. package/dist/tests/test-chalk-resolution-issue.js +304 -0
  36. package/dist/tests/test-cli.js +0 -2
  37. package/dist/tests/test-fraim-install-chalk-issue.js +254 -0
  38. package/dist/tests/test-fraim-issues.js +59 -0
  39. package/dist/tests/test-genericization.js +1 -3
  40. package/dist/tests/test-mcp-connection.js +166 -0
  41. package/dist/tests/test-mcp-issue-integration.js +144 -0
  42. package/dist/tests/test-mcp-lifecycle-methods.js +312 -0
  43. package/dist/tests/test-node-compatibility.js +71 -0
  44. package/dist/tests/test-npm-install.js +66 -0
  45. package/dist/tests/test-npm-resolution-diagnostic.js +140 -0
  46. package/dist/tests/test-session-rehydration.js +145 -0
  47. package/dist/tests/test-standalone.js +2 -8
  48. package/dist/tests/test-sync-version-update.js +93 -0
  49. package/dist/tests/test-telemetry.js +190 -0
  50. package/package.json +10 -8
  51. package/registry/agent-guardrails.md +62 -54
  52. package/registry/rules/agent-success-criteria.md +52 -0
  53. package/registry/rules/agent-testing-guidelines.md +502 -502
  54. package/registry/rules/communication.md +121 -121
  55. package/registry/rules/continuous-learning.md +54 -54
  56. package/registry/rules/ephemeral-execution.md +10 -5
  57. package/registry/rules/hitl-ppe-record-analysis.md +302 -302
  58. package/registry/rules/local-development.md +251 -251
  59. package/registry/rules/software-development-lifecycle.md +104 -104
  60. package/registry/rules/successful-debugging-patterns.md +482 -478
  61. package/registry/rules/telemetry.md +67 -0
  62. package/registry/scripts/build-scripts-generator.ts +216 -215
  63. package/registry/scripts/cleanup-branch.ts +303 -284
  64. package/registry/scripts/code-quality-check.sh +559 -559
  65. package/registry/scripts/detect-tautological-tests.sh +38 -38
  66. package/registry/scripts/evaluate-code-quality.ts +1 -1
  67. package/registry/scripts/generate-engagement-emails.ts +744 -744
  68. package/registry/scripts/generic-issues-api.ts +110 -150
  69. package/registry/scripts/newsletter-helpers.ts +874 -874
  70. package/registry/scripts/openapi-generator.ts +695 -693
  71. package/registry/scripts/performance/profile-server.ts +5 -3
  72. package/registry/scripts/prep-issue.sh +468 -455
  73. package/registry/scripts/validate-openapi-limits.ts +366 -365
  74. package/registry/scripts/validate-test-coverage.ts +280 -280
  75. package/registry/scripts/verify-pr-comments.sh +70 -70
  76. package/registry/scripts/verify-test-coverage.ts +1 -1
  77. package/registry/templates/bootstrap/ARCHITECTURE-TEMPLATE.md +53 -53
  78. package/registry/templates/evidence/Implementation-BugEvidence.md +85 -85
  79. package/registry/templates/evidence/Implementation-FeatureEvidence.md +120 -120
  80. package/registry/templates/marketing/HBR-ARTICLE-TEMPLATE.md +66 -0
  81. package/registry/workflows/bootstrap/create-architecture.md +2 -2
  82. package/registry/workflows/bootstrap/evaluate-code-quality.md +3 -3
  83. package/registry/workflows/bootstrap/verify-test-coverage.md +2 -2
  84. package/registry/workflows/customer-development/insight-analysis.md +156 -156
  85. package/registry/workflows/customer-development/interview-preparation.md +421 -421
  86. package/registry/workflows/customer-development/strategic-brainstorming.md +146 -146
  87. package/registry/workflows/customer-development/thank-customers.md +193 -191
  88. package/registry/workflows/customer-development/weekly-newsletter.md +362 -352
  89. package/registry/workflows/improve-fraim/contribute.md +32 -0
  90. package/registry/workflows/improve-fraim/file-issue.md +32 -0
  91. package/registry/workflows/marketing/hbr-article.md +73 -0
  92. package/registry/workflows/performance/analyze-performance.md +63 -59
  93. package/registry/workflows/product-building/design.md +3 -2
  94. package/registry/workflows/product-building/implement.md +4 -3
  95. package/registry/workflows/product-building/prep-issue.md +28 -17
  96. package/registry/workflows/product-building/resolve.md +3 -2
  97. package/registry/workflows/product-building/retrospect.md +3 -2
  98. package/registry/workflows/product-building/spec.md +5 -4
  99. package/registry/workflows/product-building/test.md +3 -2
  100. package/registry/workflows/quality-assurance/iterative-improvement-cycle.md +562 -562
  101. package/registry/workflows/replicate/website-discovery-analysis.md +3 -3
  102. package/registry/workflows/reviewer/review-implementation-vs-design-spec.md +632 -632
  103. package/registry/workflows/reviewer/review-implementation-vs-feature-spec.md +669 -669
  104. 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. Verify usage logging
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, usage logging, and admin API',
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');