@pushpalsdev/cli 1.0.10 → 1.0.12
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 +375 -146
- package/monitor-ui/+not-found.html +1 -2
- package/monitor-ui/_expo/static/js/web/{entry-1d5a07c47473852fe9754bfe3e6da301.js → entry-275c5f7972e2d2f4f0422fe2213a7f89.js} +259 -257
- package/monitor-ui/_expo/static/js/web/{index-5650b6cee60075cb63c1efc40dcf7683.js → index-6013f9ebc87a963a55bb9137af1a5a06.js} +6 -6
- package/monitor-ui/_sitemap.html +1 -2
- package/monitor-ui/index.html +1 -2
- package/monitor-ui/modal.html +1 -2
- package/package.json +1 -1
package/dist/pushpals-cli.js
CHANGED
|
@@ -7,11 +7,13 @@ import {
|
|
|
7
7
|
chmodSync,
|
|
8
8
|
cpSync,
|
|
9
9
|
existsSync as existsSync4,
|
|
10
|
+
lstatSync,
|
|
10
11
|
mkdirSync,
|
|
12
|
+
readdirSync,
|
|
11
13
|
readFileSync as readFileSync4,
|
|
12
14
|
writeFileSync
|
|
13
15
|
} from "fs";
|
|
14
|
-
import { dirname, extname, join as join2, resolve as resolve4 } from "path";
|
|
16
|
+
import { basename, delimiter, dirname, extname, join as join2, resolve as resolve4 } from "path";
|
|
15
17
|
import { createInterface } from "readline";
|
|
16
18
|
|
|
17
19
|
// ../shared/src/client_preflight.ts
|
|
@@ -1104,6 +1106,17 @@ function formatClientRuntimePreflightLines(result, prefix) {
|
|
|
1104
1106
|
return lines;
|
|
1105
1107
|
}
|
|
1106
1108
|
|
|
1109
|
+
// ../shared/src/communication.ts
|
|
1110
|
+
function stripPresenceSourcePrefix(value) {
|
|
1111
|
+
return value.replace(/^(agent|client)(?:[\s:./_-]+)+/i, "");
|
|
1112
|
+
}
|
|
1113
|
+
function normalizePresenceClientLabel(value) {
|
|
1114
|
+
return stripPresenceSourcePrefix(String(value ?? "")).replace(/\s+/g, " ").trim();
|
|
1115
|
+
}
|
|
1116
|
+
function normalizePresenceLookupToken(value) {
|
|
1117
|
+
return normalizePresenceClientLabel(value).toLowerCase().replace(/[^a-z0-9]+/g, "");
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1107
1120
|
// ../shared/src/repo.ts
|
|
1108
1121
|
import { existsSync as existsSync3, readFileSync as readFileSync3, statSync } from "fs";
|
|
1109
1122
|
import { resolve as resolve3 } from "path";
|
|
@@ -1144,6 +1157,21 @@ function resolveGitStateFilePath(repoRoot, fileName) {
|
|
|
1144
1157
|
return resolve3(gitMetadataDir, normalizedFileName);
|
|
1145
1158
|
}
|
|
1146
1159
|
|
|
1160
|
+
// ../shared/src/session_event_visibility.ts
|
|
1161
|
+
var HEARTBEAT_STATUS_RE = /\bheartbeat\b/i;
|
|
1162
|
+
function isHeartbeatStatusSessionEvent(event) {
|
|
1163
|
+
const type = String(event?.type ?? "").trim().toLowerCase();
|
|
1164
|
+
if (type !== "status")
|
|
1165
|
+
return false;
|
|
1166
|
+
const payload = event?.payload ?? {};
|
|
1167
|
+
const detail = typeof payload.detail === "string" ? payload.detail.trim() : "";
|
|
1168
|
+
const message = typeof payload.message === "string" ? payload.message.trim() : "";
|
|
1169
|
+
return HEARTBEAT_STATUS_RE.test(detail) || HEARTBEAT_STATUS_RE.test(message);
|
|
1170
|
+
}
|
|
1171
|
+
function shouldDisplayInteractiveSessionEvent(event) {
|
|
1172
|
+
return !isHeartbeatStatusSessionEvent(event);
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1147
1175
|
// ../../scripts/pushpals-cli.ts
|
|
1148
1176
|
var DEFAULT_MONITOR_PORT = 8081;
|
|
1149
1177
|
var MONITOR_SCAN_PORTS = 32;
|
|
@@ -1163,6 +1191,7 @@ var GITHUB_HEADERS = {
|
|
|
1163
1191
|
Accept: "application/vnd.github+json",
|
|
1164
1192
|
"User-Agent": "pushpals-cli"
|
|
1165
1193
|
};
|
|
1194
|
+
var ASK_REMOTE_BUDDY_COMMAND = "/ask_remote_buddy";
|
|
1166
1195
|
var stateVersion = 1;
|
|
1167
1196
|
var cliTimestampedConsoleInstalled = false;
|
|
1168
1197
|
function formatTimestampedCliLine(line, at = new Date) {
|
|
@@ -1172,6 +1201,21 @@ function formatTimestampedCliLine(line, at = new Date) {
|
|
|
1172
1201
|
}
|
|
1173
1202
|
return `[${at.toISOString()}]${text}`;
|
|
1174
1203
|
}
|
|
1204
|
+
function normalizeCliInteractiveMessage(input) {
|
|
1205
|
+
const trimmed = String(input ?? "").trim();
|
|
1206
|
+
const command = ASK_REMOTE_BUDDY_COMMAND.toLowerCase();
|
|
1207
|
+
if (!trimmed.toLowerCase().startsWith(command)) {
|
|
1208
|
+
return { text: trimmed };
|
|
1209
|
+
}
|
|
1210
|
+
const rest = trimmed.slice(command.length).replace(/^[:\-]\s*/, "").trim();
|
|
1211
|
+
if (!rest) {
|
|
1212
|
+
return {
|
|
1213
|
+
text: "",
|
|
1214
|
+
usageMessage: "Usage: /ask_remote_buddy <request>. Example: /ask_remote_buddy fix the failing job status in the dashboard."
|
|
1215
|
+
};
|
|
1216
|
+
}
|
|
1217
|
+
return { text: rest };
|
|
1218
|
+
}
|
|
1175
1219
|
function installTimestampedCliConsole() {
|
|
1176
1220
|
if (cliTimestampedConsoleInstalled)
|
|
1177
1221
|
return;
|
|
@@ -1205,12 +1249,12 @@ function printUsage() {
|
|
|
1205
1249
|
console.log("");
|
|
1206
1250
|
console.log("Options:");
|
|
1207
1251
|
console.log(" --server-url <url> Override PushPals server URL");
|
|
1208
|
-
console.log(" --local-agent-url <url> Override LocalBuddy URL");
|
|
1252
|
+
console.log(" --local-agent-url <url> Override LocalBuddy URL for monitoring/runtime state");
|
|
1209
1253
|
console.log(" --session-id <id> Override session ID");
|
|
1210
1254
|
console.log(" --hub-url <url> Override monitoring hub URL");
|
|
1211
1255
|
console.log(" --runtime-root <path> Override embedded runtime directory for auto-start");
|
|
1212
1256
|
console.log(" --runtime-tag <tag> Override runtime release tag (e.g. v1.0.2)");
|
|
1213
|
-
console.log(" --no-auto-start Disable runtime auto-start when
|
|
1257
|
+
console.log(" --no-auto-start Disable runtime auto-start when the server is down");
|
|
1214
1258
|
console.log(" --no-stream Disable live session event stream");
|
|
1215
1259
|
console.log(" --runtime-only Start the local runtime and wait for shutdown without opening the interactive chat");
|
|
1216
1260
|
console.log(" -h, --help Show this help");
|
|
@@ -1223,8 +1267,8 @@ function printUsage() {
|
|
|
1223
1267
|
console.log("");
|
|
1224
1268
|
console.log("Notes:");
|
|
1225
1269
|
console.log(" - Must be run from inside a git repository.");
|
|
1226
|
-
console.log(" - Auto-start can bootstrap server/
|
|
1227
|
-
console.log(" -
|
|
1270
|
+
console.log(" - Auto-start can bootstrap server/remotebuddy/source_control_manager and LocalBuddy when runtime config enables it.");
|
|
1271
|
+
console.log(" - Interactive CLI talks directly to server sessions; LocalBuddy is optional.");
|
|
1228
1272
|
}
|
|
1229
1273
|
function parseArgs(argv) {
|
|
1230
1274
|
const options = { noAutoStart: false, noStream: false, runtimeOnly: false };
|
|
@@ -1311,12 +1355,6 @@ function parsePositiveInt(value, fallback) {
|
|
|
1311
1355
|
return fallback;
|
|
1312
1356
|
return parsed;
|
|
1313
1357
|
}
|
|
1314
|
-
function normalizePath(value) {
|
|
1315
|
-
const normalized = resolve4(value).replace(/\\/g, "/").replace(/\/+$/, "");
|
|
1316
|
-
if (process.platform === "win32")
|
|
1317
|
-
return normalized.toLowerCase();
|
|
1318
|
-
return normalized;
|
|
1319
|
-
}
|
|
1320
1358
|
function jsonHtmlBootstrap(value) {
|
|
1321
1359
|
return JSON.stringify(value).replace(/</g, "\\u003c");
|
|
1322
1360
|
}
|
|
@@ -1374,6 +1412,42 @@ function resolveBundledRuntimeAssetSource() {
|
|
|
1374
1412
|
function looksLikeMonitoringHubBuild(root) {
|
|
1375
1413
|
return existsSync4(join2(root, "index.html")) && existsSync4(join2(root, "_expo"));
|
|
1376
1414
|
}
|
|
1415
|
+
function latestPathMtimeMs(pathValue) {
|
|
1416
|
+
if (!existsSync4(pathValue))
|
|
1417
|
+
return 0;
|
|
1418
|
+
const stat = lstatSync(pathValue);
|
|
1419
|
+
let latest = stat.mtimeMs;
|
|
1420
|
+
if (!stat.isDirectory())
|
|
1421
|
+
return latest;
|
|
1422
|
+
for (const entry of readdirSync(pathValue)) {
|
|
1423
|
+
latest = Math.max(latest, latestPathMtimeMs(join2(pathValue, entry)));
|
|
1424
|
+
}
|
|
1425
|
+
return latest;
|
|
1426
|
+
}
|
|
1427
|
+
function bundledMonitoringHubSourceWatchPaths(sourceRoot) {
|
|
1428
|
+
return [
|
|
1429
|
+
join2(sourceRoot, "apps", "client", "app"),
|
|
1430
|
+
join2(sourceRoot, "apps", "client", "assets"),
|
|
1431
|
+
join2(sourceRoot, "apps", "client", "components"),
|
|
1432
|
+
join2(sourceRoot, "apps", "client", "constants"),
|
|
1433
|
+
join2(sourceRoot, "apps", "client", "hooks"),
|
|
1434
|
+
join2(sourceRoot, "apps", "client", "scripts"),
|
|
1435
|
+
join2(sourceRoot, "apps", "client", "src"),
|
|
1436
|
+
join2(sourceRoot, "apps", "client", "app.json"),
|
|
1437
|
+
join2(sourceRoot, "apps", "client", "package.json"),
|
|
1438
|
+
join2(sourceRoot, "packages", "shared", "src"),
|
|
1439
|
+
join2(sourceRoot, "scripts", "sync-cli-monitor-ui.ts")
|
|
1440
|
+
];
|
|
1441
|
+
}
|
|
1442
|
+
function bundledMonitoringHubNeedsRefresh(existingRoot, sourceRoot) {
|
|
1443
|
+
if (!looksLikeMonitoringHubBuild(existingRoot))
|
|
1444
|
+
return true;
|
|
1445
|
+
const bundleMtimeMs = latestPathMtimeMs(existingRoot);
|
|
1446
|
+
if (bundleMtimeMs <= 0)
|
|
1447
|
+
return true;
|
|
1448
|
+
const sourceMtimeMs = bundledMonitoringHubSourceWatchPaths(sourceRoot).reduce((latest, pathValue) => Math.max(latest, latestPathMtimeMs(pathValue)), 0);
|
|
1449
|
+
return sourceMtimeMs > bundleMtimeMs;
|
|
1450
|
+
}
|
|
1377
1451
|
function resolveBundledMonitoringHubRoot() {
|
|
1378
1452
|
const candidates = [
|
|
1379
1453
|
resolve4(import.meta.dir, "..", "monitor-ui"),
|
|
@@ -1413,11 +1487,15 @@ function exportBundledMonitoringHubFromSourceCheckout(sourceRoot) {
|
|
|
1413
1487
|
}
|
|
1414
1488
|
async function ensureBundledMonitoringHubRoot() {
|
|
1415
1489
|
const existingRoot = resolveBundledMonitoringHubRoot();
|
|
1416
|
-
if (existingRoot)
|
|
1417
|
-
return existingRoot;
|
|
1418
1490
|
const sourceRoot = resolveCliSourceCheckoutRoot();
|
|
1419
1491
|
if (!sourceRoot)
|
|
1420
|
-
return
|
|
1492
|
+
return existingRoot;
|
|
1493
|
+
if (existingRoot && !bundledMonitoringHubNeedsRefresh(existingRoot, sourceRoot)) {
|
|
1494
|
+
return existingRoot;
|
|
1495
|
+
}
|
|
1496
|
+
if (existingRoot) {
|
|
1497
|
+
console.log("[pushpals] Packaged monitor UI is stale; refreshing the exported client monitor...");
|
|
1498
|
+
}
|
|
1421
1499
|
exportBundledMonitoringHubFromSourceCheckout(sourceRoot);
|
|
1422
1500
|
return resolveBundledMonitoringHubRoot();
|
|
1423
1501
|
}
|
|
@@ -1637,6 +1715,7 @@ function buildEmbeddedRuntimeEnv(baseEnv, opts) {
|
|
|
1637
1715
|
PUSHPALS_PROMPTS_ROOT_OVERRIDE: opts.repoRoot
|
|
1638
1716
|
},
|
|
1639
1717
|
PUSHPALS_PROTOCOL_SCHEMAS_DIR: join2(opts.runtimeRoot, "protocol", "schemas"),
|
|
1718
|
+
...typeof opts.sessionId === "string" && opts.sessionId.trim() ? { PUSHPALS_SESSION_ID: opts.sessionId.trim() } : {},
|
|
1640
1719
|
...typeof env.PUSHPALS_GIT_BIN === "string" && env.PUSHPALS_GIT_BIN.trim() ? { PUSHPALS_GIT_BIN: env.PUSHPALS_GIT_BIN.trim() } : {}
|
|
1641
1720
|
};
|
|
1642
1721
|
}
|
|
@@ -1827,6 +1906,34 @@ function stopRuntimeServices(services) {
|
|
|
1827
1906
|
} catch {}
|
|
1828
1907
|
}
|
|
1829
1908
|
}
|
|
1909
|
+
function prependExecutableDirToPath(env, executablePath, platform = process.platform) {
|
|
1910
|
+
const resolvedPath = String(executablePath ?? "").trim();
|
|
1911
|
+
if (!resolvedPath)
|
|
1912
|
+
return env;
|
|
1913
|
+
if (!resolvedPath.includes("/") && !resolvedPath.includes("\\")) {
|
|
1914
|
+
return env;
|
|
1915
|
+
}
|
|
1916
|
+
const executableDir = dirname(resolvedPath);
|
|
1917
|
+
const existingPath = platform === "win32" ? String(env.Path ?? env.PATH ?? "") : String(env.PATH ?? "");
|
|
1918
|
+
const pathEntries = existingPath.split(delimiter).map((entry) => entry.trim()).filter((entry) => entry.length > 0);
|
|
1919
|
+
const hasDir = pathEntries.some((entry) => entry.toLowerCase() === executableDir.toLowerCase());
|
|
1920
|
+
const nextPath = hasDir ? existingPath : [executableDir, ...pathEntries].join(delimiter);
|
|
1921
|
+
if (platform === "win32") {
|
|
1922
|
+
env.Path = nextPath;
|
|
1923
|
+
env.PATH = nextPath;
|
|
1924
|
+
} else {
|
|
1925
|
+
env.PATH = nextPath;
|
|
1926
|
+
}
|
|
1927
|
+
return env;
|
|
1928
|
+
}
|
|
1929
|
+
function applyResolvedGitBinaryToRuntimeEnv(env, resolvedGitBinary, platform = process.platform) {
|
|
1930
|
+
const resolvedPath = String(resolvedGitBinary ?? "").trim();
|
|
1931
|
+
if (!resolvedPath)
|
|
1932
|
+
return env;
|
|
1933
|
+
prependExecutableDirToPath(env, resolvedPath, platform);
|
|
1934
|
+
env.PUSHPALS_GIT_BIN = basename(resolvedPath);
|
|
1935
|
+
return env;
|
|
1936
|
+
}
|
|
1830
1937
|
function isOptionalEmbeddedService(name) {
|
|
1831
1938
|
return name === "source_control_manager";
|
|
1832
1939
|
}
|
|
@@ -1860,6 +1967,111 @@ async function probeServer(serverUrl) {
|
|
|
1860
1967
|
return false;
|
|
1861
1968
|
}
|
|
1862
1969
|
}
|
|
1970
|
+
function normalizeRepoPathForComparison(repoPath) {
|
|
1971
|
+
const normalized = resolve4(String(repoPath ?? "")).replace(/\\/g, "/").replace(/\/+$/, "");
|
|
1972
|
+
return process.platform === "win32" ? normalized.toLowerCase() : normalized;
|
|
1973
|
+
}
|
|
1974
|
+
async function fetchServerRepoRoot(serverUrl) {
|
|
1975
|
+
const response = await fetchWithTimeout(`${serverUrl}/system/status`, {}, 1e4);
|
|
1976
|
+
if (!response.ok) {
|
|
1977
|
+
throw new Error(`status probe failed with HTTP ${response.status}`);
|
|
1978
|
+
}
|
|
1979
|
+
const payload = await response.json().catch(() => ({}));
|
|
1980
|
+
const repoRoot = payload?.repo && typeof payload.repo.root === "string" ? payload.repo.root.trim() : "";
|
|
1981
|
+
if (!repoRoot) {
|
|
1982
|
+
throw new Error("server did not report repo.root in /system/status");
|
|
1983
|
+
}
|
|
1984
|
+
return repoRoot;
|
|
1985
|
+
}
|
|
1986
|
+
async function ensureServerRepoAffinity(serverUrl, currentRepoRoot) {
|
|
1987
|
+
const serverRepoRoot = await fetchServerRepoRoot(serverUrl);
|
|
1988
|
+
if (normalizeRepoPathForComparison(serverRepoRoot) === normalizeRepoPathForComparison(currentRepoRoot)) {
|
|
1989
|
+
return;
|
|
1990
|
+
}
|
|
1991
|
+
throw new Error(`repo mismatch: currentRepo=${currentRepoRoot} serverRepo=${serverRepoRoot}. Stop the existing runtime or switch to the matching repo.`);
|
|
1992
|
+
}
|
|
1993
|
+
function isRemoteBuddyClientRow(row) {
|
|
1994
|
+
const clientId = normalizePresenceLookupToken(row.clientId);
|
|
1995
|
+
const label = normalizePresenceLookupToken(row.label);
|
|
1996
|
+
return clientId.includes("remotebuddy") || label.includes("remotebuddy");
|
|
1997
|
+
}
|
|
1998
|
+
function extractRemoteBuddySessionConsumerHealth(statusPayload, sessionId) {
|
|
1999
|
+
const rows = Array.isArray(statusPayload?.clients?.items) ? statusPayload.clients?.items ?? [] : [];
|
|
2000
|
+
const sessionRows = rows.filter((row) => {
|
|
2001
|
+
if (!row || typeof row !== "object" || Array.isArray(row))
|
|
2002
|
+
return false;
|
|
2003
|
+
return String(row.sessionId ?? "").trim() === sessionId;
|
|
2004
|
+
});
|
|
2005
|
+
const remotebuddyRows = sessionRows.filter(isRemoteBuddyClientRow);
|
|
2006
|
+
const connectedRow = remotebuddyRows.find((row) => String(row.status ?? "").trim().toLowerCase() === "connected");
|
|
2007
|
+
if (connectedRow) {
|
|
2008
|
+
return {
|
|
2009
|
+
ok: true,
|
|
2010
|
+
detail: `RemoteBuddy session consumer connected (${String(connectedRow.clientId ?? "").trim()})`,
|
|
2011
|
+
clientId: String(connectedRow.clientId ?? "").trim() || undefined,
|
|
2012
|
+
sessionId
|
|
2013
|
+
};
|
|
2014
|
+
}
|
|
2015
|
+
const anyRemoteBuddyRows = rows.filter((row) => {
|
|
2016
|
+
if (!row || typeof row !== "object" || Array.isArray(row))
|
|
2017
|
+
return false;
|
|
2018
|
+
return isRemoteBuddyClientRow(row);
|
|
2019
|
+
});
|
|
2020
|
+
const connectedOtherSession = anyRemoteBuddyRows.find((row) => {
|
|
2021
|
+
const rowSessionId = String(row.sessionId ?? "").trim();
|
|
2022
|
+
if (!rowSessionId || rowSessionId === sessionId)
|
|
2023
|
+
return false;
|
|
2024
|
+
return String(row.status ?? "").trim().toLowerCase() === "connected";
|
|
2025
|
+
});
|
|
2026
|
+
if (connectedOtherSession) {
|
|
2027
|
+
const otherSessionId = String(connectedOtherSession.sessionId ?? "").trim();
|
|
2028
|
+
const otherClientId = String(connectedOtherSession.clientId ?? "").trim();
|
|
2029
|
+
return {
|
|
2030
|
+
ok: false,
|
|
2031
|
+
detail: `RemoteBuddy is connected to session ${otherSessionId || "unknown"} ` + `(${otherClientId || "unknown client"}), not ${sessionId}`,
|
|
2032
|
+
clientId: otherClientId || undefined,
|
|
2033
|
+
sessionId: otherSessionId || undefined
|
|
2034
|
+
};
|
|
2035
|
+
}
|
|
2036
|
+
if (remotebuddyRows.length > 0) {
|
|
2037
|
+
return {
|
|
2038
|
+
ok: false,
|
|
2039
|
+
detail: `RemoteBuddy session consumer exists for ${sessionId} but is not connected`,
|
|
2040
|
+
clientId: String(remotebuddyRows[0]?.clientId ?? "").trim() || undefined,
|
|
2041
|
+
sessionId
|
|
2042
|
+
};
|
|
2043
|
+
}
|
|
2044
|
+
if (anyRemoteBuddyRows.length > 0) {
|
|
2045
|
+
const knownSessions = [...new Set(anyRemoteBuddyRows.map((row) => String(row.sessionId ?? "").trim()))].filter(Boolean).sort();
|
|
2046
|
+
const suffix = knownSessions.length > 0 ? ` Known RemoteBuddy sessions: ${knownSessions.join(", ")}.` : "";
|
|
2047
|
+
return {
|
|
2048
|
+
ok: false,
|
|
2049
|
+
detail: `No connected RemoteBuddy session consumer found for session ${sessionId}.${suffix}`.trim()
|
|
2050
|
+
};
|
|
2051
|
+
}
|
|
2052
|
+
return {
|
|
2053
|
+
ok: false,
|
|
2054
|
+
detail: `No connected RemoteBuddy session consumer found for session ${sessionId}`
|
|
2055
|
+
};
|
|
2056
|
+
}
|
|
2057
|
+
async function probeRemoteBuddySessionConsumer(serverUrl, sessionId) {
|
|
2058
|
+
try {
|
|
2059
|
+
const response = await fetchWithTimeout(`${serverUrl}/system/status`, {}, 1e4);
|
|
2060
|
+
if (!response.ok) {
|
|
2061
|
+
return {
|
|
2062
|
+
ok: false,
|
|
2063
|
+
detail: `system status probe failed with HTTP ${response.status}`
|
|
2064
|
+
};
|
|
2065
|
+
}
|
|
2066
|
+
const payload = await response.json().catch(() => ({}));
|
|
2067
|
+
return extractRemoteBuddySessionConsumerHealth(payload, sessionId);
|
|
2068
|
+
} catch (err) {
|
|
2069
|
+
return {
|
|
2070
|
+
ok: false,
|
|
2071
|
+
detail: `system status probe failed: ${String(err)}`
|
|
2072
|
+
};
|
|
2073
|
+
}
|
|
2074
|
+
}
|
|
1863
2075
|
async function probeSourceControlManager(port) {
|
|
1864
2076
|
if (!Number.isFinite(port) || port <= 0)
|
|
1865
2077
|
return false;
|
|
@@ -1911,13 +2123,43 @@ function createRuntimeClientId(prefix) {
|
|
|
1911
2123
|
async function probeLocalBuddy(localAgentUrl) {
|
|
1912
2124
|
return await fetchJsonWithTimeout(`${localAgentUrl}/healthz`, {}, LOCALBUDDY_TIMEOUT_MS);
|
|
1913
2125
|
}
|
|
2126
|
+
function resolveCliLocalBuddyAutostart(runtimeOnly, runtimeConfigEnabled) {
|
|
2127
|
+
return runtimeOnly ? runtimeConfigEnabled : false;
|
|
2128
|
+
}
|
|
2129
|
+
async function ensureServerSession(serverUrl, requestedSessionId, client) {
|
|
2130
|
+
const response = await fetchWithTimeout(`${serverUrl}/sessions`, {
|
|
2131
|
+
method: "POST",
|
|
2132
|
+
headers: { "Content-Type": "application/json" },
|
|
2133
|
+
body: JSON.stringify({
|
|
2134
|
+
sessionId: requestedSessionId,
|
|
2135
|
+
client: {
|
|
2136
|
+
clientId: client.clientId,
|
|
2137
|
+
kind: client.kind,
|
|
2138
|
+
label: client.label,
|
|
2139
|
+
version: client.version,
|
|
2140
|
+
platform: client.platform,
|
|
2141
|
+
repoRoot: client.repoRoot
|
|
2142
|
+
}
|
|
2143
|
+
})
|
|
2144
|
+
}, 15000);
|
|
2145
|
+
if (!response.ok) {
|
|
2146
|
+
const detail = await response.text().catch(() => "");
|
|
2147
|
+
throw new Error(`Failed to create or join session ${requestedSessionId}: HTTP ${response.status}${detail ? ` ${detail}` : ""}`);
|
|
2148
|
+
}
|
|
2149
|
+
const payload = await response.json().catch(() => ({}));
|
|
2150
|
+
const sessionId = typeof payload.sessionId === "string" && payload.sessionId.trim() ? payload.sessionId.trim() : "";
|
|
2151
|
+
if (!sessionId) {
|
|
2152
|
+
throw new Error("Server session bootstrap returned no sessionId.");
|
|
2153
|
+
}
|
|
2154
|
+
return sessionId;
|
|
2155
|
+
}
|
|
1914
2156
|
async function autoStartRuntimeServices(opts) {
|
|
1915
2157
|
const { runtimePreflight } = opts.preparedRuntime;
|
|
1916
2158
|
const runtimeRoot = opts.preparedRuntime.runtimeRoot;
|
|
1917
2159
|
const runtimeTag = opts.preparedRuntime.runtimeTag || await resolveRuntimeReleaseTag(opts.requestedRuntimeTag);
|
|
1918
|
-
const
|
|
1919
|
-
const
|
|
1920
|
-
console.log(`[pushpals]
|
|
2160
|
+
const startLocalBuddy = opts.startLocalBuddy ?? Boolean(runtimePreflight.config.localbuddy.enabled);
|
|
2161
|
+
const localBuddyEnabled = startLocalBuddy;
|
|
2162
|
+
console.log(`[pushpals] Runtime unavailable. Auto-starting runtime for repo: ${opts.repoRoot}`);
|
|
1921
2163
|
console.log(`[pushpals] runtimeRoot=${runtimeRoot}`);
|
|
1922
2164
|
console.log(`[pushpals] runtimeTag=${runtimeTag}`);
|
|
1923
2165
|
if (!runtimePreflight.ok) {
|
|
@@ -1928,11 +2170,15 @@ async function autoStartRuntimeServices(opts) {
|
|
|
1928
2170
|
const runtimeEnv = buildEmbeddedRuntimeEnv(process.env, {
|
|
1929
2171
|
repoRoot: opts.repoRoot,
|
|
1930
2172
|
runtimeRoot,
|
|
1931
|
-
useRuntimeConfig: opts.preparedRuntime.preflightUsesEmbeddedRuntime
|
|
2173
|
+
useRuntimeConfig: opts.preparedRuntime.preflightUsesEmbeddedRuntime,
|
|
2174
|
+
sessionId: opts.sessionId
|
|
1932
2175
|
});
|
|
2176
|
+
if (runtimeEnv.PUSHPALS_GIT_BIN) {
|
|
2177
|
+
applyResolvedGitBinaryToRuntimeEnv(runtimeEnv, runtimeEnv.PUSHPALS_GIT_BIN);
|
|
2178
|
+
}
|
|
1933
2179
|
const resolvedGitBinary = await resolveCommandPath("git", opts.repoRoot, normalizeChildProcessEnv(process.env));
|
|
1934
2180
|
if (resolvedGitBinary) {
|
|
1935
|
-
runtimeEnv
|
|
2181
|
+
applyResolvedGitBinaryToRuntimeEnv(runtimeEnv, resolvedGitBinary);
|
|
1936
2182
|
}
|
|
1937
2183
|
const services = [];
|
|
1938
2184
|
const runToken = timestampFileToken();
|
|
@@ -1978,7 +2224,7 @@ ${tail}` : ""}`);
|
|
|
1978
2224
|
services.push(localbuddyService);
|
|
1979
2225
|
console.log(`[pushpals] localbuddy log: ${localbuddyService.logPath}`);
|
|
1980
2226
|
} else {
|
|
1981
|
-
console.log("[pushpals] Embedded LocalBuddy disabled
|
|
2227
|
+
console.log("[pushpals] Embedded LocalBuddy disabled for this CLI session; skipping start.");
|
|
1982
2228
|
}
|
|
1983
2229
|
console.log("[pushpals] Starting embedded RemoteBuddy...");
|
|
1984
2230
|
const remotebuddyService = spawnRuntimeService("remotebuddy", [runtimeBinaries.remotebuddy], opts.repoRoot, runtimeEnv, logPathFor("remotebuddy"));
|
|
@@ -2030,7 +2276,8 @@ ${tail}` : ""}`);
|
|
|
2030
2276
|
}
|
|
2031
2277
|
}
|
|
2032
2278
|
const health = localBuddyEnabled ? await probeLocalBuddy(opts.localAgentUrl) : null;
|
|
2033
|
-
|
|
2279
|
+
const remoteBuddyHealth2 = await probeRemoteBuddySessionConsumer(opts.serverUrl, opts.sessionId);
|
|
2280
|
+
if ((!localBuddyEnabled || health?.ok) && remoteBuddyHealth2.ok) {
|
|
2034
2281
|
const stabilityDeadline = Date.now() + DEFAULT_SERVICE_STABILITY_GRACE_MS;
|
|
2035
2282
|
while (Date.now() < stabilityDeadline) {
|
|
2036
2283
|
for (let i = services.length - 1;i >= 0; i--) {
|
|
@@ -2061,10 +2308,14 @@ ${tail}` : ""}`);
|
|
|
2061
2308
|
await Bun.sleep(DEFAULT_RUNTIME_BOOT_POLL_MS);
|
|
2062
2309
|
}
|
|
2063
2310
|
stopRuntimeServices(services);
|
|
2311
|
+
const remoteBuddyHealth = await probeRemoteBuddySessionConsumer(opts.serverUrl, opts.sessionId);
|
|
2312
|
+
if (!localBuddyEnabled && !remoteBuddyHealth.ok) {
|
|
2313
|
+
throw new Error(`Timed out waiting for RemoteBuddy session consumer readiness after ${DEFAULT_RUNTIME_BOOT_TIMEOUT_MS}ms (${remoteBuddyHealth.detail})`);
|
|
2314
|
+
}
|
|
2064
2315
|
if (!localBuddyEnabled) {
|
|
2065
2316
|
throw new Error(`Timed out waiting for embedded runtime readiness after ${DEFAULT_RUNTIME_BOOT_TIMEOUT_MS}ms`);
|
|
2066
2317
|
}
|
|
2067
|
-
throw new Error(`Timed out waiting for LocalBuddy at ${opts.localAgentUrl} after ${DEFAULT_RUNTIME_BOOT_TIMEOUT_MS}ms`);
|
|
2318
|
+
throw new Error(`Timed out waiting for LocalBuddy at ${opts.localAgentUrl} and RemoteBuddy session consumer after ${DEFAULT_RUNTIME_BOOT_TIMEOUT_MS}ms`);
|
|
2068
2319
|
}
|
|
2069
2320
|
function readCliState(pathValue) {
|
|
2070
2321
|
if (!existsSync4(pathValue))
|
|
@@ -2117,7 +2368,6 @@ async function looksLikeMonitoringHub(url) {
|
|
|
2117
2368
|
function buildMonitoringHubRuntimeBootstrap(opts) {
|
|
2118
2369
|
return {
|
|
2119
2370
|
serverUrl: opts.serverUrl,
|
|
2120
|
-
localAgentUrl: opts.localAgentUrl,
|
|
2121
2371
|
sessionId: opts.sessionId,
|
|
2122
2372
|
clientId: `cli-monitor-${opts.sessionId}`,
|
|
2123
2373
|
clientKind: "cli_monitor",
|
|
@@ -2202,7 +2452,10 @@ async function serveBundledMonitoringHub(assetRoot, pathname, bootstrap) {
|
|
|
2202
2452
|
});
|
|
2203
2453
|
}
|
|
2204
2454
|
function buildEmbeddedMonitoringHubHtml(opts) {
|
|
2205
|
-
const bootstrap = jsonHtmlBootstrap(
|
|
2455
|
+
const bootstrap = jsonHtmlBootstrap({
|
|
2456
|
+
serverUrl: opts.serverUrl,
|
|
2457
|
+
sessionId: opts.sessionId
|
|
2458
|
+
});
|
|
2206
2459
|
return `<!doctype html>
|
|
2207
2460
|
<html lang="en">
|
|
2208
2461
|
<head>
|
|
@@ -2298,7 +2551,6 @@ function buildEmbeddedMonitoringHubHtml(opts) {
|
|
|
2298
2551
|
cardsEl.innerHTML = cards.map((card) => '<div class="card"><div class="label">' + esc(card.label) + '</div><div class="value">' + esc(card.value) + '</div><div class="sub">' + esc(card.sub) + '</div></div>').join("");
|
|
2299
2552
|
metaEl.innerHTML = [
|
|
2300
2553
|
'<span class="pill">server ' + esc(boot.serverUrl) + '</span>',
|
|
2301
|
-
'<span class="pill">localbuddy ' + esc(boot.localAgentUrl) + '</span>',
|
|
2302
2554
|
'<span class="pill">session ' + esc(boot.sessionId) + '</span>',
|
|
2303
2555
|
'<span class="pill">repo ' + esc(repo?.root ?? repo?.remoteUrl ?? "current repo") + '</span>'
|
|
2304
2556
|
].join("");
|
|
@@ -2355,7 +2607,6 @@ async function startEmbeddedMonitoringHub(opts) {
|
|
|
2355
2607
|
}
|
|
2356
2608
|
const bootstrap = buildMonitoringHubRuntimeBootstrap({
|
|
2357
2609
|
serverUrl: opts.serverUrl,
|
|
2358
|
-
localAgentUrl: opts.localAgentUrl,
|
|
2359
2610
|
sessionId: opts.sessionId
|
|
2360
2611
|
});
|
|
2361
2612
|
const candidatePorts = Array.from({ length: MONITOR_SCAN_PORTS }, (_, index) => opts.preferredPort + index).concat(0);
|
|
@@ -2426,72 +2677,30 @@ async function resolveMonitoringHub(opts) {
|
|
|
2426
2677
|
}
|
|
2427
2678
|
return embedded;
|
|
2428
2679
|
}
|
|
2429
|
-
async function
|
|
2430
|
-
let response;
|
|
2680
|
+
async function sendMessageToServerSession(serverUrl, sessionId, text) {
|
|
2431
2681
|
try {
|
|
2432
|
-
response = await fetchWithTimeout(`${
|
|
2682
|
+
const response = await fetchWithTimeout(`${serverUrl}/sessions/${encodeURIComponent(sessionId)}/message`, {
|
|
2433
2683
|
method: "POST",
|
|
2434
2684
|
headers: { "Content-Type": "application/json" },
|
|
2435
2685
|
body: JSON.stringify({ text })
|
|
2436
|
-
},
|
|
2686
|
+
}, 15000);
|
|
2687
|
+
if (!response.ok) {
|
|
2688
|
+
const detail = await response.text().catch(() => "");
|
|
2689
|
+
console.error(`[pushpals] Session message rejected: HTTP ${response.status}${detail ? ` ${detail}` : ""}`);
|
|
2690
|
+
return false;
|
|
2691
|
+
}
|
|
2692
|
+
return true;
|
|
2437
2693
|
} catch (err) {
|
|
2438
|
-
console.error(`[pushpals] Failed to reach
|
|
2694
|
+
console.error(`[pushpals] Failed to reach server session endpoint: ${String(err)}`);
|
|
2439
2695
|
return false;
|
|
2440
2696
|
}
|
|
2441
|
-
if (!response.ok) {
|
|
2442
|
-
const detail = await response.text().catch(() => "");
|
|
2443
|
-
console.error(`[pushpals] LocalBuddy rejected message: HTTP ${response.status} ${detail}`);
|
|
2444
|
-
return false;
|
|
2445
|
-
}
|
|
2446
|
-
const reader = response.body?.getReader();
|
|
2447
|
-
if (!reader) {
|
|
2448
|
-
console.error("[pushpals] LocalBuddy response stream missing.");
|
|
2449
|
-
return false;
|
|
2450
|
-
}
|
|
2451
|
-
let buffer = "";
|
|
2452
|
-
const decoder = new TextDecoder;
|
|
2453
|
-
let complete = false;
|
|
2454
|
-
let ok = true;
|
|
2455
|
-
while (true) {
|
|
2456
|
-
const { done, value } = await reader.read();
|
|
2457
|
-
if (done)
|
|
2458
|
-
break;
|
|
2459
|
-
buffer += decoder.decode(value, { stream: true });
|
|
2460
|
-
const chunks = buffer.split(`
|
|
2461
|
-
|
|
2462
|
-
`);
|
|
2463
|
-
buffer = chunks.pop() ?? "";
|
|
2464
|
-
for (const chunk of chunks) {
|
|
2465
|
-
const dataLine = chunk.split(/\r?\n/).map((line) => line.trim()).find((line) => line.startsWith("data: "));
|
|
2466
|
-
if (!dataLine)
|
|
2467
|
-
continue;
|
|
2468
|
-
try {
|
|
2469
|
-
const payload = JSON.parse(dataLine.slice(6));
|
|
2470
|
-
const type = String(payload.type ?? "").trim().toLowerCase();
|
|
2471
|
-
const message = String(payload.message ?? "").trim();
|
|
2472
|
-
if (type === "status" && message) {
|
|
2473
|
-
console.log(`[localbuddy] ${message}`);
|
|
2474
|
-
} else if (type === "error") {
|
|
2475
|
-
ok = false;
|
|
2476
|
-
console.log(`[localbuddy] ERROR: ${message || "Unknown failure"}`);
|
|
2477
|
-
} else if (type === "complete") {
|
|
2478
|
-
complete = true;
|
|
2479
|
-
const requestId = payload.data && typeof payload.data.requestId === "string" ? payload.data.requestId : "";
|
|
2480
|
-
if (requestId) {
|
|
2481
|
-
console.log(`[localbuddy] requestId=${requestId}`);
|
|
2482
|
-
} else if (message) {
|
|
2483
|
-
console.log(`[localbuddy] ${message}`);
|
|
2484
|
-
}
|
|
2485
|
-
}
|
|
2486
|
-
} catch {}
|
|
2487
|
-
}
|
|
2488
|
-
}
|
|
2489
|
-
return ok && complete;
|
|
2490
2697
|
}
|
|
2491
2698
|
function formatSessionEventLine(event) {
|
|
2492
2699
|
const type = String(event.type ?? "").toLowerCase();
|
|
2493
2700
|
const from = String(event.from ?? "");
|
|
2494
2701
|
const payload = event.payload ?? {};
|
|
2702
|
+
if (!shouldDisplayInteractiveSessionEvent(event))
|
|
2703
|
+
return null;
|
|
2495
2704
|
if (type === "message")
|
|
2496
2705
|
return null;
|
|
2497
2706
|
if (type === "assistant_message") {
|
|
@@ -2653,6 +2862,15 @@ async function main() {
|
|
|
2653
2862
|
const serverUrl = normalizeLoopbackUrl(parsed.serverUrl ?? process.env.PUSHPALS_SERVER_URL, config.server.url);
|
|
2654
2863
|
const localAgentUrl = normalizeLoopbackUrl(parsed.localAgentUrl ?? process.env.EXPO_PUBLIC_LOCAL_AGENT_URL, config.client.localAgentUrl);
|
|
2655
2864
|
const sessionId = String(parsed.sessionId ?? process.env.PUSHPALS_SESSION_ID ?? config.sessionId).trim();
|
|
2865
|
+
const cliVersion = String(process.env.PUSHPALS_CLI_PACKAGE_VERSION ?? "").trim() || "unknown";
|
|
2866
|
+
const cliClient = {
|
|
2867
|
+
clientId: createRuntimeClientId("cli"),
|
|
2868
|
+
kind: "cli",
|
|
2869
|
+
label: "CLI",
|
|
2870
|
+
version: cliVersion,
|
|
2871
|
+
platform: `${process.platform}/${process.arch}`,
|
|
2872
|
+
repoRoot
|
|
2873
|
+
};
|
|
2656
2874
|
let autoStartedServices = [];
|
|
2657
2875
|
const stopAutoStartedServices = () => {
|
|
2658
2876
|
if (autoStartedServices.length === 0)
|
|
@@ -2661,66 +2879,76 @@ async function main() {
|
|
|
2661
2879
|
autoStartedServices = [];
|
|
2662
2880
|
};
|
|
2663
2881
|
let serverHealthy = await probeServer(serverUrl);
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2882
|
+
const serverWasAlreadyHealthy = serverHealthy;
|
|
2883
|
+
let remoteBuddyConsumerHealth = {
|
|
2884
|
+
ok: false,
|
|
2885
|
+
detail: `No connected RemoteBuddy session consumer found for session ${sessionId}`
|
|
2886
|
+
};
|
|
2887
|
+
if (!serverHealthy) {
|
|
2888
|
+
if (!parsed.noAutoStart) {
|
|
2889
|
+
try {
|
|
2890
|
+
autoStartedServices = await autoStartRuntimeServices({
|
|
2891
|
+
repoRoot,
|
|
2892
|
+
serverUrl,
|
|
2893
|
+
localAgentUrl,
|
|
2894
|
+
sessionId,
|
|
2895
|
+
sourceControlManagerPort: config.sourceControlManager.port,
|
|
2896
|
+
sourceControlManagerRemote: config.sourceControlManager.remote,
|
|
2897
|
+
preparedRuntime,
|
|
2898
|
+
requestedRuntimeTag: parsed.runtimeTag,
|
|
2899
|
+
startLocalBuddy: resolveCliLocalBuddyAutostart(parsed.runtimeOnly, Boolean(config.localbuddy.enabled))
|
|
2900
|
+
});
|
|
2901
|
+
serverHealthy = await probeServer(serverUrl);
|
|
2902
|
+
} catch (err) {
|
|
2903
|
+
console.error(`[pushpals] Auto-start failed: ${String(err)}`);
|
|
2904
|
+
stopAutoStartedServices();
|
|
2905
|
+
}
|
|
2906
|
+
}
|
|
2907
|
+
if (!serverHealthy) {
|
|
2908
|
+
console.error(`[pushpals] Server is unavailable at ${serverUrl}.`);
|
|
2909
|
+
if (parsed.noAutoStart) {
|
|
2910
|
+
console.error("[pushpals] Auto-start is disabled (--no-auto-start).");
|
|
2911
|
+
} else {
|
|
2912
|
+
console.error("[pushpals] Auto-start could not bring the embedded runtime online.");
|
|
2913
|
+
}
|
|
2914
|
+
process.exit(1);
|
|
2915
|
+
}
|
|
2916
|
+
}
|
|
2917
|
+
try {
|
|
2918
|
+
await ensureServerRepoAffinity(serverUrl, repoRoot);
|
|
2919
|
+
} catch (err) {
|
|
2920
|
+
stopAutoStartedServices();
|
|
2921
|
+
console.error(`[pushpals] Repo affinity check failed: ${String(err)}`);
|
|
2922
|
+
process.exit(1);
|
|
2923
|
+
}
|
|
2924
|
+
let activeSessionId = sessionId;
|
|
2925
|
+
if (!parsed.runtimeOnly) {
|
|
2667
2926
|
try {
|
|
2668
|
-
|
|
2669
|
-
repoRoot,
|
|
2670
|
-
serverUrl,
|
|
2671
|
-
localAgentUrl,
|
|
2672
|
-
sourceControlManagerPort: config.sourceControlManager.port,
|
|
2673
|
-
sourceControlManagerRemote: config.sourceControlManager.remote,
|
|
2674
|
-
preparedRuntime,
|
|
2675
|
-
requestedRuntimeTag: parsed.runtimeTag,
|
|
2676
|
-
requireLocalBuddy: !parsed.runtimeOnly
|
|
2677
|
-
});
|
|
2678
|
-
serverHealthy = await probeServer(serverUrl);
|
|
2679
|
-
health = await probeLocalBuddy(localAgentUrl);
|
|
2927
|
+
activeSessionId = await ensureServerSession(serverUrl, sessionId, cliClient);
|
|
2680
2928
|
} catch (err) {
|
|
2681
|
-
console.error(`[pushpals] Auto-start failed: ${String(err)}`);
|
|
2682
2929
|
stopAutoStartedServices();
|
|
2930
|
+
console.error(`[pushpals] Session bootstrap failed: ${String(err)}`);
|
|
2931
|
+
process.exit(1);
|
|
2683
2932
|
}
|
|
2684
2933
|
}
|
|
2685
|
-
|
|
2934
|
+
remoteBuddyConsumerHealth = await probeRemoteBuddySessionConsumer(serverUrl, activeSessionId);
|
|
2935
|
+
if (!serverHealthy) {
|
|
2686
2936
|
console.error(`[pushpals] Server is unavailable at ${serverUrl}.`);
|
|
2687
|
-
if (parsed.noAutoStart) {
|
|
2688
|
-
console.error("[pushpals] Auto-start is disabled (--no-auto-start).");
|
|
2689
|
-
} else {
|
|
2690
|
-
console.error("[pushpals] Auto-start could not bring the embedded runtime online.");
|
|
2691
|
-
}
|
|
2692
2937
|
process.exit(1);
|
|
2693
2938
|
}
|
|
2694
|
-
if (!
|
|
2695
|
-
|
|
2696
|
-
|
|
2939
|
+
if (!remoteBuddyConsumerHealth.ok) {
|
|
2940
|
+
stopAutoStartedServices();
|
|
2941
|
+
console.error(`[pushpals] RemoteBuddy is not ready for session ${activeSessionId}: ${remoteBuddyConsumerHealth.detail}`);
|
|
2942
|
+
if (serverWasAlreadyHealthy) {
|
|
2943
|
+
console.error("[pushpals] A PushPals runtime is already serving this repo, but it does not have a connected RemoteBuddy consumer for this session.");
|
|
2944
|
+
console.error("[pushpals] Refusing to start another embedded RemoteBuddy against the same runtime. Restart or stop the existing runtime before retrying.");
|
|
2945
|
+
} else if (parsed.noAutoStart) {
|
|
2697
2946
|
console.error("[pushpals] Auto-start is disabled (--no-auto-start).");
|
|
2698
2947
|
} else {
|
|
2699
|
-
console.error("[pushpals] Auto-start could not bring
|
|
2948
|
+
console.error("[pushpals] Auto-start could not bring the embedded runtime into a usable state.");
|
|
2700
2949
|
}
|
|
2701
2950
|
process.exit(1);
|
|
2702
2951
|
}
|
|
2703
|
-
let localBuddySessionId = sessionId;
|
|
2704
|
-
if (!parsed.runtimeOnly) {
|
|
2705
|
-
const localBuddyRepo = health?.repo ? resolve4(health.repo) : "";
|
|
2706
|
-
if (!localBuddyRepo) {
|
|
2707
|
-
stopAutoStartedServices();
|
|
2708
|
-
console.error("[pushpals] LocalBuddy health response did not include repo path.");
|
|
2709
|
-
process.exit(1);
|
|
2710
|
-
}
|
|
2711
|
-
if (normalizePath(localBuddyRepo) !== normalizePath(repoRoot)) {
|
|
2712
|
-
stopAutoStartedServices();
|
|
2713
|
-
console.error("[pushpals] Repo mismatch detected.");
|
|
2714
|
-
console.error(`[pushpals] currentRepo=${repoRoot}`);
|
|
2715
|
-
console.error(`[pushpals] localBuddyRepo=${localBuddyRepo}`);
|
|
2716
|
-
console.error("[pushpals] LocalBuddy must run against the same repo. Start PushPals from this repo and retry.");
|
|
2717
|
-
process.exit(1);
|
|
2718
|
-
}
|
|
2719
|
-
localBuddySessionId = health?.sessionId && String(health.sessionId).trim() ? String(health.sessionId).trim() : sessionId;
|
|
2720
|
-
if (sessionId && sessionId !== localBuddySessionId) {
|
|
2721
|
-
console.warn(`[pushpals] Requested sessionId=${sessionId}, but LocalBuddy is currently attached to sessionId=${localBuddySessionId}.`);
|
|
2722
|
-
}
|
|
2723
|
-
}
|
|
2724
2952
|
const statePath = resolveCliStatePath(repoRoot);
|
|
2725
2953
|
const saved = statePath ? readCliState(statePath) : {};
|
|
2726
2954
|
const preferredHubUrl = normalizeUrl(parsed.monitoringHubUrl ?? process.env.PUSHPALS_MONITOR_URL ?? saved.monitoringHubUrl ?? "");
|
|
@@ -2729,8 +2957,7 @@ async function main() {
|
|
|
2729
2957
|
preferredUrl: preferredHubUrl,
|
|
2730
2958
|
fallbackPort: monitorPort,
|
|
2731
2959
|
serverUrl,
|
|
2732
|
-
|
|
2733
|
-
sessionId: localBuddySessionId
|
|
2960
|
+
sessionId: activeSessionId
|
|
2734
2961
|
});
|
|
2735
2962
|
const monitoringHubUrl = monitoringHub?.url ?? "";
|
|
2736
2963
|
if (statePath) {
|
|
@@ -2738,7 +2965,7 @@ async function main() {
|
|
|
2738
2965
|
monitoringHubUrl: monitoringHubUrl || undefined,
|
|
2739
2966
|
serverUrl,
|
|
2740
2967
|
localAgentUrl,
|
|
2741
|
-
sessionId:
|
|
2968
|
+
sessionId: activeSessionId,
|
|
2742
2969
|
repoRoot
|
|
2743
2970
|
});
|
|
2744
2971
|
} else {
|
|
@@ -2754,8 +2981,7 @@ async function main() {
|
|
|
2754
2981
|
console.log("[pushpals] monitoringHubUrl=unavailable");
|
|
2755
2982
|
}
|
|
2756
2983
|
console.log(`[pushpals] serverUrl=${serverUrl}`);
|
|
2757
|
-
console.log(`[pushpals]
|
|
2758
|
-
console.log(`[pushpals] sessionId=${localBuddySessionId}`);
|
|
2984
|
+
console.log(`[pushpals] sessionId=${activeSessionId}`);
|
|
2759
2985
|
console.log(`[pushpals] repoRoot=${repoRoot}`);
|
|
2760
2986
|
console.log(`[pushpals] cliStateFile=${statePath ?? "unavailable"}`);
|
|
2761
2987
|
if (parsed.runtimeOnly) {
|
|
@@ -2763,15 +2989,6 @@ async function main() {
|
|
|
2763
2989
|
} else {
|
|
2764
2990
|
console.log("[pushpals] Type a message and press Enter. Use /exit or exit to quit.");
|
|
2765
2991
|
}
|
|
2766
|
-
const cliVersion = String(process.env.PUSHPALS_CLI_PACKAGE_VERSION ?? "").trim() || "unknown";
|
|
2767
|
-
const cliClient = {
|
|
2768
|
-
clientId: createRuntimeClientId("cli"),
|
|
2769
|
-
kind: "cli",
|
|
2770
|
-
label: "CLI",
|
|
2771
|
-
version: cliVersion,
|
|
2772
|
-
platform: `${process.platform}/${process.arch}`,
|
|
2773
|
-
repoRoot
|
|
2774
|
-
};
|
|
2775
2992
|
const streamAbort = new AbortController;
|
|
2776
2993
|
let rl = null;
|
|
2777
2994
|
const printIncoming = (line) => {
|
|
@@ -2786,7 +3003,7 @@ ${line}
|
|
|
2786
3003
|
}
|
|
2787
3004
|
console.log(line);
|
|
2788
3005
|
};
|
|
2789
|
-
const streamTask = parsed.noStream ? Promise.resolve() : parsed.runtimeOnly ? Promise.resolve() : runSessionStream(serverUrl,
|
|
3006
|
+
const streamTask = parsed.noStream ? Promise.resolve() : parsed.runtimeOnly ? Promise.resolve() : runSessionStream(serverUrl, activeSessionId, cliClient, printIncoming, streamAbort.signal);
|
|
2790
3007
|
let shuttingDown = false;
|
|
2791
3008
|
const requestStop = () => {
|
|
2792
3009
|
if (shuttingDown)
|
|
@@ -2864,8 +3081,7 @@ ${line}
|
|
|
2864
3081
|
}
|
|
2865
3082
|
if (text === "/status") {
|
|
2866
3083
|
console.log(`[pushpals] serverUrl=${serverUrl}`);
|
|
2867
|
-
console.log(`[pushpals]
|
|
2868
|
-
console.log(`[pushpals] sessionId=${localBuddySessionId}`);
|
|
3084
|
+
console.log(`[pushpals] sessionId=${activeSessionId}`);
|
|
2869
3085
|
console.log(`[pushpals] repoRoot=${repoRoot}`);
|
|
2870
3086
|
console.log(monitoringHubUrl ? `[pushpals] monitoringHubUrl=${monitoringHubUrl}` : "[pushpals] monitoringHubUrl=unavailable");
|
|
2871
3087
|
rl.prompt();
|
|
@@ -2882,7 +3098,13 @@ ${line}
|
|
|
2882
3098
|
rl.prompt();
|
|
2883
3099
|
continue;
|
|
2884
3100
|
}
|
|
2885
|
-
const
|
|
3101
|
+
const normalized = normalizeCliInteractiveMessage(text);
|
|
3102
|
+
if (normalized.usageMessage) {
|
|
3103
|
+
console.log(`[pushpals] ${normalized.usageMessage}`);
|
|
3104
|
+
rl.prompt();
|
|
3105
|
+
continue;
|
|
3106
|
+
}
|
|
3107
|
+
const ok = await sendMessageToServerSession(serverUrl, activeSessionId, normalized.text);
|
|
2886
3108
|
if (!ok) {
|
|
2887
3109
|
console.log("[pushpals] Message failed.");
|
|
2888
3110
|
}
|
|
@@ -2901,15 +3123,22 @@ export {
|
|
|
2901
3123
|
startEmbeddedMonitoringHub,
|
|
2902
3124
|
resolveCommandPath,
|
|
2903
3125
|
resolveCliStatePath,
|
|
3126
|
+
resolveCliLocalBuddyAutostart,
|
|
2904
3127
|
resolveBundledRuntimeAssetSource,
|
|
2905
3128
|
resolveBundledMonitoringHubRoot,
|
|
2906
3129
|
prepareCliRuntime,
|
|
3130
|
+
normalizeRepoPathForComparison,
|
|
3131
|
+
normalizeCliInteractiveMessage,
|
|
2907
3132
|
normalizeChildProcessEnv,
|
|
2908
3133
|
isCliExitCommand,
|
|
2909
3134
|
injectMonitoringHubBootstrap,
|
|
2910
3135
|
formatTimestampedCliLine,
|
|
3136
|
+
formatSessionEventLine,
|
|
3137
|
+
extractRemoteBuddySessionConsumerHealth,
|
|
3138
|
+
bundledMonitoringHubNeedsRefresh,
|
|
2911
3139
|
buildServiceStopCommand,
|
|
2912
3140
|
buildOpenMonitoringHubCommand,
|
|
2913
3141
|
buildEmbeddedRuntimeEnv,
|
|
2914
|
-
buildEmbeddedMonitoringHubHtml
|
|
3142
|
+
buildEmbeddedMonitoringHubHtml,
|
|
3143
|
+
applyResolvedGitBinaryToRuntimeEnv
|
|
2915
3144
|
};
|