@genrupt/cli 0.1.3 → 0.1.4
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/README.md +24 -1
- package/dist/agent.js +43 -45
- package/dist/constants.js +1 -1
- package/dist/doctor.js +59 -1
- package/dist/globalCli.js +189 -0
- package/dist/setup.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,14 +6,37 @@ Genrupt CLI for local-file workflows and local MCP clients.
|
|
|
6
6
|
|
|
7
7
|
Use this when setting up Genrupt for an agent:
|
|
8
8
|
|
|
9
|
+
Windows user-local launcher:
|
|
10
|
+
|
|
11
|
+
```powershell
|
|
12
|
+
powershell.exe -NoProfile -ExecutionPolicy Bypass -Command "irm https://genrupt.com/downloads/genrupt-cli/install.ps1 | iex"
|
|
13
|
+
genrupt agent install
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Universal one-time run:
|
|
17
|
+
|
|
9
18
|
```bash
|
|
10
19
|
npx -y @genrupt/cli@latest agent install
|
|
11
20
|
```
|
|
12
21
|
|
|
13
|
-
This checks auth, installs Genrupt skills for the detected agent, and writes a local runtime manifest.
|
|
22
|
+
This checks auth, installs Genrupt skills for the detected agent, and writes a local runtime manifest. The user-local launcher avoids `npm install -g`; the `npx` runtime install still tries to install or update the global CLI, but global npm install failures do not block the skills/runtime setup.
|
|
14
23
|
|
|
15
24
|
## Install
|
|
16
25
|
|
|
26
|
+
User-local launcher without npm global writes:
|
|
27
|
+
|
|
28
|
+
```powershell
|
|
29
|
+
powershell.exe -NoProfile -ExecutionPolicy Bypass -Command "irm https://genrupt.com/downloads/genrupt-cli/install.ps1 | iex"
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
macOS/Linux:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
curl -fsSL https://genrupt.com/downloads/genrupt-cli/install.sh | sh
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Optional global npm install:
|
|
39
|
+
|
|
17
40
|
```bash
|
|
18
41
|
npm install -g @genrupt/cli
|
|
19
42
|
```
|
package/dist/agent.js
CHANGED
|
@@ -1,48 +1,36 @@
|
|
|
1
|
-
import { execFile } from "node:child_process";
|
|
2
|
-
import { readFile } from "node:fs/promises";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import { promisify } from "node:util";
|
|
5
1
|
import { getValidConfig, login } from "./auth.js";
|
|
6
2
|
import { DEFAULT_ORIGIN } from "./constants.js";
|
|
3
|
+
import { buildGlobalCliDiagnosticsLines, buildNoGlobalInstallRemediation, clearGlobalCliInstallFailure, collectGlobalCliDiagnostics, getGlobalCliPackageVersion, runCommandDetailed, summarizeCommandFailure, writeGlobalCliInstallFailure, } from "./globalCli.js";
|
|
7
4
|
import { downloadRuntimeManifest, installSkills } from "./skills.js";
|
|
8
5
|
import { classifyCliError, emitRuntimeTelemetry } from "./telemetry.js";
|
|
9
6
|
import { CLI_PACKAGE_NAME, isVersionAtLeast } from "./version.js";
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
return process.platform === "win32" ? `${command}.cmd` : command;
|
|
7
|
+
async function installGlobalCli() {
|
|
8
|
+
return runCommandDetailed("npm", ["install", "-g", `${CLI_PACKAGE_NAME}@latest`], 180_000);
|
|
13
9
|
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
return result.stdout.trim();
|
|
10
|
+
function printGlobalCliInstallDiagnostics(params) {
|
|
11
|
+
console.log(`Could not install ${CLI_PACKAGE_NAME}@latest globally.`);
|
|
12
|
+
console.log(summarizeCommandFailure(params.failure));
|
|
13
|
+
if (params.failure.stdout) {
|
|
14
|
+
console.log();
|
|
15
|
+
console.log("npm stdout:");
|
|
16
|
+
console.log(params.failure.stdout);
|
|
22
17
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
throw new Error(`${command} ${args.join(" ")} failed${detail ? `: ${detail}` : ""}`);
|
|
18
|
+
if (params.failure.stderr) {
|
|
19
|
+
console.log();
|
|
20
|
+
console.log("npm stderr:");
|
|
21
|
+
console.log(params.failure.stderr);
|
|
28
22
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
33
|
-
export async function getGlobalCliPackageVersion() {
|
|
34
|
-
try {
|
|
35
|
-
const globalRoot = await getGlobalNodeModulesPath();
|
|
36
|
-
const packageJsonPath = path.join(globalRoot, "@genrupt", "cli", "package.json");
|
|
37
|
-
const packageJson = JSON.parse(await readFile(packageJsonPath, "utf8"));
|
|
38
|
-
return typeof packageJson.version === "string" ? packageJson.version : undefined;
|
|
23
|
+
console.log();
|
|
24
|
+
console.log("Global npm diagnostics:");
|
|
25
|
+
for (const line of buildGlobalCliDiagnosticsLines(params.diagnostics)) {
|
|
26
|
+
console.log(` ${line}`);
|
|
39
27
|
}
|
|
40
|
-
|
|
41
|
-
|
|
28
|
+
console.log();
|
|
29
|
+
for (const line of buildNoGlobalInstallRemediation()) {
|
|
30
|
+
console.log(line);
|
|
42
31
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
await runCommand("npm", ["install", "-g", `${CLI_PACKAGE_NAME}@latest`], 180_000);
|
|
32
|
+
console.log();
|
|
33
|
+
console.log("Continuing with the current npx CLI run.");
|
|
46
34
|
}
|
|
47
35
|
async function ensureGlobalCli(minimumVersion) {
|
|
48
36
|
const currentVersion = await getGlobalCliPackageVersion();
|
|
@@ -55,21 +43,28 @@ async function ensureGlobalCli(minimumVersion) {
|
|
|
55
43
|
: "Genrupt CLI is not installed globally.";
|
|
56
44
|
console.log(reason);
|
|
57
45
|
console.log(`Installing ${CLI_PACKAGE_NAME}@latest globally...`);
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
console.log(`Could not install ${CLI_PACKAGE_NAME}@latest globally.\n${message}\n` +
|
|
64
|
-
"Continuing with the current npx CLI run.");
|
|
46
|
+
const installResult = await installGlobalCli();
|
|
47
|
+
if (!installResult.ok) {
|
|
48
|
+
const diagnostics = await collectGlobalCliDiagnostics();
|
|
49
|
+
await writeGlobalCliInstallFailure(installResult, diagnostics).catch(() => { });
|
|
50
|
+
printGlobalCliInstallDiagnostics({ failure: installResult, diagnostics });
|
|
65
51
|
return false;
|
|
66
52
|
}
|
|
67
53
|
const nextVersion = await getGlobalCliPackageVersion();
|
|
68
54
|
if (!nextVersion || !isVersionAtLeast(nextVersion, minimumVersion)) {
|
|
69
|
-
|
|
70
|
-
|
|
55
|
+
const diagnostics = await collectGlobalCliDiagnostics();
|
|
56
|
+
await writeGlobalCliInstallFailure(installResult, diagnostics).catch(() => { });
|
|
57
|
+
console.log(`Global ${CLI_PACKAGE_NAME} did not report a usable version after install.`);
|
|
58
|
+
console.log();
|
|
59
|
+
console.log("Global npm diagnostics:");
|
|
60
|
+
for (const line of buildGlobalCliDiagnosticsLines(diagnostics)) {
|
|
61
|
+
console.log(` ${line}`);
|
|
62
|
+
}
|
|
63
|
+
console.log();
|
|
64
|
+
console.log("Continuing with the current npx CLI run.");
|
|
71
65
|
return false;
|
|
72
66
|
}
|
|
67
|
+
await clearGlobalCliInstallFailure().catch(() => { });
|
|
73
68
|
console.log(`Genrupt CLI is installed globally (${nextVersion}).`);
|
|
74
69
|
return true;
|
|
75
70
|
}
|
|
@@ -158,6 +153,9 @@ export async function installAgentRuntime(options = {}) {
|
|
|
158
153
|
console.log(` npx -y ${CLI_PACKAGE_NAME}@latest doctor`);
|
|
159
154
|
console.log(` npx -y ${CLI_PACKAGE_NAME}@latest setup claude-code`);
|
|
160
155
|
console.log();
|
|
161
|
-
console.log(
|
|
156
|
+
console.log("To install a user-local launcher without npm global writes:");
|
|
157
|
+
console.log(` powershell.exe -NoProfile -ExecutionPolicy Bypass -Command "irm https://genrupt.com/downloads/genrupt-cli/install.ps1 | iex"`);
|
|
158
|
+
console.log();
|
|
159
|
+
console.log(`To repair the global npm command later: npm install -g ${CLI_PACKAGE_NAME}@latest`);
|
|
162
160
|
}
|
|
163
161
|
}
|
package/dist/constants.js
CHANGED
|
@@ -3,5 +3,5 @@ export const DEFAULT_MCP_SERVER_URL = `${DEFAULT_ORIGIN}/api/agent/mcp`;
|
|
|
3
3
|
export const OAUTH_DEVICE_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:device_code";
|
|
4
4
|
export const OAUTH_SCOPE = "mcp";
|
|
5
5
|
export const CLI_CLIENT_NAME = "Genrupt CLI";
|
|
6
|
-
export const CLI_VERSION = "0.1.
|
|
6
|
+
export const CLI_VERSION = "0.1.4";
|
|
7
7
|
export const ACCESS_TOKEN_REFRESH_SKEW_MS = 60_000;
|
package/dist/doctor.js
CHANGED
|
@@ -2,6 +2,7 @@ import { existsSync } from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { getValidConfig } from "./auth.js";
|
|
4
4
|
import { CLI_VERSION } from "./constants.js";
|
|
5
|
+
import { buildNoGlobalInstallRemediation, collectGlobalCliDiagnostics, readGlobalCliInstallFailure, } from "./globalCli.js";
|
|
5
6
|
import { listRemoteMcpTools } from "./mcpClient.js";
|
|
6
7
|
import { GENRUPT_SKILL_NAMES, computeInstalledSkillDigests, defaultTargetForAgent, detectAgent, getInstalledRuntimeManifestPath, readInstalledRuntimeManifest, } from "./skills.js";
|
|
7
8
|
import { emitRuntimeTelemetry } from "./telemetry.js";
|
|
@@ -40,6 +41,56 @@ function buildSkillsCheck(params) {
|
|
|
40
41
|
detail: `Installed for ${params.agent} at ${params.targetRoot}`,
|
|
41
42
|
};
|
|
42
43
|
}
|
|
44
|
+
function buildPackageManagerCheck(diagnostics) {
|
|
45
|
+
if (diagnostics.npmVersion && diagnostics.npxVersion) {
|
|
46
|
+
return {
|
|
47
|
+
label: "Node/npm",
|
|
48
|
+
status: "ok",
|
|
49
|
+
detail: `Node ${diagnostics.nodeVersion}, npm ${diagnostics.npmVersion}, npx ${diagnostics.npxVersion}`,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
label: "Node/npm",
|
|
54
|
+
status: "warn",
|
|
55
|
+
detail: `Node ${diagnostics.nodeVersion}; npm ${diagnostics.npmVersion ?? "unavailable"}; npx ${diagnostics.npxVersion ?? "unavailable"}`,
|
|
56
|
+
remediation: "Install Node.js 20 or newer with npm/npx enabled.",
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function buildGlobalCliCheck(diagnostics) {
|
|
60
|
+
if (diagnostics.globalCliVersion) {
|
|
61
|
+
if (isVersionNewer(CLI_VERSION, diagnostics.globalCliVersion)) {
|
|
62
|
+
return {
|
|
63
|
+
label: "Global CLI",
|
|
64
|
+
status: "warn",
|
|
65
|
+
detail: `Global ${CLI_PACKAGE_NAME} ${diagnostics.globalCliVersion} is older than this CLI (${CLI_VERSION}); npm prefix ${diagnostics.npmPrefix ?? "unknown"}`,
|
|
66
|
+
remediation: buildNoGlobalInstallRemediation().join(" "),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
label: "Global CLI",
|
|
71
|
+
status: "ok",
|
|
72
|
+
detail: `Global ${CLI_PACKAGE_NAME} ${diagnostics.globalCliVersion}; npm prefix ${diagnostics.npmPrefix ?? "unknown"}`,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
label: "Global CLI",
|
|
77
|
+
status: "warn",
|
|
78
|
+
detail: `Global ${CLI_PACKAGE_NAME} is not installed; npm prefix ${diagnostics.npmPrefix ?? "unknown"}; npm global root ${diagnostics.npmGlobalRoot ?? "unknown"}`,
|
|
79
|
+
remediation: buildNoGlobalInstallRemediation().join(" "),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
function buildLastGlobalInstallFailureCheck(failure) {
|
|
83
|
+
if (!failure)
|
|
84
|
+
return null;
|
|
85
|
+
const status = failure.status === null ? "unknown" : String(failure.status);
|
|
86
|
+
const output = failure.stderr || failure.stdout || failure.errorMessage || "no output captured";
|
|
87
|
+
return {
|
|
88
|
+
label: "Last global install failure",
|
|
89
|
+
status: "warn",
|
|
90
|
+
detail: `${failure.command} ${failure.args.join(" ")} failed at ${failure.recordedAt} with exit ${status}: ${output}`,
|
|
91
|
+
remediation: buildNoGlobalInstallRemediation().join(" "),
|
|
92
|
+
};
|
|
93
|
+
}
|
|
43
94
|
async function buildRuntimeManifestCheck(targetRoot) {
|
|
44
95
|
const manifest = await readInstalledRuntimeManifest(targetRoot);
|
|
45
96
|
if (!manifest) {
|
|
@@ -106,6 +157,13 @@ export async function runDoctor(options = {}) {
|
|
|
106
157
|
status: "ok",
|
|
107
158
|
detail: `${CLI_PACKAGE_NAME} ${CLI_VERSION}`,
|
|
108
159
|
});
|
|
160
|
+
const globalCliDiagnostics = await collectGlobalCliDiagnostics();
|
|
161
|
+
checks.push(buildPackageManagerCheck(globalCliDiagnostics));
|
|
162
|
+
checks.push(buildGlobalCliCheck(globalCliDiagnostics));
|
|
163
|
+
const lastInstallFailureCheck = buildLastGlobalInstallFailureCheck(await readGlobalCliInstallFailure());
|
|
164
|
+
if (lastInstallFailureCheck) {
|
|
165
|
+
checks.push(lastInstallFailureCheck);
|
|
166
|
+
}
|
|
109
167
|
try {
|
|
110
168
|
const latestVersion = await fetchLatestCliVersion();
|
|
111
169
|
latestCliVersion = latestVersion;
|
|
@@ -115,7 +173,7 @@ export async function runDoctor(options = {}) {
|
|
|
115
173
|
label: "CLI update",
|
|
116
174
|
status: "warn",
|
|
117
175
|
detail: `Latest published CLI is ${latestVersion}; current is ${CLI_VERSION}`,
|
|
118
|
-
remediation: `Run:
|
|
176
|
+
remediation: `Run: npx -y ${CLI_PACKAGE_NAME}@latest agent install`,
|
|
119
177
|
});
|
|
120
178
|
}
|
|
121
179
|
else {
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { promisify } from "node:util";
|
|
6
|
+
import { getConfigDir } from "./config.js";
|
|
7
|
+
import { CLI_PACKAGE_NAME } from "./version.js";
|
|
8
|
+
const execFileAsync = promisify(execFile);
|
|
9
|
+
const MAX_CAPTURED_OUTPUT_LENGTH = 4000;
|
|
10
|
+
function resolveCommand(command, args) {
|
|
11
|
+
if (process.platform !== "win32" || (command !== "npm" && command !== "npx")) {
|
|
12
|
+
return { command, args };
|
|
13
|
+
}
|
|
14
|
+
const cliFileName = command === "npm" ? "npm-cli.js" : "npx-cli.js";
|
|
15
|
+
const candidates = [
|
|
16
|
+
path.join(path.dirname(process.execPath), "node_modules", "npm", "bin", cliFileName),
|
|
17
|
+
process.env.ProgramFiles
|
|
18
|
+
? path.join(process.env.ProgramFiles, "nodejs", "node_modules", "npm", "bin", cliFileName)
|
|
19
|
+
: null,
|
|
20
|
+
process.env["ProgramFiles(x86)"]
|
|
21
|
+
? path.join(process.env["ProgramFiles(x86)"], "nodejs", "node_modules", "npm", "bin", cliFileName)
|
|
22
|
+
: null,
|
|
23
|
+
].filter((candidate) => Boolean(candidate));
|
|
24
|
+
const cliPath = candidates.find((candidate) => existsSync(candidate));
|
|
25
|
+
if (cliPath) {
|
|
26
|
+
return {
|
|
27
|
+
command: process.execPath,
|
|
28
|
+
args: [cliPath, ...args],
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
command: `${command}.cmd`,
|
|
33
|
+
args,
|
|
34
|
+
shell: true,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function trimCapturedOutput(value) {
|
|
38
|
+
if (!value)
|
|
39
|
+
return undefined;
|
|
40
|
+
const trimmed = value.trim();
|
|
41
|
+
if (!trimmed)
|
|
42
|
+
return undefined;
|
|
43
|
+
return trimmed.slice(0, MAX_CAPTURED_OUTPUT_LENGTH);
|
|
44
|
+
}
|
|
45
|
+
function normalizeStatus(error) {
|
|
46
|
+
if (error && typeof error === "object") {
|
|
47
|
+
if ("code" in error && typeof error.code === "number")
|
|
48
|
+
return error.code;
|
|
49
|
+
if ("status" in error && typeof error.status === "number")
|
|
50
|
+
return error.status;
|
|
51
|
+
}
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
function readErrorString(error, key) {
|
|
55
|
+
if (!error || typeof error !== "object")
|
|
56
|
+
return "";
|
|
57
|
+
const value = error[key];
|
|
58
|
+
return typeof value === "string" ? value : "";
|
|
59
|
+
}
|
|
60
|
+
export async function runCommandDetailed(command, args, timeoutMs = 30_000) {
|
|
61
|
+
const resolved = resolveCommand(command, args);
|
|
62
|
+
try {
|
|
63
|
+
const result = await execFileAsync(resolved.command, resolved.args, {
|
|
64
|
+
encoding: "utf8",
|
|
65
|
+
timeout: timeoutMs,
|
|
66
|
+
windowsHide: true,
|
|
67
|
+
...(resolved.shell ? { shell: resolved.shell } : {}),
|
|
68
|
+
});
|
|
69
|
+
return {
|
|
70
|
+
command,
|
|
71
|
+
args,
|
|
72
|
+
ok: true,
|
|
73
|
+
status: 0,
|
|
74
|
+
stdout: result.stdout.trim(),
|
|
75
|
+
stderr: result.stderr.trim(),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
return {
|
|
80
|
+
command,
|
|
81
|
+
args,
|
|
82
|
+
ok: false,
|
|
83
|
+
status: normalizeStatus(error),
|
|
84
|
+
stdout: readErrorString(error, "stdout").trim(),
|
|
85
|
+
stderr: readErrorString(error, "stderr").trim(),
|
|
86
|
+
errorMessage: error instanceof Error ? error.message : String(error),
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
async function getCommandOutput(command, args, timeoutMs = 30_000) {
|
|
91
|
+
const result = await runCommandDetailed(command, args, timeoutMs);
|
|
92
|
+
if (result.ok && result.stdout) {
|
|
93
|
+
return { value: result.stdout };
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
value: undefined,
|
|
97
|
+
error: summarizeCommandFailure(result),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
export function summarizeCommandFailure(result) {
|
|
101
|
+
const status = result.status === null ? "unknown" : String(result.status);
|
|
102
|
+
const detail = result.stderr || result.stdout || result.errorMessage || "no output";
|
|
103
|
+
return `${result.command} ${result.args.join(" ")} failed (exit ${status}): ${detail}`;
|
|
104
|
+
}
|
|
105
|
+
export async function getGlobalNodeModulesPath() {
|
|
106
|
+
const result = await getCommandOutput("npm", ["root", "-g"]);
|
|
107
|
+
if (!result.value) {
|
|
108
|
+
throw new Error(result.error ?? "npm root -g failed");
|
|
109
|
+
}
|
|
110
|
+
return result.value;
|
|
111
|
+
}
|
|
112
|
+
export async function getGlobalCliPackageVersion() {
|
|
113
|
+
try {
|
|
114
|
+
const globalRoot = await getGlobalNodeModulesPath();
|
|
115
|
+
const packageJsonPath = path.join(globalRoot, "@genrupt", "cli", "package.json");
|
|
116
|
+
const packageJson = JSON.parse(await readFile(packageJsonPath, "utf8"));
|
|
117
|
+
return typeof packageJson.version === "string" ? packageJson.version : undefined;
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
return undefined;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
export async function collectGlobalCliDiagnostics() {
|
|
124
|
+
const [npmVersion, npxVersion, npmPrefix, npmGlobalRoot] = await Promise.all([
|
|
125
|
+
getCommandOutput("npm", ["--version"]),
|
|
126
|
+
getCommandOutput("npx", ["--version"]),
|
|
127
|
+
getCommandOutput("npm", ["config", "get", "prefix"]),
|
|
128
|
+
getCommandOutput("npm", ["root", "-g"]),
|
|
129
|
+
]);
|
|
130
|
+
return {
|
|
131
|
+
nodeVersion: process.version,
|
|
132
|
+
npmVersion: npmVersion.value,
|
|
133
|
+
npmVersionError: npmVersion.error,
|
|
134
|
+
npxVersion: npxVersion.value,
|
|
135
|
+
npxVersionError: npxVersion.error,
|
|
136
|
+
npmPrefix: npmPrefix.value,
|
|
137
|
+
npmPrefixError: npmPrefix.error,
|
|
138
|
+
npmGlobalRoot: npmGlobalRoot.value,
|
|
139
|
+
npmGlobalRootError: npmGlobalRoot.error,
|
|
140
|
+
globalCliVersion: await getGlobalCliPackageVersion(),
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
function getInstallFailurePath() {
|
|
144
|
+
return path.join(getConfigDir(), "global-cli-install-failure.json");
|
|
145
|
+
}
|
|
146
|
+
export async function writeGlobalCliInstallFailure(result, diagnostics) {
|
|
147
|
+
await mkdir(getConfigDir(), { recursive: true });
|
|
148
|
+
const record = {
|
|
149
|
+
recordedAt: new Date().toISOString(),
|
|
150
|
+
command: result.command,
|
|
151
|
+
args: result.args,
|
|
152
|
+
status: result.status,
|
|
153
|
+
stdout: trimCapturedOutput(result.stdout),
|
|
154
|
+
stderr: trimCapturedOutput(result.stderr),
|
|
155
|
+
errorMessage: trimCapturedOutput(result.errorMessage),
|
|
156
|
+
diagnostics,
|
|
157
|
+
};
|
|
158
|
+
await writeFile(getInstallFailurePath(), `${JSON.stringify(record, null, 2)}\n`, "utf8");
|
|
159
|
+
}
|
|
160
|
+
export async function readGlobalCliInstallFailure() {
|
|
161
|
+
try {
|
|
162
|
+
return JSON.parse(await readFile(getInstallFailurePath(), "utf8"));
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
export async function clearGlobalCliInstallFailure() {
|
|
169
|
+
await rm(getInstallFailurePath(), { force: true });
|
|
170
|
+
}
|
|
171
|
+
export function buildGlobalCliDiagnosticsLines(diagnostics) {
|
|
172
|
+
return [
|
|
173
|
+
`Node: ${diagnostics.nodeVersion}`,
|
|
174
|
+
`npm: ${diagnostics.npmVersion ?? `unavailable (${diagnostics.npmVersionError ?? "unknown"})`}`,
|
|
175
|
+
`npx: ${diagnostics.npxVersion ?? `unavailable (${diagnostics.npxVersionError ?? "unknown"})`}`,
|
|
176
|
+
`npm prefix: ${diagnostics.npmPrefix ?? `unavailable (${diagnostics.npmPrefixError ?? "unknown"})`}`,
|
|
177
|
+
`npm global root: ${diagnostics.npmGlobalRoot ?? `unavailable (${diagnostics.npmGlobalRootError ?? "unknown"})`}`,
|
|
178
|
+
`global ${CLI_PACKAGE_NAME}: ${diagnostics.globalCliVersion ?? "not installed"}`,
|
|
179
|
+
];
|
|
180
|
+
}
|
|
181
|
+
export function buildNoGlobalInstallRemediation() {
|
|
182
|
+
return [
|
|
183
|
+
"Install the user-local launcher without npm global writes:",
|
|
184
|
+
` powershell.exe -NoProfile -ExecutionPolicy Bypass -Command "irm https://genrupt.com/downloads/genrupt-cli/install.ps1 | iex"`,
|
|
185
|
+
"Or keep using npx:",
|
|
186
|
+
` npx -y ${CLI_PACKAGE_NAME}@latest doctor`,
|
|
187
|
+
"If you want the global npm command, check that npm's global prefix is writable or run the install from an elevated terminal.",
|
|
188
|
+
];
|
|
189
|
+
}
|
package/dist/setup.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
2
|
import { assertLoggedIn } from "./auth.js";
|
|
3
3
|
import { CLI_VERSION } from "./constants.js";
|
|
4
|
-
import { getGlobalCliPackageVersion } from "./
|
|
4
|
+
import { getGlobalCliPackageVersion } from "./globalCli.js";
|
|
5
5
|
import { CLI_PACKAGE_NAME, isVersionAtLeast } from "./version.js";
|
|
6
6
|
function runClaude(args) {
|
|
7
7
|
return spawnSync("claude", args, {
|