fraim 2.0.308 → 2.0.310
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.
|
@@ -5,7 +5,94 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.installManagedAgent = installManagedAgent;
|
|
7
7
|
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
8
9
|
const managed_agent_paths_1 = require("./managed-agent-paths");
|
|
10
|
+
function errorMessage(error) {
|
|
11
|
+
return error instanceof Error ? error.message : 'Unknown error';
|
|
12
|
+
}
|
|
13
|
+
function isStaleNpmInstallError(error) {
|
|
14
|
+
const code = typeof error === 'object' && error !== null && 'code' in error
|
|
15
|
+
? String(error.code || '')
|
|
16
|
+
: '';
|
|
17
|
+
const message = errorMessage(error).toLowerCase();
|
|
18
|
+
return code === 'EEXIST'
|
|
19
|
+
|| message.includes('eexist')
|
|
20
|
+
|| message.includes('already exists')
|
|
21
|
+
|| message.includes('already in use');
|
|
22
|
+
}
|
|
23
|
+
function packagePathSegments(installPackage) {
|
|
24
|
+
let packageName = installPackage.trim();
|
|
25
|
+
if (!packageName)
|
|
26
|
+
return [];
|
|
27
|
+
if (packageName.startsWith('@')) {
|
|
28
|
+
const slashIndex = packageName.indexOf('/');
|
|
29
|
+
if (slashIndex === -1)
|
|
30
|
+
return [];
|
|
31
|
+
const versionAt = packageName.indexOf('@', slashIndex);
|
|
32
|
+
if (versionAt !== -1)
|
|
33
|
+
packageName = packageName.slice(0, versionAt);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
const versionAt = packageName.lastIndexOf('@');
|
|
37
|
+
if (versionAt > 0)
|
|
38
|
+
packageName = packageName.slice(0, versionAt);
|
|
39
|
+
}
|
|
40
|
+
const segments = packageName.split('/');
|
|
41
|
+
return segments.every((segment) => segment && segment !== '.' && segment !== '..' && !segment.includes('\\'))
|
|
42
|
+
? segments
|
|
43
|
+
: [];
|
|
44
|
+
}
|
|
45
|
+
function isInsideDirectory(parentDir, candidate) {
|
|
46
|
+
const relative = path_1.default.relative(path_1.default.resolve(parentDir), path_1.default.resolve(candidate));
|
|
47
|
+
return !!relative && !relative.startsWith('..') && !path_1.default.isAbsolute(relative);
|
|
48
|
+
}
|
|
49
|
+
function managedShimNames(command) {
|
|
50
|
+
return process.platform === 'win32'
|
|
51
|
+
? [command, `${command}.cmd`, `${command}.ps1`]
|
|
52
|
+
: [command];
|
|
53
|
+
}
|
|
54
|
+
function cleanupManagedInstallArtifacts(option, prefix) {
|
|
55
|
+
const resolvedPrefix = path_1.default.resolve(prefix);
|
|
56
|
+
const packageSegments = packagePathSegments(option.installPackage);
|
|
57
|
+
const candidates = new Set();
|
|
58
|
+
if (packageSegments.length > 0) {
|
|
59
|
+
candidates.add(path_1.default.join(resolvedPrefix, 'node_modules', ...packageSegments));
|
|
60
|
+
candidates.add(path_1.default.join(resolvedPrefix, 'lib', 'node_modules', ...packageSegments));
|
|
61
|
+
}
|
|
62
|
+
for (const shimName of managedShimNames(option.launchCommand)) {
|
|
63
|
+
candidates.add(path_1.default.join(resolvedPrefix, shimName));
|
|
64
|
+
candidates.add(path_1.default.join(resolvedPrefix, 'bin', shimName));
|
|
65
|
+
}
|
|
66
|
+
const removed = [];
|
|
67
|
+
for (const candidate of candidates) {
|
|
68
|
+
if (!isInsideDirectory(resolvedPrefix, candidate) || !fs_1.default.existsSync(candidate))
|
|
69
|
+
continue;
|
|
70
|
+
fs_1.default.rmSync(candidate, { recursive: true, force: true });
|
|
71
|
+
removed.push(candidate);
|
|
72
|
+
}
|
|
73
|
+
return removed;
|
|
74
|
+
}
|
|
75
|
+
async function runInstallWithStaleCleanupRetry(option, prefix, runInstall) {
|
|
76
|
+
try {
|
|
77
|
+
await runInstall();
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
if (!isStaleNpmInstallError(error))
|
|
82
|
+
throw error;
|
|
83
|
+
const removed = cleanupManagedInstallArtifacts(option, prefix);
|
|
84
|
+
try {
|
|
85
|
+
await runInstall();
|
|
86
|
+
}
|
|
87
|
+
catch (retryError) {
|
|
88
|
+
const cleanupSummary = removed.length > 0
|
|
89
|
+
? ` Removed stale managed artifacts: ${removed.join(', ')}.`
|
|
90
|
+
: ' No matching stale managed artifacts were found to remove.';
|
|
91
|
+
throw new Error(`${option.label} install hit a stale npm artifact (${errorMessage(error)}).`
|
|
92
|
+
+ `${cleanupSummary} Retry failed: ${errorMessage(retryError)}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
9
96
|
// Issue #1284/#1285 (Implementation Strategy §2): the exact "standard npm
|
|
10
97
|
// global install, then fall back to FRAIM's managed prefix" sequence used to
|
|
11
98
|
// be duplicated verbatim in `server.ts` (Hub's install-agent route) and
|
|
@@ -17,6 +104,7 @@ const managed_agent_paths_1 = require("./managed-agent-paths");
|
|
|
17
104
|
// check before invoking this — it only covers the install-then-fallback path.
|
|
18
105
|
async function installManagedAgent(option, systemPath, deps) {
|
|
19
106
|
let standardInstallError = null;
|
|
107
|
+
const prefix = (0, managed_agent_paths_1.getPortableNodeBinPath)();
|
|
20
108
|
try {
|
|
21
109
|
await deps.runProcess('npm', ['install', '-g', option.installPackage], {
|
|
22
110
|
PATH: systemPath,
|
|
@@ -40,13 +128,14 @@ async function installManagedAgent(option, systemPath, deps) {
|
|
|
40
128
|
standardInstallError = `${option.label} standard install completed, but the CLI is not runnable from the user PATH.`;
|
|
41
129
|
}
|
|
42
130
|
catch (error) {
|
|
43
|
-
|
|
131
|
+
if (isStaleNpmInstallError(error))
|
|
132
|
+
cleanupManagedInstallArtifacts(option, prefix);
|
|
133
|
+
standardInstallError = errorMessage(error);
|
|
44
134
|
}
|
|
45
135
|
// Defect A fix: co-locate npm-global shims with the node.exe/npm.cmd that
|
|
46
136
|
// actually runs the install, instead of the flat legacy directory.
|
|
47
|
-
const prefix = (0, managed_agent_paths_1.getPortableNodeBinPath)();
|
|
48
137
|
fs_1.default.mkdirSync(prefix, { recursive: true });
|
|
49
|
-
await deps.runProcess('npm', ['install', '-g', option.installPackage], { npm_config_prefix: prefix });
|
|
138
|
+
await runInstallWithStaleCleanupRetry(option, prefix, () => deps.runProcess('npm', ['install', '-g', option.installPackage], { npm_config_prefix: prefix }).then(() => undefined));
|
|
50
139
|
const ver = deps.commandVersion(option.launchCommand, (0, managed_agent_paths_1.getManagedAgentBinDirs)());
|
|
51
140
|
if (!ver) {
|
|
52
141
|
throw new Error(`${option.label} install completed, but the CLI is not runnable from FRAIM's managed PATH. Standard install failure: ${standardInstallError}`);
|
|
@@ -647,6 +647,7 @@ class FirstRunSessionService {
|
|
|
647
647
|
async runNodeRow() {
|
|
648
648
|
const row = this.getRow('node');
|
|
649
649
|
if (this.embeddedDesktop) {
|
|
650
|
+
persistShellPath(this.embeddedDesktop);
|
|
650
651
|
row.status = 'ok';
|
|
651
652
|
row.verb = `${process.version} bundled with FRAIM`;
|
|
652
653
|
this.persist();
|
|
@@ -656,6 +657,7 @@ class FirstRunSessionService {
|
|
|
656
657
|
// If node is missing here something is severely wrong; fall through to error.
|
|
657
658
|
const ver = commandVersion('node');
|
|
658
659
|
if (ver) {
|
|
660
|
+
persistShellPath(this.embeddedDesktop);
|
|
659
661
|
row.status = 'ok';
|
|
660
662
|
row.verb = `${ver} installed`;
|
|
661
663
|
this.persist();
|