@pushpalsdev/cli 1.0.11 → 1.0.13
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 +472 -164
- 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";
|
|
@@ -616,7 +618,7 @@ function loadPushPalsConfig(options = {}) {
|
|
|
616
618
|
retentionDays: remoteMemoryRetentionDays
|
|
617
619
|
},
|
|
618
620
|
autonomy: {
|
|
619
|
-
enabled: parseBoolEnv("REMOTEBUDDY_AUTONOMY_ENABLED") ?? asBoolean(remoteAutonomyNode.enabled,
|
|
621
|
+
enabled: parseBoolEnv("REMOTEBUDDY_AUTONOMY_ENABLED") ?? asBoolean(remoteAutonomyNode.enabled, true),
|
|
620
622
|
killSwitchEnabled: parseBoolEnv("REMOTEBUDDY_AUTONOMY_KILL_SWITCH_ENABLED") ?? asBoolean(remoteAutonomyNode.kill_switch_enabled, false),
|
|
621
623
|
tickIntervalMs: Math.max(5000, asInt(parseIntEnv("REMOTEBUDDY_AUTONOMY_TICK_INTERVAL_MS") ?? remoteAutonomyNode.tick_interval_ms, 120000)),
|
|
622
624
|
heartbeatLogMs: Math.max(1000, asInt(parseIntEnv("REMOTEBUDDY_AUTONOMY_HEARTBEAT_LOG_MS") ?? remoteAutonomyNode.heartbeat_log_ms, 30000)),
|
|
@@ -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,8 +1715,9 @@ 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.
|
|
1641
|
-
...typeof env.PUSHPALS_GIT_BIN === "string" && env.PUSHPALS_GIT_BIN.trim() ? { PUSHPALS_GIT_BIN: env.PUSHPALS_GIT_BIN.trim() } : {}
|
|
1718
|
+
...typeof opts.sessionId === "string" && opts.sessionId.trim() ? { PUSHPALS_SESSION_ID: opts.sessionId.trim() } : {},
|
|
1719
|
+
...typeof env.PUSHPALS_GIT_BIN === "string" && env.PUSHPALS_GIT_BIN.trim() ? { PUSHPALS_GIT_BIN: env.PUSHPALS_GIT_BIN.trim() } : {},
|
|
1720
|
+
...typeof env.PUSHPALS_GIT_BIN_ABSOLUTE === "string" && env.PUSHPALS_GIT_BIN_ABSOLUTE.trim() ? { PUSHPALS_GIT_BIN_ABSOLUTE: env.PUSHPALS_GIT_BIN_ABSOLUTE.trim() } : {}
|
|
1642
1721
|
};
|
|
1643
1722
|
}
|
|
1644
1723
|
function normalizeChildProcessEnv(baseEnv, platform = process.platform) {
|
|
@@ -1692,6 +1771,23 @@ async function resolveCommandPath(command, cwd, env) {
|
|
|
1692
1771
|
function timestampFileToken() {
|
|
1693
1772
|
return new Date().toISOString().replace(/[:.]/g, "-");
|
|
1694
1773
|
}
|
|
1774
|
+
function buildRuntimeServiceLogPaths(logDir, runToken) {
|
|
1775
|
+
return {
|
|
1776
|
+
server: join2(logDir, `${runToken}-server.log`),
|
|
1777
|
+
localbuddy: join2(logDir, `${runToken}-localbuddy.log`),
|
|
1778
|
+
remotebuddy: join2(logDir, `${runToken}-remotebuddy.log`),
|
|
1779
|
+
source_control_manager: join2(logDir, `${runToken}-source_control_manager.log`)
|
|
1780
|
+
};
|
|
1781
|
+
}
|
|
1782
|
+
function appendRuntimeServicesLogLine(logPath, line) {
|
|
1783
|
+
const text = String(line ?? "").trim();
|
|
1784
|
+
if (!text)
|
|
1785
|
+
return;
|
|
1786
|
+
try {
|
|
1787
|
+
appendFileSync(logPath, `${new Date().toISOString()} ${text}
|
|
1788
|
+
`, "utf8");
|
|
1789
|
+
} catch {}
|
|
1790
|
+
}
|
|
1695
1791
|
function readLogTail(logPath, maxLines = 40) {
|
|
1696
1792
|
if (!existsSync4(logPath))
|
|
1697
1793
|
return "";
|
|
@@ -1702,6 +1798,31 @@ function readLogTail(logPath, maxLines = 40) {
|
|
|
1702
1798
|
return lines.slice(-maxLines).join(`
|
|
1703
1799
|
`);
|
|
1704
1800
|
}
|
|
1801
|
+
function extractRemoteBuddyAutonomousEngineState(logText) {
|
|
1802
|
+
const text = String(logText ?? "");
|
|
1803
|
+
if (!text)
|
|
1804
|
+
return "unknown";
|
|
1805
|
+
let state = "unknown";
|
|
1806
|
+
for (const line of text.split(/\r?\n/)) {
|
|
1807
|
+
if (/Autonomous engine:\s*enabled\b/i.test(line)) {
|
|
1808
|
+
state = "enabled";
|
|
1809
|
+
continue;
|
|
1810
|
+
}
|
|
1811
|
+
if (/Autonomous engine:\s*disabled\b/i.test(line)) {
|
|
1812
|
+
state = "disabled";
|
|
1813
|
+
}
|
|
1814
|
+
}
|
|
1815
|
+
return state;
|
|
1816
|
+
}
|
|
1817
|
+
function readRemoteBuddyAutonomousEngineState(logPath) {
|
|
1818
|
+
if (!existsSync4(logPath))
|
|
1819
|
+
return "unknown";
|
|
1820
|
+
try {
|
|
1821
|
+
return extractRemoteBuddyAutonomousEngineState(readFileSync4(logPath, "utf8"));
|
|
1822
|
+
} catch {
|
|
1823
|
+
return "unknown";
|
|
1824
|
+
}
|
|
1825
|
+
}
|
|
1705
1826
|
async function downloadBinaryAsset(tag, assetName, outPath) {
|
|
1706
1827
|
console.log(`[pushpals] Downloading embedded runtime binary ${assetName} from ${tag}...`);
|
|
1707
1828
|
const url = `${GITHUB_RELEASE_URL}/${encodeURIComponent(tag)}/${assetName}`;
|
|
@@ -1753,9 +1874,13 @@ async function ensureRuntimeBinaries(runtimeRoot, runtimeTag) {
|
|
|
1753
1874
|
console.log("[pushpals] Embedded runtime binaries are ready.");
|
|
1754
1875
|
return runtimeBinaries;
|
|
1755
1876
|
}
|
|
1756
|
-
function spawnRuntimeService(name, command, cwd, env, logPath) {
|
|
1757
|
-
|
|
1877
|
+
function spawnRuntimeService(name, command, cwd, env, logPath, runtimeServicesLogPath) {
|
|
1878
|
+
const header = `[pushpals] service=${name} command=${command.join(" ")} cwd=${cwd}`;
|
|
1879
|
+
writeFileSync(logPath, `${header}
|
|
1758
1880
|
`, "utf8");
|
|
1881
|
+
if (runtimeServicesLogPath) {
|
|
1882
|
+
appendRuntimeServicesLogLine(runtimeServicesLogPath, header);
|
|
1883
|
+
}
|
|
1759
1884
|
const proc = Bun.spawn(command, {
|
|
1760
1885
|
cwd,
|
|
1761
1886
|
env,
|
|
@@ -1779,16 +1904,24 @@ function spawnRuntimeService(name, command, cwd, env, logPath) {
|
|
|
1779
1904
|
const lines = pending.split(/\r?\n/);
|
|
1780
1905
|
pending = lines.pop() ?? "";
|
|
1781
1906
|
for (const line of lines) {
|
|
1782
|
-
|
|
1907
|
+
const serviceLine = `[${channel}] ${line}`;
|
|
1908
|
+
appendFileSync(logPath, `${serviceLine}
|
|
1783
1909
|
`, "utf8");
|
|
1910
|
+
if (runtimeServicesLogPath) {
|
|
1911
|
+
appendRuntimeServicesLogLine(runtimeServicesLogPath, `[${name}] ${serviceLine}`);
|
|
1912
|
+
}
|
|
1784
1913
|
}
|
|
1785
1914
|
}
|
|
1786
1915
|
const rest = decoder.decode();
|
|
1787
1916
|
if (rest)
|
|
1788
1917
|
pending += rest;
|
|
1789
1918
|
if (pending.trim().length > 0) {
|
|
1790
|
-
|
|
1919
|
+
const serviceLine = `[${channel}] ${pending.trimEnd()}`;
|
|
1920
|
+
appendFileSync(logPath, `${serviceLine}
|
|
1791
1921
|
`, "utf8");
|
|
1922
|
+
if (runtimeServicesLogPath) {
|
|
1923
|
+
appendRuntimeServicesLogLine(runtimeServicesLogPath, `[${name}] ${serviceLine}`);
|
|
1924
|
+
}
|
|
1792
1925
|
}
|
|
1793
1926
|
};
|
|
1794
1927
|
pipeToLog(proc.stdout, "stdout");
|
|
@@ -1838,7 +1971,7 @@ function prependExecutableDirToPath(env, executablePath, platform = process.plat
|
|
|
1838
1971
|
const executableDir = dirname(resolvedPath);
|
|
1839
1972
|
const existingPath = platform === "win32" ? String(env.Path ?? env.PATH ?? "") : String(env.PATH ?? "");
|
|
1840
1973
|
const pathEntries = existingPath.split(delimiter).map((entry) => entry.trim()).filter((entry) => entry.length > 0);
|
|
1841
|
-
const hasDir = pathEntries.some((entry) => entry.toLowerCase() === executableDir.toLowerCase());
|
|
1974
|
+
const hasDir = pathEntries.some((entry) => platform === "win32" ? entry.toLowerCase() === executableDir.toLowerCase() : entry === executableDir);
|
|
1842
1975
|
const nextPath = hasDir ? existingPath : [executableDir, ...pathEntries].join(delimiter);
|
|
1843
1976
|
if (platform === "win32") {
|
|
1844
1977
|
env.Path = nextPath;
|
|
@@ -1854,6 +1987,11 @@ function applyResolvedGitBinaryToRuntimeEnv(env, resolvedGitBinary, platform = p
|
|
|
1854
1987
|
return env;
|
|
1855
1988
|
prependExecutableDirToPath(env, resolvedPath, platform);
|
|
1856
1989
|
env.PUSHPALS_GIT_BIN = basename(resolvedPath);
|
|
1990
|
+
if (resolvedPath.includes("/") || resolvedPath.includes("\\")) {
|
|
1991
|
+
env.PUSHPALS_GIT_BIN_ABSOLUTE = resolvedPath;
|
|
1992
|
+
} else {
|
|
1993
|
+
delete env.PUSHPALS_GIT_BIN_ABSOLUTE;
|
|
1994
|
+
}
|
|
1857
1995
|
return env;
|
|
1858
1996
|
}
|
|
1859
1997
|
function isOptionalEmbeddedService(name) {
|
|
@@ -1889,6 +2027,111 @@ async function probeServer(serverUrl) {
|
|
|
1889
2027
|
return false;
|
|
1890
2028
|
}
|
|
1891
2029
|
}
|
|
2030
|
+
function normalizeRepoPathForComparison(repoPath) {
|
|
2031
|
+
const normalized = resolve4(String(repoPath ?? "")).replace(/\\/g, "/").replace(/\/+$/, "");
|
|
2032
|
+
return process.platform === "win32" ? normalized.toLowerCase() : normalized;
|
|
2033
|
+
}
|
|
2034
|
+
async function fetchServerRepoRoot(serverUrl) {
|
|
2035
|
+
const response = await fetchWithTimeout(`${serverUrl}/system/status`, {}, 1e4);
|
|
2036
|
+
if (!response.ok) {
|
|
2037
|
+
throw new Error(`status probe failed with HTTP ${response.status}`);
|
|
2038
|
+
}
|
|
2039
|
+
const payload = await response.json().catch(() => ({}));
|
|
2040
|
+
const repoRoot = payload?.repo && typeof payload.repo.root === "string" ? payload.repo.root.trim() : "";
|
|
2041
|
+
if (!repoRoot) {
|
|
2042
|
+
throw new Error("server did not report repo.root in /system/status");
|
|
2043
|
+
}
|
|
2044
|
+
return repoRoot;
|
|
2045
|
+
}
|
|
2046
|
+
async function ensureServerRepoAffinity(serverUrl, currentRepoRoot) {
|
|
2047
|
+
const serverRepoRoot = await fetchServerRepoRoot(serverUrl);
|
|
2048
|
+
if (normalizeRepoPathForComparison(serverRepoRoot) === normalizeRepoPathForComparison(currentRepoRoot)) {
|
|
2049
|
+
return;
|
|
2050
|
+
}
|
|
2051
|
+
throw new Error(`repo mismatch: currentRepo=${currentRepoRoot} serverRepo=${serverRepoRoot}. Stop the existing runtime or switch to the matching repo.`);
|
|
2052
|
+
}
|
|
2053
|
+
function isRemoteBuddyClientRow(row) {
|
|
2054
|
+
const clientId = normalizePresenceLookupToken(row.clientId);
|
|
2055
|
+
const label = normalizePresenceLookupToken(row.label);
|
|
2056
|
+
return clientId.includes("remotebuddy") || label.includes("remotebuddy");
|
|
2057
|
+
}
|
|
2058
|
+
function extractRemoteBuddySessionConsumerHealth(statusPayload, sessionId) {
|
|
2059
|
+
const rows = Array.isArray(statusPayload?.clients?.items) ? statusPayload.clients?.items ?? [] : [];
|
|
2060
|
+
const sessionRows = rows.filter((row) => {
|
|
2061
|
+
if (!row || typeof row !== "object" || Array.isArray(row))
|
|
2062
|
+
return false;
|
|
2063
|
+
return String(row.sessionId ?? "").trim() === sessionId;
|
|
2064
|
+
});
|
|
2065
|
+
const remotebuddyRows = sessionRows.filter(isRemoteBuddyClientRow);
|
|
2066
|
+
const connectedRow = remotebuddyRows.find((row) => String(row.status ?? "").trim().toLowerCase() === "connected");
|
|
2067
|
+
if (connectedRow) {
|
|
2068
|
+
return {
|
|
2069
|
+
ok: true,
|
|
2070
|
+
detail: `RemoteBuddy session consumer connected (${String(connectedRow.clientId ?? "").trim()})`,
|
|
2071
|
+
clientId: String(connectedRow.clientId ?? "").trim() || undefined,
|
|
2072
|
+
sessionId
|
|
2073
|
+
};
|
|
2074
|
+
}
|
|
2075
|
+
const anyRemoteBuddyRows = rows.filter((row) => {
|
|
2076
|
+
if (!row || typeof row !== "object" || Array.isArray(row))
|
|
2077
|
+
return false;
|
|
2078
|
+
return isRemoteBuddyClientRow(row);
|
|
2079
|
+
});
|
|
2080
|
+
const connectedOtherSession = anyRemoteBuddyRows.find((row) => {
|
|
2081
|
+
const rowSessionId = String(row.sessionId ?? "").trim();
|
|
2082
|
+
if (!rowSessionId || rowSessionId === sessionId)
|
|
2083
|
+
return false;
|
|
2084
|
+
return String(row.status ?? "").trim().toLowerCase() === "connected";
|
|
2085
|
+
});
|
|
2086
|
+
if (connectedOtherSession) {
|
|
2087
|
+
const otherSessionId = String(connectedOtherSession.sessionId ?? "").trim();
|
|
2088
|
+
const otherClientId = String(connectedOtherSession.clientId ?? "").trim();
|
|
2089
|
+
return {
|
|
2090
|
+
ok: false,
|
|
2091
|
+
detail: `RemoteBuddy is connected to session ${otherSessionId || "unknown"} ` + `(${otherClientId || "unknown client"}), not ${sessionId}`,
|
|
2092
|
+
clientId: otherClientId || undefined,
|
|
2093
|
+
sessionId: otherSessionId || undefined
|
|
2094
|
+
};
|
|
2095
|
+
}
|
|
2096
|
+
if (remotebuddyRows.length > 0) {
|
|
2097
|
+
return {
|
|
2098
|
+
ok: false,
|
|
2099
|
+
detail: `RemoteBuddy session consumer exists for ${sessionId} but is not connected`,
|
|
2100
|
+
clientId: String(remotebuddyRows[0]?.clientId ?? "").trim() || undefined,
|
|
2101
|
+
sessionId
|
|
2102
|
+
};
|
|
2103
|
+
}
|
|
2104
|
+
if (anyRemoteBuddyRows.length > 0) {
|
|
2105
|
+
const knownSessions = [...new Set(anyRemoteBuddyRows.map((row) => String(row.sessionId ?? "").trim()))].filter(Boolean).sort();
|
|
2106
|
+
const suffix = knownSessions.length > 0 ? ` Known RemoteBuddy sessions: ${knownSessions.join(", ")}.` : "";
|
|
2107
|
+
return {
|
|
2108
|
+
ok: false,
|
|
2109
|
+
detail: `No connected RemoteBuddy session consumer found for session ${sessionId}.${suffix}`.trim()
|
|
2110
|
+
};
|
|
2111
|
+
}
|
|
2112
|
+
return {
|
|
2113
|
+
ok: false,
|
|
2114
|
+
detail: `No connected RemoteBuddy session consumer found for session ${sessionId}`
|
|
2115
|
+
};
|
|
2116
|
+
}
|
|
2117
|
+
async function probeRemoteBuddySessionConsumer(serverUrl, sessionId) {
|
|
2118
|
+
try {
|
|
2119
|
+
const response = await fetchWithTimeout(`${serverUrl}/system/status`, {}, 1e4);
|
|
2120
|
+
if (!response.ok) {
|
|
2121
|
+
return {
|
|
2122
|
+
ok: false,
|
|
2123
|
+
detail: `system status probe failed with HTTP ${response.status}`
|
|
2124
|
+
};
|
|
2125
|
+
}
|
|
2126
|
+
const payload = await response.json().catch(() => ({}));
|
|
2127
|
+
return extractRemoteBuddySessionConsumerHealth(payload, sessionId);
|
|
2128
|
+
} catch (err) {
|
|
2129
|
+
return {
|
|
2130
|
+
ok: false,
|
|
2131
|
+
detail: `system status probe failed: ${String(err)}`
|
|
2132
|
+
};
|
|
2133
|
+
}
|
|
2134
|
+
}
|
|
1892
2135
|
async function probeSourceControlManager(port) {
|
|
1893
2136
|
if (!Number.isFinite(port) || port <= 0)
|
|
1894
2137
|
return false;
|
|
@@ -1940,13 +2183,43 @@ function createRuntimeClientId(prefix) {
|
|
|
1940
2183
|
async function probeLocalBuddy(localAgentUrl) {
|
|
1941
2184
|
return await fetchJsonWithTimeout(`${localAgentUrl}/healthz`, {}, LOCALBUDDY_TIMEOUT_MS);
|
|
1942
2185
|
}
|
|
2186
|
+
function resolveCliLocalBuddyAutostart(runtimeOnly, runtimeConfigEnabled) {
|
|
2187
|
+
return runtimeOnly ? runtimeConfigEnabled : false;
|
|
2188
|
+
}
|
|
2189
|
+
async function ensureServerSession(serverUrl, requestedSessionId, client) {
|
|
2190
|
+
const response = await fetchWithTimeout(`${serverUrl}/sessions`, {
|
|
2191
|
+
method: "POST",
|
|
2192
|
+
headers: { "Content-Type": "application/json" },
|
|
2193
|
+
body: JSON.stringify({
|
|
2194
|
+
sessionId: requestedSessionId,
|
|
2195
|
+
client: {
|
|
2196
|
+
clientId: client.clientId,
|
|
2197
|
+
kind: client.kind,
|
|
2198
|
+
label: client.label,
|
|
2199
|
+
version: client.version,
|
|
2200
|
+
platform: client.platform,
|
|
2201
|
+
repoRoot: client.repoRoot
|
|
2202
|
+
}
|
|
2203
|
+
})
|
|
2204
|
+
}, 15000);
|
|
2205
|
+
if (!response.ok) {
|
|
2206
|
+
const detail = await response.text().catch(() => "");
|
|
2207
|
+
throw new Error(`Failed to create or join session ${requestedSessionId}: HTTP ${response.status}${detail ? ` ${detail}` : ""}`);
|
|
2208
|
+
}
|
|
2209
|
+
const payload = await response.json().catch(() => ({}));
|
|
2210
|
+
const sessionId = typeof payload.sessionId === "string" && payload.sessionId.trim() ? payload.sessionId.trim() : "";
|
|
2211
|
+
if (!sessionId) {
|
|
2212
|
+
throw new Error("Server session bootstrap returned no sessionId.");
|
|
2213
|
+
}
|
|
2214
|
+
return sessionId;
|
|
2215
|
+
}
|
|
1943
2216
|
async function autoStartRuntimeServices(opts) {
|
|
1944
2217
|
const { runtimePreflight } = opts.preparedRuntime;
|
|
1945
2218
|
const runtimeRoot = opts.preparedRuntime.runtimeRoot;
|
|
1946
2219
|
const runtimeTag = opts.preparedRuntime.runtimeTag || await resolveRuntimeReleaseTag(opts.requestedRuntimeTag);
|
|
1947
|
-
const
|
|
1948
|
-
const localBuddyEnabled =
|
|
1949
|
-
console.log(`[pushpals]
|
|
2220
|
+
const startLocalBuddy = opts.startLocalBuddy ?? Boolean(runtimePreflight.config.localbuddy.enabled);
|
|
2221
|
+
const localBuddyEnabled = startLocalBuddy;
|
|
2222
|
+
console.log(`[pushpals] Runtime unavailable. Auto-starting runtime for repo: ${opts.repoRoot}`);
|
|
1950
2223
|
console.log(`[pushpals] runtimeRoot=${runtimeRoot}`);
|
|
1951
2224
|
console.log(`[pushpals] runtimeTag=${runtimeTag}`);
|
|
1952
2225
|
if (!runtimePreflight.ok) {
|
|
@@ -1958,12 +2231,13 @@ async function autoStartRuntimeServices(opts) {
|
|
|
1958
2231
|
repoRoot: opts.repoRoot,
|
|
1959
2232
|
runtimeRoot,
|
|
1960
2233
|
useRuntimeConfig: opts.preparedRuntime.preflightUsesEmbeddedRuntime,
|
|
1961
|
-
|
|
2234
|
+
sessionId: opts.sessionId
|
|
1962
2235
|
});
|
|
1963
2236
|
if (runtimeEnv.PUSHPALS_GIT_BIN) {
|
|
1964
2237
|
applyResolvedGitBinaryToRuntimeEnv(runtimeEnv, runtimeEnv.PUSHPALS_GIT_BIN);
|
|
1965
2238
|
}
|
|
1966
|
-
const
|
|
2239
|
+
const gitLookupCommand = typeof runtimeEnv.PUSHPALS_GIT_BIN === "string" && runtimeEnv.PUSHPALS_GIT_BIN.trim() ? runtimeEnv.PUSHPALS_GIT_BIN.trim() : "git";
|
|
2240
|
+
const resolvedGitBinary = await resolveCommandPath(gitLookupCommand, opts.repoRoot, runtimeEnv);
|
|
1967
2241
|
if (resolvedGitBinary) {
|
|
1968
2242
|
applyResolvedGitBinaryToRuntimeEnv(runtimeEnv, resolvedGitBinary);
|
|
1969
2243
|
}
|
|
@@ -1971,11 +2245,21 @@ async function autoStartRuntimeServices(opts) {
|
|
|
1971
2245
|
const runToken = timestampFileToken();
|
|
1972
2246
|
const logDir = join2(runtimeRoot, "logs", "bootstrap");
|
|
1973
2247
|
mkdirSync(logDir, { recursive: true });
|
|
1974
|
-
const
|
|
2248
|
+
const serviceLogPaths = buildRuntimeServiceLogPaths(logDir, runToken);
|
|
2249
|
+
const runtimeServicesLogPath = join2(logDir, `${runToken}-runtime-services.log`);
|
|
2250
|
+
writeFileSync(runtimeServicesLogPath, "", "utf8");
|
|
2251
|
+
appendRuntimeServicesLogLine(runtimeServicesLogPath, `[pushpals] runtimeRoot=${runtimeRoot}`);
|
|
2252
|
+
appendRuntimeServicesLogLine(runtimeServicesLogPath, `[pushpals] runtimeTag=${runtimeTag}`);
|
|
2253
|
+
appendRuntimeServicesLogLine(runtimeServicesLogPath, `[pushpals] repoRoot=${opts.repoRoot}`);
|
|
2254
|
+
console.log(`[pushpals] runtime services log: ${runtimeServicesLogPath}`);
|
|
2255
|
+
console.log(`[pushpals] service log (server)=${serviceLogPaths.server}`);
|
|
2256
|
+
console.log(`[pushpals] service log (localbuddy)=${serviceLogPaths.localbuddy}`);
|
|
2257
|
+
console.log(`[pushpals] service log (remotebuddy)=${serviceLogPaths.remotebuddy}`);
|
|
2258
|
+
console.log(`[pushpals] service log (source_control_manager)=${serviceLogPaths.source_control_manager}`);
|
|
1975
2259
|
const serverHealthy = await probeServer(opts.serverUrl);
|
|
1976
2260
|
if (!serverHealthy) {
|
|
1977
2261
|
console.log("[pushpals] Starting embedded server...");
|
|
1978
|
-
const serverService = spawnRuntimeService("server", [runtimeBinaries.server], opts.repoRoot, runtimeEnv,
|
|
2262
|
+
const serverService = spawnRuntimeService("server", [runtimeBinaries.server], opts.repoRoot, runtimeEnv, serviceLogPaths.server, runtimeServicesLogPath);
|
|
1979
2263
|
services.push(serverService);
|
|
1980
2264
|
console.log(`[pushpals] server log: ${serverService.logPath}`);
|
|
1981
2265
|
const serverDeadline = Date.now() + DEFAULT_SERVER_BOOT_TIMEOUT_MS;
|
|
@@ -1983,6 +2267,7 @@ async function autoStartRuntimeServices(opts) {
|
|
|
1983
2267
|
while (Date.now() < serverDeadline) {
|
|
1984
2268
|
if (serverService.exited) {
|
|
1985
2269
|
const tail = readLogTail(serverService.logPath);
|
|
2270
|
+
appendRuntimeServicesLogLine(runtimeServicesLogPath, `[pushpals] embedded server exited during bootstrap (code=${serverService.exitCode ?? "unknown"}).`);
|
|
1986
2271
|
stopRuntimeServices(services);
|
|
1987
2272
|
throw new Error(`Embedded server exited during bootstrap (code=${serverService.exitCode ?? "unknown"}). ` + `See ${serverService.logPath}${tail ? `
|
|
1988
2273
|
--- server log tail ---
|
|
@@ -1996,6 +2281,7 @@ ${tail}` : ""}`);
|
|
|
1996
2281
|
}
|
|
1997
2282
|
if (!serverIsReady) {
|
|
1998
2283
|
const tail = readLogTail(serverService.logPath);
|
|
2284
|
+
appendRuntimeServicesLogLine(runtimeServicesLogPath, `[pushpals] embedded server did not become healthy within ${DEFAULT_SERVER_BOOT_TIMEOUT_MS}ms.`);
|
|
1999
2285
|
stopRuntimeServices(services);
|
|
2000
2286
|
throw new Error(`Embedded server did not become healthy within ${DEFAULT_SERVER_BOOT_TIMEOUT_MS}ms. ` + `See ${serverService.logPath}${tail ? `
|
|
2001
2287
|
--- server log tail ---
|
|
@@ -2004,61 +2290,84 @@ ${tail}` : ""}`);
|
|
|
2004
2290
|
console.log("[pushpals] Embedded server is healthy.");
|
|
2005
2291
|
} else {
|
|
2006
2292
|
console.log("[pushpals] Server already healthy; skipping embedded server start.");
|
|
2293
|
+
appendRuntimeServicesLogLine(runtimeServicesLogPath, "[pushpals] server already healthy; embedded server start skipped.");
|
|
2007
2294
|
}
|
|
2008
2295
|
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
2296
|
console.log("[pushpals] Starting embedded LocalBuddy...");
|
|
2013
|
-
const localbuddyService = spawnRuntimeService("localbuddy", [runtimeBinaries.localbuddy], opts.repoRoot, runtimeEnv,
|
|
2297
|
+
const localbuddyService = spawnRuntimeService("localbuddy", [runtimeBinaries.localbuddy], opts.repoRoot, runtimeEnv, serviceLogPaths.localbuddy, runtimeServicesLogPath);
|
|
2014
2298
|
services.push(localbuddyService);
|
|
2015
2299
|
console.log(`[pushpals] localbuddy log: ${localbuddyService.logPath}`);
|
|
2016
2300
|
} else {
|
|
2017
|
-
console.log("[pushpals] Embedded LocalBuddy disabled
|
|
2301
|
+
console.log("[pushpals] Embedded LocalBuddy disabled for this CLI session; skipping start.");
|
|
2302
|
+
appendRuntimeServicesLogLine(runtimeServicesLogPath, "[pushpals] localbuddy disabled for this CLI session; embedded localbuddy start skipped.");
|
|
2018
2303
|
}
|
|
2019
2304
|
console.log("[pushpals] Starting embedded RemoteBuddy...");
|
|
2020
|
-
const remotebuddyService = spawnRuntimeService("remotebuddy", [runtimeBinaries.remotebuddy], opts.repoRoot, runtimeEnv,
|
|
2305
|
+
const remotebuddyService = spawnRuntimeService("remotebuddy", [runtimeBinaries.remotebuddy], opts.repoRoot, runtimeEnv, serviceLogPaths.remotebuddy, runtimeServicesLogPath);
|
|
2021
2306
|
services.push(remotebuddyService);
|
|
2022
2307
|
console.log(`[pushpals] remotebuddy log: ${remotebuddyService.logPath}`);
|
|
2308
|
+
let lastReportedRemoteBuddyAutonomyState = "unknown";
|
|
2309
|
+
const reportRemoteBuddyAutonomousEngineState = () => {
|
|
2310
|
+
const autonomyState = readRemoteBuddyAutonomousEngineState(remotebuddyService.logPath);
|
|
2311
|
+
if (autonomyState === "unknown" || autonomyState === lastReportedRemoteBuddyAutonomyState) {
|
|
2312
|
+
return;
|
|
2313
|
+
}
|
|
2314
|
+
lastReportedRemoteBuddyAutonomyState = autonomyState;
|
|
2315
|
+
if (autonomyState === "enabled") {
|
|
2316
|
+
console.log("[pushpals] Embedded RemoteBuddy autonomous engine is enabled.");
|
|
2317
|
+
appendRuntimeServicesLogLine(runtimeServicesLogPath, "[pushpals] embedded remotebuddy autonomous engine is enabled.");
|
|
2318
|
+
return;
|
|
2319
|
+
}
|
|
2320
|
+
console.warn("[pushpals] Embedded RemoteBuddy autonomous engine is disabled (remotebuddy.autonomy.enabled=false).");
|
|
2321
|
+
appendRuntimeServicesLogLine(runtimeServicesLogPath, "[pushpals] embedded remotebuddy autonomous engine is disabled (remotebuddy.autonomy.enabled=false).");
|
|
2322
|
+
};
|
|
2323
|
+
reportRemoteBuddyAutonomousEngineState();
|
|
2023
2324
|
const scmHealthy = await probeSourceControlManager(opts.sourceControlManagerPort);
|
|
2024
2325
|
const scmRemoteAvailable = await repoHasRemote(opts.repoRoot, opts.sourceControlManagerRemote);
|
|
2025
|
-
const
|
|
2326
|
+
const gitForScm = typeof runtimeEnv.PUSHPALS_GIT_BIN_ABSOLUTE === "string" && runtimeEnv.PUSHPALS_GIT_BIN_ABSOLUTE.trim() ? runtimeEnv.PUSHPALS_GIT_BIN_ABSOLUTE.trim() : typeof runtimeEnv.PUSHPALS_GIT_BIN === "string" && runtimeEnv.PUSHPALS_GIT_BIN.trim() ? runtimeEnv.PUSHPALS_GIT_BIN.trim() : "git";
|
|
2327
|
+
const gitProbeCommand = [gitForScm, "--version"];
|
|
2026
2328
|
const gitAvailableForScm = await canSpawnCommand(gitProbeCommand, opts.repoRoot, runtimeEnv);
|
|
2027
2329
|
if (!scmHealthy && scmRemoteAvailable) {
|
|
2028
2330
|
if (!gitAvailableForScm) {
|
|
2029
2331
|
console.warn("[pushpals] Git is not available to embedded SourceControlManager; skipping SCM startup.");
|
|
2332
|
+
appendRuntimeServicesLogLine(runtimeServicesLogPath, "[pushpals] source_control_manager skipped: git is unavailable in embedded runtime env.");
|
|
2030
2333
|
} else {
|
|
2031
|
-
|
|
2032
|
-
console.log(`[pushpals] Embedded SourceControlManager git=${runtimeEnv.PUSHPALS_GIT_BIN}`);
|
|
2033
|
-
}
|
|
2334
|
+
console.log(`[pushpals] Embedded SourceControlManager git=${gitForScm}`);
|
|
2034
2335
|
console.log("[pushpals] Starting embedded SourceControlManager...");
|
|
2035
|
-
const sourceControlManagerService = spawnRuntimeService("source_control_manager", [runtimeBinaries.sourceControlManager, "--skip-clean-check"], opts.repoRoot, runtimeEnv,
|
|
2336
|
+
const sourceControlManagerService = spawnRuntimeService("source_control_manager", [runtimeBinaries.sourceControlManager, "--skip-clean-check"], opts.repoRoot, runtimeEnv, serviceLogPaths.source_control_manager, runtimeServicesLogPath);
|
|
2036
2337
|
services.push(sourceControlManagerService);
|
|
2037
2338
|
console.log(`[pushpals] source_control_manager log: ${sourceControlManagerService.logPath}`);
|
|
2038
2339
|
}
|
|
2039
2340
|
} else if (!scmRemoteAvailable) {
|
|
2040
2341
|
console.log(`[pushpals] Repo has no git remote "${opts.sourceControlManagerRemote}"; skipping embedded SourceControlManager.`);
|
|
2342
|
+
appendRuntimeServicesLogLine(runtimeServicesLogPath, `[pushpals] source_control_manager skipped: repo has no remote "${opts.sourceControlManagerRemote}".`);
|
|
2041
2343
|
} else if (!gitAvailableForScm) {
|
|
2042
2344
|
console.warn("[pushpals] Git is not available to embedded SourceControlManager; skipping SCM startup.");
|
|
2345
|
+
appendRuntimeServicesLogLine(runtimeServicesLogPath, "[pushpals] source_control_manager skipped: git is unavailable in embedded runtime env.");
|
|
2043
2346
|
} else {
|
|
2044
2347
|
console.log("[pushpals] SourceControlManager already healthy; skipping embedded start.");
|
|
2348
|
+
appendRuntimeServicesLogLine(runtimeServicesLogPath, "[pushpals] source_control_manager already healthy; embedded start skipped.");
|
|
2045
2349
|
}
|
|
2046
2350
|
const deadline = Date.now() + DEFAULT_RUNTIME_BOOT_TIMEOUT_MS;
|
|
2047
2351
|
while (Date.now() < deadline) {
|
|
2352
|
+
reportRemoteBuddyAutonomousEngineState();
|
|
2048
2353
|
for (let i = services.length - 1;i >= 0; i--) {
|
|
2049
2354
|
const service = services[i];
|
|
2050
2355
|
if (service.exited) {
|
|
2051
2356
|
if (isOptionalEmbeddedService(service.name)) {
|
|
2052
2357
|
console.warn(`[pushpals] Embedded ${service.name} exited during startup (code=${service.exitCode ?? "unknown"}); continuing without SCM.`);
|
|
2358
|
+
appendRuntimeServicesLogLine(runtimeServicesLogPath, `[pushpals] embedded ${service.name} exited during startup (code=${service.exitCode ?? "unknown"}); continuing.`);
|
|
2053
2359
|
const tail2 = readLogTail(service.logPath);
|
|
2054
2360
|
if (tail2) {
|
|
2055
2361
|
console.warn(`[pushpals] ${service.name} log tail:
|
|
2362
|
+
${tail2}`);
|
|
2363
|
+
appendRuntimeServicesLogLine(runtimeServicesLogPath, `[pushpals] ${service.name} log tail:
|
|
2056
2364
|
${tail2}`);
|
|
2057
2365
|
}
|
|
2058
2366
|
services.splice(i, 1);
|
|
2059
2367
|
continue;
|
|
2060
2368
|
}
|
|
2061
2369
|
const tail = readLogTail(service.logPath);
|
|
2370
|
+
appendRuntimeServicesLogLine(runtimeServicesLogPath, `[pushpals] embedded ${service.name} exited during startup (code=${service.exitCode ?? "unknown"}).`);
|
|
2062
2371
|
stopRuntimeServices(services);
|
|
2063
2372
|
throw new Error(`Embedded ${service.name} exited during startup (code=${service.exitCode ?? "unknown"}). ` + `See ${service.logPath}${tail ? `
|
|
2064
2373
|
--- ${service.name} log tail ---
|
|
@@ -2066,9 +2375,12 @@ ${tail}` : ""}`);
|
|
|
2066
2375
|
}
|
|
2067
2376
|
}
|
|
2068
2377
|
const health = localBuddyEnabled ? await probeLocalBuddy(opts.localAgentUrl) : null;
|
|
2069
|
-
|
|
2378
|
+
const remoteBuddyHealth2 = await probeRemoteBuddySessionConsumer(opts.serverUrl, opts.sessionId);
|
|
2379
|
+
if ((!localBuddyEnabled || health?.ok) && remoteBuddyHealth2.ok) {
|
|
2380
|
+
reportRemoteBuddyAutonomousEngineState();
|
|
2070
2381
|
const stabilityDeadline = Date.now() + DEFAULT_SERVICE_STABILITY_GRACE_MS;
|
|
2071
2382
|
while (Date.now() < stabilityDeadline) {
|
|
2383
|
+
reportRemoteBuddyAutonomousEngineState();
|
|
2072
2384
|
for (let i = services.length - 1;i >= 0; i--) {
|
|
2073
2385
|
const service = services[i];
|
|
2074
2386
|
if (!service.exited)
|
|
@@ -2076,14 +2388,18 @@ ${tail}` : ""}`);
|
|
|
2076
2388
|
if (isOptionalEmbeddedService(service.name)) {
|
|
2077
2389
|
const tail2 = readLogTail(service.logPath);
|
|
2078
2390
|
console.warn(`[pushpals] Embedded ${service.name} exited immediately after bootstrap (code=${service.exitCode ?? "unknown"}); continuing without SCM.`);
|
|
2391
|
+
appendRuntimeServicesLogLine(runtimeServicesLogPath, `[pushpals] embedded ${service.name} exited immediately after bootstrap (code=${service.exitCode ?? "unknown"}); continuing.`);
|
|
2079
2392
|
if (tail2) {
|
|
2080
2393
|
console.warn(`[pushpals] ${service.name} log tail:
|
|
2394
|
+
${tail2}`);
|
|
2395
|
+
appendRuntimeServicesLogLine(runtimeServicesLogPath, `[pushpals] ${service.name} log tail:
|
|
2081
2396
|
${tail2}`);
|
|
2082
2397
|
}
|
|
2083
2398
|
services.splice(i, 1);
|
|
2084
2399
|
continue;
|
|
2085
2400
|
}
|
|
2086
2401
|
const tail = readLogTail(service.logPath);
|
|
2402
|
+
appendRuntimeServicesLogLine(runtimeServicesLogPath, `[pushpals] embedded ${service.name} exited immediately after bootstrap (code=${service.exitCode ?? "unknown"}).`);
|
|
2087
2403
|
stopRuntimeServices(services);
|
|
2088
2404
|
throw new Error(`Embedded ${service.name} exited immediately after bootstrap (code=${service.exitCode ?? "unknown"}). ` + `See ${service.logPath}${tail ? `
|
|
2089
2405
|
--- ${service.name} log tail ---
|
|
@@ -2092,15 +2408,23 @@ ${tail}` : ""}`);
|
|
|
2092
2408
|
await Bun.sleep(250);
|
|
2093
2409
|
}
|
|
2094
2410
|
console.log("[pushpals] Embedded runtime is ready.");
|
|
2411
|
+
appendRuntimeServicesLogLine(runtimeServicesLogPath, "[pushpals] embedded runtime is ready.");
|
|
2095
2412
|
return services;
|
|
2096
2413
|
}
|
|
2097
2414
|
await Bun.sleep(DEFAULT_RUNTIME_BOOT_POLL_MS);
|
|
2098
2415
|
}
|
|
2099
2416
|
stopRuntimeServices(services);
|
|
2417
|
+
const remoteBuddyHealth = await probeRemoteBuddySessionConsumer(opts.serverUrl, opts.sessionId);
|
|
2418
|
+
if (!localBuddyEnabled && !remoteBuddyHealth.ok) {
|
|
2419
|
+
appendRuntimeServicesLogLine(runtimeServicesLogPath, `[pushpals] timed out waiting for RemoteBuddy session consumer readiness after ${DEFAULT_RUNTIME_BOOT_TIMEOUT_MS}ms (${remoteBuddyHealth.detail}).`);
|
|
2420
|
+
throw new Error(`Timed out waiting for RemoteBuddy session consumer readiness after ${DEFAULT_RUNTIME_BOOT_TIMEOUT_MS}ms (${remoteBuddyHealth.detail})`);
|
|
2421
|
+
}
|
|
2100
2422
|
if (!localBuddyEnabled) {
|
|
2423
|
+
appendRuntimeServicesLogLine(runtimeServicesLogPath, `[pushpals] timed out waiting for embedded runtime readiness after ${DEFAULT_RUNTIME_BOOT_TIMEOUT_MS}ms.`);
|
|
2101
2424
|
throw new Error(`Timed out waiting for embedded runtime readiness after ${DEFAULT_RUNTIME_BOOT_TIMEOUT_MS}ms`);
|
|
2102
2425
|
}
|
|
2103
|
-
|
|
2426
|
+
appendRuntimeServicesLogLine(runtimeServicesLogPath, `[pushpals] timed out waiting for LocalBuddy at ${opts.localAgentUrl} and RemoteBuddy session consumer after ${DEFAULT_RUNTIME_BOOT_TIMEOUT_MS}ms.`);
|
|
2427
|
+
throw new Error(`Timed out waiting for LocalBuddy at ${opts.localAgentUrl} and RemoteBuddy session consumer after ${DEFAULT_RUNTIME_BOOT_TIMEOUT_MS}ms`);
|
|
2104
2428
|
}
|
|
2105
2429
|
function readCliState(pathValue) {
|
|
2106
2430
|
if (!existsSync4(pathValue))
|
|
@@ -2153,7 +2477,6 @@ async function looksLikeMonitoringHub(url) {
|
|
|
2153
2477
|
function buildMonitoringHubRuntimeBootstrap(opts) {
|
|
2154
2478
|
return {
|
|
2155
2479
|
serverUrl: opts.serverUrl,
|
|
2156
|
-
localAgentUrl: opts.localAgentUrl,
|
|
2157
2480
|
sessionId: opts.sessionId,
|
|
2158
2481
|
clientId: `cli-monitor-${opts.sessionId}`,
|
|
2159
2482
|
clientKind: "cli_monitor",
|
|
@@ -2238,7 +2561,10 @@ async function serveBundledMonitoringHub(assetRoot, pathname, bootstrap) {
|
|
|
2238
2561
|
});
|
|
2239
2562
|
}
|
|
2240
2563
|
function buildEmbeddedMonitoringHubHtml(opts) {
|
|
2241
|
-
const bootstrap = jsonHtmlBootstrap(
|
|
2564
|
+
const bootstrap = jsonHtmlBootstrap({
|
|
2565
|
+
serverUrl: opts.serverUrl,
|
|
2566
|
+
sessionId: opts.sessionId
|
|
2567
|
+
});
|
|
2242
2568
|
return `<!doctype html>
|
|
2243
2569
|
<html lang="en">
|
|
2244
2570
|
<head>
|
|
@@ -2334,7 +2660,6 @@ function buildEmbeddedMonitoringHubHtml(opts) {
|
|
|
2334
2660
|
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
2661
|
metaEl.innerHTML = [
|
|
2336
2662
|
'<span class="pill">server ' + esc(boot.serverUrl) + '</span>',
|
|
2337
|
-
'<span class="pill">localbuddy ' + esc(boot.localAgentUrl) + '</span>',
|
|
2338
2663
|
'<span class="pill">session ' + esc(boot.sessionId) + '</span>',
|
|
2339
2664
|
'<span class="pill">repo ' + esc(repo?.root ?? repo?.remoteUrl ?? "current repo") + '</span>'
|
|
2340
2665
|
].join("");
|
|
@@ -2391,7 +2716,6 @@ async function startEmbeddedMonitoringHub(opts) {
|
|
|
2391
2716
|
}
|
|
2392
2717
|
const bootstrap = buildMonitoringHubRuntimeBootstrap({
|
|
2393
2718
|
serverUrl: opts.serverUrl,
|
|
2394
|
-
localAgentUrl: opts.localAgentUrl,
|
|
2395
2719
|
sessionId: opts.sessionId
|
|
2396
2720
|
});
|
|
2397
2721
|
const candidatePorts = Array.from({ length: MONITOR_SCAN_PORTS }, (_, index) => opts.preferredPort + index).concat(0);
|
|
@@ -2462,72 +2786,30 @@ async function resolveMonitoringHub(opts) {
|
|
|
2462
2786
|
}
|
|
2463
2787
|
return embedded;
|
|
2464
2788
|
}
|
|
2465
|
-
async function
|
|
2466
|
-
let response;
|
|
2789
|
+
async function sendMessageToServerSession(serverUrl, sessionId, text) {
|
|
2467
2790
|
try {
|
|
2468
|
-
response = await fetchWithTimeout(`${
|
|
2791
|
+
const response = await fetchWithTimeout(`${serverUrl}/sessions/${encodeURIComponent(sessionId)}/message`, {
|
|
2469
2792
|
method: "POST",
|
|
2470
2793
|
headers: { "Content-Type": "application/json" },
|
|
2471
2794
|
body: JSON.stringify({ text })
|
|
2472
|
-
},
|
|
2795
|
+
}, 15000);
|
|
2796
|
+
if (!response.ok) {
|
|
2797
|
+
const detail = await response.text().catch(() => "");
|
|
2798
|
+
console.error(`[pushpals] Session message rejected: HTTP ${response.status}${detail ? ` ${detail}` : ""}`);
|
|
2799
|
+
return false;
|
|
2800
|
+
}
|
|
2801
|
+
return true;
|
|
2473
2802
|
} 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.");
|
|
2803
|
+
console.error(`[pushpals] Failed to reach server session endpoint: ${String(err)}`);
|
|
2485
2804
|
return false;
|
|
2486
2805
|
}
|
|
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
2806
|
}
|
|
2527
2807
|
function formatSessionEventLine(event) {
|
|
2528
2808
|
const type = String(event.type ?? "").toLowerCase();
|
|
2529
2809
|
const from = String(event.from ?? "");
|
|
2530
2810
|
const payload = event.payload ?? {};
|
|
2811
|
+
if (!shouldDisplayInteractiveSessionEvent(event))
|
|
2812
|
+
return null;
|
|
2531
2813
|
if (type === "message")
|
|
2532
2814
|
return null;
|
|
2533
2815
|
if (type === "assistant_message") {
|
|
@@ -2686,9 +2968,23 @@ async function main() {
|
|
|
2686
2968
|
process.exit(1);
|
|
2687
2969
|
}
|
|
2688
2970
|
const config = preparedRuntime.runtimePreflight.config;
|
|
2971
|
+
if (config.remotebuddy.autonomy.enabled) {
|
|
2972
|
+
console.log("[pushpals] RemoteBuddy autonomy is enabled for CLI.");
|
|
2973
|
+
} else {
|
|
2974
|
+
console.warn("[pushpals] RemoteBuddy autonomy is disabled in config (remotebuddy.autonomy.enabled=false); continuing.");
|
|
2975
|
+
}
|
|
2689
2976
|
const serverUrl = normalizeLoopbackUrl(parsed.serverUrl ?? process.env.PUSHPALS_SERVER_URL, config.server.url);
|
|
2690
2977
|
const localAgentUrl = normalizeLoopbackUrl(parsed.localAgentUrl ?? process.env.EXPO_PUBLIC_LOCAL_AGENT_URL, config.client.localAgentUrl);
|
|
2691
2978
|
const sessionId = String(parsed.sessionId ?? process.env.PUSHPALS_SESSION_ID ?? config.sessionId).trim();
|
|
2979
|
+
const cliVersion = String(process.env.PUSHPALS_CLI_PACKAGE_VERSION ?? "").trim() || "unknown";
|
|
2980
|
+
const cliClient = {
|
|
2981
|
+
clientId: createRuntimeClientId("cli"),
|
|
2982
|
+
kind: "cli",
|
|
2983
|
+
label: "CLI",
|
|
2984
|
+
version: cliVersion,
|
|
2985
|
+
platform: `${process.platform}/${process.arch}`,
|
|
2986
|
+
repoRoot
|
|
2987
|
+
};
|
|
2692
2988
|
let autoStartedServices = [];
|
|
2693
2989
|
const stopAutoStartedServices = () => {
|
|
2694
2990
|
if (autoStartedServices.length === 0)
|
|
@@ -2697,66 +2993,76 @@ async function main() {
|
|
|
2697
2993
|
autoStartedServices = [];
|
|
2698
2994
|
};
|
|
2699
2995
|
let serverHealthy = await probeServer(serverUrl);
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2996
|
+
const serverWasAlreadyHealthy = serverHealthy;
|
|
2997
|
+
let remoteBuddyConsumerHealth = {
|
|
2998
|
+
ok: false,
|
|
2999
|
+
detail: `No connected RemoteBuddy session consumer found for session ${sessionId}`
|
|
3000
|
+
};
|
|
3001
|
+
if (!serverHealthy) {
|
|
3002
|
+
if (!parsed.noAutoStart) {
|
|
3003
|
+
try {
|
|
3004
|
+
autoStartedServices = await autoStartRuntimeServices({
|
|
3005
|
+
repoRoot,
|
|
3006
|
+
serverUrl,
|
|
3007
|
+
localAgentUrl,
|
|
3008
|
+
sessionId,
|
|
3009
|
+
sourceControlManagerPort: config.sourceControlManager.port,
|
|
3010
|
+
sourceControlManagerRemote: config.sourceControlManager.remote,
|
|
3011
|
+
preparedRuntime,
|
|
3012
|
+
requestedRuntimeTag: parsed.runtimeTag,
|
|
3013
|
+
startLocalBuddy: resolveCliLocalBuddyAutostart(parsed.runtimeOnly, Boolean(config.localbuddy.enabled))
|
|
3014
|
+
});
|
|
3015
|
+
serverHealthy = await probeServer(serverUrl);
|
|
3016
|
+
} catch (err) {
|
|
3017
|
+
console.error(`[pushpals] Auto-start failed: ${String(err)}`);
|
|
3018
|
+
stopAutoStartedServices();
|
|
3019
|
+
}
|
|
3020
|
+
}
|
|
3021
|
+
if (!serverHealthy) {
|
|
3022
|
+
console.error(`[pushpals] Server is unavailable at ${serverUrl}.`);
|
|
3023
|
+
if (parsed.noAutoStart) {
|
|
3024
|
+
console.error("[pushpals] Auto-start is disabled (--no-auto-start).");
|
|
3025
|
+
} else {
|
|
3026
|
+
console.error("[pushpals] Auto-start could not bring the embedded runtime online.");
|
|
3027
|
+
}
|
|
3028
|
+
process.exit(1);
|
|
3029
|
+
}
|
|
3030
|
+
}
|
|
3031
|
+
try {
|
|
3032
|
+
await ensureServerRepoAffinity(serverUrl, repoRoot);
|
|
3033
|
+
} catch (err) {
|
|
3034
|
+
stopAutoStartedServices();
|
|
3035
|
+
console.error(`[pushpals] Repo affinity check failed: ${String(err)}`);
|
|
3036
|
+
process.exit(1);
|
|
3037
|
+
}
|
|
3038
|
+
let activeSessionId = sessionId;
|
|
3039
|
+
if (!parsed.runtimeOnly) {
|
|
2703
3040
|
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);
|
|
3041
|
+
activeSessionId = await ensureServerSession(serverUrl, sessionId, cliClient);
|
|
2716
3042
|
} catch (err) {
|
|
2717
|
-
console.error(`[pushpals] Auto-start failed: ${String(err)}`);
|
|
2718
3043
|
stopAutoStartedServices();
|
|
3044
|
+
console.error(`[pushpals] Session bootstrap failed: ${String(err)}`);
|
|
3045
|
+
process.exit(1);
|
|
2719
3046
|
}
|
|
2720
3047
|
}
|
|
2721
|
-
|
|
3048
|
+
remoteBuddyConsumerHealth = await probeRemoteBuddySessionConsumer(serverUrl, activeSessionId);
|
|
3049
|
+
if (!serverHealthy) {
|
|
2722
3050
|
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
3051
|
process.exit(1);
|
|
2729
3052
|
}
|
|
2730
|
-
if (!
|
|
2731
|
-
|
|
2732
|
-
|
|
3053
|
+
if (!remoteBuddyConsumerHealth.ok) {
|
|
3054
|
+
stopAutoStartedServices();
|
|
3055
|
+
console.error(`[pushpals] RemoteBuddy is not ready for session ${activeSessionId}: ${remoteBuddyConsumerHealth.detail}`);
|
|
3056
|
+
if (serverWasAlreadyHealthy) {
|
|
3057
|
+
console.error("[pushpals] A PushPals runtime is already serving this repo, but it does not have a connected RemoteBuddy consumer for this session.");
|
|
3058
|
+
console.error("[pushpals] Refusing to start another embedded RemoteBuddy against the same runtime. Restart or stop the existing runtime before retrying.");
|
|
3059
|
+
} else if (parsed.noAutoStart) {
|
|
2733
3060
|
console.error("[pushpals] Auto-start is disabled (--no-auto-start).");
|
|
2734
3061
|
} else {
|
|
2735
|
-
console.error("[pushpals] Auto-start could not bring
|
|
3062
|
+
console.error("[pushpals] Auto-start could not bring the embedded runtime into a usable state.");
|
|
2736
3063
|
}
|
|
2737
3064
|
process.exit(1);
|
|
2738
3065
|
}
|
|
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
3066
|
const statePath = resolveCliStatePath(repoRoot);
|
|
2761
3067
|
const saved = statePath ? readCliState(statePath) : {};
|
|
2762
3068
|
const preferredHubUrl = normalizeUrl(parsed.monitoringHubUrl ?? process.env.PUSHPALS_MONITOR_URL ?? saved.monitoringHubUrl ?? "");
|
|
@@ -2765,8 +3071,7 @@ async function main() {
|
|
|
2765
3071
|
preferredUrl: preferredHubUrl,
|
|
2766
3072
|
fallbackPort: monitorPort,
|
|
2767
3073
|
serverUrl,
|
|
2768
|
-
|
|
2769
|
-
sessionId: localBuddySessionId
|
|
3074
|
+
sessionId: activeSessionId
|
|
2770
3075
|
});
|
|
2771
3076
|
const monitoringHubUrl = monitoringHub?.url ?? "";
|
|
2772
3077
|
if (statePath) {
|
|
@@ -2774,7 +3079,7 @@ async function main() {
|
|
|
2774
3079
|
monitoringHubUrl: monitoringHubUrl || undefined,
|
|
2775
3080
|
serverUrl,
|
|
2776
3081
|
localAgentUrl,
|
|
2777
|
-
sessionId:
|
|
3082
|
+
sessionId: activeSessionId,
|
|
2778
3083
|
repoRoot
|
|
2779
3084
|
});
|
|
2780
3085
|
} else {
|
|
@@ -2790,8 +3095,7 @@ async function main() {
|
|
|
2790
3095
|
console.log("[pushpals] monitoringHubUrl=unavailable");
|
|
2791
3096
|
}
|
|
2792
3097
|
console.log(`[pushpals] serverUrl=${serverUrl}`);
|
|
2793
|
-
console.log(`[pushpals]
|
|
2794
|
-
console.log(`[pushpals] sessionId=${localBuddySessionId}`);
|
|
3098
|
+
console.log(`[pushpals] sessionId=${activeSessionId}`);
|
|
2795
3099
|
console.log(`[pushpals] repoRoot=${repoRoot}`);
|
|
2796
3100
|
console.log(`[pushpals] cliStateFile=${statePath ?? "unavailable"}`);
|
|
2797
3101
|
if (parsed.runtimeOnly) {
|
|
@@ -2799,15 +3103,6 @@ async function main() {
|
|
|
2799
3103
|
} else {
|
|
2800
3104
|
console.log("[pushpals] Type a message and press Enter. Use /exit or exit to quit.");
|
|
2801
3105
|
}
|
|
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
3106
|
const streamAbort = new AbortController;
|
|
2812
3107
|
let rl = null;
|
|
2813
3108
|
const printIncoming = (line) => {
|
|
@@ -2822,7 +3117,7 @@ ${line}
|
|
|
2822
3117
|
}
|
|
2823
3118
|
console.log(line);
|
|
2824
3119
|
};
|
|
2825
|
-
const streamTask = parsed.noStream ? Promise.resolve() : parsed.runtimeOnly ? Promise.resolve() : runSessionStream(serverUrl,
|
|
3120
|
+
const streamTask = parsed.noStream ? Promise.resolve() : parsed.runtimeOnly ? Promise.resolve() : runSessionStream(serverUrl, activeSessionId, cliClient, printIncoming, streamAbort.signal);
|
|
2826
3121
|
let shuttingDown = false;
|
|
2827
3122
|
const requestStop = () => {
|
|
2828
3123
|
if (shuttingDown)
|
|
@@ -2900,8 +3195,7 @@ ${line}
|
|
|
2900
3195
|
}
|
|
2901
3196
|
if (text === "/status") {
|
|
2902
3197
|
console.log(`[pushpals] serverUrl=${serverUrl}`);
|
|
2903
|
-
console.log(`[pushpals]
|
|
2904
|
-
console.log(`[pushpals] sessionId=${localBuddySessionId}`);
|
|
3198
|
+
console.log(`[pushpals] sessionId=${activeSessionId}`);
|
|
2905
3199
|
console.log(`[pushpals] repoRoot=${repoRoot}`);
|
|
2906
3200
|
console.log(monitoringHubUrl ? `[pushpals] monitoringHubUrl=${monitoringHubUrl}` : "[pushpals] monitoringHubUrl=unavailable");
|
|
2907
3201
|
rl.prompt();
|
|
@@ -2918,7 +3212,13 @@ ${line}
|
|
|
2918
3212
|
rl.prompt();
|
|
2919
3213
|
continue;
|
|
2920
3214
|
}
|
|
2921
|
-
const
|
|
3215
|
+
const normalized = normalizeCliInteractiveMessage(text);
|
|
3216
|
+
if (normalized.usageMessage) {
|
|
3217
|
+
console.log(`[pushpals] ${normalized.usageMessage}`);
|
|
3218
|
+
rl.prompt();
|
|
3219
|
+
continue;
|
|
3220
|
+
}
|
|
3221
|
+
const ok = await sendMessageToServerSession(serverUrl, activeSessionId, normalized.text);
|
|
2922
3222
|
if (!ok) {
|
|
2923
3223
|
console.log("[pushpals] Message failed.");
|
|
2924
3224
|
}
|
|
@@ -2937,14 +3237,22 @@ export {
|
|
|
2937
3237
|
startEmbeddedMonitoringHub,
|
|
2938
3238
|
resolveCommandPath,
|
|
2939
3239
|
resolveCliStatePath,
|
|
3240
|
+
resolveCliLocalBuddyAutostart,
|
|
2940
3241
|
resolveBundledRuntimeAssetSource,
|
|
2941
3242
|
resolveBundledMonitoringHubRoot,
|
|
2942
3243
|
prepareCliRuntime,
|
|
3244
|
+
normalizeRepoPathForComparison,
|
|
3245
|
+
normalizeCliInteractiveMessage,
|
|
2943
3246
|
normalizeChildProcessEnv,
|
|
2944
3247
|
isCliExitCommand,
|
|
2945
3248
|
injectMonitoringHubBootstrap,
|
|
2946
3249
|
formatTimestampedCliLine,
|
|
3250
|
+
formatSessionEventLine,
|
|
3251
|
+
extractRemoteBuddySessionConsumerHealth,
|
|
3252
|
+
extractRemoteBuddyAutonomousEngineState,
|
|
3253
|
+
bundledMonitoringHubNeedsRefresh,
|
|
2947
3254
|
buildServiceStopCommand,
|
|
3255
|
+
buildRuntimeServiceLogPaths,
|
|
2948
3256
|
buildOpenMonitoringHubCommand,
|
|
2949
3257
|
buildEmbeddedRuntimeEnv,
|
|
2950
3258
|
buildEmbeddedMonitoringHubHtml,
|