@pushpalsdev/cli 1.0.11 → 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 +339 -147
- 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,7 +7,9 @@ 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";
|
|
@@ -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,7 +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"),
|
|
1640
|
-
...opts.
|
|
1718
|
+
...typeof opts.sessionId === "string" && opts.sessionId.trim() ? { PUSHPALS_SESSION_ID: opts.sessionId.trim() } : {},
|
|
1641
1719
|
...typeof env.PUSHPALS_GIT_BIN === "string" && env.PUSHPALS_GIT_BIN.trim() ? { PUSHPALS_GIT_BIN: env.PUSHPALS_GIT_BIN.trim() } : {}
|
|
1642
1720
|
};
|
|
1643
1721
|
}
|
|
@@ -1889,6 +1967,111 @@ async function probeServer(serverUrl) {
|
|
|
1889
1967
|
return false;
|
|
1890
1968
|
}
|
|
1891
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
|
+
}
|
|
1892
2075
|
async function probeSourceControlManager(port) {
|
|
1893
2076
|
if (!Number.isFinite(port) || port <= 0)
|
|
1894
2077
|
return false;
|
|
@@ -1940,13 +2123,43 @@ function createRuntimeClientId(prefix) {
|
|
|
1940
2123
|
async function probeLocalBuddy(localAgentUrl) {
|
|
1941
2124
|
return await fetchJsonWithTimeout(`${localAgentUrl}/healthz`, {}, LOCALBUDDY_TIMEOUT_MS);
|
|
1942
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
|
+
}
|
|
1943
2156
|
async function autoStartRuntimeServices(opts) {
|
|
1944
2157
|
const { runtimePreflight } = opts.preparedRuntime;
|
|
1945
2158
|
const runtimeRoot = opts.preparedRuntime.runtimeRoot;
|
|
1946
2159
|
const runtimeTag = opts.preparedRuntime.runtimeTag || await resolveRuntimeReleaseTag(opts.requestedRuntimeTag);
|
|
1947
|
-
const
|
|
1948
|
-
const localBuddyEnabled =
|
|
1949
|
-
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}`);
|
|
1950
2163
|
console.log(`[pushpals] runtimeRoot=${runtimeRoot}`);
|
|
1951
2164
|
console.log(`[pushpals] runtimeTag=${runtimeTag}`);
|
|
1952
2165
|
if (!runtimePreflight.ok) {
|
|
@@ -1958,7 +2171,7 @@ async function autoStartRuntimeServices(opts) {
|
|
|
1958
2171
|
repoRoot: opts.repoRoot,
|
|
1959
2172
|
runtimeRoot,
|
|
1960
2173
|
useRuntimeConfig: opts.preparedRuntime.preflightUsesEmbeddedRuntime,
|
|
1961
|
-
|
|
2174
|
+
sessionId: opts.sessionId
|
|
1962
2175
|
});
|
|
1963
2176
|
if (runtimeEnv.PUSHPALS_GIT_BIN) {
|
|
1964
2177
|
applyResolvedGitBinaryToRuntimeEnv(runtimeEnv, runtimeEnv.PUSHPALS_GIT_BIN);
|
|
@@ -2006,15 +2219,12 @@ ${tail}` : ""}`);
|
|
|
2006
2219
|
console.log("[pushpals] Server already healthy; skipping embedded server start.");
|
|
2007
2220
|
}
|
|
2008
2221
|
if (localBuddyEnabled) {
|
|
2009
|
-
if (requireLocalBuddy && !runtimePreflight.config.localbuddy.enabled) {
|
|
2010
|
-
console.log("[pushpals] LocalBuddy is disabled in config; forcing it on for this CLI session.");
|
|
2011
|
-
}
|
|
2012
2222
|
console.log("[pushpals] Starting embedded LocalBuddy...");
|
|
2013
2223
|
const localbuddyService = spawnRuntimeService("localbuddy", [runtimeBinaries.localbuddy], opts.repoRoot, runtimeEnv, logPathFor("localbuddy"));
|
|
2014
2224
|
services.push(localbuddyService);
|
|
2015
2225
|
console.log(`[pushpals] localbuddy log: ${localbuddyService.logPath}`);
|
|
2016
2226
|
} else {
|
|
2017
|
-
console.log("[pushpals] Embedded LocalBuddy disabled
|
|
2227
|
+
console.log("[pushpals] Embedded LocalBuddy disabled for this CLI session; skipping start.");
|
|
2018
2228
|
}
|
|
2019
2229
|
console.log("[pushpals] Starting embedded RemoteBuddy...");
|
|
2020
2230
|
const remotebuddyService = spawnRuntimeService("remotebuddy", [runtimeBinaries.remotebuddy], opts.repoRoot, runtimeEnv, logPathFor("remotebuddy"));
|
|
@@ -2066,7 +2276,8 @@ ${tail}` : ""}`);
|
|
|
2066
2276
|
}
|
|
2067
2277
|
}
|
|
2068
2278
|
const health = localBuddyEnabled ? await probeLocalBuddy(opts.localAgentUrl) : null;
|
|
2069
|
-
|
|
2279
|
+
const remoteBuddyHealth2 = await probeRemoteBuddySessionConsumer(opts.serverUrl, opts.sessionId);
|
|
2280
|
+
if ((!localBuddyEnabled || health?.ok) && remoteBuddyHealth2.ok) {
|
|
2070
2281
|
const stabilityDeadline = Date.now() + DEFAULT_SERVICE_STABILITY_GRACE_MS;
|
|
2071
2282
|
while (Date.now() < stabilityDeadline) {
|
|
2072
2283
|
for (let i = services.length - 1;i >= 0; i--) {
|
|
@@ -2097,10 +2308,14 @@ ${tail}` : ""}`);
|
|
|
2097
2308
|
await Bun.sleep(DEFAULT_RUNTIME_BOOT_POLL_MS);
|
|
2098
2309
|
}
|
|
2099
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
|
+
}
|
|
2100
2315
|
if (!localBuddyEnabled) {
|
|
2101
2316
|
throw new Error(`Timed out waiting for embedded runtime readiness after ${DEFAULT_RUNTIME_BOOT_TIMEOUT_MS}ms`);
|
|
2102
2317
|
}
|
|
2103
|
-
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`);
|
|
2104
2319
|
}
|
|
2105
2320
|
function readCliState(pathValue) {
|
|
2106
2321
|
if (!existsSync4(pathValue))
|
|
@@ -2153,7 +2368,6 @@ async function looksLikeMonitoringHub(url) {
|
|
|
2153
2368
|
function buildMonitoringHubRuntimeBootstrap(opts) {
|
|
2154
2369
|
return {
|
|
2155
2370
|
serverUrl: opts.serverUrl,
|
|
2156
|
-
localAgentUrl: opts.localAgentUrl,
|
|
2157
2371
|
sessionId: opts.sessionId,
|
|
2158
2372
|
clientId: `cli-monitor-${opts.sessionId}`,
|
|
2159
2373
|
clientKind: "cli_monitor",
|
|
@@ -2238,7 +2452,10 @@ async function serveBundledMonitoringHub(assetRoot, pathname, bootstrap) {
|
|
|
2238
2452
|
});
|
|
2239
2453
|
}
|
|
2240
2454
|
function buildEmbeddedMonitoringHubHtml(opts) {
|
|
2241
|
-
const bootstrap = jsonHtmlBootstrap(
|
|
2455
|
+
const bootstrap = jsonHtmlBootstrap({
|
|
2456
|
+
serverUrl: opts.serverUrl,
|
|
2457
|
+
sessionId: opts.sessionId
|
|
2458
|
+
});
|
|
2242
2459
|
return `<!doctype html>
|
|
2243
2460
|
<html lang="en">
|
|
2244
2461
|
<head>
|
|
@@ -2334,7 +2551,6 @@ function buildEmbeddedMonitoringHubHtml(opts) {
|
|
|
2334
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("");
|
|
2335
2552
|
metaEl.innerHTML = [
|
|
2336
2553
|
'<span class="pill">server ' + esc(boot.serverUrl) + '</span>',
|
|
2337
|
-
'<span class="pill">localbuddy ' + esc(boot.localAgentUrl) + '</span>',
|
|
2338
2554
|
'<span class="pill">session ' + esc(boot.sessionId) + '</span>',
|
|
2339
2555
|
'<span class="pill">repo ' + esc(repo?.root ?? repo?.remoteUrl ?? "current repo") + '</span>'
|
|
2340
2556
|
].join("");
|
|
@@ -2391,7 +2607,6 @@ async function startEmbeddedMonitoringHub(opts) {
|
|
|
2391
2607
|
}
|
|
2392
2608
|
const bootstrap = buildMonitoringHubRuntimeBootstrap({
|
|
2393
2609
|
serverUrl: opts.serverUrl,
|
|
2394
|
-
localAgentUrl: opts.localAgentUrl,
|
|
2395
2610
|
sessionId: opts.sessionId
|
|
2396
2611
|
});
|
|
2397
2612
|
const candidatePorts = Array.from({ length: MONITOR_SCAN_PORTS }, (_, index) => opts.preferredPort + index).concat(0);
|
|
@@ -2462,72 +2677,30 @@ async function resolveMonitoringHub(opts) {
|
|
|
2462
2677
|
}
|
|
2463
2678
|
return embedded;
|
|
2464
2679
|
}
|
|
2465
|
-
async function
|
|
2466
|
-
let response;
|
|
2680
|
+
async function sendMessageToServerSession(serverUrl, sessionId, text) {
|
|
2467
2681
|
try {
|
|
2468
|
-
response = await fetchWithTimeout(`${
|
|
2682
|
+
const response = await fetchWithTimeout(`${serverUrl}/sessions/${encodeURIComponent(sessionId)}/message`, {
|
|
2469
2683
|
method: "POST",
|
|
2470
2684
|
headers: { "Content-Type": "application/json" },
|
|
2471
2685
|
body: JSON.stringify({ text })
|
|
2472
|
-
},
|
|
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;
|
|
2473
2693
|
} catch (err) {
|
|
2474
|
-
console.error(`[pushpals] Failed to reach
|
|
2475
|
-
return false;
|
|
2476
|
-
}
|
|
2477
|
-
if (!response.ok) {
|
|
2478
|
-
const detail = await response.text().catch(() => "");
|
|
2479
|
-
console.error(`[pushpals] LocalBuddy rejected message: HTTP ${response.status} ${detail}`);
|
|
2480
|
-
return false;
|
|
2481
|
-
}
|
|
2482
|
-
const reader = response.body?.getReader();
|
|
2483
|
-
if (!reader) {
|
|
2484
|
-
console.error("[pushpals] LocalBuddy response stream missing.");
|
|
2694
|
+
console.error(`[pushpals] Failed to reach server session endpoint: ${String(err)}`);
|
|
2485
2695
|
return false;
|
|
2486
2696
|
}
|
|
2487
|
-
let buffer = "";
|
|
2488
|
-
const decoder = new TextDecoder;
|
|
2489
|
-
let complete = false;
|
|
2490
|
-
let ok = true;
|
|
2491
|
-
while (true) {
|
|
2492
|
-
const { done, value } = await reader.read();
|
|
2493
|
-
if (done)
|
|
2494
|
-
break;
|
|
2495
|
-
buffer += decoder.decode(value, { stream: true });
|
|
2496
|
-
const chunks = buffer.split(`
|
|
2497
|
-
|
|
2498
|
-
`);
|
|
2499
|
-
buffer = chunks.pop() ?? "";
|
|
2500
|
-
for (const chunk of chunks) {
|
|
2501
|
-
const dataLine = chunk.split(/\r?\n/).map((line) => line.trim()).find((line) => line.startsWith("data: "));
|
|
2502
|
-
if (!dataLine)
|
|
2503
|
-
continue;
|
|
2504
|
-
try {
|
|
2505
|
-
const payload = JSON.parse(dataLine.slice(6));
|
|
2506
|
-
const type = String(payload.type ?? "").trim().toLowerCase();
|
|
2507
|
-
const message = String(payload.message ?? "").trim();
|
|
2508
|
-
if (type === "status" && message) {
|
|
2509
|
-
console.log(`[localbuddy] ${message}`);
|
|
2510
|
-
} else if (type === "error") {
|
|
2511
|
-
ok = false;
|
|
2512
|
-
console.log(`[localbuddy] ERROR: ${message || "Unknown failure"}`);
|
|
2513
|
-
} else if (type === "complete") {
|
|
2514
|
-
complete = true;
|
|
2515
|
-
const requestId = payload.data && typeof payload.data.requestId === "string" ? payload.data.requestId : "";
|
|
2516
|
-
if (requestId) {
|
|
2517
|
-
console.log(`[localbuddy] requestId=${requestId}`);
|
|
2518
|
-
} else if (message) {
|
|
2519
|
-
console.log(`[localbuddy] ${message}`);
|
|
2520
|
-
}
|
|
2521
|
-
}
|
|
2522
|
-
} catch {}
|
|
2523
|
-
}
|
|
2524
|
-
}
|
|
2525
|
-
return ok && complete;
|
|
2526
2697
|
}
|
|
2527
2698
|
function formatSessionEventLine(event) {
|
|
2528
2699
|
const type = String(event.type ?? "").toLowerCase();
|
|
2529
2700
|
const from = String(event.from ?? "");
|
|
2530
2701
|
const payload = event.payload ?? {};
|
|
2702
|
+
if (!shouldDisplayInteractiveSessionEvent(event))
|
|
2703
|
+
return null;
|
|
2531
2704
|
if (type === "message")
|
|
2532
2705
|
return null;
|
|
2533
2706
|
if (type === "assistant_message") {
|
|
@@ -2689,6 +2862,15 @@ async function main() {
|
|
|
2689
2862
|
const serverUrl = normalizeLoopbackUrl(parsed.serverUrl ?? process.env.PUSHPALS_SERVER_URL, config.server.url);
|
|
2690
2863
|
const localAgentUrl = normalizeLoopbackUrl(parsed.localAgentUrl ?? process.env.EXPO_PUBLIC_LOCAL_AGENT_URL, config.client.localAgentUrl);
|
|
2691
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
|
+
};
|
|
2692
2874
|
let autoStartedServices = [];
|
|
2693
2875
|
const stopAutoStartedServices = () => {
|
|
2694
2876
|
if (autoStartedServices.length === 0)
|
|
@@ -2697,66 +2879,76 @@ async function main() {
|
|
|
2697
2879
|
autoStartedServices = [];
|
|
2698
2880
|
};
|
|
2699
2881
|
let serverHealthy = await probeServer(serverUrl);
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
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) {
|
|
2703
2926
|
try {
|
|
2704
|
-
|
|
2705
|
-
repoRoot,
|
|
2706
|
-
serverUrl,
|
|
2707
|
-
localAgentUrl,
|
|
2708
|
-
sourceControlManagerPort: config.sourceControlManager.port,
|
|
2709
|
-
sourceControlManagerRemote: config.sourceControlManager.remote,
|
|
2710
|
-
preparedRuntime,
|
|
2711
|
-
requestedRuntimeTag: parsed.runtimeTag,
|
|
2712
|
-
requireLocalBuddy: !parsed.runtimeOnly
|
|
2713
|
-
});
|
|
2714
|
-
serverHealthy = await probeServer(serverUrl);
|
|
2715
|
-
health = await probeLocalBuddy(localAgentUrl);
|
|
2927
|
+
activeSessionId = await ensureServerSession(serverUrl, sessionId, cliClient);
|
|
2716
2928
|
} catch (err) {
|
|
2717
|
-
console.error(`[pushpals] Auto-start failed: ${String(err)}`);
|
|
2718
2929
|
stopAutoStartedServices();
|
|
2930
|
+
console.error(`[pushpals] Session bootstrap failed: ${String(err)}`);
|
|
2931
|
+
process.exit(1);
|
|
2719
2932
|
}
|
|
2720
2933
|
}
|
|
2721
|
-
|
|
2934
|
+
remoteBuddyConsumerHealth = await probeRemoteBuddySessionConsumer(serverUrl, activeSessionId);
|
|
2935
|
+
if (!serverHealthy) {
|
|
2722
2936
|
console.error(`[pushpals] Server is unavailable at ${serverUrl}.`);
|
|
2723
|
-
if (parsed.noAutoStart) {
|
|
2724
|
-
console.error("[pushpals] Auto-start is disabled (--no-auto-start).");
|
|
2725
|
-
} else {
|
|
2726
|
-
console.error("[pushpals] Auto-start could not bring the embedded runtime online.");
|
|
2727
|
-
}
|
|
2728
2937
|
process.exit(1);
|
|
2729
2938
|
}
|
|
2730
|
-
if (!
|
|
2731
|
-
|
|
2732
|
-
|
|
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) {
|
|
2733
2946
|
console.error("[pushpals] Auto-start is disabled (--no-auto-start).");
|
|
2734
2947
|
} else {
|
|
2735
|
-
console.error("[pushpals] Auto-start could not bring
|
|
2948
|
+
console.error("[pushpals] Auto-start could not bring the embedded runtime into a usable state.");
|
|
2736
2949
|
}
|
|
2737
2950
|
process.exit(1);
|
|
2738
2951
|
}
|
|
2739
|
-
let localBuddySessionId = sessionId;
|
|
2740
|
-
if (!parsed.runtimeOnly) {
|
|
2741
|
-
const localBuddyRepo = health?.repo ? resolve4(health.repo) : "";
|
|
2742
|
-
if (!localBuddyRepo) {
|
|
2743
|
-
stopAutoStartedServices();
|
|
2744
|
-
console.error("[pushpals] LocalBuddy health response did not include repo path.");
|
|
2745
|
-
process.exit(1);
|
|
2746
|
-
}
|
|
2747
|
-
if (normalizePath(localBuddyRepo) !== normalizePath(repoRoot)) {
|
|
2748
|
-
stopAutoStartedServices();
|
|
2749
|
-
console.error("[pushpals] Repo mismatch detected.");
|
|
2750
|
-
console.error(`[pushpals] currentRepo=${repoRoot}`);
|
|
2751
|
-
console.error(`[pushpals] localBuddyRepo=${localBuddyRepo}`);
|
|
2752
|
-
console.error("[pushpals] LocalBuddy must run against the same repo. Start PushPals from this repo and retry.");
|
|
2753
|
-
process.exit(1);
|
|
2754
|
-
}
|
|
2755
|
-
localBuddySessionId = health?.sessionId && String(health.sessionId).trim() ? String(health.sessionId).trim() : sessionId;
|
|
2756
|
-
if (sessionId && sessionId !== localBuddySessionId) {
|
|
2757
|
-
console.warn(`[pushpals] Requested sessionId=${sessionId}, but LocalBuddy is currently attached to sessionId=${localBuddySessionId}.`);
|
|
2758
|
-
}
|
|
2759
|
-
}
|
|
2760
2952
|
const statePath = resolveCliStatePath(repoRoot);
|
|
2761
2953
|
const saved = statePath ? readCliState(statePath) : {};
|
|
2762
2954
|
const preferredHubUrl = normalizeUrl(parsed.monitoringHubUrl ?? process.env.PUSHPALS_MONITOR_URL ?? saved.monitoringHubUrl ?? "");
|
|
@@ -2765,8 +2957,7 @@ async function main() {
|
|
|
2765
2957
|
preferredUrl: preferredHubUrl,
|
|
2766
2958
|
fallbackPort: monitorPort,
|
|
2767
2959
|
serverUrl,
|
|
2768
|
-
|
|
2769
|
-
sessionId: localBuddySessionId
|
|
2960
|
+
sessionId: activeSessionId
|
|
2770
2961
|
});
|
|
2771
2962
|
const monitoringHubUrl = monitoringHub?.url ?? "";
|
|
2772
2963
|
if (statePath) {
|
|
@@ -2774,7 +2965,7 @@ async function main() {
|
|
|
2774
2965
|
monitoringHubUrl: monitoringHubUrl || undefined,
|
|
2775
2966
|
serverUrl,
|
|
2776
2967
|
localAgentUrl,
|
|
2777
|
-
sessionId:
|
|
2968
|
+
sessionId: activeSessionId,
|
|
2778
2969
|
repoRoot
|
|
2779
2970
|
});
|
|
2780
2971
|
} else {
|
|
@@ -2790,8 +2981,7 @@ async function main() {
|
|
|
2790
2981
|
console.log("[pushpals] monitoringHubUrl=unavailable");
|
|
2791
2982
|
}
|
|
2792
2983
|
console.log(`[pushpals] serverUrl=${serverUrl}`);
|
|
2793
|
-
console.log(`[pushpals]
|
|
2794
|
-
console.log(`[pushpals] sessionId=${localBuddySessionId}`);
|
|
2984
|
+
console.log(`[pushpals] sessionId=${activeSessionId}`);
|
|
2795
2985
|
console.log(`[pushpals] repoRoot=${repoRoot}`);
|
|
2796
2986
|
console.log(`[pushpals] cliStateFile=${statePath ?? "unavailable"}`);
|
|
2797
2987
|
if (parsed.runtimeOnly) {
|
|
@@ -2799,15 +2989,6 @@ async function main() {
|
|
|
2799
2989
|
} else {
|
|
2800
2990
|
console.log("[pushpals] Type a message and press Enter. Use /exit or exit to quit.");
|
|
2801
2991
|
}
|
|
2802
|
-
const cliVersion = String(process.env.PUSHPALS_CLI_PACKAGE_VERSION ?? "").trim() || "unknown";
|
|
2803
|
-
const cliClient = {
|
|
2804
|
-
clientId: createRuntimeClientId("cli"),
|
|
2805
|
-
kind: "cli",
|
|
2806
|
-
label: "CLI",
|
|
2807
|
-
version: cliVersion,
|
|
2808
|
-
platform: `${process.platform}/${process.arch}`,
|
|
2809
|
-
repoRoot
|
|
2810
|
-
};
|
|
2811
2992
|
const streamAbort = new AbortController;
|
|
2812
2993
|
let rl = null;
|
|
2813
2994
|
const printIncoming = (line) => {
|
|
@@ -2822,7 +3003,7 @@ ${line}
|
|
|
2822
3003
|
}
|
|
2823
3004
|
console.log(line);
|
|
2824
3005
|
};
|
|
2825
|
-
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);
|
|
2826
3007
|
let shuttingDown = false;
|
|
2827
3008
|
const requestStop = () => {
|
|
2828
3009
|
if (shuttingDown)
|
|
@@ -2900,8 +3081,7 @@ ${line}
|
|
|
2900
3081
|
}
|
|
2901
3082
|
if (text === "/status") {
|
|
2902
3083
|
console.log(`[pushpals] serverUrl=${serverUrl}`);
|
|
2903
|
-
console.log(`[pushpals]
|
|
2904
|
-
console.log(`[pushpals] sessionId=${localBuddySessionId}`);
|
|
3084
|
+
console.log(`[pushpals] sessionId=${activeSessionId}`);
|
|
2905
3085
|
console.log(`[pushpals] repoRoot=${repoRoot}`);
|
|
2906
3086
|
console.log(monitoringHubUrl ? `[pushpals] monitoringHubUrl=${monitoringHubUrl}` : "[pushpals] monitoringHubUrl=unavailable");
|
|
2907
3087
|
rl.prompt();
|
|
@@ -2918,7 +3098,13 @@ ${line}
|
|
|
2918
3098
|
rl.prompt();
|
|
2919
3099
|
continue;
|
|
2920
3100
|
}
|
|
2921
|
-
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);
|
|
2922
3108
|
if (!ok) {
|
|
2923
3109
|
console.log("[pushpals] Message failed.");
|
|
2924
3110
|
}
|
|
@@ -2937,13 +3123,19 @@ export {
|
|
|
2937
3123
|
startEmbeddedMonitoringHub,
|
|
2938
3124
|
resolveCommandPath,
|
|
2939
3125
|
resolveCliStatePath,
|
|
3126
|
+
resolveCliLocalBuddyAutostart,
|
|
2940
3127
|
resolveBundledRuntimeAssetSource,
|
|
2941
3128
|
resolveBundledMonitoringHubRoot,
|
|
2942
3129
|
prepareCliRuntime,
|
|
3130
|
+
normalizeRepoPathForComparison,
|
|
3131
|
+
normalizeCliInteractiveMessage,
|
|
2943
3132
|
normalizeChildProcessEnv,
|
|
2944
3133
|
isCliExitCommand,
|
|
2945
3134
|
injectMonitoringHubBootstrap,
|
|
2946
3135
|
formatTimestampedCliLine,
|
|
3136
|
+
formatSessionEventLine,
|
|
3137
|
+
extractRemoteBuddySessionConsumerHealth,
|
|
3138
|
+
bundledMonitoringHubNeedsRefresh,
|
|
2947
3139
|
buildServiceStopCommand,
|
|
2948
3140
|
buildOpenMonitoringHubCommand,
|
|
2949
3141
|
buildEmbeddedRuntimeEnv,
|