fraim-framework 2.0.127 → 2.0.128
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/cli/commands/init-project.js +5 -1
- package/dist/src/cli/commands/sync.js +11 -4
- package/dist/src/first-run/install-state.js +7 -5
- package/dist/src/first-run/server.js +29 -24
- package/dist/src/first-run/session-service.js +638 -194
- package/dist/src/first-run/types.js +69 -12
- package/package.json +1 -1
- package/public/first-run/error-frame.js +89 -0
- package/public/first-run/index.html +35 -221
- package/public/first-run/script.js +417 -361
- package/public/first-run/styles.css +386 -0
|
@@ -160,11 +160,15 @@ const createGitHubLabels = (projectRoot) => {
|
|
|
160
160
|
};
|
|
161
161
|
const runInitProject = async (options = {}) => {
|
|
162
162
|
console.log(chalk_1.default.blue('Initializing FRAIM project...'));
|
|
163
|
+
const failHard = options.failHard ?? 'exit';
|
|
163
164
|
const globalSetup = checkGlobalSetup();
|
|
164
165
|
if (!globalSetup.exists) {
|
|
165
166
|
console.log(chalk_1.default.red('Global FRAIM setup not found.'));
|
|
166
167
|
console.log(chalk_1.default.yellow('Please run global setup first:'));
|
|
167
168
|
console.log(chalk_1.default.cyan(' fraim setup'));
|
|
169
|
+
if (failHard === 'throw') {
|
|
170
|
+
throw new Error('Global FRAIM setup not found.');
|
|
171
|
+
}
|
|
168
172
|
process.exit(1);
|
|
169
173
|
}
|
|
170
174
|
const projectRoot = options.projectRoot || process.cwd();
|
|
@@ -312,7 +316,7 @@ const runInitProject = async (options = {}) => {
|
|
|
312
316
|
result.repositoryDetected = true;
|
|
313
317
|
}
|
|
314
318
|
if (!process.env.FRAIM_SKIP_SYNC) {
|
|
315
|
-
await (0, sync_1.runSync)({ projectRoot });
|
|
319
|
+
await (0, sync_1.runSync)({ projectRoot, failHard });
|
|
316
320
|
result.syncPerformed = true;
|
|
317
321
|
}
|
|
318
322
|
else {
|
|
@@ -101,7 +101,14 @@ function updateVersionInConfig(fraimDir) {
|
|
|
101
101
|
console.warn(chalk_1.default.yellow('Could not update version in config.json.'));
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
|
+
function failSync(mode, message) {
|
|
105
|
+
if (mode === 'throw') {
|
|
106
|
+
throw new Error(message);
|
|
107
|
+
}
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
104
110
|
const runSync = async (options) => {
|
|
111
|
+
const failHard = options.failHard ?? 'exit';
|
|
105
112
|
// Handle --global flag: sync to user-level ~/.fraim/ instead of project
|
|
106
113
|
if (options.global) {
|
|
107
114
|
console.log(chalk_1.default.blue('Syncing FRAIM content to user-level directory (~/.fraim/)...'));
|
|
@@ -112,7 +119,7 @@ const runSync = async (options) => {
|
|
|
112
119
|
}
|
|
113
120
|
catch (error) {
|
|
114
121
|
console.error(chalk_1.default.red(`User-level sync failed: ${error.message}`));
|
|
115
|
-
|
|
122
|
+
failSync(failHard, `User-level sync failed: ${error.message}`);
|
|
116
123
|
}
|
|
117
124
|
return;
|
|
118
125
|
}
|
|
@@ -158,7 +165,7 @@ const runSync = async (options) => {
|
|
|
158
165
|
}
|
|
159
166
|
console.error(chalk_1.default.red(`Local sync failed: ${result.error}`));
|
|
160
167
|
console.error(chalk_1.default.yellow('Make sure the FRAIM MCP server is running locally (npm run dev).'));
|
|
161
|
-
|
|
168
|
+
failSync(failHard, `Local sync failed: ${result.error}`);
|
|
162
169
|
}
|
|
163
170
|
let apiKey = loadUserApiKey() || config.apiKey || process.env.FRAIM_API_KEY;
|
|
164
171
|
if (!apiKey) {
|
|
@@ -170,7 +177,7 @@ const runSync = async (options) => {
|
|
|
170
177
|
console.error(chalk_1.default.red('No API key configured. Cannot sync.'));
|
|
171
178
|
console.error(chalk_1.default.yellow(`Set FRAIM_API_KEY in your environment, or add apiKey to ~/.fraim/config.json or ${(0, project_fraim_paths_1.getWorkspaceFraimDisplayPath)('config.json')}`));
|
|
172
179
|
console.error(chalk_1.default.yellow('Or use --local to sync from a locally running FRAIM server.'));
|
|
173
|
-
|
|
180
|
+
failSync(failHard, 'No API key configured.');
|
|
174
181
|
}
|
|
175
182
|
}
|
|
176
183
|
console.log(chalk_1.default.blue('Syncing FRAIM jobs from remote server...'));
|
|
@@ -188,7 +195,7 @@ const runSync = async (options) => {
|
|
|
188
195
|
updateVersionInConfig(fraimDir);
|
|
189
196
|
return;
|
|
190
197
|
}
|
|
191
|
-
|
|
198
|
+
failSync(failHard, `Remote sync failed: ${result.error}`);
|
|
192
199
|
}
|
|
193
200
|
console.log(chalk_1.default.green(`Successfully synced ${result.employeeJobsSynced} ai-employee jobs, ${result.managerJobsSynced} ai-manager jobs, ${result.skillsSynced} skills, ${result.rulesSynced} rules, ${result.scriptsSynced} scripts, and ${result.docsSynced} docs from remote`));
|
|
194
201
|
updateVersionInConfig(fraimDir);
|
|
@@ -27,14 +27,12 @@ function maskInstallKey(key) {
|
|
|
27
27
|
function createInitialFirstRunState(key) {
|
|
28
28
|
const now = new Date().toISOString();
|
|
29
29
|
return {
|
|
30
|
-
version:
|
|
30
|
+
version: 2,
|
|
31
31
|
installKeyRef: maskInstallKey(key),
|
|
32
32
|
platform: process.platform,
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
restartDeferredAgents: [],
|
|
33
|
+
agentId: 'claude-code',
|
|
34
|
+
rows: (0, types_1.createInitialRows)(),
|
|
36
35
|
resourcesUrl: types_1.FIRST_RUN_RESOURCES_URL,
|
|
37
|
-
stepStates: (0, types_1.createDefaultStepStates)(),
|
|
38
36
|
createdAt: now,
|
|
39
37
|
updatedAt: now,
|
|
40
38
|
};
|
|
@@ -46,6 +44,10 @@ function loadFirstRunState() {
|
|
|
46
44
|
}
|
|
47
45
|
try {
|
|
48
46
|
const state = JSON.parse(fs_1.default.readFileSync(statePath, 'utf8'));
|
|
47
|
+
// Reject persisted v1 state — schema changed materially in #352 v1.
|
|
48
|
+
if (state.version !== 2) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
49
51
|
if (typeof state.installKeyRef === 'string' && !state.installKeyRef.includes('...')) {
|
|
50
52
|
state.installKeyRef = maskInstallKey(state.installKeyRef);
|
|
51
53
|
}
|
|
@@ -8,6 +8,7 @@ const express_1 = __importDefault(require("express"));
|
|
|
8
8
|
const fs_1 = __importDefault(require("fs"));
|
|
9
9
|
const path_1 = __importDefault(require("path"));
|
|
10
10
|
const child_process_1 = require("child_process");
|
|
11
|
+
const session_service_1 = require("./session-service");
|
|
11
12
|
function resolveFirstRunPublicDir() {
|
|
12
13
|
const candidates = [
|
|
13
14
|
path_1.default.resolve(process.cwd(), 'public/first-run'),
|
|
@@ -47,6 +48,9 @@ function pickProjectPath() {
|
|
|
47
48
|
});
|
|
48
49
|
return result.status === 0 ? result.stdout.trim() || null : null;
|
|
49
50
|
}
|
|
51
|
+
function isCanonicalRowId(value) {
|
|
52
|
+
return typeof value === 'string' && session_service_1.FIRST_RUN_ROW_IDS.includes(value);
|
|
53
|
+
}
|
|
50
54
|
class FirstRunServer {
|
|
51
55
|
constructor(options) {
|
|
52
56
|
this.app = (0, express_1.default)();
|
|
@@ -100,21 +104,25 @@ class FirstRunServer {
|
|
|
100
104
|
this.app.get('/api/first-run/session', (_req, res) => {
|
|
101
105
|
res.json(this.sessionService.getSession());
|
|
102
106
|
});
|
|
103
|
-
this.app.post('/api/first-run/
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
107
|
+
this.app.post('/api/first-run/rows/:rowId/run', async (req, res) => {
|
|
108
|
+
const { rowId } = req.params;
|
|
109
|
+
if (!isCanonicalRowId(rowId)) {
|
|
110
|
+
return res.status(400).json({ error: `Unknown row id: ${rowId}` });
|
|
111
|
+
}
|
|
112
|
+
try {
|
|
113
|
+
const result = await this.sessionService.runRow(rowId, req.body || {});
|
|
114
|
+
return res.json(result);
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
return res.status(500).json({ error: error instanceof Error ? error.message : 'Could not run row.' });
|
|
109
118
|
}
|
|
110
|
-
return res.json(this.sessionService.selectAgent(req.body.agentId));
|
|
111
119
|
});
|
|
112
|
-
this.app.post('/api/first-run/
|
|
120
|
+
this.app.post('/api/first-run/agent/change', (req, res) => {
|
|
113
121
|
try {
|
|
114
|
-
return res.json(
|
|
122
|
+
return res.json(this.sessionService.changeAgent(req.body || {}));
|
|
115
123
|
}
|
|
116
124
|
catch (error) {
|
|
117
|
-
return res.status(500).json({ error: error instanceof Error ? error.message : 'Could not
|
|
125
|
+
return res.status(500).json({ error: error instanceof Error ? error.message : 'Could not change agent.' });
|
|
118
126
|
}
|
|
119
127
|
});
|
|
120
128
|
this.app.post('/api/first-run/project-path/pick', (_req, res) => {
|
|
@@ -129,25 +137,22 @@ class FirstRunServer {
|
|
|
129
137
|
return res.status(500).json({ error: error instanceof Error ? error.message : 'Could not open the folder picker.' });
|
|
130
138
|
}
|
|
131
139
|
});
|
|
132
|
-
this.app.post('/api/first-run/project', async (req, res) => {
|
|
133
|
-
if (!req.body.projectPath) {
|
|
134
|
-
return res.status(400).json({ error: 'projectPath is required.' });
|
|
135
|
-
}
|
|
136
|
-
try {
|
|
137
|
-
return res.json(await this.sessionService.initializeProject(req.body.projectPath, req.body.initializeGit !== false));
|
|
138
|
-
}
|
|
139
|
-
catch (error) {
|
|
140
|
-
return res.status(500).json({ error: error instanceof Error ? error.message : 'Could not initialize the project.' });
|
|
141
|
-
}
|
|
142
|
-
});
|
|
143
|
-
this.app.post('/api/first-run/launch', (_req, res) => {
|
|
144
|
-
return res.json(this.sessionService.launchAndProbe());
|
|
145
|
-
});
|
|
146
140
|
this.app.post('/api/first-run/finish', (_req, res) => {
|
|
147
141
|
const result = this.sessionService.finish();
|
|
148
142
|
this.finishResolver?.();
|
|
149
143
|
return res.json(result);
|
|
150
144
|
});
|
|
145
|
+
// Hub-launch helper — starts an AiHubServer for the chosen project and
|
|
146
|
+
// opens the user's browser. v2 (#355) replaces the in-process spawn with
|
|
147
|
+
// a durable launcher binary.
|
|
148
|
+
this.app.post('/api/first-run/open-hub', async (_req, res) => {
|
|
149
|
+
try {
|
|
150
|
+
return res.json(await this.sessionService.openHub());
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
return res.status(500).json({ error: error instanceof Error ? error.message : 'Could not open Hub.' });
|
|
154
|
+
}
|
|
155
|
+
});
|
|
151
156
|
}
|
|
152
157
|
}
|
|
153
158
|
exports.FirstRunServer = FirstRunServer;
|