Files
openclaw/scripts/lib/plain-gh.mjs
Peter Steinberger edecdbd05e refactor(config): config-surface reduction tranche 3 — product consolidations (review request) (#111527)
* refactor(config): consolidate media model lists

* refactor(config): unify memory configuration

* refactor(config): consolidate TTS ownership

* refactor(config): move typing policy to agents

* refactor(config): retire product-level config surfaces

* refactor(config): share scoped tool policy type

* chore(config): refresh generated baselines

* fix(config): honor agent typing overrides

* fix(config): migrate sibling config consumers

* refactor(infra): keep base64url decoder private

* fix(config): strip invalid legacy TTS values

* chore(config): refresh rebased baseline hash

* fix(doctor): route legacy messages.tts.realtime voice to talk during tts move

* refactor(config): polish final layout names

* refactor(config): freeze retired tuning defaults

* feat(config): add fast mode default symmetry

* refactor(config): key agent entries by id

* docs(config): update final layout reference

* test(config): cover final layout migrations

* chore(config): refresh final layout baselines

* fix(config): align final layout runtime readers

* fix(config): align remaining readers

* fix(config): stabilize final layout migrations

* fix(config): finalize config projection proof

* fix(config): address final layout review

* docs(release): preserve historical config names

* fix(config): complete keyed agent migration

* fix(config): close final migration gaps

* fix(config): finish full-branch review

* fix(config): complete runtime secret detection

* fix(config): close final review findings

* fix(config): finish canonical docs and heartbeat migration

* fix(config): integrate latest main after rebase

* refactor(env): isolate test-only controls

* refactor(env): isolate build and development controls

* refactor(env): collapse process identity indirection

* refactor(env): remove duplicate config and temp aliases

* docs(env): define the operator-facing allowlist

* ci(env): ratchet production variable count

* fix(env): remove stale provider helper import

* fix(env): make ratchet sorting explicit

* test(env): keep test seam in dead-code audit

* test(env): cover ratchet growth and boundary; document surface budgets

* docs(config): document tier-eval consolidations

* docs(config): clarify speech preference ownership

* test(memory): align retired tuning fixtures

* refactor(memory): freeze engine heuristics

* refactor(config): apply tier-eval tranche

* refactor(tts): move persona shaping to providers

* refactor(compaction): move prompt policy to providers

* test(config): align hookified prompt fixtures

* chore(deadcode): classify test-only exports

* chore(github): remove unused spawn helper

* chore(deadcode): classify queue diagnostics

* chore(deadcode): remove unused lane snapshot export

* chore(plugin-sdk): ratchet consolidated surface

* fix(config): integrate latest main after rebase
2026-07-21 20:28:43 -07:00

101 lines
2.6 KiB
JavaScript

import { execFileSync } from "node:child_process";
import fs from "node:fs";
import path from "node:path";
const PLAIN_GH_MAX_BUFFER_BYTES = 32 * 1024 * 1024;
export const PLAIN_GH_SYSTEM_CANDIDATES = [
// Prefer package-manager opt paths: bin/gh may intentionally be an Octopool shim.
"/opt/homebrew/opt/gh/bin/gh",
"/usr/local/opt/gh/bin/gh",
"/home/linuxbrew/.linuxbrew/opt/gh/bin/gh",
"/opt/homebrew/bin/gh",
"/usr/local/bin/gh",
];
function isExecutable(filePath) {
try {
fs.accessSync(filePath, fs.constants.X_OK);
return true;
} catch {
return false;
}
}
function pathEntries(env) {
return String(env.PATH ?? "")
.split(path.delimiter)
.filter(Boolean);
}
export function plainGhEnv(env = process.env) {
const next = { ...env };
delete next.CLICOLOR;
delete next.CLICOLOR_FORCE;
delete next.COLORTERM;
delete next.GH_FORCE_TTY;
next.NO_COLOR = "1";
next.FORCE_COLOR = "0";
next.CLICOLOR = "0";
next.CLICOLOR_FORCE = "0";
return next;
}
export function resolvePlainGhBin(
env = process.env,
systemCandidates = PLAIN_GH_SYSTEM_CANDIDATES,
) {
if (env.OPENCLAW_GH_BIN) {
if (isExecutable(env.OPENCLAW_GH_BIN)) {
return env.OPENCLAW_GH_BIN;
}
throw new Error(`OPENCLAW_GH_BIN is not executable: ${env.OPENCLAW_GH_BIN}`);
}
for (const candidate of systemCandidates) {
if (isExecutable(candidate)) {
return candidate;
}
}
const homeBin = env.HOME ? path.join(env.HOME, "bin") : "";
for (const entry of pathEntries(env)) {
if (homeBin && entry === homeBin) {
continue;
}
const candidate = path.join(entry, process.platform === "win32" ? "gh.exe" : "gh");
if (isExecutable(candidate)) {
return candidate;
}
}
for (const entry of pathEntries(env)) {
const candidate = path.join(entry, process.platform === "win32" ? "gh.exe" : "gh");
if (isExecutable(candidate)) {
return candidate;
}
}
throw new Error("missing required command: gh");
}
export function execPlainGh(args, options = {}) {
const env = plainGhEnv(options.env ?? process.env);
const ghBin = resolvePlainGhBin(env);
return execFileSync(ghBin, args, {
...options,
env,
maxBuffer: options.maxBuffer ?? PLAIN_GH_MAX_BUFFER_BYTES,
});
}
export function execGhApiRead(endpoint, options = {}) {
const env = plainGhEnv(options.env ?? process.env);
// Keep reads on the normal PATH shim; OPENCLAW_GH_BIN pins maintainer writes.
delete env.OPENCLAW_GH_BIN;
return execFileSync("gh", ["api", endpoint, "--method", "GET"], {
...options,
env,
maxBuffer: options.maxBuffer ?? PLAIN_GH_MAX_BUFFER_BYTES,
});
}