fraim 2.0.360-next.3 → 2.0.361-next.0
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 +20 -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
|
@@ -46,20 +46,25 @@ function directory(root, value, field) {
|
|
|
46
46
|
throw new Error(`Invalid artifactPaths.${field}: target is not a directory`);
|
|
47
47
|
return target.replace(/\\/g, '/');
|
|
48
48
|
}
|
|
49
|
-
function resolveArtifactPaths(homeRoot, contentRoot) {
|
|
49
|
+
function resolveArtifactPaths(homeRoot, contentRoot, configPath, configOverrides) {
|
|
50
50
|
if (!node_path_1.default.isAbsolute(homeRoot) || !node_path_1.default.isAbsolute(contentRoot))
|
|
51
51
|
throw new Error('Absolute owning homeRoot and contentRoot are required');
|
|
52
52
|
homeRoot = node_path_1.default.resolve(homeRoot);
|
|
53
53
|
contentRoot = node_path_1.default.resolve(contentRoot);
|
|
54
54
|
if (!within(homeRoot, contentRoot))
|
|
55
55
|
throw new Error('Personalized contentRoot escapes owning homeRoot');
|
|
56
|
-
const
|
|
56
|
+
const resolvedConfigPath = configPath ?? node_path_1.default.join(node_path_1.default.dirname(contentRoot), 'config.json');
|
|
57
57
|
let config = {};
|
|
58
|
-
|
|
59
|
-
config =
|
|
58
|
+
if (configOverrides !== undefined) {
|
|
59
|
+
config = configOverrides;
|
|
60
60
|
}
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
else if (node_fs_1.default.existsSync(resolvedConfigPath)) {
|
|
62
|
+
try {
|
|
63
|
+
config = (0, local_runtime_1.readJsonIfObject)(resolvedConfigPath, { strict: true });
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
throw new Error(`Cannot read artifact configuration: ${resolvedConfigPath}`);
|
|
67
|
+
}
|
|
63
68
|
}
|
|
64
69
|
const configured = config.artifactPaths === undefined ? {} : config.artifactPaths;
|
|
65
70
|
// Defaults follow the configured home layout, but every value has the same root-relative basis.
|
|
@@ -90,7 +95,7 @@ function resolveArtifactPaths(homeRoot, contentRoot) {
|
|
|
90
95
|
if (within(target, other) || within(other, target))
|
|
91
96
|
throw new Error(`Artifact directories overlap: ${field} and ${otherField}`);
|
|
92
97
|
}
|
|
93
|
-
return { configPath:
|
|
98
|
+
return { configPath: resolvedConfigPath.replace(/\\/g, '/'), evidence: outputs.evidence, raw: outputs.raw,
|
|
94
99
|
retrospectives: outputs.retrospectives,
|
|
95
100
|
archiveRaw: directory(homeRoot, values.learningArchive + '/raw', 'learningArchive'),
|
|
96
101
|
archiveRetrospectives: directory(homeRoot, values.learningArchive + '/retrospectives', 'learningArchive') };
|
|
@@ -12,7 +12,7 @@ const learning_usage_store_1 = require("../../../src/local-mcp-server/learning-u
|
|
|
12
12
|
const owner_identity_1 = require("./owner-identity");
|
|
13
13
|
const proposal_queue_1 = require("./proposal-queue");
|
|
14
14
|
const completion_state_1 = require("./completion-state");
|
|
15
|
-
const
|
|
15
|
+
const pack_home_1 = require("../../../src/cli/utils/pack-home");
|
|
16
16
|
const learning_source_record_1 = require("../learning-usage/learning-source-record");
|
|
17
17
|
const corpus_audit_1 = require("./corpus-audit");
|
|
18
18
|
const outcome_types_1 = require("./outcome-types");
|
|
@@ -34,7 +34,7 @@ function resolveAssessmentSources(input, homes) {
|
|
|
34
34
|
const originalFile = (0, owner_identity_1.configuredArtifactPath)(home, node_path_1.default.join((0, owner_identity_1.ownerArtifactRoot)(home), ref.relativePath));
|
|
35
35
|
let file = originalFile;
|
|
36
36
|
if (!node_fs_1.default.existsSync(file)) {
|
|
37
|
-
const paths = (0,
|
|
37
|
+
const paths = (0, pack_home_1.resolvePackArtifactPaths)(home);
|
|
38
38
|
const archive = (0, completion_state_1.sourcePathKey)(node_path_1.default.dirname(file)) === (0, completion_state_1.sourcePathKey)(paths.raw) ? paths.archiveRaw
|
|
39
39
|
: (0, completion_state_1.sourcePathKey)(node_path_1.default.dirname(file)) === (0, completion_state_1.sourcePathKey)(paths.retrospectives) ? paths.archiveRetrospectives : undefined;
|
|
40
40
|
if (archive)
|
|
@@ -83,7 +83,8 @@ function verifyAssessmentSources(sources, state) {
|
|
|
83
83
|
const originalBody = source.archived ? body.replace(/^(synthesized:)[^\r\n]*/m, '$1') : body;
|
|
84
84
|
if ((0, proposal_queue_1.hashContent)(body) !== source.ref.sha256 && (0, proposal_queue_1.hashContent)(originalBody) !== source.ref.sha256)
|
|
85
85
|
throw new Error('assessment-source-hash-conflict');
|
|
86
|
-
const
|
|
86
|
+
const locations = (0, pack_home_1.resolvePackArtifactPaths)(source.home);
|
|
87
|
+
const parsed = (0, learning_source_record_1.parseLearningSourceRecord)(body, source.file, (0, owner_identity_1.ownerArtifactRoot)(source.home), source.home.contentRoot, locations);
|
|
87
88
|
if (parsed.status !== 'valid')
|
|
88
89
|
throw new Error(`invalid-assessment-source: ${parsed.reason}`);
|
|
89
90
|
if (parsed.record.sourceId !== source.ref.sourceId
|
|
@@ -17,7 +17,7 @@ const proposal_queue_1 = require("./proposal-queue");
|
|
|
17
17
|
const entry_authoring_1 = require("./entry-authoring");
|
|
18
18
|
const run_publication_1 = require("./run-publication");
|
|
19
19
|
const completion_state_2 = require("./completion-state");
|
|
20
|
-
const
|
|
20
|
+
const pack_home_1 = require("../../../src/cli/utils/pack-home");
|
|
21
21
|
const learning_source_record_1 = require("../learning-usage/learning-source-record");
|
|
22
22
|
const corpus_audit_1 = require("./corpus-audit");
|
|
23
23
|
const { learningEntryBlocks, learningEntryMetadata } = require('./entry-format');
|
|
@@ -453,14 +453,14 @@ function sourceCompletion(run, homes, options, afterStage) {
|
|
|
453
453
|
if (new Set(normalized.map(item => process.platform === 'win32' ? item.path.toLowerCase() : item.path)).size !== normalized.length)
|
|
454
454
|
throw new Error('duplicate-analyzed-source');
|
|
455
455
|
const runHome = options.resolveHome(run.owner);
|
|
456
|
-
const retrospectiveFile = (0, owner_identity_1.configuredArtifactPath)(runHome, node_path_1.default.join((0,
|
|
456
|
+
const retrospectiveFile = (0, owner_identity_1.configuredArtifactPath)(runHome, node_path_1.default.join((0, pack_home_1.resolvePackArtifactPaths)(runHome).retrospectives, node_path_1.default.basename(options.retrospective.relativePath)));
|
|
457
457
|
const allowed = (source) => {
|
|
458
458
|
for (const home of homes) {
|
|
459
459
|
const configured = options.resolveHome(home.owner);
|
|
460
460
|
(0, owner_identity_1.assertSameOwner)(home.owner, configured.owner);
|
|
461
461
|
if ((0, completion_state_2.sourcePathKey)(home.contentRoot) !== (0, completion_state_2.sourcePathKey)(configured.contentRoot) || (0, completion_state_2.sourcePathKey)(home.root) !== (0, completion_state_2.sourcePathKey)(configured.root) || (0, completion_state_2.sourcePathKey)((0, owner_identity_1.ownerArtifactRoot)(home)) !== (0, completion_state_2.sourcePathKey)((0, owner_identity_1.ownerArtifactRoot)(configured)))
|
|
462
462
|
throw new Error('analyzed-source-outside-home');
|
|
463
|
-
const locations = (0,
|
|
463
|
+
const locations = (0, pack_home_1.resolvePackArtifactPaths)(configured);
|
|
464
464
|
const sourceDirectory = source.kind === 'raw' ? locations.raw : locations.retrospectives;
|
|
465
465
|
const archiveDirectory = source.kind === 'raw' ? locations.archiveRaw : locations.archiveRetrospectives;
|
|
466
466
|
if ((0, completion_state_2.sourcePathKey)(node_path_1.default.dirname(source.path)) !== (0, completion_state_2.sourcePathKey)(sourceDirectory))
|
|
@@ -471,7 +471,7 @@ function sourceCompletion(run, homes, options, afterStage) {
|
|
|
471
471
|
const relative = node_path_1.default.relative(configured.contentRoot, directory);
|
|
472
472
|
return relative === '..' || relative.startsWith('..' + node_path_1.default.sep) || node_path_1.default.isAbsolute(relative);
|
|
473
473
|
});
|
|
474
|
-
return { root: (0, owner_identity_1.ownerArtifactRoot)(configured), contentRoot: configured.contentRoot, owner: configured.owner, destination,
|
|
474
|
+
return { root: (0, owner_identity_1.ownerArtifactRoot)(configured), contentRoot: configured.contentRoot, owner: configured.owner, destination, locations,
|
|
475
475
|
layout: { contentRoot: node_path_1.default.resolve(configured.contentRoot).replace(/\\/g, '/'), sourceDirectory, archiveDirectory, ...(outsideContent ? { homeRoot: (0, owner_identity_1.ownerArtifactRoot)(configured) } : {}) } };
|
|
476
476
|
}
|
|
477
477
|
throw new Error('analyzed-source-outside-home');
|
|
@@ -510,7 +510,7 @@ function sourceCompletion(run, homes, options, afterStage) {
|
|
|
510
510
|
if (!node_path_1.default.isAbsolute(ref))
|
|
511
511
|
continue;
|
|
512
512
|
const isInput = homes.some(home => {
|
|
513
|
-
const locations = (0,
|
|
513
|
+
const locations = (0, pack_home_1.resolvePackArtifactPaths)(home);
|
|
514
514
|
return [locations.raw, locations.retrospectives].some(directory => (0, completion_state_2.sourcePathKey)(node_path_1.default.dirname(ref)) === (0, completion_state_2.sourcePathKey)(directory));
|
|
515
515
|
});
|
|
516
516
|
if (isInput && !normalized.some(item => (0, completion_state_2.sourcePathKey)(item.path) === (0, completion_state_2.sourcePathKey)(ref) && item.sha256 === action.sourceHashes?.[ref])
|
|
@@ -536,8 +536,8 @@ function sourceCompletion(run, homes, options, afterStage) {
|
|
|
536
536
|
&& !options.deferredSources?.some(item => (0, completion_state_2.sourcePathKey)(source.path) === (0, completion_state_2.sourcePathKey)(item.path)))))
|
|
537
537
|
throw new Error('completion-sources-conflict');
|
|
538
538
|
const date = new Date().toISOString().slice(0, 10);
|
|
539
|
-
const validateInput = (body, source, root, contentRoot, owner, archivePath) => {
|
|
540
|
-
const parsed = (0, learning_source_record_1.parseLearningSourceRecord)(body, archivePath ?? source.path, root, contentRoot);
|
|
539
|
+
const validateInput = (body, source, root, contentRoot, owner, configuredPaths, archivePath) => {
|
|
540
|
+
const parsed = (0, learning_source_record_1.parseLearningSourceRecord)(body, archivePath ?? source.path, root, contentRoot, configuredPaths);
|
|
541
541
|
if (parsed.status !== 'valid' || parsed.record.kind !== source.kind
|
|
542
542
|
|| !(0, completion_state_1.sourceAuthorAllowed)(state, parsed.record.author, owner)
|
|
543
543
|
|| parsed.record.state !== (archivePath ? 'archived' : 'pending')
|
|
@@ -551,13 +551,13 @@ function sourceCompletion(run, homes, options, afterStage) {
|
|
|
551
551
|
throw new Error(`completion-sources-conflict: ${source.path}: original plan changed`);
|
|
552
552
|
return recorded;
|
|
553
553
|
}
|
|
554
|
-
const { root, destination, contentRoot, owner, layout } = allowed(source);
|
|
554
|
+
const { root, destination, contentRoot, owner, layout, locations } = allowed(source);
|
|
555
555
|
regular(source.path, root);
|
|
556
556
|
regular(destination, root);
|
|
557
557
|
const body = node_fs_1.default.readFileSync(source.path, 'utf8');
|
|
558
558
|
if ((0, proposal_queue_1.hashContent)(body) !== source.sha256)
|
|
559
559
|
throw new Error('analyzed-source-changed');
|
|
560
|
-
validateInput(body, source, root, contentRoot, owner);
|
|
560
|
+
validateInput(body, source, root, contentRoot, owner, locations);
|
|
561
561
|
return { ...source, destination, resultHash: (0, proposal_queue_1.hashContent)(stamp(body, date)), synthesizedAt: date, layout };
|
|
562
562
|
});
|
|
563
563
|
const reservationsAndPlans = [...(prior ?? []), ...plans.filter(source => !prior?.some(item => (0, completion_state_2.sourcePathKey)(item.path) === (0, completion_state_2.sourcePathKey)(source.path)))];
|
|
@@ -637,7 +637,7 @@ function sourceCompletion(run, homes, options, afterStage) {
|
|
|
637
637
|
throw new Error('source-participants-conflict');
|
|
638
638
|
persistRejections(false);
|
|
639
639
|
for (const source of plans) {
|
|
640
|
-
const { root, destination, contentRoot, owner, layout } = allowed(source);
|
|
640
|
+
const { root, destination, contentRoot, owner, layout, locations } = allowed(source);
|
|
641
641
|
regular(source.path, root);
|
|
642
642
|
regular(destination, root);
|
|
643
643
|
if ((0, completion_state_2.sourcePathKey)(destination) !== (0, completion_state_2.sourcePathKey)(source.destination) || (source.layout && Object.keys(layout).some(key => typeof source.layout[key] !== 'string' || (0, completion_state_2.sourcePathKey)(layout[key]) !== (0, completion_state_2.sourcePathKey)(source.layout[key]))))
|
|
@@ -646,7 +646,7 @@ function sourceCompletion(run, homes, options, afterStage) {
|
|
|
646
646
|
if (body !== undefined) {
|
|
647
647
|
if ((0, proposal_queue_1.hashContent)(body) !== source.sha256)
|
|
648
648
|
throw new Error('analyzed-source-changed');
|
|
649
|
-
validateInput(body, source, root, contentRoot, owner);
|
|
649
|
+
validateInput(body, source, root, contentRoot, owner, locations);
|
|
650
650
|
}
|
|
651
651
|
if (!node_fs_1.default.existsSync(destination)) {
|
|
652
652
|
if (body === undefined)
|
|
@@ -668,12 +668,12 @@ function sourceCompletion(run, homes, options, afterStage) {
|
|
|
668
668
|
const archivedBody = node_fs_1.default.readFileSync(destination, 'utf8');
|
|
669
669
|
if ((0, proposal_queue_1.hashContent)(archivedBody) !== source.resultHash)
|
|
670
670
|
throw new Error('source-archive-conflict');
|
|
671
|
-
validateInput(archivedBody, source, root, contentRoot, owner, destination);
|
|
671
|
+
validateInput(archivedBody, source, root, contentRoot, owner, locations, destination);
|
|
672
672
|
afterStage?.('source-verified');
|
|
673
673
|
if (body !== undefined) {
|
|
674
674
|
const currentRunHome = options.resolveHome(run.owner);
|
|
675
675
|
(0, owner_identity_1.assertSameOwner)(run.owner, currentRunHome.owner);
|
|
676
|
-
const currentRunPaths = (0,
|
|
676
|
+
const currentRunPaths = (0, pack_home_1.resolvePackArtifactPaths)(currentRunHome);
|
|
677
677
|
if ((0, completion_state_2.sourcePathKey)((0, owner_identity_1.ownerArtifactRoot)(currentRunHome)) !== (0, completion_state_2.sourcePathKey)((0, owner_identity_1.ownerArtifactRoot)(runHome))
|
|
678
678
|
|| (0, completion_state_2.sourcePathKey)(currentRunHome.contentRoot) !== (0, completion_state_2.sourcePathKey)(runHome.contentRoot)
|
|
679
679
|
|| (0, completion_state_2.sourcePathKey)(node_path_1.default.join(currentRunPaths.evidence, 'sleep')) !== (0, completion_state_2.sourcePathKey)(run.completionDirectory)
|
|
@@ -736,7 +736,7 @@ function completeRun(run, summary, homes, options, afterStage) {
|
|
|
736
736
|
sources.push(source);
|
|
737
737
|
}
|
|
738
738
|
const sourceHome = (file) => homes.find(home => {
|
|
739
|
-
const locations = (0,
|
|
739
|
+
const locations = (0, pack_home_1.resolvePackArtifactPaths)(home);
|
|
740
740
|
return [locations.raw, locations.retrospectives].some(directory => (0, completion_state_2.sourcePathKey)(node_path_1.default.dirname(file)) === (0, completion_state_2.sourcePathKey)(directory));
|
|
741
741
|
});
|
|
742
742
|
for (const action of actions)
|
|
@@ -774,7 +774,7 @@ function completeRun(run, summary, homes, options, afterStage) {
|
|
|
774
774
|
const recorded = state.processedSources?.find(item => (0, completion_state_2.sourcePathKey)(item.path) === (0, completion_state_2.sourcePathKey)(source.path));
|
|
775
775
|
if (recorded && (recorded.sha256 !== source.sha256 || recorded.kind !== source.kind))
|
|
776
776
|
throw new Error('original-source-plan-changed');
|
|
777
|
-
const locations = (0,
|
|
777
|
+
const locations = (0, pack_home_1.resolvePackArtifactPaths)(home);
|
|
778
778
|
const destination = (0, owner_identity_1.configuredArtifactPath)(home, node_path_1.default.join(source.kind === 'raw' ? locations.archiveRaw : locations.archiveRetrospectives, node_path_1.default.basename(source.path)));
|
|
779
779
|
if (reserved.includes((0, completion_state_2.sourcePathKey)(source.path)) || reserved.includes((0, completion_state_2.sourcePathKey)(destination)))
|
|
780
780
|
throw new Error('source-reserved-by-pending-run');
|
|
@@ -787,7 +787,7 @@ function completeRun(run, summary, homes, options, afterStage) {
|
|
|
787
787
|
const body = node_fs_1.default.readFileSync(source.path, 'utf8');
|
|
788
788
|
if ((0, proposal_queue_1.hashContent)(body) !== source.sha256)
|
|
789
789
|
throw new Error('analyzed-source-changed');
|
|
790
|
-
const parsed = (0, learning_source_record_1.parseLearningSourceRecord)(body, source.path, (0, owner_identity_1.ownerArtifactRoot)(home), home.contentRoot);
|
|
790
|
+
const parsed = (0, learning_source_record_1.parseLearningSourceRecord)(body, source.path, (0, owner_identity_1.ownerArtifactRoot)(home), home.contentRoot, locations);
|
|
791
791
|
if (parsed.status !== 'valid' || parsed.record.kind !== source.kind || !(0, completion_state_1.sourceAuthorAllowed)(state, parsed.record.author, home.owner) || parsed.record.state !== 'pending' || parsed.record.synthesized)
|
|
792
792
|
throw new Error('invalid-learning-source');
|
|
793
793
|
// References may use immutable identity, content-relative or home-relative path, or
|
|
@@ -835,7 +835,7 @@ function completeRun(run, summary, homes, options, afterStage) {
|
|
|
835
835
|
const retrospective = options.retrospective;
|
|
836
836
|
const retrospectiveHome = options.resolveHome(run.owner);
|
|
837
837
|
(0, owner_identity_1.assertSameOwner)(run.owner, retrospectiveHome.owner);
|
|
838
|
-
const locations = (0,
|
|
838
|
+
const locations = (0, pack_home_1.resolvePackArtifactPaths)(retrospectiveHome);
|
|
839
839
|
const retrospectiveDirectory = node_path_1.default.relative((0, owner_identity_1.ownerArtifactRoot)(retrospectiveHome), locations.retrospectives).replace(/\\/g, '/');
|
|
840
840
|
const legacyDirectory = node_path_1.default.relative(retrospectiveHome.contentRoot, locations.retrospectives).replace(/\\/g, '/');
|
|
841
841
|
const expectedPaths = [retrospectiveDirectory, legacyDirectory].filter(directory => directory.split('/').every(part => part && part !== '.' && part !== '..'))
|
|
@@ -852,7 +852,7 @@ function completeRun(run, summary, homes, options, afterStage) {
|
|
|
852
852
|
const verifyRetrospective = () => {
|
|
853
853
|
// Meaning, attribution and template fidelity are reviewed by the agent.
|
|
854
854
|
// This guard binds the actual document, including on interruption retries.
|
|
855
|
-
const currentLocations = (0,
|
|
855
|
+
const currentLocations = (0, pack_home_1.resolvePackArtifactPaths)(retrospectiveHome);
|
|
856
856
|
if ((0, completion_state_2.sourcePathKey)(node_path_1.default.join(currentLocations.evidence, 'sleep')) !== (0, completion_state_2.sourcePathKey)(run.completionDirectory)
|
|
857
857
|
|| (0, completion_state_2.sourcePathKey)(currentLocations.retrospectives) !== (0, completion_state_2.sourcePathKey)(node_path_1.default.dirname(retrospectiveFile)))
|
|
858
858
|
throw new Error('completion-artifact-paths-changed');
|
|
@@ -862,7 +862,7 @@ function completeRun(run, summary, homes, options, afterStage) {
|
|
|
862
862
|
const body = node_fs_1.default.readFileSync(retrospectiveFile, 'utf8');
|
|
863
863
|
if (!body.trim() || (0, proposal_queue_1.hashContent)(body) !== retrospective.sha256)
|
|
864
864
|
throw new Error('completion-retrospective-changed');
|
|
865
|
-
const parsed = (0, learning_source_record_1.parseLearningSourceRecord)(body, retrospectiveFile, (0, owner_identity_1.ownerArtifactRoot)(retrospectiveHome), retrospectiveHome.contentRoot);
|
|
865
|
+
const parsed = (0, learning_source_record_1.parseLearningSourceRecord)(body, retrospectiveFile, (0, owner_identity_1.ownerArtifactRoot)(retrospectiveHome), retrospectiveHome.contentRoot, currentLocations);
|
|
866
866
|
if (parsed.status !== 'valid' || parsed.record.kind !== 'retrospective' || parsed.record.author !== run.reviewer || parsed.record.runId !== run.runId || parsed.record.synthesized)
|
|
867
867
|
throw new Error('completion-retrospective-invalid');
|
|
868
868
|
};
|
|
@@ -326,6 +326,7 @@ exports.syncCommand = new commander_1.Command('sync')
|
|
|
326
326
|
.option('--skip-updates', 'Skip checking for CLI updates (legacy)')
|
|
327
327
|
.option('--local', 'Sync from local development server (port derived from git branch)')
|
|
328
328
|
.option('--global', 'Sync machine-level FRAIM content only (~/.fraim/), skip project maintenance')
|
|
329
|
+
.option('--automatic', 'Refresh catalogs without changing the selected MCP launcher version')
|
|
329
330
|
.option('--project-root <path>', 'Explicit project root for configuration and local exclusion maintenance')
|
|
330
331
|
.option('--project-adapters', 'Deprecated compatibility option (no effect; use machine-level FRAIM setup)')
|
|
331
332
|
.action(exports.runSync);
|
|
@@ -1,22 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Agent CLI PATH/version health checks for FRAIM doctor command
|
|
4
|
-
* Issue #1284/#1285: detect drift between FRAIM's own managed-PATH resolution,
|
|
5
|
-
* the ambient shell PATH, and the actual npm-global install for agent CLIs
|
|
6
|
-
* FRAIM installs through its managed-Node fallback.
|
|
7
|
-
*/
|
|
8
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
9
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
10
|
-
};
|
|
11
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
3
|
exports.getAgentCliHealthChecks = getAgentCliHealthChecks;
|
|
13
4
|
exports.checkAgentCliHealthByCommand = checkAgentCliHealthByCommand;
|
|
14
|
-
const child_process_1 = require("child_process");
|
|
15
|
-
const path_1 = __importDefault(require("path"));
|
|
16
|
-
const redact_paths_1 = require("../redact-paths");
|
|
17
5
|
const managed_agent_paths_1 = require("../../utils/managed-agent-paths");
|
|
18
|
-
const
|
|
6
|
+
const agent_command_probe_1 = require("../../utils/agent-command-probe");
|
|
19
7
|
const command_resolution_1 = require("../../mcp/command-resolution");
|
|
8
|
+
const operation_scope_1 = require("../../utils/operation-scope");
|
|
9
|
+
const redact_paths_1 = require("../redact-paths");
|
|
10
|
+
const command_resolution_2 = require("../../mcp/command-resolution");
|
|
20
11
|
// CLIs with a managed-install fallback (npm install -g into FRAIM's portable
|
|
21
12
|
// Node when no system install is found). Add gemini/copilot here when their
|
|
22
13
|
// installManagedAgent wiring lands.
|
|
@@ -24,50 +15,6 @@ const MANAGED_CLIS = [
|
|
|
24
15
|
{ id: 'codex', label: 'Codex', command: 'codex' },
|
|
25
16
|
{ id: 'claude-code', label: 'Claude Code', command: 'claude' },
|
|
26
17
|
];
|
|
27
|
-
// Windows cannot CreateProcess a `.cmd`/`.bat` file directly (spawnSync on a
|
|
28
|
-
// resolved absolute `.cmd` path throws EINVAL) — it must go through cmd.exe,
|
|
29
|
-
// matching the same wrapping `hosts.ts`'s `resolveHostInvocation()` and
|
|
30
|
-
// `server.ts`'s `hubCommandVersion()` already use. Duplicated here (not
|
|
31
|
-
// imported from `hosts.ts`) because `src/cli` is a pure layer that cannot
|
|
32
|
-
// import the server-layer `src/ai-hub` module (`scripts/validate-purity.ts`).
|
|
33
|
-
function quoteWindowsArg(value) {
|
|
34
|
-
if (value.length === 0)
|
|
35
|
-
return '""';
|
|
36
|
-
return `"${value.replace(/(\\*)"/g, '$1$1\\"').replace(/(\\+)$/g, '$1$1')}"`;
|
|
37
|
-
}
|
|
38
|
-
function escapeWindowsArg(value) {
|
|
39
|
-
return /[\s"]/u.test(value) ? quoteWindowsArg(value) : value;
|
|
40
|
-
}
|
|
41
|
-
function probeVersion(commandPath) {
|
|
42
|
-
try {
|
|
43
|
-
const executable = process.platform === 'win32' ? 'cmd.exe' : commandPath;
|
|
44
|
-
const args = process.platform === 'win32'
|
|
45
|
-
? ['/d', '/s', '/c', `${escapeWindowsArg(commandPath)} --version`]
|
|
46
|
-
: ['--version'];
|
|
47
|
-
const result = (0, child_process_1.spawnSync)(executable, args, { encoding: 'utf8', timeout: 5000, windowsHide: true });
|
|
48
|
-
if (result.status !== 0 || result.error)
|
|
49
|
-
return null;
|
|
50
|
-
// Issue #1256 (AC-D1/D2): only stdout counts as a version — see versionFromProbeOutput.
|
|
51
|
-
return (0, managed_agent_paths_1.versionFromProbeOutput)(result.stdout, result.stderr);
|
|
52
|
-
}
|
|
53
|
-
catch {
|
|
54
|
-
return null;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
// The persisted Windows User PATH (what a brand-new terminal reads) can
|
|
58
|
-
// disagree with this process's own inherited `process.env.PATH` (what a
|
|
59
|
-
// shell already open before the fix landed still uses) — issue #1285's
|
|
60
|
-
// exact "update said success but --version looked wrong" ambiguity.
|
|
61
|
-
// Windows-only; there is no equivalent persisted-PATH registry on macOS/Linux.
|
|
62
|
-
// Issue #1748: this used to shell out to PowerShell for HKCU Environment Path on its own,
|
|
63
|
-
// a second independently-written Windows registry PATH reader. Two implementations of the
|
|
64
|
-
// same read drift apart, and a parsing fix applied to one is easy to miss on the other,
|
|
65
|
-
// which is the same PATH-resolution-drift bug class this check exists to report. Delegates
|
|
66
|
-
// to the shared reader instead, which also covers HKLM rather than only the user hive and
|
|
67
|
-
// expands REG_EXPAND_SZ references.
|
|
68
|
-
function readPersistedUserPath() {
|
|
69
|
-
return (0, machine_path_1.resolveMachinePathCached)();
|
|
70
|
-
}
|
|
71
18
|
// Issue #1482 (Privacy PRIV05 / GDPR Art. 5(1)(c)): the single exit point for every branch
|
|
72
19
|
// below. `message` and `suggestion` are forwarded off this process — to the browser as
|
|
73
20
|
// `driftWarning`/`driftSuggestion` by `POST /api/ai-hub/check-agent`, and to the Hub's server
|
|
@@ -75,132 +22,36 @@ function readPersistedUserPath() {
|
|
|
75
22
|
// Redacting here rather than at each branch that happens to build a path into its message is
|
|
76
23
|
// deliberate: a branch added to `computeAgentCliHealth()` later is covered whether or not its
|
|
77
24
|
// author remembers this constraint. `details` is not redacted; see `redact-paths.ts` for why.
|
|
78
|
-
async function runAgentCliHealthCheck(cli) {
|
|
79
|
-
return (0, redact_paths_1.redactCheckResultPaths)(await computeAgentCliHealth(cli));
|
|
25
|
+
async function runAgentCliHealthCheck(cli, options = {}) {
|
|
26
|
+
return (0, operation_scope_1.withOperation)(options, 30000, async (signal) => (0, redact_paths_1.redactCheckResultPaths)(await computeAgentCliHealth(cli, signal)));
|
|
80
27
|
}
|
|
81
|
-
async function computeAgentCliHealth(cli) {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
// of `versionMismatch` below short-circuit on it, so this check would return
|
|
98
|
-
// "consistent" while the #1285 stale-shim drift it exists to catch went unreported.
|
|
99
|
-
// The machine PATH is what a newly opened shell resolves, which is the notion of
|
|
100
|
-
// "ambient" this check actually wants once the process is no longer self-mutating.
|
|
101
|
-
const machinePathForAmbient = (0, machine_path_1.resolveMachinePathCached)();
|
|
102
|
-
const ambientPath = (0, command_resolution_1.getSystemCommandPath)(cli.command)
|
|
103
|
-
|| (machinePathForAmbient ? (0, command_resolution_1.getSystemCommandPath)(cli.command, machinePathForAmbient) : null);
|
|
104
|
-
const managedSearchPath = (0, managed_agent_paths_1.buildPathWithManagedAgentBins)(process.env.PATH);
|
|
105
|
-
const managedPath = (0, command_resolution_1.getSystemCommandPath)(cli.command, managedSearchPath);
|
|
106
|
-
if (!ambientPath && !managedPath) {
|
|
107
|
-
// Issue #1256 (AC-A3): before concluding the CLI is absent, try the same shared
|
|
108
|
-
// location-agnostic recovery the Hub's version probe and check-agent route use. This
|
|
109
|
-
// only changes the answer for a CLI this process's raw/managed PATH cannot see at all
|
|
110
|
-
// (e.g. doctor itself invoked from a PATH-truncated context) — it never overrides a
|
|
111
|
-
// drift verdict computed from a real ambient/managed hit above.
|
|
112
|
-
const recoveredSearchPath = (0, managed_agent_paths_1.buildRecoveredAgentPath)(process.env.PATH);
|
|
113
|
-
const recoveredPath = (0, command_resolution_1.getSystemCommandPath)(cli.command, recoveredSearchPath);
|
|
114
|
-
if (!recoveredPath) {
|
|
115
|
-
return {
|
|
116
|
-
status: 'passed',
|
|
117
|
-
message: `${cli.label} is not installed; nothing to check.`,
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
const recoveredVersion = probeVersion(recoveredPath);
|
|
121
|
-
if (!recoveredVersion) {
|
|
122
|
-
return {
|
|
123
|
-
status: 'error',
|
|
124
|
-
// Issue #1482: basename only. The full path stays in `details` for local/self-heal use.
|
|
125
|
-
message: `${cli.label} is installed (launcher \`${(0, redact_paths_1.redactPathForDisplay)(recoveredPath)}\`) but failed to run (\`${cli.command} --version\` produced no output).`,
|
|
126
|
-
suggestion: `Reinstall ${cli.label}, then run "${cli.command} --version" again to confirm it's fixed.`,
|
|
127
|
-
details: { recoveredPath },
|
|
128
|
-
};
|
|
28
|
+
async function computeAgentCliHealth(cli, signal) {
|
|
29
|
+
const env = await (0, agent_command_probe_1.agentProbeEnvironmentAsync)(undefined, process.env, { signal });
|
|
30
|
+
const failures = [];
|
|
31
|
+
const selected = await (0, agent_command_probe_1.selectAgentCommand)(cli.command, { env, signal, onFailure: reason => failures.push(reason) });
|
|
32
|
+
const ambientPath = (0, command_resolution_2.getSystemCommandPath)(cli.command);
|
|
33
|
+
const candidate = (0, command_resolution_1.getPortableManagedCommandPath)(cli.command);
|
|
34
|
+
const managedPath = candidate && (0, managed_agent_paths_1.isOwnedManagedAgentTarget)(candidate) ? candidate : null;
|
|
35
|
+
let repairable = false;
|
|
36
|
+
const managed = managedPath ? await (0, agent_command_probe_1.selectAgentCommand)(managedPath, { env, signal, onFailure: reason => { repairable = reason === 'exit'; } }) : null;
|
|
37
|
+
const ambient = ambientPath ? await (0, agent_command_probe_1.selectAgentCommand)(ambientPath, { env: { ...process.env }, signal }) : null;
|
|
38
|
+
const details = { repairable, launchFailure: selected ? undefined : failures.at(-1), ambientPath, ambientVersion: ambient?.version ?? null,
|
|
39
|
+
managedPath, managedVersion: managed?.version ?? null,
|
|
40
|
+
recoveredPath: selected?.target ?? (0, command_resolution_2.getSystemCommandPath)(cli.command, env.PATH), recoveredVersion: selected?.version ?? null };
|
|
41
|
+
if (!selected) {
|
|
42
|
+
if (!failures.length && !ambientPath && !managedPath && !(0, command_resolution_2.getSystemCommandPath)(cli.command, env.PATH)) {
|
|
43
|
+
return { status: 'passed', message: cli.label + ' is not installed; nothing to check.', details };
|
|
129
44
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
};
|
|
45
|
+
const explanation = (0, agent_command_probe_1.agentProbeFailureMessage)(failures.at(-1));
|
|
46
|
+
const boundary = explanation.indexOf('. ');
|
|
47
|
+
return { status: failures.at(-1) === 'timeout' || failures.at(-1) === 'cancelled' ? 'warning' : 'error',
|
|
48
|
+
message: cli.label + ': ' + explanation.slice(0, boundary + 1), suggestion: explanation.slice(boundary + 2), details };
|
|
135
49
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
: managedPath === ambientPath
|
|
140
|
-
? ambientVersion
|
|
141
|
-
: probeVersion(managedPath);
|
|
142
|
-
// Issue #1412: a resolved file that fails to execute at all (e.g. an
|
|
143
|
-
// interrupted update left a non-executable fallback launcher) is a harder
|
|
144
|
-
// failure than drift — `getSystemCommandPath()` only proves the file
|
|
145
|
-
// exists, not that it can run. Without this check, a single broken install
|
|
146
|
-
// with nothing else on PATH to disagree with (ambientPath === managedPath,
|
|
147
|
-
// both versions null) falls straight through the mismatch check below and
|
|
148
|
-
// reads as "consistent". Check this before computing npm-global/drift
|
|
149
|
-
// details that don't matter once the CLI can't run at all.
|
|
150
|
-
const brokenPath = ambientPath && !ambientVersion
|
|
151
|
-
? ambientPath
|
|
152
|
-
: managedPath && !managedVersion
|
|
153
|
-
? managedPath
|
|
154
|
-
: null;
|
|
155
|
-
if (brokenPath) {
|
|
156
|
-
return {
|
|
157
|
-
status: 'error',
|
|
158
|
-
// Issue #1482: basename only, same as the recovery branch above.
|
|
159
|
-
message: `${cli.label} is installed (launcher \`${(0, redact_paths_1.redactPathForDisplay)(brokenPath)}\`) but failed to run (\`${cli.command} --version\` produced no output). An interrupted or partial update may have left a non-functional launcher.`,
|
|
160
|
-
suggestion: `Reinstall ${cli.label}, then run "${cli.command} --version" again to confirm it's fixed.`,
|
|
161
|
-
details: { ambientPath, ambientVersion, managedPath, managedVersion },
|
|
162
|
-
};
|
|
50
|
+
if (ambientPath && (ambientPath !== selected.target || !ambient)) {
|
|
51
|
+
return { status: 'warning', message: cli.label + ' works in Hub (' + selected.version + '), but the current terminal resolves a different or broken launcher (' + (ambient?.version || 'unavailable') + ').',
|
|
52
|
+
suggestion: 'Open a new terminal and check the agent version.', details };
|
|
163
53
|
}
|
|
164
|
-
|
|
165
|
-
const npmGlobalPath = npmGlobalBinDirs.length > 0
|
|
166
|
-
? (0, command_resolution_1.getSystemCommandPath)(cli.command, npmGlobalBinDirs.join(path_1.default.delimiter))
|
|
167
|
-
: null;
|
|
168
|
-
const npmGlobalVersion = npmGlobalPath ? probeVersion(npmGlobalPath) : null;
|
|
169
|
-
// Drift means either: the ambient shell resolves a different version than
|
|
170
|
-
// the actual npm-global install, or this process's own ambient PATH
|
|
171
|
-
// resolves a *different file* than FRAIM's freshly-recomputed managed-dir
|
|
172
|
-
// resolution would — the exact "stale shim still shadows the current one"
|
|
173
|
-
// signature from #1285.
|
|
174
|
-
const versionMismatch = Boolean((ambientVersion && npmGlobalVersion && ambientVersion !== npmGlobalVersion)
|
|
175
|
-
|| (ambientPath && managedPath && ambientPath !== managedPath));
|
|
176
|
-
if (!versionMismatch) {
|
|
177
|
-
return {
|
|
178
|
-
status: 'passed',
|
|
179
|
-
message: `${cli.label} is consistent (${ambientVersion || managedVersion || 'unknown version'}).`,
|
|
180
|
-
details: { ambientPath, managedPath, npmGlobalPath },
|
|
181
|
-
};
|
|
182
|
-
}
|
|
183
|
-
const persistedUserPath = readPersistedUserPath();
|
|
184
|
-
const persistedResolved = persistedUserPath ? (0, command_resolution_1.getSystemCommandPath)(cli.command, persistedUserPath) : null;
|
|
185
|
-
const persistedVersion = persistedResolved ? probeVersion(persistedResolved) : null;
|
|
186
|
-
const persistedMatchesManaged = Boolean(persistedResolved && managedPath && persistedResolved === managedPath);
|
|
187
|
-
const suggestion = persistedUserPath === null
|
|
188
|
-
? `Restart your terminal, then run "${cli.command} --version" again to confirm.`
|
|
189
|
-
: persistedMatchesManaged
|
|
190
|
-
? `Your saved PATH is already correct — open a new terminal window so it takes effect, then run "${cli.command} --version" again.`
|
|
191
|
-
: `Your saved PATH still resolves the old ${cli.label} build. Run "${cli.command} update" (or reinstall ${cli.label}), then open a new terminal.`;
|
|
192
|
-
return {
|
|
193
|
-
status: 'warning',
|
|
194
|
-
message: `${cli.label} PATH drift detected: ambient ${ambientVersion || 'not found'} vs managed ${managedVersion || 'not found'} vs npm-global ${npmGlobalVersion || 'not found'}.`,
|
|
195
|
-
suggestion,
|
|
196
|
-
details: {
|
|
197
|
-
ambientPath, ambientVersion,
|
|
198
|
-
managedPath, managedVersion,
|
|
199
|
-
npmGlobalPath, npmGlobalVersion,
|
|
200
|
-
persistedUserPathResolvedTo: persistedResolved,
|
|
201
|
-
persistedUserPathVersion: persistedVersion,
|
|
202
|
-
},
|
|
203
|
-
};
|
|
54
|
+
return { status: 'passed', message: cli.label + ' is consistent (' + selected.version + ').', details };
|
|
204
55
|
}
|
|
205
56
|
function agentCliHealthCheck(cli) {
|
|
206
57
|
return {
|
|
@@ -217,12 +68,12 @@ function getAgentCliHealthChecks() {
|
|
|
217
68
|
// manager who clicks "Check if Ready" right after `codex update` sees PATH
|
|
218
69
|
// drift immediately instead of a bare pass/fail. Returns null for a CLI this
|
|
219
70
|
// module does not track.
|
|
220
|
-
async function checkAgentCliHealthByCommand(command) {
|
|
71
|
+
async function checkAgentCliHealthByCommand(command, options = {}) {
|
|
221
72
|
const cli = MANAGED_CLIS.find((entry) => entry.command === command);
|
|
222
73
|
if (!cli)
|
|
223
74
|
return null;
|
|
224
75
|
try {
|
|
225
|
-
return await runAgentCliHealthCheck(cli);
|
|
76
|
+
return await runAgentCliHealthCheck(cli, options);
|
|
226
77
|
}
|
|
227
78
|
catch {
|
|
228
79
|
return null;
|
|
@@ -15,6 +15,7 @@ const user_config_1 = require("../utils/user-config");
|
|
|
15
15
|
const run_record_1 = require("../../../registry/scripts/learning/run-record");
|
|
16
16
|
const completion_state_1 = require("../../../registry/scripts/learning/completion-state");
|
|
17
17
|
const artifact_paths_1 = require("../../../registry/scripts/artifact-paths");
|
|
18
|
+
const pack_home_2 = require("../utils/pack-home");
|
|
18
19
|
const local_runtime_1 = require("../../../registry/scripts/learning-usage/local-runtime");
|
|
19
20
|
const learning_source_record_1 = require("../../../registry/scripts/learning-usage/learning-source-record");
|
|
20
21
|
const corpus_audit_1 = require("../../../registry/scripts/learning/corpus-audit");
|
|
@@ -76,7 +77,7 @@ function discoverLearningSources(projectPath, owner, orgRunId) {
|
|
|
76
77
|
reservationHomes.push(record);
|
|
77
78
|
if (record.status !== 'unavailable') {
|
|
78
79
|
try {
|
|
79
|
-
record.artifactPaths = (0,
|
|
80
|
+
record.artifactPaths = (0, pack_home_2.resolvePackArtifactPaths)(record);
|
|
80
81
|
}
|
|
81
82
|
catch (error) {
|
|
82
83
|
record.status = 'unavailable';
|
|
@@ -13,6 +13,7 @@ const node_crypto_1 = require("node:crypto");
|
|
|
13
13
|
const runtime_1 = require("./runtime");
|
|
14
14
|
const completion_state_1 = require("../../../registry/scripts/learning/completion-state");
|
|
15
15
|
const artifact_paths_1 = require("../../../registry/scripts/artifact-paths");
|
|
16
|
+
const pack_home_1 = require("../utils/pack-home");
|
|
16
17
|
const entry_authoring_1 = require("../../../registry/scripts/learning/entry-authoring");
|
|
17
18
|
const owner_identity_1 = require("../../../registry/scripts/learning/owner-identity");
|
|
18
19
|
const run_record_1 = require("../../../registry/scripts/learning/run-record");
|
|
@@ -21,14 +22,14 @@ const run_publication_1 = require("../../../registry/scripts/learning/run-public
|
|
|
21
22
|
const corpus_audit_1 = require("../../../registry/scripts/learning/corpus-audit");
|
|
22
23
|
const outcome_assessment_1 = require("../../../registry/scripts/learning/outcome-assessment");
|
|
23
24
|
const project_fraim_paths_1 = require("../../core/utils/project-fraim-paths");
|
|
24
|
-
const
|
|
25
|
+
const pack_home_2 = require("../utils/pack-home");
|
|
25
26
|
const user_config_1 = require("../utils/user-config");
|
|
26
27
|
const local_registry_resolver_1 = require("../../core/utils/local-registry-resolver");
|
|
27
28
|
function resolveQueueHome(projectPath, level) {
|
|
28
29
|
return (0, owner_identity_1.resolveOwnerHome)(projectPath, level, { runtimeModule: runtime_1.runtimeModuleStatic });
|
|
29
30
|
}
|
|
30
31
|
function resolveReaderCorpusRoots(projectPath) {
|
|
31
|
-
const manager = (0,
|
|
32
|
+
const manager = (0, pack_home_2.resolvePackHome)('manager', { migrateLegacy: false });
|
|
32
33
|
const portable = (0, project_fraim_paths_1.getConfiguredPortableLearningsDir)(projectPath);
|
|
33
34
|
const same = (left, right) => node_path_1.default.resolve(left) === node_path_1.default.resolve(right);
|
|
34
35
|
const roots = manager.synced ? [] : [node_path_1.default.join((0, project_fraim_paths_1.getUserFraimDirPath)(), 'manager', 'learnings')];
|
|
@@ -37,7 +38,7 @@ function resolveReaderCorpusRoots(projectPath) {
|
|
|
37
38
|
return [...new Set(roots)];
|
|
38
39
|
}
|
|
39
40
|
function evidenceDirectory(home) {
|
|
40
|
-
const directory = node_path_1.default.join((0,
|
|
41
|
+
const directory = node_path_1.default.join((0, pack_home_1.resolvePackArtifactPaths)(home).evidence, 'sleep');
|
|
41
42
|
(0, owner_identity_1.configuredArtifactPath)(home, directory);
|
|
42
43
|
return directory;
|
|
43
44
|
}
|
|
@@ -115,8 +116,8 @@ async function runQueueCommand(argv) {
|
|
|
115
116
|
if (input.kind === 'learning' && input.operation !== 'remove') {
|
|
116
117
|
if (!input.target.entryId)
|
|
117
118
|
input.target.entryId = 'L-' + (0, node_crypto_1.randomUUID)().replace(/-/g, '').slice(0, 10);
|
|
118
|
-
const managerHome = (0,
|
|
119
|
-
const orgHome = (0,
|
|
119
|
+
const managerHome = (0, pack_home_2.resolvePackHome)('manager', { migrateLegacy: false });
|
|
120
|
+
const orgHome = (0, pack_home_2.resolvePackHome)('org', { migrateLegacy: false });
|
|
120
121
|
const resolver = new local_registry_resolver_1.LocalRegistryResolver({ workspaceRoot: projectPath, managerLocalRoot: managerHome.contentRoot, managerCacheRoot: managerHome.contentRoot, orgCacheRoot: orgHome.contentRoot, remoteContentResolver: async () => { throw new Error('job-definition-unavailable'); } });
|
|
121
122
|
if (input.content?.domain?.basis !== 'manager') {
|
|
122
123
|
for (const origin of input.content.trace.originatingJobs) {
|
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.resolveManagedCommand = exports.getPortableManagedCommandPath = exports.getSystemCommandPath = void 0;
|
|
6
|
+
exports.resolveManagedCommand = exports.getPortableManagedCommandPath = exports.getSystemCommandPath = exports.getSystemCommandCandidates = void 0;
|
|
7
7
|
const fs_1 = __importDefault(require("fs"));
|
|
8
8
|
const path_1 = __importDefault(require("path"));
|
|
9
9
|
const managed_agent_paths_1 = require("../utils/managed-agent-paths");
|
|
@@ -20,25 +20,30 @@ const getPathEntries = (basePath) => {
|
|
|
20
20
|
.map((entry) => entry.trim())
|
|
21
21
|
.filter(Boolean);
|
|
22
22
|
};
|
|
23
|
-
const getSystemCommandCandidates = (command, basePath) => {
|
|
23
|
+
const getSystemCommandCandidates = (command, basePath, pathExt = process.env.PATHEXT) => {
|
|
24
24
|
if (!command || path_1.default.isAbsolute(command)) {
|
|
25
25
|
return command ? [command] : [];
|
|
26
26
|
}
|
|
27
27
|
const commandNames = process.platform === 'win32'
|
|
28
28
|
? command.includes('.')
|
|
29
29
|
? [command]
|
|
30
|
-
:
|
|
30
|
+
: (pathExt || '.COM;.EXE;.BAT;.CMD').split(';')
|
|
31
|
+
.filter(ext => /^\.(com|exe|bat|cmd)$/i.test(ext)).map(ext => command + ext.toLowerCase())
|
|
31
32
|
: [command];
|
|
32
33
|
return getPathEntries(basePath).flatMap((entry) => commandNames.map((name) => path_1.default.join(entry, name)));
|
|
33
34
|
};
|
|
35
|
+
exports.getSystemCommandCandidates = getSystemCommandCandidates;
|
|
34
36
|
// `basePath` defaults to the current process PATH; pass an explicit PATH
|
|
35
37
|
// string to resolve a command against a different candidate list (e.g. the
|
|
36
38
|
// npm-global bin dirs computed by `resolveNpmGlobalBinDirs`) without mutating
|
|
37
39
|
// `process.env.PATH`.
|
|
38
40
|
const getSystemCommandPath = (command, basePath) => {
|
|
39
|
-
for (const candidate of getSystemCommandCandidates(command, basePath)) {
|
|
41
|
+
for (const candidate of (0, exports.getSystemCommandCandidates)(command, basePath)) {
|
|
40
42
|
try {
|
|
41
43
|
const stats = fs_1.default.statSync(candidate);
|
|
44
|
+
fs_1.default.accessSync(candidate, process.platform === 'win32' ? fs_1.default.constants.R_OK : fs_1.default.constants.X_OK);
|
|
45
|
+
if (process.platform === 'win32')
|
|
46
|
+
fs_1.default.closeSync(fs_1.default.openSync(candidate, 'r'));
|
|
42
47
|
if (stats.isFile()) {
|
|
43
48
|
return candidate;
|
|
44
49
|
}
|