fraim-framework 2.0.38 → 2.0.42

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.
@@ -0,0 +1,322 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ const node_test_1 = require("node:test");
40
+ const node_assert_1 = __importDefault(require("node:assert"));
41
+ const fs_1 = __importDefault(require("fs"));
42
+ const path_1 = __importDefault(require("path"));
43
+ const os_1 = __importDefault(require("os"));
44
+ // Test various setup scenarios without interactive prompts
45
+ (0, node_test_1.test)('Setup Scenarios - Fresh Installation', async () => {
46
+ const tempHomeDir = path_1.default.join(os_1.default.tmpdir(), '.fraim-test-fresh-' + Date.now());
47
+ const originalHomedir = os_1.default.homedir;
48
+ os_1.default.homedir = () => tempHomeDir;
49
+ try {
50
+ // Test 1: No existing global config
51
+ const globalConfigPath = path_1.default.join(tempHomeDir, '.fraim', 'config.json');
52
+ // Verify no config exists initially
53
+ (0, node_assert_1.default)(!fs_1.default.existsSync(globalConfigPath), 'No global config should exist initially');
54
+ // Simulate what setup command would create
55
+ fs_1.default.mkdirSync(path_1.default.dirname(globalConfigPath), { recursive: true });
56
+ const newConfig = {
57
+ version: '2.0.27',
58
+ apiKey: 'fraim_fresh_install_key',
59
+ githubToken: 'ghp_fresh_install_token',
60
+ configuredAt: new Date().toISOString(),
61
+ userPreferences: {
62
+ autoSync: true,
63
+ backupConfigs: true
64
+ }
65
+ };
66
+ fs_1.default.writeFileSync(globalConfigPath, JSON.stringify(newConfig, null, 2));
67
+ // Verify config was created correctly
68
+ (0, node_assert_1.default)(fs_1.default.existsSync(globalConfigPath), 'Global config should be created');
69
+ const savedConfig = JSON.parse(fs_1.default.readFileSync(globalConfigPath, 'utf8'));
70
+ node_assert_1.default.strictEqual(savedConfig.apiKey, 'fraim_fresh_install_key', 'FRAIM key should be saved');
71
+ node_assert_1.default.strictEqual(savedConfig.githubToken, 'ghp_fresh_install_token', 'GitHub token should be saved');
72
+ (0, node_assert_1.default)(savedConfig.userPreferences, 'User preferences should be saved');
73
+ console.log('✅ Fresh installation scenario passed');
74
+ }
75
+ finally {
76
+ // Cleanup
77
+ fs_1.default.rmSync(tempHomeDir, { recursive: true, force: true });
78
+ os_1.default.homedir = originalHomedir;
79
+ }
80
+ });
81
+ (0, node_test_1.test)('Setup Scenarios - Existing Config Without GitHub Token', async () => {
82
+ const tempHomeDir = path_1.default.join(os_1.default.tmpdir(), '.fraim-test-existing-' + Date.now());
83
+ const originalHomedir = os_1.default.homedir;
84
+ os_1.default.homedir = () => tempHomeDir;
85
+ try {
86
+ const globalConfigPath = path_1.default.join(tempHomeDir, '.fraim', 'config.json');
87
+ // Create existing config without GitHub token (simulates old version)
88
+ fs_1.default.mkdirSync(path_1.default.dirname(globalConfigPath), { recursive: true });
89
+ const oldConfig = {
90
+ version: '2.0.20',
91
+ apiKey: 'fraim_existing_key',
92
+ configuredAt: '2024-01-01T00:00:00.000Z',
93
+ userPreferences: {
94
+ autoSync: true,
95
+ backupConfigs: true
96
+ }
97
+ // No githubToken
98
+ };
99
+ fs_1.default.writeFileSync(globalConfigPath, JSON.stringify(oldConfig, null, 2));
100
+ // Test loadGlobalConfig behavior with missing GitHub token
101
+ const { loadGlobalConfig } = await Promise.resolve().then(() => __importStar(require('../src/cli/commands/add-ide')));
102
+ const result = loadGlobalConfig();
103
+ (0, node_assert_1.default)(result, 'Should load existing config');
104
+ node_assert_1.default.strictEqual(result.fraimKey, 'fraim_existing_key', 'Should preserve existing FRAIM key');
105
+ node_assert_1.default.strictEqual(result.githubToken, undefined, 'GitHub token should be undefined');
106
+ // Simulate adding GitHub token (what add-ide would do)
107
+ const updatedConfig = {
108
+ ...oldConfig,
109
+ githubToken: 'ghp_newly_added_token',
110
+ version: '2.0.27'
111
+ };
112
+ fs_1.default.writeFileSync(globalConfigPath, JSON.stringify(updatedConfig, null, 2));
113
+ // Verify token was added
114
+ const result2 = loadGlobalConfig();
115
+ (0, node_assert_1.default)(result2, 'Should load updated config');
116
+ node_assert_1.default.strictEqual(result2.fraimKey, 'fraim_existing_key', 'Should preserve existing FRAIM key');
117
+ node_assert_1.default.strictEqual(result2.githubToken, 'ghp_newly_added_token', 'Should have new GitHub token');
118
+ console.log('✅ Existing config without GitHub token scenario passed');
119
+ }
120
+ finally {
121
+ // Cleanup
122
+ fs_1.default.rmSync(tempHomeDir, { recursive: true, force: true });
123
+ os_1.default.homedir = originalHomedir;
124
+ }
125
+ });
126
+ (0, node_test_1.test)('Setup Scenarios - Corrupted Config Recovery', async () => {
127
+ const tempHomeDir = path_1.default.join(os_1.default.tmpdir(), '.fraim-test-corrupted-' + Date.now());
128
+ const originalHomedir = os_1.default.homedir;
129
+ os_1.default.homedir = () => tempHomeDir;
130
+ try {
131
+ const globalConfigPath = path_1.default.join(tempHomeDir, '.fraim', 'config.json');
132
+ // Create corrupted config file
133
+ fs_1.default.mkdirSync(path_1.default.dirname(globalConfigPath), { recursive: true });
134
+ fs_1.default.writeFileSync(globalConfigPath, 'invalid json content {');
135
+ // Test loadGlobalConfig behavior with corrupted file
136
+ const { loadGlobalConfig } = await Promise.resolve().then(() => __importStar(require('../src/cli/commands/add-ide')));
137
+ const result = loadGlobalConfig();
138
+ node_assert_1.default.strictEqual(result, null, 'Should return null for corrupted config');
139
+ // Simulate recovery by creating new config
140
+ const newConfig = {
141
+ version: '2.0.27',
142
+ apiKey: 'fraim_recovered_key',
143
+ githubToken: 'ghp_recovered_token',
144
+ configuredAt: new Date().toISOString(),
145
+ userPreferences: {
146
+ autoSync: true,
147
+ backupConfigs: true
148
+ }
149
+ };
150
+ fs_1.default.writeFileSync(globalConfigPath, JSON.stringify(newConfig, null, 2));
151
+ // Verify recovery
152
+ const result2 = loadGlobalConfig();
153
+ (0, node_assert_1.default)(result2, 'Should load recovered config');
154
+ node_assert_1.default.strictEqual(result2.fraimKey, 'fraim_recovered_key', 'Should have recovered FRAIM key');
155
+ node_assert_1.default.strictEqual(result2.githubToken, 'ghp_recovered_token', 'Should have recovered GitHub token');
156
+ console.log('✅ Corrupted config recovery scenario passed');
157
+ }
158
+ finally {
159
+ // Cleanup
160
+ fs_1.default.rmSync(tempHomeDir, { recursive: true, force: true });
161
+ os_1.default.homedir = originalHomedir;
162
+ }
163
+ });
164
+ (0, node_test_1.test)('Setup Scenarios - Multiple Users Same Machine', async () => {
165
+ // Simulate multiple users on the same machine
166
+ const user1HomeDir = path_1.default.join(os_1.default.tmpdir(), '.fraim-test-user1-' + Date.now());
167
+ const user2HomeDir = path_1.default.join(os_1.default.tmpdir(), '.fraim-test-user2-' + Date.now());
168
+ const originalHomedir = os_1.default.homedir;
169
+ try {
170
+ // User 1 setup
171
+ os_1.default.homedir = () => user1HomeDir;
172
+ const user1ConfigPath = path_1.default.join(user1HomeDir, '.fraim', 'config.json');
173
+ fs_1.default.mkdirSync(path_1.default.dirname(user1ConfigPath), { recursive: true });
174
+ const user1Config = {
175
+ version: '2.0.27',
176
+ apiKey: 'fraim_user1_key',
177
+ githubToken: 'ghp_user1_token',
178
+ configuredAt: new Date().toISOString(),
179
+ userPreferences: {
180
+ autoSync: true,
181
+ backupConfigs: true
182
+ }
183
+ };
184
+ fs_1.default.writeFileSync(user1ConfigPath, JSON.stringify(user1Config, null, 2));
185
+ // User 2 setup
186
+ os_1.default.homedir = () => user2HomeDir;
187
+ const user2ConfigPath = path_1.default.join(user2HomeDir, '.fraim', 'config.json');
188
+ fs_1.default.mkdirSync(path_1.default.dirname(user2ConfigPath), { recursive: true });
189
+ const user2Config = {
190
+ version: '2.0.27',
191
+ apiKey: 'fraim_user2_key',
192
+ githubToken: 'ghp_user2_token',
193
+ configuredAt: new Date().toISOString(),
194
+ userPreferences: {
195
+ autoSync: false,
196
+ backupConfigs: false
197
+ }
198
+ };
199
+ fs_1.default.writeFileSync(user2ConfigPath, JSON.stringify(user2Config, null, 2));
200
+ // Verify both configs exist and are separate
201
+ (0, node_assert_1.default)(fs_1.default.existsSync(user1ConfigPath), 'User 1 config should exist');
202
+ (0, node_assert_1.default)(fs_1.default.existsSync(user2ConfigPath), 'User 2 config should exist');
203
+ const user1Data = JSON.parse(fs_1.default.readFileSync(user1ConfigPath, 'utf8'));
204
+ const user2Data = JSON.parse(fs_1.default.readFileSync(user2ConfigPath, 'utf8'));
205
+ // Verify they have different keys
206
+ node_assert_1.default.strictEqual(user1Data.apiKey, 'fraim_user1_key', 'User 1 should have their own key');
207
+ node_assert_1.default.strictEqual(user2Data.apiKey, 'fraim_user2_key', 'User 2 should have their own key');
208
+ node_assert_1.default.notStrictEqual(user1Data.apiKey, user2Data.apiKey, 'Users should have different keys');
209
+ // Verify they have different preferences
210
+ node_assert_1.default.strictEqual(user1Data.userPreferences.autoSync, true, 'User 1 preferences');
211
+ node_assert_1.default.strictEqual(user2Data.userPreferences.autoSync, false, 'User 2 preferences');
212
+ console.log('✅ Multiple users scenario passed');
213
+ }
214
+ finally {
215
+ // Cleanup
216
+ fs_1.default.rmSync(user1HomeDir, { recursive: true, force: true });
217
+ fs_1.default.rmSync(user2HomeDir, { recursive: true, force: true });
218
+ os_1.default.homedir = originalHomedir;
219
+ }
220
+ });
221
+ (0, node_test_1.test)('Setup Scenarios - Project Config Independence', async () => {
222
+ const tempHomeDir = path_1.default.join(os_1.default.tmpdir(), '.fraim-test-independence-home-' + Date.now());
223
+ const project1Dir = path_1.default.join(os_1.default.tmpdir(), '.fraim-test-project1-' + Date.now());
224
+ const project2Dir = path_1.default.join(os_1.default.tmpdir(), '.fraim-test-project2-' + Date.now());
225
+ const originalHomedir = os_1.default.homedir;
226
+ const originalCwd = process.cwd;
227
+ os_1.default.homedir = () => tempHomeDir;
228
+ try {
229
+ // Create global config
230
+ const globalConfigPath = path_1.default.join(tempHomeDir, '.fraim', 'config.json');
231
+ fs_1.default.mkdirSync(path_1.default.dirname(globalConfigPath), { recursive: true });
232
+ const globalConfig = {
233
+ version: '2.0.27',
234
+ apiKey: 'fraim_global_key',
235
+ githubToken: 'ghp_global_token',
236
+ configuredAt: new Date().toISOString(),
237
+ userPreferences: {
238
+ autoSync: true,
239
+ backupConfigs: true
240
+ }
241
+ };
242
+ fs_1.default.writeFileSync(globalConfigPath, JSON.stringify(globalConfig, null, 2));
243
+ // Create project 1 config
244
+ process.cwd = () => project1Dir;
245
+ const project1ConfigPath = path_1.default.join(project1Dir, '.fraim', 'config.json');
246
+ fs_1.default.mkdirSync(path_1.default.dirname(project1ConfigPath), { recursive: true });
247
+ const project1Config = {
248
+ version: '2.0.27',
249
+ project: {
250
+ name: 'project-one'
251
+ },
252
+ git: {
253
+ defaultBranch: 'main',
254
+ repoOwner: 'user',
255
+ repoName: 'project-one'
256
+ }
257
+ };
258
+ fs_1.default.writeFileSync(project1ConfigPath, JSON.stringify(project1Config, null, 2));
259
+ // Create project 2 config
260
+ process.cwd = () => project2Dir;
261
+ const project2ConfigPath = path_1.default.join(project2Dir, '.fraim', 'config.json');
262
+ fs_1.default.mkdirSync(path_1.default.dirname(project2ConfigPath), { recursive: true });
263
+ const project2Config = {
264
+ version: '2.0.27',
265
+ project: {
266
+ name: 'project-two'
267
+ },
268
+ git: {
269
+ defaultBranch: 'master',
270
+ repoOwner: 'user',
271
+ repoName: 'project-two'
272
+ }
273
+ };
274
+ fs_1.default.writeFileSync(project2ConfigPath, JSON.stringify(project2Config, null, 2));
275
+ // Verify all configs exist
276
+ (0, node_assert_1.default)(fs_1.default.existsSync(globalConfigPath), 'Global config should exist');
277
+ (0, node_assert_1.default)(fs_1.default.existsSync(project1ConfigPath), 'Project 1 config should exist');
278
+ (0, node_assert_1.default)(fs_1.default.existsSync(project2ConfigPath), 'Project 2 config should exist');
279
+ // Verify global config has secrets, projects don't
280
+ const globalData = JSON.parse(fs_1.default.readFileSync(globalConfigPath, 'utf8'));
281
+ const project1Data = JSON.parse(fs_1.default.readFileSync(project1ConfigPath, 'utf8'));
282
+ const project2Data = JSON.parse(fs_1.default.readFileSync(project2ConfigPath, 'utf8'));
283
+ // Global should have secrets
284
+ (0, node_assert_1.default)(globalData.apiKey, 'Global config should have API key');
285
+ (0, node_assert_1.default)(globalData.githubToken, 'Global config should have GitHub token');
286
+ // Projects should not have secrets
287
+ (0, node_assert_1.default)(!project1Data.apiKey, 'Project 1 should not have API key');
288
+ (0, node_assert_1.default)(!project1Data.githubToken, 'Project 1 should not have GitHub token');
289
+ (0, node_assert_1.default)(!project2Data.apiKey, 'Project 2 should not have API key');
290
+ (0, node_assert_1.default)(!project2Data.githubToken, 'Project 2 should not have GitHub token');
291
+ // Projects should have different settings
292
+ node_assert_1.default.strictEqual(project1Data.project.name, 'project-one', 'Project 1 name');
293
+ node_assert_1.default.strictEqual(project2Data.project.name, 'project-two', 'Project 2 name');
294
+ node_assert_1.default.strictEqual(project1Data.git.defaultBranch, 'main', 'Project 1 branch');
295
+ node_assert_1.default.strictEqual(project2Data.git.defaultBranch, 'master', 'Project 2 branch');
296
+ console.log('✅ Project config independence scenario passed');
297
+ }
298
+ finally {
299
+ // Cleanup
300
+ fs_1.default.rmSync(tempHomeDir, { recursive: true, force: true });
301
+ fs_1.default.rmSync(project1Dir, { recursive: true, force: true });
302
+ fs_1.default.rmSync(project2Dir, { recursive: true, force: true });
303
+ os_1.default.homedir = originalHomedir;
304
+ process.cwd = originalCwd;
305
+ }
306
+ });
307
+ (0, node_test_1.test)('Setup Scenarios - Token Validation Edge Cases', async () => {
308
+ // Test various token format validations
309
+ const { isValidTokenFormat } = await Promise.resolve().then(() => __importStar(require('../src/cli/setup/token-validator')));
310
+ // Test FRAIM key validation
311
+ (0, node_assert_1.default)(isValidTokenFormat('fraim_valid_key_123', 'fraim'), 'Valid FRAIM key should pass');
312
+ (0, node_assert_1.default)(!isValidTokenFormat('invalid_key', 'fraim'), 'Invalid FRAIM key should fail');
313
+ (0, node_assert_1.default)(!isValidTokenFormat('', 'fraim'), 'Empty FRAIM key should fail');
314
+ (0, node_assert_1.default)(!isValidTokenFormat('github_pat_123', 'fraim'), 'GitHub token as FRAIM key should fail');
315
+ // Test GitHub token validation
316
+ (0, node_assert_1.default)(isValidTokenFormat('ghp_valid_token_123', 'github'), 'Valid GitHub classic token should pass');
317
+ (0, node_assert_1.default)(isValidTokenFormat('github_pat_valid_token_123', 'github'), 'Valid GitHub PAT should pass');
318
+ (0, node_assert_1.default)(!isValidTokenFormat('invalid_token', 'github'), 'Invalid GitHub token should fail');
319
+ (0, node_assert_1.default)(!isValidTokenFormat('', 'github'), 'Empty GitHub token should fail');
320
+ (0, node_assert_1.default)(!isValidTokenFormat('fraim_123', 'github'), 'FRAIM key as GitHub token should fail');
321
+ console.log('✅ Token validation edge cases passed');
322
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fraim-framework",
3
- "version": "2.0.38",
3
+ "version": "2.0.42",
4
4
  "description": "FRAIM v2: Framework for Rigor-based AI Management - Transform from solo developer to AI manager orchestrating production-ready code with enterprise-grade discipline",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -10,7 +10,7 @@
10
10
  "scripts": {
11
11
  "setup": "node setup.js",
12
12
  "dev": "tsx --watch src/fraim-mcp-server.ts",
13
- "build": "tsc && npm run build:stubs && npm run validate:registry",
13
+ "build": "tsc && node scripts/copy-ai-manager-rules.js && npm run build:stubs && npm run validate:registry",
14
14
  "build:stubs": "tsx scripts/build-stub-registry.ts",
15
15
  "test": "node scripts/test-with-server.js",
16
16
  "start:fraim": "tsx src/fraim-mcp-server.ts",
@@ -86,6 +86,7 @@
86
86
  "cors": "^2.8.5",
87
87
  "dotenv": "^16.4.7",
88
88
  "express": "^5.2.1",
89
+ "fraim-framework": "^2.0.38",
89
90
  "markdown-it": "^14.1.0",
90
91
  "markdown-it-highlightjs": "^4.2.0",
91
92
  "mongodb": "^7.0.0",