fraim 2.0.312 → 2.0.313
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.
|
@@ -292,10 +292,12 @@ const runSync = async (options) => {
|
|
|
292
292
|
registryFiles: localRegistryFiles
|
|
293
293
|
});
|
|
294
294
|
if (result.success) {
|
|
295
|
-
// Local-dev sync bypasses Layer 1 (machine-level sync) below, but scripts
|
|
296
|
-
// and
|
|
295
|
+
// Local-dev sync bypasses Layer 1 (machine-level sync) below, but scripts,
|
|
296
|
+
// docs, and job stubs still need to land in ~/.fraim/ or the reported counts
|
|
297
|
+
// are a lie.
|
|
297
298
|
await (0, remote_sync_1.syncScriptsToUserDir)(localRegistryFiles);
|
|
298
299
|
await (0, remote_sync_1.syncDocsToUserDir)(localRegistryFiles);
|
|
300
|
+
await (0, remote_sync_1.syncJobsToUserDir)(localRegistryFiles);
|
|
299
301
|
const { ensureUserLevelDependencies: ensureLocalUserLevelDependencies } = await Promise.resolve().then(() => __importStar(require('../setup/user-level-sync')));
|
|
300
302
|
ensureLocalUserLevelDependencies();
|
|
301
303
|
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 local server`));
|
|
@@ -363,6 +365,13 @@ const runSync = async (options) => {
|
|
|
363
365
|
if (docsSynced > 0) {
|
|
364
366
|
console.log(chalk_1.default.green(` Synced ${docsSynced} docs to ~/.fraim/docs/`));
|
|
365
367
|
}
|
|
368
|
+
// 1b3. Sync ai-employee/ai-manager job stubs to ~/.fraim/ (Issue #1644: this
|
|
369
|
+
// machine-level baseline is what makes every project's job catalog current
|
|
370
|
+
// without its own project-level sync)
|
|
371
|
+
const { employeeJobsSynced: machineEmployeeJobsSynced, managerJobsSynced: machineManagerJobsSynced } = await (0, remote_sync_1.syncJobsToUserDir)(registryFiles);
|
|
372
|
+
if (machineEmployeeJobsSynced > 0 || machineManagerJobsSynced > 0) {
|
|
373
|
+
console.log(chalk_1.default.green(` Synced ${machineEmployeeJobsSynced} ai-employee jobs and ${machineManagerJobsSynced} ai-manager jobs to ~/.fraim/`));
|
|
374
|
+
}
|
|
366
375
|
// 1c. Refresh org home
|
|
367
376
|
await refreshOrgCache(remoteUrl, apiKey);
|
|
368
377
|
// 1d. Refresh manager home
|
|
@@ -15,6 +15,7 @@ exports.SYNCED_CONTENT_BANNER_MARKER = void 0;
|
|
|
15
15
|
exports.fetchRegistryFiles = fetchRegistryFiles;
|
|
16
16
|
exports.syncScriptsToUserDir = syncScriptsToUserDir;
|
|
17
17
|
exports.syncDocsToUserDir = syncDocsToUserDir;
|
|
18
|
+
exports.syncJobsToUserDir = syncJobsToUserDir;
|
|
18
19
|
exports.syncFromRemote = syncFromRemote;
|
|
19
20
|
const axios_1 = __importDefault(require("axios"));
|
|
20
21
|
const fs_1 = require("fs");
|
|
@@ -235,6 +236,56 @@ async function syncScriptsToUserDir(files) {
|
|
|
235
236
|
async function syncDocsToUserDir(files) {
|
|
236
237
|
return syncTypedFilesToUserDir(files, 'docs', 'docs', 'docs');
|
|
237
238
|
}
|
|
239
|
+
function stripJobsPrefix(rawPath) {
|
|
240
|
+
const normalized = rawPath.replace(/\\/g, '/');
|
|
241
|
+
return normalized.startsWith('jobs/') ? normalized.substring('jobs/'.length) : normalized;
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Write one role's job stubs (ai-employee or ai-manager) into
|
|
245
|
+
* `~/.fraim/<role>/jobs/`. Shared by syncJobsToUserDir for both roles so the
|
|
246
|
+
* clean-then-write logic exists in exactly one place.
|
|
247
|
+
*/
|
|
248
|
+
async function syncRoleJobsToUserDir(roleFiles, role) {
|
|
249
|
+
if (roleFiles.length === 0)
|
|
250
|
+
return 0;
|
|
251
|
+
const userDir = (0, script_sync_utils_1.getUserFraimDir)();
|
|
252
|
+
const targetDir = (0, path_1.join)(userDir, role, 'jobs');
|
|
253
|
+
if (!(0, fs_1.existsSync)(targetDir)) {
|
|
254
|
+
(0, fs_1.mkdirSync)(targetDir, { recursive: true });
|
|
255
|
+
}
|
|
256
|
+
cleanDirectory(targetDir, (candidatePath) => {
|
|
257
|
+
if ((0, path_1.resolve)(candidatePath) !== (0, path_1.resolve)(targetDir)) {
|
|
258
|
+
assertPathInsideDirectory(targetDir, candidatePath, `${role} job directory`);
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
const rolePrefix = new RegExp(`^${role}/`);
|
|
262
|
+
for (const file of roleFiles) {
|
|
263
|
+
const relPath = stripJobsPrefix(file.path).replace(rolePrefix, '');
|
|
264
|
+
const { filePath } = resolveUserRegistryFile(targetDir, relPath, `${role} job file`);
|
|
265
|
+
const fileDir = (0, path_1.dirname)(filePath);
|
|
266
|
+
if (!(0, fs_1.existsSync)(fileDir)) {
|
|
267
|
+
(0, fs_1.mkdirSync)(fileDir, { recursive: true });
|
|
268
|
+
}
|
|
269
|
+
(0, fs_1.writeFileSync)(filePath, applySyncedContentBanner(file), 'utf8');
|
|
270
|
+
}
|
|
271
|
+
return roleFiles.length;
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Sync job stub files to the machine-level `~/.fraim/ai-employee/jobs/` and
|
|
275
|
+
* `~/.fraim/ai-manager/jobs/` directories. Mirrors syncScriptsToUserDir /
|
|
276
|
+
* syncDocsToUserDir so every FRAIM project on the machine sees the current
|
|
277
|
+
* job catalog without needing its own project-level sync (issue #1644).
|
|
278
|
+
*/
|
|
279
|
+
async function syncJobsToUserDir(files) {
|
|
280
|
+
const allJobFiles = files.filter((f) => f.type === 'job');
|
|
281
|
+
const managerJobFiles = allJobFiles.filter((f) => stripJobsPrefix(f.path).startsWith('ai-manager/'));
|
|
282
|
+
const employeeJobFiles = allJobFiles.filter((f) => !stripJobsPrefix(f.path).startsWith('ai-manager/'));
|
|
283
|
+
const [employeeJobsSynced, managerJobsSynced] = await Promise.all([
|
|
284
|
+
syncRoleJobsToUserDir(employeeJobFiles, 'ai-employee'),
|
|
285
|
+
syncRoleJobsToUserDir(managerJobFiles, 'ai-manager'),
|
|
286
|
+
]);
|
|
287
|
+
return { employeeJobsSynced, managerJobsSynced };
|
|
288
|
+
}
|
|
238
289
|
/**
|
|
239
290
|
* Sync jobs and scripts from remote FRAIM server
|
|
240
291
|
*/
|
|
@@ -792,9 +792,15 @@ class FraimLocalMCPServer {
|
|
|
792
792
|
(0, fs_1.writeFileSync)(metadataPath, JSON.stringify(metadata, null, 2), 'utf8');
|
|
793
793
|
}
|
|
794
794
|
getLocalCatalogSyncReason(projectRoot) {
|
|
795
|
+
// Issue #1644: baseline job stubs are machine-level content (Layer 1 of
|
|
796
|
+
// `fraim sync` always writes them to ~/.fraim/), so both the existence
|
|
797
|
+
// check and the freshness metadata below must target the same
|
|
798
|
+
// machine-level home. Checking a project-level path here while reading
|
|
799
|
+
// machine-level metadata let a project whose own stub dirs merely existed
|
|
800
|
+
// (even if stale) pass on the strength of a sync some *other* project ran.
|
|
795
801
|
const catalogRoots = [
|
|
796
|
-
(0, path_1.join)(
|
|
797
|
-
(0, path_1.join)(
|
|
802
|
+
(0, path_1.join)((0, project_fraim_paths_1.getUserFraimDirPath)(), 'ai-employee', 'jobs'),
|
|
803
|
+
(0, path_1.join)((0, project_fraim_paths_1.getUserFraimDirPath)(), 'ai-manager', 'jobs')
|
|
798
804
|
];
|
|
799
805
|
if (catalogRoots.some((catalogRoot) => !(0, fs_1.existsSync)(catalogRoot))) {
|
|
800
806
|
return 'local job catalog is missing';
|