@pushpalsdev/cli 1.0.15 → 1.0.17
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/pushpals-cli.js +444 -61
- package/package.json +1 -1
package/dist/pushpals-cli.js
CHANGED
|
@@ -11,9 +11,10 @@ import {
|
|
|
11
11
|
mkdirSync,
|
|
12
12
|
readdirSync,
|
|
13
13
|
readFileSync as readFileSync4,
|
|
14
|
+
rmSync,
|
|
14
15
|
writeFileSync
|
|
15
16
|
} from "fs";
|
|
16
|
-
import { basename, delimiter, dirname, extname, join as join2, resolve as resolve4 } from "path";
|
|
17
|
+
import { basename, delimiter, dirname, extname, join as join2, resolve as resolve4, win32 as pathWin32 } from "path";
|
|
17
18
|
import { createInterface } from "readline";
|
|
18
19
|
|
|
19
20
|
// ../shared/src/client_preflight.ts
|
|
@@ -1257,6 +1258,7 @@ function printUsage() {
|
|
|
1257
1258
|
console.log(" --no-auto-start Disable runtime auto-start when the server is down");
|
|
1258
1259
|
console.log(" --no-stream Disable live session event stream");
|
|
1259
1260
|
console.log(" --runtime-only Start the local runtime and wait for shutdown without opening the interactive chat");
|
|
1261
|
+
console.log(" --clear Remove repo-local PushPals state and exit");
|
|
1260
1262
|
console.log(" -h, --help Show this help");
|
|
1261
1263
|
console.log("");
|
|
1262
1264
|
console.log("Chat commands:");
|
|
@@ -1271,7 +1273,12 @@ function printUsage() {
|
|
|
1271
1273
|
console.log(" - Interactive CLI talks directly to server sessions; LocalBuddy is optional.");
|
|
1272
1274
|
}
|
|
1273
1275
|
function parseArgs(argv) {
|
|
1274
|
-
const options = {
|
|
1276
|
+
const options = {
|
|
1277
|
+
noAutoStart: false,
|
|
1278
|
+
noStream: false,
|
|
1279
|
+
runtimeOnly: false,
|
|
1280
|
+
clear: false
|
|
1281
|
+
};
|
|
1275
1282
|
for (let i = 0;i < argv.length; i++) {
|
|
1276
1283
|
const arg = argv[i];
|
|
1277
1284
|
if (arg === "-h" || arg === "--help") {
|
|
@@ -1290,6 +1297,10 @@ function parseArgs(argv) {
|
|
|
1290
1297
|
options.runtimeOnly = true;
|
|
1291
1298
|
continue;
|
|
1292
1299
|
}
|
|
1300
|
+
if (arg === "--clear") {
|
|
1301
|
+
options.clear = true;
|
|
1302
|
+
continue;
|
|
1303
|
+
}
|
|
1293
1304
|
if (arg === "--server-url") {
|
|
1294
1305
|
options.serverUrl = argv[++i];
|
|
1295
1306
|
continue;
|
|
@@ -1358,23 +1369,35 @@ function parsePositiveInt(value, fallback) {
|
|
|
1358
1369
|
function jsonHtmlBootstrap(value) {
|
|
1359
1370
|
return JSON.stringify(value).replace(/</g, "\\u003c");
|
|
1360
1371
|
}
|
|
1372
|
+
async function runGitWithEnv(args, cwd, env) {
|
|
1373
|
+
try {
|
|
1374
|
+
const proc = Bun.spawn(["git", ...args], {
|
|
1375
|
+
cwd,
|
|
1376
|
+
env,
|
|
1377
|
+
stdout: "pipe",
|
|
1378
|
+
stderr: "pipe"
|
|
1379
|
+
});
|
|
1380
|
+
const [stdout, stderr, exitCode] = await Promise.all([
|
|
1381
|
+
new Response(proc.stdout).text(),
|
|
1382
|
+
new Response(proc.stderr).text(),
|
|
1383
|
+
proc.exited
|
|
1384
|
+
]);
|
|
1385
|
+
return { ok: exitCode === 0, stdout: stdout.trim(), stderr: stderr.trim(), exitCode };
|
|
1386
|
+
} catch (err) {
|
|
1387
|
+
return {
|
|
1388
|
+
ok: false,
|
|
1389
|
+
stdout: "",
|
|
1390
|
+
stderr: err instanceof Error ? err.message : String(err),
|
|
1391
|
+
exitCode: -1
|
|
1392
|
+
};
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1361
1395
|
async function runGit(args, cwd) {
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
GIT_TERMINAL_PROMPT: "0",
|
|
1367
|
-
GCM_INTERACTIVE: "Never"
|
|
1368
|
-
},
|
|
1369
|
-
stdout: "pipe",
|
|
1370
|
-
stderr: "pipe"
|
|
1396
|
+
return await runGitWithEnv(args, cwd, {
|
|
1397
|
+
...process.env,
|
|
1398
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
1399
|
+
GCM_INTERACTIVE: "Never"
|
|
1371
1400
|
});
|
|
1372
|
-
const [stdout, stderr, exitCode] = await Promise.all([
|
|
1373
|
-
new Response(proc.stdout).text(),
|
|
1374
|
-
new Response(proc.stderr).text(),
|
|
1375
|
-
proc.exited
|
|
1376
|
-
]);
|
|
1377
|
-
return { ok: exitCode === 0, stdout: stdout.trim(), stderr: stderr.trim(), exitCode };
|
|
1378
1401
|
}
|
|
1379
1402
|
async function resolveCurrentGitRepoRoot(cwd) {
|
|
1380
1403
|
const inside = await runGit(["rev-parse", "--is-inside-work-tree"], cwd);
|
|
@@ -1535,25 +1558,24 @@ async function fetchLatestReleaseTag() {
|
|
|
1535
1558
|
throw new Error("Latest release payload did not include tag_name");
|
|
1536
1559
|
return tagName;
|
|
1537
1560
|
}
|
|
1538
|
-
|
|
1561
|
+
function resolvePreferredRuntimeReleaseTag(explicitTag, env = process.env) {
|
|
1539
1562
|
const fromArg = String(explicitTag ?? "").trim();
|
|
1540
1563
|
if (fromArg)
|
|
1541
1564
|
return fromArg;
|
|
1542
|
-
const fromEnv = String(
|
|
1565
|
+
const fromEnv = String(env.PUSHPALS_RUNTIME_TAG ?? "").trim();
|
|
1543
1566
|
if (fromEnv)
|
|
1544
1567
|
return fromEnv;
|
|
1545
|
-
const packageVersion = parseSemverFromPackageVersion(
|
|
1568
|
+
const packageVersion = parseSemverFromPackageVersion(env.PUSHPALS_CLI_PACKAGE_VERSION);
|
|
1569
|
+
if (packageVersion)
|
|
1570
|
+
return `v${packageVersion}`;
|
|
1571
|
+
return "";
|
|
1572
|
+
}
|
|
1573
|
+
async function resolveRuntimeReleaseTag(explicitTag) {
|
|
1574
|
+
const preferredTag = resolvePreferredRuntimeReleaseTag(explicitTag, process.env);
|
|
1575
|
+
if (preferredTag)
|
|
1576
|
+
return preferredTag;
|
|
1546
1577
|
console.log("[pushpals] Resolving embedded runtime release tag from GitHub...");
|
|
1547
|
-
|
|
1548
|
-
return await fetchLatestReleaseTag();
|
|
1549
|
-
} catch (err) {
|
|
1550
|
-
if (packageVersion) {
|
|
1551
|
-
const fallbackTag = `v${packageVersion}`;
|
|
1552
|
-
console.warn(`[pushpals] Could not resolve latest runtime tag; falling back to package version tag ${fallbackTag}: ${String(err)}`);
|
|
1553
|
-
return fallbackTag;
|
|
1554
|
-
}
|
|
1555
|
-
throw err;
|
|
1556
|
-
}
|
|
1578
|
+
return await fetchLatestReleaseTag();
|
|
1557
1579
|
}
|
|
1558
1580
|
function writeTextFileIfMissing(pathValue, text) {
|
|
1559
1581
|
if (existsSync4(pathValue))
|
|
@@ -1751,10 +1773,7 @@ function normalizeChildProcessEnv(baseEnv, platform = process.platform) {
|
|
|
1751
1773
|
return env;
|
|
1752
1774
|
}
|
|
1753
1775
|
async function resolveCommandPath(command, cwd, env) {
|
|
1754
|
-
const lookupCommands = process.platform === "win32" ? [
|
|
1755
|
-
["where.exe", command],
|
|
1756
|
-
["where", command]
|
|
1757
|
-
] : [["which", command]];
|
|
1776
|
+
const lookupCommands = process.platform === "win32" ? resolveWindowsWhereExecutableCandidatesForEnv(env, process.platform).map((lookup) => [lookup, command]) : [["which", command]];
|
|
1758
1777
|
for (const lookup of lookupCommands) {
|
|
1759
1778
|
try {
|
|
1760
1779
|
const proc = Bun.spawn(lookup, {
|
|
@@ -1848,12 +1867,14 @@ async function ensureRuntimeBinaries(runtimeRoot, runtimeTag) {
|
|
|
1848
1867
|
server: join2(binDir, runtimeBinaryFilename("server", platformKey)),
|
|
1849
1868
|
localbuddy: join2(binDir, runtimeBinaryFilename("localbuddy", platformKey)),
|
|
1850
1869
|
remotebuddy: join2(binDir, runtimeBinaryFilename("remotebuddy", platformKey)),
|
|
1870
|
+
workerpals: join2(binDir, runtimeBinaryFilename("workerpals", platformKey)),
|
|
1851
1871
|
sourceControlManager: join2(binDir, runtimeBinaryFilename("source_control_manager", platformKey))
|
|
1852
1872
|
};
|
|
1853
1873
|
const requiredAssets = [
|
|
1854
1874
|
runtimeBinaries.server,
|
|
1855
1875
|
runtimeBinaries.localbuddy,
|
|
1856
1876
|
runtimeBinaries.remotebuddy,
|
|
1877
|
+
runtimeBinaries.workerpals,
|
|
1857
1878
|
runtimeBinaries.sourceControlManager
|
|
1858
1879
|
];
|
|
1859
1880
|
let downloadedCount = 0;
|
|
@@ -1999,6 +2020,83 @@ function applyResolvedGitBinaryToRuntimeEnv(env, resolvedGitBinary, platform = p
|
|
|
1999
2020
|
}
|
|
2000
2021
|
return env;
|
|
2001
2022
|
}
|
|
2023
|
+
function resolveRuntimeGitExecutableCandidates(env, platform = process.platform) {
|
|
2024
|
+
const candidates = [];
|
|
2025
|
+
const seen = new Set;
|
|
2026
|
+
const pushCandidate = (value) => {
|
|
2027
|
+
const trimmed = String(value ?? "").trim();
|
|
2028
|
+
if (!trimmed)
|
|
2029
|
+
return;
|
|
2030
|
+
const key = platform === "win32" ? trimmed.toLowerCase() : trimmed;
|
|
2031
|
+
if (seen.has(key))
|
|
2032
|
+
return;
|
|
2033
|
+
seen.add(key);
|
|
2034
|
+
candidates.push(trimmed);
|
|
2035
|
+
};
|
|
2036
|
+
pushCandidate(env.PUSHPALS_GIT_BIN ?? "");
|
|
2037
|
+
pushCandidate(env.PUSHPALS_GIT_BIN_ABSOLUTE ?? "");
|
|
2038
|
+
pushCandidate(platform === "win32" ? "git.exe" : "git");
|
|
2039
|
+
pushCandidate("git");
|
|
2040
|
+
return candidates;
|
|
2041
|
+
}
|
|
2042
|
+
function resolveWindowsShellExecutableCandidatesForEnv(env, platform = process.platform) {
|
|
2043
|
+
if (platform !== "win32")
|
|
2044
|
+
return [];
|
|
2045
|
+
const candidates = [];
|
|
2046
|
+
const seen = new Set;
|
|
2047
|
+
const pushCandidate = (value) => {
|
|
2048
|
+
const trimmed = String(value ?? "").trim();
|
|
2049
|
+
if (!trimmed)
|
|
2050
|
+
return;
|
|
2051
|
+
const key = trimmed.toLowerCase();
|
|
2052
|
+
if (seen.has(key))
|
|
2053
|
+
return;
|
|
2054
|
+
seen.add(key);
|
|
2055
|
+
candidates.push(trimmed);
|
|
2056
|
+
};
|
|
2057
|
+
const comSpec = String(env.ComSpec ?? env.COMSPEC ?? process.env.ComSpec ?? process.env.COMSPEC ?? "").trim();
|
|
2058
|
+
const systemRoot = String(env.SystemRoot ?? env.SYSTEMROOT ?? process.env.SystemRoot ?? process.env.SYSTEMROOT ?? "").trim();
|
|
2059
|
+
pushCandidate(comSpec);
|
|
2060
|
+
if (systemRoot) {
|
|
2061
|
+
pushCandidate(pathWin32.join(systemRoot, "System32", "cmd.exe"));
|
|
2062
|
+
pushCandidate(pathWin32.join(systemRoot, "Sysnative", "cmd.exe"));
|
|
2063
|
+
}
|
|
2064
|
+
pushCandidate("cmd.exe");
|
|
2065
|
+
return candidates;
|
|
2066
|
+
}
|
|
2067
|
+
function resolveWindowsWhereExecutableCandidatesForEnv(env, platform = process.platform) {
|
|
2068
|
+
if (platform !== "win32")
|
|
2069
|
+
return [];
|
|
2070
|
+
const candidates = [];
|
|
2071
|
+
const seen = new Set;
|
|
2072
|
+
const pushCandidate = (value) => {
|
|
2073
|
+
const trimmed = String(value ?? "").trim();
|
|
2074
|
+
if (!trimmed)
|
|
2075
|
+
return;
|
|
2076
|
+
const key = trimmed.toLowerCase();
|
|
2077
|
+
if (seen.has(key))
|
|
2078
|
+
return;
|
|
2079
|
+
seen.add(key);
|
|
2080
|
+
candidates.push(trimmed);
|
|
2081
|
+
};
|
|
2082
|
+
const systemRoot = String(env.SystemRoot ?? env.SYSTEMROOT ?? process.env.SystemRoot ?? process.env.SYSTEMROOT ?? "").trim();
|
|
2083
|
+
if (systemRoot) {
|
|
2084
|
+
pushCandidate(pathWin32.join(systemRoot, "System32", "where.exe"));
|
|
2085
|
+
pushCandidate(pathWin32.join(systemRoot, "Sysnative", "where.exe"));
|
|
2086
|
+
}
|
|
2087
|
+
pushCandidate("where.exe");
|
|
2088
|
+
pushCandidate("where");
|
|
2089
|
+
return candidates;
|
|
2090
|
+
}
|
|
2091
|
+
function quoteWindowsCmdArg(value) {
|
|
2092
|
+
const text = String(value ?? "");
|
|
2093
|
+
if (!text.length)
|
|
2094
|
+
return '""';
|
|
2095
|
+
if (!/[ \t"]/.test(text))
|
|
2096
|
+
return text;
|
|
2097
|
+
const escaped = text.replace(/(\\*)"/g, "$1$1\\\"").replace(/(\\+)$/g, "$1$1");
|
|
2098
|
+
return `"${escaped}"`;
|
|
2099
|
+
}
|
|
2002
2100
|
function isOptionalEmbeddedService(name) {
|
|
2003
2101
|
return name === "source_control_manager";
|
|
2004
2102
|
}
|
|
@@ -2017,12 +2115,107 @@ async function canSpawnCommand(command, cwd, env) {
|
|
|
2017
2115
|
return false;
|
|
2018
2116
|
}
|
|
2019
2117
|
}
|
|
2020
|
-
async function
|
|
2021
|
-
|
|
2022
|
-
if (!normalizedRemote)
|
|
2118
|
+
async function canSpawnGitViaWindowsShell(commandArgs, cwd, env, platform = process.platform) {
|
|
2119
|
+
if (platform !== "win32")
|
|
2023
2120
|
return false;
|
|
2024
|
-
const
|
|
2025
|
-
|
|
2121
|
+
const commandLine = commandArgs.map((arg) => quoteWindowsCmdArg(arg)).join(" ");
|
|
2122
|
+
for (const shellExecutable of resolveWindowsShellExecutableCandidatesForEnv(env, platform)) {
|
|
2123
|
+
try {
|
|
2124
|
+
const proc = Bun.spawn([shellExecutable, "/d", "/s", "/c", commandLine], {
|
|
2125
|
+
cwd,
|
|
2126
|
+
env,
|
|
2127
|
+
stdin: "ignore",
|
|
2128
|
+
stdout: "ignore",
|
|
2129
|
+
stderr: "ignore"
|
|
2130
|
+
});
|
|
2131
|
+
const exitCode = await proc.exited;
|
|
2132
|
+
return exitCode === 0;
|
|
2133
|
+
} catch {}
|
|
2134
|
+
}
|
|
2135
|
+
return false;
|
|
2136
|
+
}
|
|
2137
|
+
async function resolveSourceControlManagerGitProbe(cwd, env, platform = process.platform) {
|
|
2138
|
+
const candidates = resolveRuntimeGitExecutableCandidates(env, platform);
|
|
2139
|
+
for (const candidate of candidates) {
|
|
2140
|
+
if (await canSpawnCommand([candidate, "--version"], cwd, env)) {
|
|
2141
|
+
return { ok: true, detail: candidate };
|
|
2142
|
+
}
|
|
2143
|
+
}
|
|
2144
|
+
if (platform === "win32") {
|
|
2145
|
+
for (const candidate of candidates) {
|
|
2146
|
+
if (await canSpawnGitViaWindowsShell([candidate, "--version"], cwd, env, platform)) {
|
|
2147
|
+
return { ok: true, detail: `${candidate} via shell` };
|
|
2148
|
+
}
|
|
2149
|
+
}
|
|
2150
|
+
}
|
|
2151
|
+
return {
|
|
2152
|
+
ok: false,
|
|
2153
|
+
detail: candidates.join(", ") || "git"
|
|
2154
|
+
};
|
|
2155
|
+
}
|
|
2156
|
+
async function precheckSourceControlManagerGitAvailability(opts) {
|
|
2157
|
+
const platform = opts.platform ?? process.platform;
|
|
2158
|
+
const env = buildEmbeddedRuntimeEnv(opts.baseEnv ?? process.env, {
|
|
2159
|
+
repoRoot: opts.repoRoot,
|
|
2160
|
+
runtimeRoot: opts.runtimeRoot,
|
|
2161
|
+
useRuntimeConfig: opts.preflightUsesEmbeddedRuntime,
|
|
2162
|
+
sessionId: opts.sessionId
|
|
2163
|
+
});
|
|
2164
|
+
if (env.PUSHPALS_GIT_BIN) {
|
|
2165
|
+
applyResolvedGitBinaryToRuntimeEnv(env, env.PUSHPALS_GIT_BIN, platform);
|
|
2166
|
+
}
|
|
2167
|
+
const remoteStatus = opts.gitRemoteCheckFn ? await opts.gitRemoteCheckFn(opts.repoRoot, opts.remote, env) : opts.repoHasRemoteFn ? await opts.repoHasRemoteFn(opts.repoRoot, opts.remote) ? { status: "ok", remote: opts.remote } : { status: "missing_remote", remote: opts.remote } : await checkGitRemoteConfigured(opts.repoRoot, opts.remote, env);
|
|
2168
|
+
if (remoteStatus.status === "missing_remote") {
|
|
2169
|
+
return {
|
|
2170
|
+
status: "skipped",
|
|
2171
|
+
detail: `git remote "${opts.remote}" is not configured`,
|
|
2172
|
+
env
|
|
2173
|
+
};
|
|
2174
|
+
}
|
|
2175
|
+
if (remoteStatus.status === "error") {
|
|
2176
|
+
return {
|
|
2177
|
+
status: "failed",
|
|
2178
|
+
detail: `git remote "${opts.remote}" could not be inspected: ${remoteStatus.detail}`,
|
|
2179
|
+
env
|
|
2180
|
+
};
|
|
2181
|
+
}
|
|
2182
|
+
const gitLookupCommand = typeof env.PUSHPALS_GIT_BIN === "string" && env.PUSHPALS_GIT_BIN.trim() ? env.PUSHPALS_GIT_BIN.trim() : platform === "win32" ? "git.exe" : "git";
|
|
2183
|
+
const resolvedGitBinary = await (opts.resolveCommandPathFn ?? resolveCommandPath)(gitLookupCommand, opts.repoRoot, env);
|
|
2184
|
+
if (resolvedGitBinary) {
|
|
2185
|
+
applyResolvedGitBinaryToRuntimeEnv(env, resolvedGitBinary, platform);
|
|
2186
|
+
}
|
|
2187
|
+
const gitProbe = await (opts.gitProbeFn ?? resolveSourceControlManagerGitProbe)(opts.repoRoot, env, platform);
|
|
2188
|
+
if (!gitProbe.ok) {
|
|
2189
|
+
return {
|
|
2190
|
+
status: "failed",
|
|
2191
|
+
detail: gitProbe.detail,
|
|
2192
|
+
env
|
|
2193
|
+
};
|
|
2194
|
+
}
|
|
2195
|
+
return {
|
|
2196
|
+
status: "ok",
|
|
2197
|
+
detail: gitProbe.detail,
|
|
2198
|
+
env
|
|
2199
|
+
};
|
|
2200
|
+
}
|
|
2201
|
+
async function checkGitRemoteConfigured(repoRoot, remote, env) {
|
|
2202
|
+
const normalizedRemote = String(remote ?? "").trim();
|
|
2203
|
+
if (!normalizedRemote) {
|
|
2204
|
+
return { status: "missing_remote", remote: normalizedRemote };
|
|
2205
|
+
}
|
|
2206
|
+
const result = await runGitWithEnv(["remote", "get-url", normalizedRemote], repoRoot, env ?? {
|
|
2207
|
+
...process.env,
|
|
2208
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
2209
|
+
GCM_INTERACTIVE: "Never"
|
|
2210
|
+
});
|
|
2211
|
+
if (result.ok && result.stdout) {
|
|
2212
|
+
return { status: "ok", remote: normalizedRemote };
|
|
2213
|
+
}
|
|
2214
|
+
const detail = result.stderr || result.stdout || `exit ${result.exitCode}`;
|
|
2215
|
+
if (/no such remote/i.test(detail)) {
|
|
2216
|
+
return { status: "missing_remote", remote: normalizedRemote };
|
|
2217
|
+
}
|
|
2218
|
+
return { status: "error", remote: normalizedRemote, detail };
|
|
2026
2219
|
}
|
|
2027
2220
|
async function checkPushpalsBranchOnRemote(repoRoot, remote, branch) {
|
|
2028
2221
|
const normalizedRemote = String(remote ?? "").trim();
|
|
@@ -2030,10 +2223,18 @@ async function checkPushpalsBranchOnRemote(repoRoot, remote, branch) {
|
|
|
2030
2223
|
if (!normalizedRemote || !normalizedBranch) {
|
|
2031
2224
|
return { status: "ok" };
|
|
2032
2225
|
}
|
|
2033
|
-
const
|
|
2034
|
-
if (
|
|
2226
|
+
const remoteStatus = await checkGitRemoteConfigured(repoRoot, normalizedRemote);
|
|
2227
|
+
if (remoteStatus.status === "missing_remote") {
|
|
2035
2228
|
return { status: "missing_remote", remote: normalizedRemote };
|
|
2036
2229
|
}
|
|
2230
|
+
if (remoteStatus.status === "error") {
|
|
2231
|
+
return {
|
|
2232
|
+
status: "error",
|
|
2233
|
+
remote: normalizedRemote,
|
|
2234
|
+
branch: normalizedBranch,
|
|
2235
|
+
detail: remoteStatus.detail
|
|
2236
|
+
};
|
|
2237
|
+
}
|
|
2037
2238
|
const ref = `refs/heads/${normalizedBranch}`;
|
|
2038
2239
|
const result = await runGit(["ls-remote", "--heads", normalizedRemote, ref], repoRoot);
|
|
2039
2240
|
if (!result.ok) {
|
|
@@ -2070,6 +2271,151 @@ async function enforcePushpalsRemoteBranchPrecheck(repoRoot, remote, branch) {
|
|
|
2070
2271
|
console.error(`[pushpals] Precheck failed: could not verify remote branch "${result.remote}/${result.branch}": ${result.detail}`);
|
|
2071
2272
|
return false;
|
|
2072
2273
|
}
|
|
2274
|
+
function isPathEqualOrWithin(parentPath, childPath) {
|
|
2275
|
+
const parent = normalizeRepoPathForComparison(parentPath);
|
|
2276
|
+
const child = normalizeRepoPathForComparison(childPath);
|
|
2277
|
+
return child === parent || child.startsWith(`${parent}/`);
|
|
2278
|
+
}
|
|
2279
|
+
function appendCliClearTarget(targets, label, pathValue) {
|
|
2280
|
+
const resolvedPath = String(pathValue ?? "").trim();
|
|
2281
|
+
if (!resolvedPath)
|
|
2282
|
+
return;
|
|
2283
|
+
const normalized = normalizeRepoPathForComparison(resolvedPath);
|
|
2284
|
+
if (targets.some((target) => normalizeRepoPathForComparison(target.path) === normalized))
|
|
2285
|
+
return;
|
|
2286
|
+
targets.push({ label, path: resolve4(resolvedPath) });
|
|
2287
|
+
}
|
|
2288
|
+
function buildCliClearTargets(opts) {
|
|
2289
|
+
const targets = [];
|
|
2290
|
+
const dataDir = resolve4(opts.config.paths.dataDir);
|
|
2291
|
+
appendCliClearTarget(targets, "runtime data", dataDir);
|
|
2292
|
+
const scmStateDir = resolve4(opts.config.sourceControlManager.stateDir);
|
|
2293
|
+
if (!isPathEqualOrWithin(dataDir, scmStateDir)) {
|
|
2294
|
+
appendCliClearTarget(targets, "SourceControlManager state", scmStateDir);
|
|
2295
|
+
}
|
|
2296
|
+
const scmRepoPath = resolve4(opts.config.sourceControlManager.repoPath);
|
|
2297
|
+
if (normalizeRepoPathForComparison(scmRepoPath) !== normalizeRepoPathForComparison(opts.repoRoot) && isPathEqualOrWithin(opts.repoRoot, scmRepoPath)) {
|
|
2298
|
+
appendCliClearTarget(targets, "SourceControlManager worktree", scmRepoPath);
|
|
2299
|
+
}
|
|
2300
|
+
appendCliClearTarget(targets, "CLI state file", opts.cliStatePath ?? null);
|
|
2301
|
+
appendCliClearTarget(targets, "client monitor state file", resolveGitStateFilePath(opts.repoRoot, "pushpals-client-state.json"));
|
|
2302
|
+
appendCliClearTarget(targets, "runtime bootstrap logs", join2(opts.runtimeRoot, "logs", "bootstrap"));
|
|
2303
|
+
return targets;
|
|
2304
|
+
}
|
|
2305
|
+
function removeCliClearTarget(target) {
|
|
2306
|
+
if (!existsSync4(target.path))
|
|
2307
|
+
return "missing";
|
|
2308
|
+
try {
|
|
2309
|
+
rmSync(target.path, { recursive: true, force: true });
|
|
2310
|
+
return "removed";
|
|
2311
|
+
} catch (err) {
|
|
2312
|
+
return {
|
|
2313
|
+
...target,
|
|
2314
|
+
detail: err instanceof Error ? err.message : String(err)
|
|
2315
|
+
};
|
|
2316
|
+
}
|
|
2317
|
+
}
|
|
2318
|
+
async function requestLocalRuntimeShutdownForClear(serverUrl, repoRoot) {
|
|
2319
|
+
if (!await probeServer(serverUrl)) {
|
|
2320
|
+
return { attempted: false, accepted: false };
|
|
2321
|
+
}
|
|
2322
|
+
try {
|
|
2323
|
+
await ensureServerRepoAffinity(serverUrl, repoRoot);
|
|
2324
|
+
} catch (err) {
|
|
2325
|
+
return {
|
|
2326
|
+
attempted: false,
|
|
2327
|
+
accepted: false,
|
|
2328
|
+
detail: `skipping shutdown because ${String(err)}`
|
|
2329
|
+
};
|
|
2330
|
+
}
|
|
2331
|
+
try {
|
|
2332
|
+
const response = await fetchWithTimeout(`${serverUrl}/admin/shutdown`, {
|
|
2333
|
+
method: "POST",
|
|
2334
|
+
headers: { "Content-Type": "application/json" },
|
|
2335
|
+
body: JSON.stringify({ reason: "pushpals --clear" })
|
|
2336
|
+
}, 5000);
|
|
2337
|
+
if (!response.ok) {
|
|
2338
|
+
const detail = await response.text().catch(() => "");
|
|
2339
|
+
return {
|
|
2340
|
+
attempted: true,
|
|
2341
|
+
accepted: false,
|
|
2342
|
+
detail: `HTTP ${response.status}${detail ? ` ${detail}` : ""}`
|
|
2343
|
+
};
|
|
2344
|
+
}
|
|
2345
|
+
return { attempted: true, accepted: true };
|
|
2346
|
+
} catch (err) {
|
|
2347
|
+
return {
|
|
2348
|
+
attempted: true,
|
|
2349
|
+
accepted: false,
|
|
2350
|
+
detail: err instanceof Error ? err.message : String(err)
|
|
2351
|
+
};
|
|
2352
|
+
}
|
|
2353
|
+
}
|
|
2354
|
+
async function clearPushpalsState(opts) {
|
|
2355
|
+
console.log("[pushpals] Clear requested. Removing repo-local PushPals state.");
|
|
2356
|
+
const shutdown = await requestLocalRuntimeShutdownForClear(opts.serverUrl, opts.repoRoot);
|
|
2357
|
+
if (shutdown.attempted && shutdown.accepted) {
|
|
2358
|
+
console.log("[pushpals] Local runtime shutdown accepted; waiting for services to exit...");
|
|
2359
|
+
await Bun.sleep(1500);
|
|
2360
|
+
} else if (shutdown.attempted) {
|
|
2361
|
+
console.warn(`[pushpals] Local runtime shutdown request was not accepted${shutdown.detail ? `: ${shutdown.detail}` : "."}`);
|
|
2362
|
+
} else if (shutdown.detail) {
|
|
2363
|
+
console.warn(`[pushpals] ${shutdown.detail}`);
|
|
2364
|
+
}
|
|
2365
|
+
const targets = buildCliClearTargets({
|
|
2366
|
+
repoRoot: opts.repoRoot,
|
|
2367
|
+
runtimeRoot: opts.runtimeRoot,
|
|
2368
|
+
config: opts.config,
|
|
2369
|
+
cliStatePath: opts.cliStatePath
|
|
2370
|
+
});
|
|
2371
|
+
const removed = [];
|
|
2372
|
+
const missing = [];
|
|
2373
|
+
let failed = [];
|
|
2374
|
+
for (const target of targets) {
|
|
2375
|
+
const result = removeCliClearTarget(target);
|
|
2376
|
+
if (result === "removed") {
|
|
2377
|
+
removed.push(target);
|
|
2378
|
+
continue;
|
|
2379
|
+
}
|
|
2380
|
+
if (result === "missing") {
|
|
2381
|
+
missing.push(target);
|
|
2382
|
+
continue;
|
|
2383
|
+
}
|
|
2384
|
+
failed.push(result);
|
|
2385
|
+
}
|
|
2386
|
+
if (failed.length > 0 && shutdown.accepted) {
|
|
2387
|
+
await Bun.sleep(1000);
|
|
2388
|
+
const retryFailures = [];
|
|
2389
|
+
for (const failure of failed) {
|
|
2390
|
+
const retry = removeCliClearTarget(failure);
|
|
2391
|
+
if (retry === "removed") {
|
|
2392
|
+
removed.push({ label: failure.label, path: failure.path });
|
|
2393
|
+
continue;
|
|
2394
|
+
}
|
|
2395
|
+
if (retry === "missing") {
|
|
2396
|
+
missing.push({ label: failure.label, path: failure.path });
|
|
2397
|
+
continue;
|
|
2398
|
+
}
|
|
2399
|
+
retryFailures.push(retry);
|
|
2400
|
+
}
|
|
2401
|
+
failed = retryFailures;
|
|
2402
|
+
}
|
|
2403
|
+
for (const target of removed) {
|
|
2404
|
+
console.log(`[pushpals] Cleared ${target.label}: ${target.path}`);
|
|
2405
|
+
}
|
|
2406
|
+
for (const target of missing) {
|
|
2407
|
+
console.log(`[pushpals] Nothing to clear for ${target.label}: ${target.path}`);
|
|
2408
|
+
}
|
|
2409
|
+
for (const failure of failed) {
|
|
2410
|
+
console.error(`[pushpals] Failed to clear ${failure.label}: ${failure.path} (${failure.detail})`);
|
|
2411
|
+
}
|
|
2412
|
+
if (failed.length > 0) {
|
|
2413
|
+
console.error("[pushpals] Clear completed with errors.");
|
|
2414
|
+
return 1;
|
|
2415
|
+
}
|
|
2416
|
+
console.log("[pushpals] Clear completed.");
|
|
2417
|
+
return 0;
|
|
2418
|
+
}
|
|
2073
2419
|
async function probeServer(serverUrl) {
|
|
2074
2420
|
try {
|
|
2075
2421
|
const response = await fetchWithTimeout(`${serverUrl}/healthz`, {}, HTTP_TIMEOUT_MS);
|
|
@@ -2284,6 +2630,7 @@ async function autoStartRuntimeServices(opts) {
|
|
|
2284
2630
|
useRuntimeConfig: opts.preparedRuntime.preflightUsesEmbeddedRuntime,
|
|
2285
2631
|
sessionId: opts.sessionId
|
|
2286
2632
|
});
|
|
2633
|
+
runtimeEnv.PUSHPALS_WORKERPALS_BIN = runtimeBinaries.workerpals;
|
|
2287
2634
|
if (runtimeEnv.PUSHPALS_GIT_BIN) {
|
|
2288
2635
|
applyResolvedGitBinaryToRuntimeEnv(runtimeEnv, runtimeEnv.PUSHPALS_GIT_BIN);
|
|
2289
2636
|
}
|
|
@@ -2374,27 +2721,25 @@ ${tail}` : ""}`);
|
|
|
2374
2721
|
};
|
|
2375
2722
|
reportRemoteBuddyAutonomousEngineState();
|
|
2376
2723
|
const scmHealthy = await probeSourceControlManager(opts.sourceControlManagerPort);
|
|
2377
|
-
const
|
|
2378
|
-
const
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
if (!scmHealthy && scmRemoteAvailable) {
|
|
2382
|
-
if (!gitAvailableForScm) {
|
|
2724
|
+
const scmGitProbe = await resolveSourceControlManagerGitProbe(opts.repoRoot, runtimeEnv, process.platform);
|
|
2725
|
+
const scmRemoteStatus = await checkGitRemoteConfigured(opts.repoRoot, opts.sourceControlManagerRemote, runtimeEnv);
|
|
2726
|
+
if (!scmHealthy) {
|
|
2727
|
+
if (!scmGitProbe.ok) {
|
|
2383
2728
|
console.warn("[pushpals] Git is not available to embedded SourceControlManager; skipping SCM startup.");
|
|
2384
|
-
appendRuntimeServicesLogLine(runtimeServicesLogPath,
|
|
2385
|
-
} else {
|
|
2386
|
-
console.
|
|
2729
|
+
appendRuntimeServicesLogLine(runtimeServicesLogPath, `[pushpals] source_control_manager skipped: git is unavailable in embedded runtime env (${scmGitProbe.detail}).`);
|
|
2730
|
+
} else if (scmRemoteStatus.status === "error") {
|
|
2731
|
+
console.warn(`[pushpals] Could not inspect SourceControlManager git remote "${opts.sourceControlManagerRemote}"; skipping SCM startup.`);
|
|
2732
|
+
appendRuntimeServicesLogLine(runtimeServicesLogPath, `[pushpals] source_control_manager skipped: remote "${opts.sourceControlManagerRemote}" could not be inspected (${scmRemoteStatus.detail}).`);
|
|
2733
|
+
} else if (scmRemoteStatus.status === "ok") {
|
|
2734
|
+
console.log(`[pushpals] Embedded SourceControlManager git=${scmGitProbe.detail}`);
|
|
2387
2735
|
console.log("[pushpals] Starting embedded SourceControlManager...");
|
|
2388
2736
|
const sourceControlManagerService = spawnRuntimeService("source_control_manager", [runtimeBinaries.sourceControlManager, "--skip-clean-check"], opts.repoRoot, runtimeEnv, serviceLogPaths.source_control_manager, runtimeServicesLogPath);
|
|
2389
2737
|
services.push(sourceControlManagerService);
|
|
2390
2738
|
console.log(`[pushpals] source_control_manager log: ${sourceControlManagerService.logPath}`);
|
|
2739
|
+
} else {
|
|
2740
|
+
console.log(`[pushpals] Repo has no git remote "${opts.sourceControlManagerRemote}"; skipping embedded SourceControlManager.`);
|
|
2741
|
+
appendRuntimeServicesLogLine(runtimeServicesLogPath, `[pushpals] source_control_manager skipped: repo has no remote "${opts.sourceControlManagerRemote}".`);
|
|
2391
2742
|
}
|
|
2392
|
-
} else if (!scmRemoteAvailable) {
|
|
2393
|
-
console.log(`[pushpals] Repo has no git remote "${opts.sourceControlManagerRemote}"; skipping embedded SourceControlManager.`);
|
|
2394
|
-
appendRuntimeServicesLogLine(runtimeServicesLogPath, `[pushpals] source_control_manager skipped: repo has no remote "${opts.sourceControlManagerRemote}".`);
|
|
2395
|
-
} else if (!gitAvailableForScm) {
|
|
2396
|
-
console.warn("[pushpals] Git is not available to embedded SourceControlManager; skipping SCM startup.");
|
|
2397
|
-
appendRuntimeServicesLogLine(runtimeServicesLogPath, "[pushpals] source_control_manager skipped: git is unavailable in embedded runtime env.");
|
|
2398
2743
|
} else {
|
|
2399
2744
|
console.log("[pushpals] SourceControlManager already healthy; skipping embedded start.");
|
|
2400
2745
|
appendRuntimeServicesLogLine(runtimeServicesLogPath, "[pushpals] source_control_manager already healthy; embedded start skipped.");
|
|
@@ -2461,7 +2806,10 @@ ${tail}` : ""}`);
|
|
|
2461
2806
|
}
|
|
2462
2807
|
console.log("[pushpals] Embedded runtime is ready.");
|
|
2463
2808
|
appendRuntimeServicesLogLine(runtimeServicesLogPath, "[pushpals] embedded runtime is ready.");
|
|
2464
|
-
return
|
|
2809
|
+
return {
|
|
2810
|
+
services,
|
|
2811
|
+
pushpalsLogPath: runtimeServicesLogPath
|
|
2812
|
+
};
|
|
2465
2813
|
}
|
|
2466
2814
|
await Bun.sleep(DEFAULT_RUNTIME_BOOT_POLL_MS);
|
|
2467
2815
|
}
|
|
@@ -2492,6 +2840,7 @@ function readCliState(pathValue) {
|
|
|
2492
2840
|
localAgentUrl: typeof parsed.localAgentUrl === "string" ? parsed.localAgentUrl : undefined,
|
|
2493
2841
|
sessionId: typeof parsed.sessionId === "string" ? parsed.sessionId : undefined,
|
|
2494
2842
|
repoRoot: typeof parsed.repoRoot === "string" ? parsed.repoRoot : undefined,
|
|
2843
|
+
pushpalsLogPath: typeof parsed.pushpalsLogPath === "string" ? parsed.pushpalsLogPath : undefined,
|
|
2495
2844
|
updatedAt: typeof parsed.updatedAt === "string" ? parsed.updatedAt : undefined
|
|
2496
2845
|
};
|
|
2497
2846
|
} catch {
|
|
@@ -3006,6 +3355,19 @@ async function main() {
|
|
|
3006
3355
|
runtimeRoot: parsed.runtimeRoot,
|
|
3007
3356
|
runtimeTag: parsed.runtimeTag
|
|
3008
3357
|
});
|
|
3358
|
+
const config = preparedRuntime.runtimePreflight.config;
|
|
3359
|
+
const statePath = resolveCliStatePath(repoRoot);
|
|
3360
|
+
if (parsed.clear) {
|
|
3361
|
+
const serverUrl2 = normalizeLoopbackUrl(parsed.serverUrl ?? process.env.PUSHPALS_SERVER_URL, config.server.url);
|
|
3362
|
+
const exitCode = await clearPushpalsState({
|
|
3363
|
+
repoRoot,
|
|
3364
|
+
runtimeRoot: preparedRuntime.runtimeRoot,
|
|
3365
|
+
config,
|
|
3366
|
+
serverUrl: serverUrl2,
|
|
3367
|
+
cliStatePath: statePath
|
|
3368
|
+
});
|
|
3369
|
+
process.exit(exitCode);
|
|
3370
|
+
}
|
|
3009
3371
|
console.log("[pushpals] Running runtime preflight...");
|
|
3010
3372
|
console.log(`[pushpals] runtimeRoot=${preparedRuntime.runtimeRoot}`);
|
|
3011
3373
|
if (preparedRuntime.runtimeTag) {
|
|
@@ -3019,12 +3381,21 @@ async function main() {
|
|
|
3019
3381
|
if (!preparedRuntime.runtimePreflight.ok) {
|
|
3020
3382
|
process.exit(1);
|
|
3021
3383
|
}
|
|
3022
|
-
const config = preparedRuntime.runtimePreflight.config;
|
|
3023
3384
|
if (config.remotebuddy.autonomy.enabled) {
|
|
3024
3385
|
console.log("[pushpals] RemoteBuddy autonomy is enabled for CLI.");
|
|
3025
3386
|
} else {
|
|
3026
3387
|
console.warn("[pushpals] RemoteBuddy autonomy is disabled in config (remotebuddy.autonomy.enabled=false); continuing.");
|
|
3027
3388
|
}
|
|
3389
|
+
const scmGitPrecheck = await precheckSourceControlManagerGitAvailability({
|
|
3390
|
+
repoRoot,
|
|
3391
|
+
remote: config.sourceControlManager.remote,
|
|
3392
|
+
runtimeRoot: preparedRuntime.runtimeRoot,
|
|
3393
|
+
preflightUsesEmbeddedRuntime: preparedRuntime.preflightUsesEmbeddedRuntime
|
|
3394
|
+
});
|
|
3395
|
+
if (scmGitPrecheck.status === "failed") {
|
|
3396
|
+
console.error(`[pushpals] Precheck failed: embedded SourceControlManager git command is unavailable (${scmGitPrecheck.detail}).`);
|
|
3397
|
+
process.exit(1);
|
|
3398
|
+
}
|
|
3028
3399
|
const precheckPassed = await enforcePushpalsRemoteBranchPrecheck(repoRoot, config.sourceControlManager.remote, config.sourceControlManager.mainBranch);
|
|
3029
3400
|
if (!precheckPassed) {
|
|
3030
3401
|
process.exit(1);
|
|
@@ -3042,6 +3413,7 @@ async function main() {
|
|
|
3042
3413
|
repoRoot
|
|
3043
3414
|
};
|
|
3044
3415
|
let autoStartedServices = [];
|
|
3416
|
+
let pushpalsLogPath;
|
|
3045
3417
|
const stopAutoStartedServices = () => {
|
|
3046
3418
|
if (autoStartedServices.length === 0)
|
|
3047
3419
|
return;
|
|
@@ -3057,7 +3429,7 @@ async function main() {
|
|
|
3057
3429
|
if (!serverHealthy) {
|
|
3058
3430
|
if (!parsed.noAutoStart) {
|
|
3059
3431
|
try {
|
|
3060
|
-
|
|
3432
|
+
const startedRuntime = await autoStartRuntimeServices({
|
|
3061
3433
|
repoRoot,
|
|
3062
3434
|
serverUrl,
|
|
3063
3435
|
localAgentUrl,
|
|
@@ -3068,6 +3440,8 @@ async function main() {
|
|
|
3068
3440
|
requestedRuntimeTag: parsed.runtimeTag,
|
|
3069
3441
|
startLocalBuddy: resolveCliLocalBuddyAutostart(parsed.runtimeOnly, Boolean(config.localbuddy.enabled))
|
|
3070
3442
|
});
|
|
3443
|
+
autoStartedServices = startedRuntime.services;
|
|
3444
|
+
pushpalsLogPath = startedRuntime.pushpalsLogPath;
|
|
3071
3445
|
serverHealthy = await probeServer(serverUrl);
|
|
3072
3446
|
} catch (err) {
|
|
3073
3447
|
console.error(`[pushpals] Auto-start failed: ${String(err)}`);
|
|
@@ -3119,8 +3493,8 @@ async function main() {
|
|
|
3119
3493
|
}
|
|
3120
3494
|
process.exit(1);
|
|
3121
3495
|
}
|
|
3122
|
-
const statePath = resolveCliStatePath(repoRoot);
|
|
3123
3496
|
const saved = statePath ? readCliState(statePath) : {};
|
|
3497
|
+
pushpalsLogPath = pushpalsLogPath || (typeof saved.pushpalsLogPath === "string" ? saved.pushpalsLogPath : undefined);
|
|
3124
3498
|
const preferredHubUrl = normalizeUrl(parsed.monitoringHubUrl ?? process.env.PUSHPALS_MONITOR_URL ?? saved.monitoringHubUrl ?? "");
|
|
3125
3499
|
const monitorPort = parsePositiveInt(process.env.PUSHPALS_CLIENT_PORT, DEFAULT_MONITOR_PORT);
|
|
3126
3500
|
const monitoringHub = await resolveMonitoringHub({
|
|
@@ -3136,7 +3510,8 @@ async function main() {
|
|
|
3136
3510
|
serverUrl,
|
|
3137
3511
|
localAgentUrl,
|
|
3138
3512
|
sessionId: activeSessionId,
|
|
3139
|
-
repoRoot
|
|
3513
|
+
repoRoot,
|
|
3514
|
+
pushpalsLogPath
|
|
3140
3515
|
});
|
|
3141
3516
|
} else {
|
|
3142
3517
|
console.warn("[pushpals] Could not resolve git metadata dir; skipping CLI state persistence.");
|
|
@@ -3153,6 +3528,7 @@ async function main() {
|
|
|
3153
3528
|
console.log(`[pushpals] serverUrl=${serverUrl}`);
|
|
3154
3529
|
console.log(`[pushpals] sessionId=${activeSessionId}`);
|
|
3155
3530
|
console.log(`[pushpals] repoRoot=${repoRoot}`);
|
|
3531
|
+
console.log(`[pushpals] pushpalsLog=${pushpalsLogPath ?? "unavailable"}`);
|
|
3156
3532
|
console.log(`[pushpals] cliStateFile=${statePath ?? "unavailable"}`);
|
|
3157
3533
|
if (parsed.runtimeOnly) {
|
|
3158
3534
|
console.log("[pushpals] runtimeOnly=true");
|
|
@@ -3253,6 +3629,7 @@ ${line}
|
|
|
3253
3629
|
console.log(`[pushpals] serverUrl=${serverUrl}`);
|
|
3254
3630
|
console.log(`[pushpals] sessionId=${activeSessionId}`);
|
|
3255
3631
|
console.log(`[pushpals] repoRoot=${repoRoot}`);
|
|
3632
|
+
console.log(`[pushpals] pushpalsLog=${pushpalsLogPath ?? "unavailable"}`);
|
|
3256
3633
|
console.log(monitoringHubUrl ? `[pushpals] monitoringHubUrl=${monitoringHubUrl}` : "[pushpals] monitoringHubUrl=unavailable");
|
|
3257
3634
|
rl.prompt();
|
|
3258
3635
|
continue;
|
|
@@ -3291,12 +3668,17 @@ if (import.meta.main) {
|
|
|
3291
3668
|
}
|
|
3292
3669
|
export {
|
|
3293
3670
|
startEmbeddedMonitoringHub,
|
|
3671
|
+
resolveWindowsWhereExecutableCandidatesForEnv,
|
|
3672
|
+
resolveWindowsShellExecutableCandidatesForEnv,
|
|
3673
|
+
resolveRuntimeGitExecutableCandidates,
|
|
3674
|
+
resolvePreferredRuntimeReleaseTag,
|
|
3294
3675
|
resolveCommandPath,
|
|
3295
3676
|
resolveCliStatePath,
|
|
3296
3677
|
resolveCliLocalBuddyAutostart,
|
|
3297
3678
|
resolveBundledRuntimeAssetSource,
|
|
3298
3679
|
resolveBundledMonitoringHubRoot,
|
|
3299
3680
|
prepareCliRuntime,
|
|
3681
|
+
precheckSourceControlManagerGitAvailability,
|
|
3300
3682
|
normalizeRepoPathForComparison,
|
|
3301
3683
|
normalizeCliInteractiveMessage,
|
|
3302
3684
|
normalizeChildProcessEnv,
|
|
@@ -3312,5 +3694,6 @@ export {
|
|
|
3312
3694
|
buildOpenMonitoringHubCommand,
|
|
3313
3695
|
buildEmbeddedRuntimeEnv,
|
|
3314
3696
|
buildEmbeddedMonitoringHubHtml,
|
|
3697
|
+
buildCliClearTargets,
|
|
3315
3698
|
applyResolvedGitBinaryToRuntimeEnv
|
|
3316
3699
|
};
|