mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-13 09:06:08 +00:00
refactor(cli): hide shell support internals
This commit is contained in:
@@ -2,11 +2,14 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
isNodeVersionManagerRuntime,
|
||||
LINUX_CA_BUNDLE_PATHS,
|
||||
resolveAutoNodeExtraCaCerts,
|
||||
resolveLinuxSystemCaBundle,
|
||||
} from "./node-extra-ca-certs.js";
|
||||
|
||||
const DEBIAN_CA_BUNDLE_PATH = "/etc/ssl/certs/ca-certificates.crt";
|
||||
const FEDORA_CA_BUNDLE_PATH = "/etc/pki/tls/certs/ca-bundle.crt";
|
||||
const GENERIC_CA_BUNDLE_PATH = "/etc/ssl/ca-bundle.pem";
|
||||
|
||||
function allowOnly(path: string) {
|
||||
return (candidate: string) => {
|
||||
if (candidate !== path) {
|
||||
@@ -20,7 +23,7 @@ describe("resolveLinuxSystemCaBundle", () => {
|
||||
expect(
|
||||
resolveLinuxSystemCaBundle({
|
||||
platform: "darwin",
|
||||
accessSync: allowOnly(LINUX_CA_BUNDLE_PATHS[0]),
|
||||
accessSync: allowOnly(DEBIAN_CA_BUNDLE_PATH),
|
||||
}),
|
||||
).toBeUndefined();
|
||||
});
|
||||
@@ -29,9 +32,9 @@ describe("resolveLinuxSystemCaBundle", () => {
|
||||
expect(
|
||||
resolveLinuxSystemCaBundle({
|
||||
platform: "linux",
|
||||
accessSync: allowOnly(LINUX_CA_BUNDLE_PATHS[1]),
|
||||
accessSync: allowOnly(FEDORA_CA_BUNDLE_PATH),
|
||||
}),
|
||||
).toBe(LINUX_CA_BUNDLE_PATHS[1]);
|
||||
).toBe(FEDORA_CA_BUNDLE_PATH);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -120,7 +123,7 @@ describe("resolveAutoNodeExtraCaCerts", () => {
|
||||
NODE_EXTRA_CA_CERTS: "/custom/ca.pem",
|
||||
},
|
||||
platform: "linux",
|
||||
accessSync: allowOnly(LINUX_CA_BUNDLE_PATHS[0]),
|
||||
accessSync: allowOnly(DEBIAN_CA_BUNDLE_PATH),
|
||||
}),
|
||||
).toBeUndefined();
|
||||
});
|
||||
@@ -131,7 +134,7 @@ describe("resolveAutoNodeExtraCaCerts", () => {
|
||||
env: {},
|
||||
platform: "linux",
|
||||
execPath: "/usr/bin/node",
|
||||
accessSync: allowOnly(LINUX_CA_BUNDLE_PATHS[0]),
|
||||
accessSync: allowOnly(DEBIAN_CA_BUNDLE_PATH),
|
||||
}),
|
||||
).toBeUndefined();
|
||||
});
|
||||
@@ -142,8 +145,8 @@ describe("resolveAutoNodeExtraCaCerts", () => {
|
||||
env: { NVM_DIR: "/home/test/.nvm" },
|
||||
platform: "linux",
|
||||
execPath: "/usr/bin/node",
|
||||
accessSync: allowOnly(LINUX_CA_BUNDLE_PATHS[2]),
|
||||
accessSync: allowOnly(GENERIC_CA_BUNDLE_PATH),
|
||||
}),
|
||||
).toBe(LINUX_CA_BUNDLE_PATHS[2]);
|
||||
).toBe(GENERIC_CA_BUNDLE_PATH);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Resolves additional CA certificate settings for Node child processes.
|
||||
import fs from "node:fs";
|
||||
|
||||
export const LINUX_CA_BUNDLE_PATHS = [
|
||||
const LINUX_CA_BUNDLE_PATHS = [
|
||||
"/etc/ssl/certs/ca-certificates.crt",
|
||||
"/etc/pki/tls/certs/ca-bundle.crt",
|
||||
"/etc/ssl/ca-bundle.pem",
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
// Verifies startup environment merge behavior for Node subprocesses.
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { LINUX_CA_BUNDLE_PATHS } from "./node-extra-ca-certs.js";
|
||||
import { resolveNodeStartupTlsEnvironment } from "./node-startup-env.js";
|
||||
|
||||
const FEDORA_CA_BUNDLE_PATH = "/etc/pki/tls/certs/ca-bundle.crt";
|
||||
const GENERIC_CA_BUNDLE_PATH = "/etc/ssl/ca-bundle.pem";
|
||||
|
||||
function allowOnly(path: string) {
|
||||
return (candidate: string) => {
|
||||
if (candidate !== path) {
|
||||
@@ -45,10 +47,10 @@ describe("resolveNodeStartupTlsEnvironment", () => {
|
||||
env: { NVM_DIR: "/home/test/.nvm" },
|
||||
platform: "linux",
|
||||
execPath: "/usr/bin/node",
|
||||
accessSync: allowOnly(LINUX_CA_BUNDLE_PATHS[1]),
|
||||
accessSync: allowOnly(FEDORA_CA_BUNDLE_PATH),
|
||||
}),
|
||||
).toEqual({
|
||||
NODE_EXTRA_CA_CERTS: LINUX_CA_BUNDLE_PATHS[1],
|
||||
NODE_EXTRA_CA_CERTS: FEDORA_CA_BUNDLE_PATH,
|
||||
NODE_USE_SYSTEM_CA: undefined,
|
||||
});
|
||||
});
|
||||
@@ -71,10 +73,8 @@ describe("resolveNodeStartupTlsEnvironment", () => {
|
||||
env: { NVM_DIR: "/home/test/.nvm" },
|
||||
platform: "linux",
|
||||
execPath: "/usr/bin/node",
|
||||
accessSync: allowOnly(LINUX_CA_BUNDLE_PATHS[2]),
|
||||
accessSync: allowOnly(GENERIC_CA_BUNDLE_PATH),
|
||||
}).NODE_EXTRA_CA_CERTS;
|
||||
if (value !== undefined) {
|
||||
expect(LINUX_CA_BUNDLE_PATHS).toContain(value);
|
||||
}
|
||||
expect(value).toBe(GENERIC_CA_BUNDLE_PATH);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,14 +3,9 @@ import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
buildFishOptionCompletionLine,
|
||||
buildFishSubcommandCompletionLine,
|
||||
escapeFishDescription,
|
||||
} from "./completion-fish.js";
|
||||
|
||||
describe("completion-fish helpers", () => {
|
||||
it("escapes single quotes in descriptions", () => {
|
||||
expect(escapeFishDescription("Bob's plugin")).toBe("Bob'\\''s plugin");
|
||||
});
|
||||
|
||||
it("builds a subcommand completion line", () => {
|
||||
const line = buildFishSubcommandCompletionLine({
|
||||
rootCmd: "openclaw",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Fish completion line builders for subcommands and options.
|
||||
export function escapeFishDescription(value: string): string {
|
||||
function escapeFishDescription(value: string): string {
|
||||
return value.replace(/'/g, "'\\''");
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import path from "node:path";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import {
|
||||
formatCompletionReloadCommand,
|
||||
formatCompletionSourceLine,
|
||||
installCompletion,
|
||||
resolveCompletionCachePath,
|
||||
resolveCompletionProfilePath,
|
||||
@@ -29,10 +28,7 @@ describe("completion-runtime", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("formats PowerShell source and reload commands with single-quoted paths", () => {
|
||||
expect(
|
||||
formatCompletionSourceLine("powershell", "openclaw", "C:\\Users\\Ada\\open'claw.ps1"),
|
||||
).toBe(". 'C:\\Users\\Ada\\open''claw.ps1'");
|
||||
it("formats PowerShell reload commands with single-quoted paths", () => {
|
||||
expect(formatCompletionReloadCommand("powershell", "C:\\Users\\Ada\\profile.ps1")).toBe(
|
||||
". 'C:\\Users\\Ada\\profile.ps1'",
|
||||
);
|
||||
@@ -88,7 +84,7 @@ describe("completion-runtime", () => {
|
||||
|
||||
it("installs PowerShell completion into the concrete profile path", async () => {
|
||||
const homeDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-completion-home-"));
|
||||
const stateDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-completion-state-"));
|
||||
const stateDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-completion-state-bob's-"));
|
||||
|
||||
process.env.HOME = homeDir;
|
||||
process.env.OPENCLAW_STATE_DIR = stateDir;
|
||||
@@ -102,7 +98,7 @@ describe("completion-runtime", () => {
|
||||
|
||||
const profilePath = resolveCompletionProfilePath("powershell");
|
||||
const profile = await fs.readFile(profilePath, "utf-8");
|
||||
expect(profile).toBe(`# OpenClaw Completion\n. '${cachePath}'\n`);
|
||||
expect(profile).toBe(`# OpenClaw Completion\n. '${cachePath.replace(/'/g, "''")}'\n`);
|
||||
} finally {
|
||||
await fs.rm(homeDir, { recursive: true, force: true });
|
||||
await fs.rm(stateDir, { recursive: true, force: true });
|
||||
|
||||
@@ -82,12 +82,7 @@ function escapePowerShellSingleQuotedString(value: string): string {
|
||||
return value.replace(/'/g, "''");
|
||||
}
|
||||
|
||||
/** Formats the profile line that sources the cached completion script for a shell. */
|
||||
export function formatCompletionSourceLine(
|
||||
shell: CompletionShell,
|
||||
_binName: string,
|
||||
cachePath: string,
|
||||
): string {
|
||||
function formatCompletionSourceLine(shell: CompletionShell, cachePath: string): string {
|
||||
if (shell === "powershell") {
|
||||
return `. '${escapePowerShellSingleQuotedString(cachePath)}'`;
|
||||
}
|
||||
@@ -258,7 +253,7 @@ export async function installCompletion(shell: string, yes: boolean, binName = "
|
||||
switch (shell) {
|
||||
case "zsh":
|
||||
profilePath = resolveCompletionProfilePath("zsh");
|
||||
sourceLine = formatCompletionSourceLine("zsh", binName, cachePath);
|
||||
sourceLine = formatCompletionSourceLine("zsh", cachePath);
|
||||
break;
|
||||
case "bash":
|
||||
profilePath = resolveCompletionProfilePath("bash");
|
||||
@@ -268,15 +263,15 @@ export async function installCompletion(shell: string, yes: boolean, binName = "
|
||||
const home = process.env.HOME || os.homedir();
|
||||
profilePath = path.join(home, ".bash_profile");
|
||||
}
|
||||
sourceLine = formatCompletionSourceLine("bash", binName, cachePath);
|
||||
sourceLine = formatCompletionSourceLine("bash", cachePath);
|
||||
break;
|
||||
case "fish":
|
||||
profilePath = resolveCompletionProfilePath("fish");
|
||||
sourceLine = formatCompletionSourceLine("fish", binName, cachePath);
|
||||
sourceLine = formatCompletionSourceLine("fish", cachePath);
|
||||
break;
|
||||
case "powershell":
|
||||
profilePath = resolveCompletionProfilePath("powershell");
|
||||
sourceLine = formatCompletionSourceLine("powershell", binName, cachePath);
|
||||
sourceLine = formatCompletionSourceLine("powershell", cachePath);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user