fraim 2.0.360 → 2.0.361-next.1
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/registry/scripts/artifact-paths.js +12 -7
- package/dist/registry/scripts/learning/outcome-assessment.js +4 -3
- package/dist/registry/scripts/learning/run-application.js +19 -19
- package/dist/src/cli/commands/sync.js +1 -0
- package/dist/src/cli/doctor/checks/agent-cli-health-checks.js +32 -181
- package/dist/src/cli/learning/inventory.js +2 -1
- package/dist/src/cli/learning/queue-operations.js +6 -5
- package/dist/src/cli/mcp/command-resolution.js +9 -4
- package/dist/src/cli/utils/agent-command-probe.js +148 -0
- package/dist/src/cli/utils/background-command.js +122 -0
- package/dist/src/cli/utils/machine-path.js +27 -0
- package/dist/src/cli/utils/managed-agent-install.js +76 -37
- package/dist/src/cli/utils/managed-agent-paths.js +99 -45
- package/dist/src/cli/utils/managed-node-runtime.js +94 -64
- package/dist/src/cli/utils/operation-scope.js +88 -0
- package/dist/src/cli/utils/pack-home.js +34 -1
- package/dist/src/cli/utils/win32-cmd-spawn.js +25 -12
- package/dist/src/first-run/server.js +4 -2
- package/dist/src/first-run/session-service.js +15 -55
- package/dist/src/local-mcp-server/learning-context-builder.js +31 -36
- package/package.json +1 -1
|
@@ -1,10 +1,30 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.quoteWindowsArgument = quoteWindowsArgument;
|
|
3
4
|
exports.spawnViaCmdExe = spawnViaCmdExe;
|
|
4
5
|
exports.buildCmdExeArgs = buildCmdExeArgs;
|
|
5
6
|
const child_process_1 = require("child_process");
|
|
6
|
-
function
|
|
7
|
-
|
|
7
|
+
function escapeCmd(value) { return value.replace(/([()\][%!^"`<>&|;, *?])/g, '^$1'); }
|
|
8
|
+
/** Quote one argument for the Windows native CRT command-line decoder. */
|
|
9
|
+
function quoteWindowsArgument(value) {
|
|
10
|
+
return `"${value.replace(/(\\*)"/g, '$1$1\\"').replace(/(\\+)$/g, '$1$1')}"`;
|
|
11
|
+
}
|
|
12
|
+
/** Preserve native argv across both parsing passes of a CMD %* forwarding shim. */
|
|
13
|
+
function forwardingCmdArgument(value, passes = 2) {
|
|
14
|
+
if (/^[a-zA-Z0-9_./:=+\-]+$/.test(value))
|
|
15
|
+
return value;
|
|
16
|
+
let quoted = '"';
|
|
17
|
+
let slashes = 0;
|
|
18
|
+
for (const character of value) {
|
|
19
|
+
if (character === '\\') {
|
|
20
|
+
slashes++;
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
quoted += '\\'.repeat(character === '"' ? slashes * 2 + 1 : slashes) + character;
|
|
24
|
+
slashes = 0;
|
|
25
|
+
}
|
|
26
|
+
quoted += '\\'.repeat(slashes * 2) + '"';
|
|
27
|
+
return passes === 1 ? escapeCmd(quoted) : escapeCmd(escapeCmd(quoted));
|
|
8
28
|
}
|
|
9
29
|
/**
|
|
10
30
|
* Spawn a command through cmd.exe on Windows so .cmd shims resolve correctly.
|
|
@@ -12,12 +32,8 @@ function quoteCmdToken(s) {
|
|
|
12
32
|
* containing spaces are not double-escaped by Node's CreateProcess call.
|
|
13
33
|
*/
|
|
14
34
|
function spawnViaCmdExe(command, args, options) {
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
const innerCmd = tokens.join(' ');
|
|
18
|
-
const needsOuterQuote = /[\s"&|<>^]/.test(command);
|
|
19
|
-
const cmdString = needsOuterQuote ? `"${innerCmd}"` : innerCmd;
|
|
20
|
-
return (0, child_process_1.spawnSync)(comSpec, ['/d', '/s', '/c', cmdString], {
|
|
35
|
+
const invocation = buildCmdExeArgs(command, args);
|
|
36
|
+
return (0, child_process_1.spawnSync)(invocation.command, invocation.args, {
|
|
21
37
|
...options,
|
|
22
38
|
windowsHide: true,
|
|
23
39
|
windowsVerbatimArguments: true,
|
|
@@ -28,12 +44,9 @@ function spawnViaCmdExe(command, args, options) {
|
|
|
28
44
|
* Returns the command, args, and a verbatim flag to set windowsVerbatimArguments: true.
|
|
29
45
|
*/
|
|
30
46
|
function buildCmdExeArgs(command, args) {
|
|
31
|
-
const tokens = [command, ...args].map(quoteCmdToken);
|
|
32
|
-
const innerCmd = tokens.join(' ');
|
|
33
|
-
const needsOuterQuote = /[\s"&|<>^]/.test(command);
|
|
34
47
|
return {
|
|
35
48
|
command: process.env.ComSpec || 'cmd.exe',
|
|
36
|
-
args: ['/d', '/s', '/c',
|
|
49
|
+
args: ['/d', '/v:off', '/s', '/c', `"${[escapeCmd(command), ...args.map(arg => forwardingCmdArgument(arg, /\.(exe|com)$/i.test(command) ? 1 : 2))].join(' ')}"`],
|
|
37
50
|
verbatim: true,
|
|
38
51
|
};
|
|
39
52
|
}
|
|
@@ -143,6 +143,7 @@ class FirstRunServer {
|
|
|
143
143
|
this.registerRoutes();
|
|
144
144
|
}
|
|
145
145
|
async start(port) {
|
|
146
|
+
this.sessionService.resumePendingOperations();
|
|
146
147
|
await new Promise((resolve, reject) => {
|
|
147
148
|
this.httpServer = this.app.listen(port, '127.0.0.1');
|
|
148
149
|
this.httpServer.once('listening', () => resolve());
|
|
@@ -153,6 +154,7 @@ class FirstRunServer {
|
|
|
153
154
|
await this.finishPromise;
|
|
154
155
|
}
|
|
155
156
|
async stop() {
|
|
157
|
+
this.sessionService.cancelPendingOperations();
|
|
156
158
|
if (!this.httpServer) {
|
|
157
159
|
return;
|
|
158
160
|
}
|
|
@@ -274,13 +276,13 @@ class FirstRunServer {
|
|
|
274
276
|
return res.status(500).json({ error: error instanceof Error ? error.message : 'Could not trigger login.' });
|
|
275
277
|
}
|
|
276
278
|
});
|
|
277
|
-
this.app.post('/api/first-run/check-agent', (req, res) => {
|
|
279
|
+
this.app.post('/api/first-run/check-agent', async (req, res) => {
|
|
278
280
|
try {
|
|
279
281
|
const { agentId } = req.body || {};
|
|
280
282
|
if (!agentId || typeof agentId !== 'string') {
|
|
281
283
|
return res.status(400).json({ error: 'agentId is required.' });
|
|
282
284
|
}
|
|
283
|
-
return res.json(this.sessionService.checkAgentReady(agentId));
|
|
285
|
+
return res.json(await this.sessionService.checkAgentReady(agentId));
|
|
284
286
|
}
|
|
285
287
|
catch (error) {
|
|
286
288
|
return res.status(500).json({ error: error instanceof Error ? error.message : 'Could not check agent.' });
|
|
@@ -36,10 +36,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
36
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.FIRST_RUN_ROW_IDS = exports.FirstRunSessionService = void 0;
|
|
39
|
+
exports.FIRST_RUN_ROW_IDS = exports.FirstRunSessionService = exports.runProcess = void 0;
|
|
40
40
|
exports.commandVersion = commandVersion;
|
|
41
|
-
exports.runProcess = runProcess;
|
|
42
41
|
exports.buildFirstRunHubLaunchArgs = buildFirstRunHubLaunchArgs;
|
|
42
|
+
const agent_command_probe_1 = require("../cli/utils/agent-command-probe");
|
|
43
43
|
const fs_1 = __importDefault(require("fs"));
|
|
44
44
|
const os_1 = __importDefault(require("os"));
|
|
45
45
|
const path_1 = __importDefault(require("path"));
|
|
@@ -68,24 +68,8 @@ function getFakeStateMode() {
|
|
|
68
68
|
return 'default';
|
|
69
69
|
}
|
|
70
70
|
function commandVersion(command, extraBinDirs, basePath) {
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
? ['/d', '/s', '/c', `${command} --version`]
|
|
74
|
-
: ['--version'];
|
|
75
|
-
const pathValue = extraBinDirs && extraBinDirs.length > 0
|
|
76
|
-
? (0, managed_agent_paths_1.appendBinDirsToPath)(basePath ?? process.env.PATH ?? process.env.Path, extraBinDirs)
|
|
77
|
-
: basePath;
|
|
78
|
-
const recoveredPath = (0, managed_agent_paths_1.buildRecoveredAgentPath)(pathValue ?? process.env.PATH ?? process.env.Path);
|
|
79
|
-
const env = { ...process.env, PATH: recoveredPath, Path: recoveredPath };
|
|
80
|
-
const result = (0, child_process_1.spawnSync)(executable, args, {
|
|
81
|
-
windowsHide: true,
|
|
82
|
-
encoding: 'utf8',
|
|
83
|
-
timeout: 5000,
|
|
84
|
-
env,
|
|
85
|
-
});
|
|
86
|
-
if (result.status !== 0 || result.error)
|
|
87
|
-
return null;
|
|
88
|
-
return (0, managed_agent_paths_1.versionFromProbeOutput)(result.stdout, result.stderr);
|
|
71
|
+
const supplied = extraBinDirs?.length ? (0, managed_agent_paths_1.appendBinDirsToPath)(basePath ?? process.env.PATH, extraBinDirs) : basePath;
|
|
72
|
+
return (0, agent_command_probe_1.selectAgentCommandSync)(command, { env: (0, agent_command_probe_1.agentProbeEnvironment)(supplied) })?.version ?? null;
|
|
89
73
|
}
|
|
90
74
|
function ensureOutputDirs() {
|
|
91
75
|
fs_1.default.mkdirSync((0, script_sync_utils_1.getUserFraimDir)(), { recursive: true });
|
|
@@ -121,37 +105,7 @@ function appendInstallLog(line) {
|
|
|
121
105
|
ensureOutputDirs();
|
|
122
106
|
fs_1.default.appendFileSync(getInstallLogPath(), `[${new Date().toISOString()}] ${line}${os_1.default.EOL}`);
|
|
123
107
|
}
|
|
124
|
-
|
|
125
|
-
return new Promise((resolve, reject) => {
|
|
126
|
-
const [realCmd, realArgs] = process.platform === 'win32'
|
|
127
|
-
? ['cmd.exe', ['/d', '/s', '/c', command, ...args]]
|
|
128
|
-
: [command, args];
|
|
129
|
-
const childEnv = { ...process.env };
|
|
130
|
-
for (const [key, value] of Object.entries(env || {})) {
|
|
131
|
-
if (value === undefined)
|
|
132
|
-
delete childEnv[key];
|
|
133
|
-
else
|
|
134
|
-
childEnv[key] = value;
|
|
135
|
-
}
|
|
136
|
-
const child = (0, child_process_1.spawn)(realCmd, realArgs, {
|
|
137
|
-
env: childEnv,
|
|
138
|
-
windowsHide: true,
|
|
139
|
-
shell: false,
|
|
140
|
-
});
|
|
141
|
-
let stdout = '';
|
|
142
|
-
let stderr = '';
|
|
143
|
-
child.stdout.on('data', (chunk) => { stdout += chunk.toString(); });
|
|
144
|
-
child.stderr.on('data', (chunk) => { stderr += chunk.toString(); });
|
|
145
|
-
child.on('error', reject);
|
|
146
|
-
child.on('close', (code) => {
|
|
147
|
-
if (code === 0) {
|
|
148
|
-
resolve({ stdout, stderr });
|
|
149
|
-
return;
|
|
150
|
-
}
|
|
151
|
-
reject(new Error(`${command} ${args.join(' ')} failed with code ${code}\n${stdout}${stderr}`.trim()));
|
|
152
|
-
});
|
|
153
|
-
});
|
|
154
|
-
}
|
|
108
|
+
exports.runProcess = managed_agent_install_1.runAgentInstallCommand;
|
|
155
109
|
function findAgentOption(agentId) {
|
|
156
110
|
return types_1.FIRST_RUN_AGENT_OPTIONS.find((option) => option.id === agentId);
|
|
157
111
|
}
|
|
@@ -210,7 +164,11 @@ function buildFirstRunHubLaunchArgs() {
|
|
|
210
164
|
return ['-y', 'fraim-hub@latest'];
|
|
211
165
|
}
|
|
212
166
|
class FirstRunSessionService {
|
|
167
|
+
resumePendingOperations() { if (this.operationAbort.signal.aborted)
|
|
168
|
+
this.operationAbort = new AbortController(); }
|
|
169
|
+
cancelPendingOperations() { this.operationAbort.abort(); }
|
|
213
170
|
constructor(options) {
|
|
171
|
+
this.operationAbort = new AbortController();
|
|
214
172
|
// Issue #646: the key may be absent when first-run is launched by the macOS
|
|
215
173
|
// installer (.dmg) instead of `fraim first-run --key=…`. In that case the
|
|
216
174
|
// wizard prompts the user to paste their key (see setKey / needsKey).
|
|
@@ -782,7 +740,8 @@ class FirstRunSessionService {
|
|
|
782
740
|
}
|
|
783
741
|
try {
|
|
784
742
|
const systemPath = (0, managed_agent_paths_1.stripManagedAgentBinDirsFromPath)(process.env.PATH);
|
|
785
|
-
const existingVersion =
|
|
743
|
+
const existingVersion = (await (0, agent_command_probe_1.selectAgentCommand)(option.launchCommand, { signal: this.operationAbort.signal }))?.version;
|
|
744
|
+
this.operationAbort.signal.throwIfAborted();
|
|
786
745
|
if (existingVersion) {
|
|
787
746
|
this.setAgentInstallStatus(agentId, 'needs-sign-in', `Sign in to ${option.label} to activate it.`);
|
|
788
747
|
appendInstallLog(`agent-reused ${agentId}`);
|
|
@@ -801,10 +760,11 @@ class FirstRunSessionService {
|
|
|
801
760
|
// already-managed, or freshly downloaded) and fold its directory into the PATH passed down,
|
|
802
761
|
// so the standard attempt can actually succeed instead of silently falling through.
|
|
803
762
|
const npm = await (0, managed_node_runtime_1.ensureManagedNpm)({
|
|
763
|
+
signal: this.operationAbort.signal,
|
|
804
764
|
onProgress: (message) => appendInstallLog(`managed-node: ${message}`),
|
|
805
765
|
});
|
|
806
766
|
const pathWithNpm = (0, managed_agent_paths_1.appendBinDirsToPath)(systemPath, [npm.binDir]);
|
|
807
|
-
const outcome = await (0, managed_agent_install_1.installManagedAgent)({ label: option.label, installPackage: option.installPackage, launchCommand: option.launchCommand }, pathWithNpm, { runProcess, commandVersion });
|
|
767
|
+
const outcome = await (0, managed_agent_install_1.installManagedAgent)({ label: option.label, installPackage: option.installPackage, launchCommand: option.launchCommand, npmPath: npm.npmPath, signal: this.operationAbort.signal }, pathWithNpm, { runProcess: exports.runProcess, commandVersion: managed_agent_install_1.verifyInstalledAgentCommand });
|
|
808
768
|
this.setAgentInstallStatus(agentId, 'needs-sign-in', `Sign in to ${option.label} to activate it.`);
|
|
809
769
|
appendInstallLog(outcome.outcome === 'standard' ? `agent-installed-standard ${agentId}` : `agent-installed-managed ${agentId}`);
|
|
810
770
|
this.persist();
|
|
@@ -848,7 +808,7 @@ class FirstRunSessionService {
|
|
|
848
808
|
};
|
|
849
809
|
}
|
|
850
810
|
}
|
|
851
|
-
checkAgentReady(agentId) {
|
|
811
|
+
async checkAgentReady(agentId) {
|
|
852
812
|
const option = findAgentOption(agentId);
|
|
853
813
|
if (!option) {
|
|
854
814
|
return { ok: false, ready: false, message: `Unknown agent: ${agentId}` };
|
|
@@ -859,7 +819,7 @@ class FirstRunSessionService {
|
|
|
859
819
|
this.persist();
|
|
860
820
|
return { ok: true, ready: true, message: `${option.label} is ready (fake-mode).` };
|
|
861
821
|
}
|
|
862
|
-
const ver =
|
|
822
|
+
const ver = (await (0, agent_command_probe_1.selectAgentCommand)(option.launchCommand, { signal: this.operationAbort.signal }))?.version;
|
|
863
823
|
if (ver) {
|
|
864
824
|
this.setAgentInstallStatus(agentId, 'ready', `${option.label} is ready.`);
|
|
865
825
|
this.updateAgentSummaryRow();
|
|
@@ -22,6 +22,7 @@ exports.normalizeLearningDomain = normalizeLearningDomain;
|
|
|
22
22
|
exports.applyLearningEntryChange = applyLearningEntryChange;
|
|
23
23
|
exports.isTruthyFlag = isTruthyFlag;
|
|
24
24
|
const artifact_paths_1 = require("../../registry/scripts/artifact-paths");
|
|
25
|
+
const pack_home_1 = require("../cli/utils/pack-home");
|
|
25
26
|
/**
|
|
26
27
|
* LearningContextBuilder builds the learning context section for a user.
|
|
27
28
|
*
|
|
@@ -39,7 +40,7 @@ const brand_store_1 = require("../core/brand-store");
|
|
|
39
40
|
// Issue #1043 round 2: org and manager content lives at one home named by
|
|
40
41
|
// config. These reads used to hardcode the removed ~/.fraim/{org,manager}
|
|
41
42
|
// cache, which silently returned nothing once that cache stopped existing.
|
|
42
|
-
const
|
|
43
|
+
const pack_home_2 = require("../cli/utils/pack-home");
|
|
43
44
|
const learning_domains_1 = require("../config/learning-domains");
|
|
44
45
|
const entry_authoring_1 = require("../../registry/scripts/learning/entry-authoring");
|
|
45
46
|
/** Catalog-backed allowlist for existing files, including retired storage identities. */
|
|
@@ -87,14 +88,14 @@ Object.defineProperty(exports, "generateLearningEntryId", { enumerable: true, ge
|
|
|
87
88
|
* therefore contributes no tier, matching the behavior before round 2.
|
|
88
89
|
*/
|
|
89
90
|
function managerSecondaryLearningsBase() {
|
|
90
|
-
const home = (0,
|
|
91
|
+
const home = (0, pack_home_2.resolvePackHome)('manager', { migrateLegacy: false });
|
|
91
92
|
if (home.synced)
|
|
92
93
|
return (0, path_1.join)(home.contentRoot, 'learnings');
|
|
93
94
|
// pack-home-exempt: deliberate historical secondary location, see above.
|
|
94
95
|
return (0, path_1.join)((0, project_fraim_paths_1.getUserFraimDirPath)(), 'manager', 'learnings');
|
|
95
96
|
}
|
|
96
97
|
function getLearningRoots(workspaceRoot) {
|
|
97
|
-
const managerHome = (0,
|
|
98
|
+
const managerHome = (0, pack_home_2.resolvePackHome)('manager', { migrateLegacy: false });
|
|
98
99
|
const managerHomeBase = (0, path_1.join)(managerHome.contentRoot, 'learnings');
|
|
99
100
|
const managerCacheBase = managerSecondaryLearningsBase();
|
|
100
101
|
// Derive the display base from the actual cache path relative to ~/.fraim so
|
|
@@ -117,7 +118,7 @@ function sameResolvedPath(a, b) {
|
|
|
117
118
|
return (0, path_1.resolve)(a) === (0, path_1.resolve)(b);
|
|
118
119
|
}
|
|
119
120
|
function portablePersonalBaseIsLive(globalBase) {
|
|
120
|
-
return sameResolvedPath((0, path_1.join)((0,
|
|
121
|
+
return sameResolvedPath((0, path_1.join)((0, pack_home_2.resolvePackHome)('manager', { migrateLegacy: false }).contentRoot, 'learnings'), globalBase)
|
|
121
122
|
|| !sameResolvedPath(globalBase, (0, project_fraim_paths_1.getUserFraimLearningsDir)());
|
|
122
123
|
}
|
|
123
124
|
function resolvePersonalLearningFile(repoBase, managerCacheBase, managerCacheDisplayBase, globalBase, globalDisplayBase, fileName) {
|
|
@@ -389,7 +390,7 @@ function readFrontmatter(content) {
|
|
|
389
390
|
/** Configured owning homes only; discovery never provisions or infers a home. */
|
|
390
391
|
function getLearningInputHomes(workspaceRoot) {
|
|
391
392
|
const homes = [{ root: workspaceRoot, contentRoot: (0, path_1.dirname)((0, project_fraim_paths_1.getWorkspaceLearningsDir)(workspaceRoot)) },
|
|
392
|
-
(0,
|
|
393
|
+
(0, pack_home_2.resolvePackHome)('manager', { migrateLegacy: false }), (0, pack_home_2.resolvePackHome)('org', { migrateLegacy: false })];
|
|
393
394
|
const seen = new Set();
|
|
394
395
|
return homes.filter(home => {
|
|
395
396
|
const key = (0, learning_source_record_1.normalizeLearningSourcePath)(home.root) + ':' + (0, learning_source_record_1.normalizeLearningSourcePath)(home.contentRoot);
|
|
@@ -400,7 +401,7 @@ function getLearningInputHomes(workspaceRoot) {
|
|
|
400
401
|
});
|
|
401
402
|
}
|
|
402
403
|
/** Guard the physical owner boundary before reading any input bytes. */
|
|
403
|
-
function readLearningSourceFile(filePath, homeRoot, contentRoot) {
|
|
404
|
+
function readLearningSourceFile(filePath, homeRoot, contentRoot, configuredPaths) {
|
|
404
405
|
try {
|
|
405
406
|
if (!(0, fs_1.lstatSync)(filePath).isFile() || (0, fs_1.lstatSync)(filePath).isSymbolicLink()) {
|
|
406
407
|
return { status: 'invalid', reason: 'Learning source must be a regular file' };
|
|
@@ -410,7 +411,7 @@ function readLearningSourceFile(filePath, homeRoot, contentRoot) {
|
|
|
410
411
|
return { status: 'invalid', reason: 'Learning source escapes configured home' };
|
|
411
412
|
}
|
|
412
413
|
const content = (0, fs_1.readFileSync)(filePath, 'utf8');
|
|
413
|
-
return { ...(0, learning_source_record_1.parseLearningSourceRecord)(content, filePath, homeRoot, contentRoot), content };
|
|
414
|
+
return { ...(0, learning_source_record_1.parseLearningSourceRecord)(content, filePath, homeRoot, contentRoot, configuredPaths), content };
|
|
414
415
|
}
|
|
415
416
|
catch {
|
|
416
417
|
return { status: 'invalid', reason: 'Learning source unavailable' };
|
|
@@ -434,7 +435,7 @@ function collectPendingL0SourceFiles(workspaceRoot, resolvedUserId, _roots) {
|
|
|
434
435
|
for (const home of homes) {
|
|
435
436
|
let paths;
|
|
436
437
|
try {
|
|
437
|
-
paths = (0,
|
|
438
|
+
paths = (0, pack_home_1.resolvePackArtifactPaths)(home);
|
|
438
439
|
}
|
|
439
440
|
catch {
|
|
440
441
|
continue;
|
|
@@ -449,7 +450,7 @@ function collectPendingL0SourceFiles(workspaceRoot, resolvedUserId, _roots) {
|
|
|
449
450
|
const file = (0, path_1.join)(directory, name);
|
|
450
451
|
if (reservations.has((0, learning_source_record_1.normalizeLearningSourcePath)(file)))
|
|
451
452
|
continue;
|
|
452
|
-
const result = readLearningSourceFile(file, home.root, home.contentRoot);
|
|
453
|
+
const result = readLearningSourceFile(file, home.root, home.contentRoot, paths);
|
|
453
454
|
if (result.status !== 'valid' || result.record.author !== resolvedUserId
|
|
454
455
|
|| result.record.state !== 'pending' || result.record.synthesized
|
|
455
456
|
|| isTruthyFlag(readFrontmatter(result.content).dismissed))
|
|
@@ -474,15 +475,17 @@ function collectPendingL0SourceFiles(workspaceRoot, resolvedUserId, _roots) {
|
|
|
474
475
|
* it cannot drift from the read path again.
|
|
475
476
|
*/
|
|
476
477
|
function orgCacheDisplayPath(fileName) {
|
|
477
|
-
const cachePath = (0, path_1.join)((0,
|
|
478
|
+
const cachePath = (0, path_1.join)((0, pack_home_2.resolvePackHome)('org', { migrateLegacy: false }).contentRoot, 'learnings', fileName);
|
|
478
479
|
return userFraimDisplayPathFor(cachePath);
|
|
479
480
|
}
|
|
480
481
|
function userFraimDisplayPathFor(absolutePath) {
|
|
481
|
-
const fraimDir = (0, project_fraim_paths_1.getUserFraimDirPath)();
|
|
482
|
-
const
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
return
|
|
482
|
+
const fraimDir = (0, path_1.resolve)((0, project_fraim_paths_1.getUserFraimDirPath)());
|
|
483
|
+
const resolved = (0, path_1.resolve)(absolutePath);
|
|
484
|
+
const rel = (0, path_1.relative)(fraimDir, resolved);
|
|
485
|
+
const isWithin = rel !== '..' && !rel.startsWith('..' + path_1.sep) && !(0, path_1.isAbsolute)(rel);
|
|
486
|
+
return isWithin
|
|
487
|
+
? (0, project_fraim_paths_1.getUserFraimDisplayPath)(rel.replace(/\\/g, '/'))
|
|
488
|
+
: resolved.replace(/\\/g, '/');
|
|
486
489
|
}
|
|
487
490
|
/**
|
|
488
491
|
* Resolve an L2 org-scope learning file (issue #563): a repo-local override
|
|
@@ -497,7 +500,7 @@ function resolveOrgLearningFile(repoLearningsBase, fileName) {
|
|
|
497
500
|
if ((0, fs_1.existsSync)(repoPath)) {
|
|
498
501
|
return { present: true, path: repoPath, displayPath: `${REPO_LEARNINGS_REL}/${fileName}` };
|
|
499
502
|
}
|
|
500
|
-
const cachePath = (0, path_1.join)((0,
|
|
503
|
+
const cachePath = (0, path_1.join)((0, pack_home_2.resolvePackHome)('org', { migrateLegacy: false }).contentRoot, 'learnings', fileName);
|
|
501
504
|
if ((0, fs_1.existsSync)(cachePath)) {
|
|
502
505
|
return { present: true, path: cachePath, displayPath: orgCacheDisplayPath(fileName) };
|
|
503
506
|
}
|
|
@@ -510,7 +513,7 @@ function resolveOrgLearningFile(repoLearningsBase, fileName) {
|
|
|
510
513
|
*/
|
|
511
514
|
function resolveOrgLearningFileTiers(repoLearningsBase, fileName) {
|
|
512
515
|
const tiers = [];
|
|
513
|
-
const cachePath = (0, path_1.join)((0,
|
|
516
|
+
const cachePath = (0, path_1.join)((0, pack_home_2.resolvePackHome)('org', { migrateLegacy: false }).contentRoot, 'learnings', fileName);
|
|
514
517
|
if ((0, fs_1.existsSync)(cachePath)) {
|
|
515
518
|
tiers.push({
|
|
516
519
|
level: 'org',
|
|
@@ -738,7 +741,7 @@ function resolveOrgContextFile(workspaceRoot, relativePath, orgCacheEligible = t
|
|
|
738
741
|
};
|
|
739
742
|
}
|
|
740
743
|
if (orgCacheEligible) {
|
|
741
|
-
const cachePath = (0, path_1.join)((0,
|
|
744
|
+
const cachePath = (0, path_1.join)((0, pack_home_2.resolvePackHome)('org', { migrateLegacy: false }).contentRoot, relativePath);
|
|
742
745
|
if ((0, fs_1.existsSync)(cachePath)) {
|
|
743
746
|
return {
|
|
744
747
|
present: true,
|
|
@@ -747,20 +750,12 @@ function resolveOrgContextFile(workspaceRoot, relativePath, orgCacheEligible = t
|
|
|
747
750
|
}
|
|
748
751
|
}
|
|
749
752
|
if (!orgCacheEligible) {
|
|
750
|
-
const managerHome = (0,
|
|
753
|
+
const managerHome = (0, pack_home_2.resolvePackHome)('manager', { migrateLegacy: false });
|
|
751
754
|
const managerCachePath = (0, path_1.join)(managerHome.contentRoot, relativePath);
|
|
752
755
|
if ((0, fs_1.existsSync)(managerCachePath)) {
|
|
753
|
-
// Derive the display path relative to ~/.fraim. For the standard
|
|
754
|
-
// single-machine backend contentRoot is ~/.fraim/personalized-employee
|
|
755
|
-
// so the display path is ~/.fraim/personalized-employee/... For a
|
|
756
|
-
// synced backend it may be elsewhere (e.g. ~/.fraim/packs/manager-repo).
|
|
757
|
-
const fraimDir = (0, project_fraim_paths_1.getUserFraimDirPath)();
|
|
758
|
-
const relToFraim = managerCachePath.startsWith(fraimDir)
|
|
759
|
-
? managerCachePath.slice(fraimDir.length).replace(/\\/g, '/').replace(/^\//, '')
|
|
760
|
-
: `personalized-employee/${relativePath}`;
|
|
761
756
|
return {
|
|
762
757
|
present: true,
|
|
763
|
-
displayPath: (
|
|
758
|
+
displayPath: userFraimDisplayPathFor(managerCachePath)
|
|
764
759
|
};
|
|
765
760
|
}
|
|
766
761
|
}
|
|
@@ -927,7 +922,7 @@ function resolveTeamContextFile(workspaceRoot, key, options = {}) {
|
|
|
927
922
|
// destination (R5.1), so writePath stays empty and the caller must route
|
|
928
923
|
// edits through the org backend's propose-and-approve flow.
|
|
929
924
|
if (key === 'org' || key === 'orgRules') {
|
|
930
|
-
const cachePath = (0, path_1.join)((0,
|
|
925
|
+
const cachePath = (0, path_1.join)((0, pack_home_2.resolvePackHome)('org', { migrateLegacy: false, ...options }).contentRoot, relativePath);
|
|
931
926
|
if ((0, fs_1.existsSync)(cachePath)) {
|
|
932
927
|
const fraimDir = (0, project_fraim_paths_1.getUserFraimDirPath)();
|
|
933
928
|
const orgDisplayPath = cachePath.startsWith(fraimDir)
|
|
@@ -944,7 +939,7 @@ function resolveTeamContextFile(workspaceRoot, key, options = {}) {
|
|
|
944
939
|
}
|
|
945
940
|
}
|
|
946
941
|
if (key === 'manager' || key === 'managerRules') {
|
|
947
|
-
const managerHome = (0,
|
|
942
|
+
const managerHome = (0, pack_home_2.resolvePackHome)('manager', { migrateLegacy: false, ...options });
|
|
948
943
|
const managerCachePath = (0, path_1.join)(managerHome.contentRoot, relativePath);
|
|
949
944
|
if ((0, fs_1.existsSync)(managerCachePath)) {
|
|
950
945
|
const fraimDir = (0, project_fraim_paths_1.getUserFraimDirPath)();
|
|
@@ -986,7 +981,7 @@ function resolveTeamContextFile(workspaceRoot, key, options = {}) {
|
|
|
986
981
|
function orgBrandCandidateDirs(workspaceRoot) {
|
|
987
982
|
return [
|
|
988
983
|
(0, path_1.join)((0, project_fraim_paths_1.getWorkspaceFraimDir)(workspaceRoot), 'personalized-employee', 'context'),
|
|
989
|
-
(0, path_1.join)((0,
|
|
984
|
+
(0, path_1.join)((0, pack_home_2.resolvePackHome)('org', { migrateLegacy: false }).contentRoot, 'context'),
|
|
990
985
|
(0, path_1.join)((0, project_fraim_paths_1.getUserFraimDirPath)(), 'personalized-employee', 'context'),
|
|
991
986
|
];
|
|
992
987
|
}
|
|
@@ -1561,8 +1556,8 @@ function readPreservedLearnings(workspaceRoot, userId, scope, level = 'machine',
|
|
|
1561
1556
|
// Only the human library reads these observations. An unavailable store must
|
|
1562
1557
|
// fail this read, so a client can retain known history rather than invent zeroes.
|
|
1563
1558
|
const resolvedUserId = scope === 'org' ? userId : resolveLearningUserId(workspaceRoot, userId, roots);
|
|
1564
|
-
const managerHome = (0,
|
|
1565
|
-
const orgHome = scope === 'org' ? (0,
|
|
1559
|
+
const managerHome = (0, pack_home_2.resolvePackHome)('manager', { migrateLegacy: false });
|
|
1560
|
+
const orgHome = scope === 'org' ? (0, pack_home_2.resolvePackHome)('org', { migrateLegacy: false }) : null;
|
|
1566
1561
|
const assessmentHomes = [
|
|
1567
1562
|
{ level: 'Project', root: (0, project_fraim_paths_1.getWorkspaceFraimDir)(workspaceRoot), contentRoot: (0, path_1.join)((0, project_fraim_paths_1.getWorkspaceFraimDir)(workspaceRoot), 'personalized-employee') },
|
|
1568
1563
|
{ level: 'Manager', root: managerHome.root, contentRoot: managerHome.contentRoot },
|
|
@@ -1605,8 +1600,8 @@ function readPreservedLearnings(workspaceRoot, userId, scope, level = 'machine',
|
|
|
1605
1600
|
// #806: domain-scoped org learnings — union from both locations.
|
|
1606
1601
|
const allDomainFiles = [
|
|
1607
1602
|
...listDomainLearningFiles(roots.repoLearningsBase, 'org', fileType, effectiveCatalog).map((d) => ({ ...d, base: roots.repoLearningsBase })),
|
|
1608
|
-
...listDomainLearningFiles((0, path_1.join)((0,
|
|
1609
|
-
.map((d) => ({ ...d, base: (0, path_1.join)((0,
|
|
1603
|
+
...listDomainLearningFiles((0, path_1.join)((0, pack_home_2.resolvePackHome)('org', { migrateLegacy: false }).contentRoot, 'learnings'), 'org', fileType, effectiveCatalog)
|
|
1604
|
+
.map((d) => ({ ...d, base: (0, path_1.join)((0, pack_home_2.resolvePackHome)('org', { migrateLegacy: false }).contentRoot, 'learnings') })),
|
|
1610
1605
|
];
|
|
1611
1606
|
const seenDomainFile = new Set();
|
|
1612
1607
|
for (const { fileName, domain, base } of allDomainFiles) {
|
|
@@ -1667,7 +1662,7 @@ function resolveLearningFilePath(workspaceRoot, userId, ref, effectiveCatalog) {
|
|
|
1667
1662
|
// Write org entries to the org pack home — same location share-with-others uses.
|
|
1668
1663
|
// The Hub read path already merges pack home + repo-local, so edits land where
|
|
1669
1664
|
// the agent reads them without needing a repo commit.
|
|
1670
|
-
return (0, path_1.join)((0,
|
|
1665
|
+
return (0, path_1.join)((0, pack_home_2.resolvePackHome)('org', { migrateLegacy: false }).contentRoot, 'learnings', learningFileName('org', fileType, ref.domain, effectiveCatalog));
|
|
1671
1666
|
}
|
|
1672
1667
|
const resolvedUserId = resolveLearningUserId(workspaceRoot, userId, roots);
|
|
1673
1668
|
// Issue #1070: for the 'machine' level use the configured manager home.
|