rterm-backend 3.0.2 → 3.0.4

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.
Files changed (2) hide show
  1. package/bin/gybackend.cjs +55 -23
  2. package/package.json +2 -2
package/bin/gybackend.cjs CHANGED
@@ -347146,6 +347146,9 @@ function createObservabilityBridge(deps) {
347146
347146
  dashboardState: async () => requireObs(deps).dashboard.state(),
347147
347147
  /** dashboard summary line. */
347148
347148
  dashboardSummary: async () => requireObs(deps).dashboard.summary(),
347149
+ /** monitor-status diagnostic: why stats aren't displaying per terminal. */
347150
+ monitorStatus: async () => requireObs(deps).monitorStatus.report(),
347151
+ monitorStatusSummary: async () => ({ summary: requireObs(deps).monitorStatus.summary() }),
347149
347152
  // ── secrets vault (metadata only — never values) ────────────────────────
347150
347153
  secretsList: async (params) => requireObs(deps).secrets.list(params),
347151
347154
  secretsSet: async (params) => {
@@ -347291,6 +347294,8 @@ var OBSERVABILITY_METHODS = [
347291
347294
  "observability:metricsPrometheus",
347292
347295
  "observability:dashboardState",
347293
347296
  "observability:dashboardSummary",
347297
+ "observability:monitorStatus",
347298
+ "observability:monitorStatusSummary",
347294
347299
  "observability:secretsList",
347295
347300
  "observability:secretsSet",
347296
347301
  "observability:secretsDelete",
@@ -347678,6 +347683,21 @@ async function getCloudInventory(args, context2) {
347678
347683
  emit9(context2, "get_cloud_inventory", args, out);
347679
347684
  return out;
347680
347685
  }
347686
+ var getMonitorStatusSchema = external_exports.object({
347687
+ format: external_exports.enum(["report", "summary"]).optional().describe("'report' = full per-terminal JSON; 'summary' = compact text (default).")
347688
+ });
347689
+ async function getMonitorStatus(args, context2) {
347690
+ const { o, msg } = obs(context2, "get_monitor_status", args);
347691
+ if (!o) return msg;
347692
+ let out;
347693
+ if (args.format === "report") {
347694
+ out = JSON.stringify(o.monitorStatus.report(), null, 2);
347695
+ } else {
347696
+ out = o.monitorStatus.summary();
347697
+ }
347698
+ emit9(context2, "get_monitor_status", args, out);
347699
+ return out;
347700
+ }
347681
347701
  var getLiveDashboardSchema = external_exports.object({
347682
347702
  action: external_exports.enum(["state", "subscribers"]).describe("'state' = current dashboard state; 'subscribers' = connected client count.")
347683
347703
  });
@@ -348149,6 +348169,11 @@ function buildToolsForModel(readFileSupport) {
348149
348169
  description: "Cloud resource inventory (AWS/GCP/Azure) \u2014 summary counts by provider/state, query instances (filter by provider/state/region), or pull fresh inventory.",
348150
348170
  schema: getCloudInventorySchema
348151
348171
  },
348172
+ {
348173
+ name: "get_monitor_status",
348174
+ description: "Monitor-status diagnostic \u2014 reports why stats aren't displaying per terminal (publisher wired, session exists, collection stuck in-flight, platform, last-collect age).",
348175
+ schema: getMonitorStatusSchema
348176
+ },
348152
348177
  {
348153
348178
  name: "get_live_dashboard",
348154
348179
  description: "Live multi-client dashboard \u2014 read the current unified dashboard state/summary, or the number of connected dashboard subscribers.",
@@ -348232,6 +348257,7 @@ var toolImplementations = {
348232
348257
  manageGitops,
348233
348258
  managePlaybookVersion,
348234
348259
  getCloudInventory,
348260
+ getMonitorStatus,
348235
348261
  getLiveDashboard,
348236
348262
  ingestApmSpans,
348237
348263
  getApmSummary,
@@ -386599,6 +386625,32 @@ function renderLiveDashboardHtml(state, opts = {}) {
386599
386625
  return renderDashboardHtml(state, { ...opts, live: true, refreshSeconds: 0 });
386600
386626
  }
386601
386627
 
386628
+ // ../../packages/backend/src/services/dashboard/dashboardHttpAuth.ts
386629
+ function isLoopbackAddress(raw) {
386630
+ return /^(127\.|::1|::ffff:127\.)/.test(raw) || raw === "" || raw === "localhost";
386631
+ }
386632
+ function extractDashboardToken(req) {
386633
+ const authz = typeof req.headers.authorization === "string" ? req.headers.authorization : "";
386634
+ const bearer = /^Bearer\s+(.+)$/i.exec(authz)?.[1]?.trim();
386635
+ const headerTok = typeof req.headers["x-access-token"] === "string" ? String(req.headers["x-access-token"]).trim() : "";
386636
+ let queryTok = "";
386637
+ try {
386638
+ queryTok = new URL(req.url ?? "/", "http://localhost").searchParams.get("access_token")?.trim() ?? "";
386639
+ } catch {
386640
+ }
386641
+ return bearer || headerTok || queryTok;
386642
+ }
386643
+ async function dashboardHttpAuthorized(req, verifyToken) {
386644
+ if (isLoopbackAddress(String(req.socket?.remoteAddress ?? ""))) return true;
386645
+ const token = extractDashboardToken(req);
386646
+ if (!token) return false;
386647
+ try {
386648
+ return await verifyToken(token);
386649
+ } catch {
386650
+ return false;
386651
+ }
386652
+ }
386653
+
386602
386654
  // ../../packages/backend/src/services/ResourceMonitorService.ts
386603
386655
  var import_os2 = __toESM(require("os"), 1);
386604
386656
  var DEFAULT_POLL_INTERVAL_MS = 2e3;
@@ -386842,6 +386894,7 @@ var ResourceMonitorService = class {
386842
386894
  }
386843
386895
  const sourceTerminalId = session.sourceTerminalId || terminalIds[0];
386844
386896
  const snapshot = await this.collectSnapshot(sourceTerminalId);
386897
+ session.lastCollectAt = Date.now();
386845
386898
  if (this.publisher) {
386846
386899
  terminalIds.forEach((terminalId) => {
386847
386900
  this.publisher?.(
@@ -388704,27 +388757,6 @@ async function startGyBackend() {
388704
388757
  gatewayService.broadcastRaw("skills:updated", result.skills);
388705
388758
  gatewayService.broadcastRaw("memory:updated", result.memory);
388706
388759
  };
388707
- const dashboardHttpAuthorized = async (req) => {
388708
- const remote = String(req.socket?.remoteAddress ?? "");
388709
- if (/^(127\.|::1|::ffff:127\.)/.test(remote) || remote === "" || remote === "localhost") {
388710
- return true;
388711
- }
388712
- const authz = typeof req.headers.authorization === "string" ? req.headers.authorization : "";
388713
- const bearer = /^Bearer\s+(.+)$/i.exec(authz)?.[1]?.trim();
388714
- const headerTok = typeof req.headers["x-access-token"] === "string" ? String(req.headers["x-access-token"]).trim() : "";
388715
- let queryTok = "";
388716
- try {
388717
- queryTok = new URL(req.url ?? "/", "http://localhost").searchParams.get("access_token")?.trim() ?? "";
388718
- } catch {
388719
- }
388720
- const token = bearer || headerTok || queryTok;
388721
- if (!token) return false;
388722
- try {
388723
- return await accessTokenService.verifyToken(token);
388724
- } catch {
388725
- return false;
388726
- }
388727
- };
388728
388760
  const wsGatewayControlService = new WebSocketGatewayControlService({
388729
388761
  createAdapter: (host, port, ipFilter) => new WebSocketGatewayAdapter(gatewayService, {
388730
388762
  host,
@@ -388742,7 +388774,7 @@ async function startGyBackend() {
388742
388774
  {
388743
388775
  path: "/dashboard",
388744
388776
  handler: async (req, res) => {
388745
- if (!await dashboardHttpAuthorized(req)) {
388777
+ if (!await dashboardHttpAuthorized(req, (t) => accessTokenService.verifyToken(t))) {
388746
388778
  res.writeHead(401, { "content-type": "text/plain; charset=utf-8" });
388747
388779
  res.end("missing/invalid access token");
388748
388780
  return;
@@ -388760,7 +388792,7 @@ async function startGyBackend() {
388760
388792
  {
388761
388793
  path: "/dashboard/json",
388762
388794
  handler: async (req, res) => {
388763
- if (!await dashboardHttpAuthorized(req)) {
388795
+ if (!await dashboardHttpAuthorized(req, (t) => accessTokenService.verifyToken(t))) {
388764
388796
  res.writeHead(401, { "content-type": "text/plain; charset=utf-8" });
388765
388797
  res.end("missing/invalid access token");
388766
388798
  return;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "rterm-backend",
3
- "version": "3.0.2",
4
- "description": "rterm-backend — the headless AI-native backend for RTerm (dual-published as neuralOS). run RTerm-as-a-service (AI agent, SSH/WinRM/Serial/local terminals, fleet orchestration, advanced automation, SRE observability, Netdata, AWS APerf, plugin system, governance/audit, Prometheus/OTel metrics export, secrets vault, on-call paging, AI cost budgets, GitOps, cloud inventory, APM/DEM/Infra/ETW ingestion, AgentSpan durable-agent bridge). The RTerm desktop app stays RTerm; neuralOS is the standalone backend daemon. Dual-published as rterm-backend. v3.0.2: live browser dashboard at /dashboard (same-port HTTP + WS push).",
3
+ "version": "3.0.4",
4
+ "description": "rterm-backend — the headless AI-native backend for RTerm (dual-published as neuralOS). run RTerm-as-a-service (AI agent, SSH/WinRM/Serial/local terminals, fleet orchestration, advanced automation, SRE observability, Netdata, AWS APerf, plugin system, governance/audit, Prometheus/OTel metrics export, secrets vault, on-call paging, AI cost budgets, GitOps, cloud inventory, APM/DEM/Infra/ETW ingestion, AgentSpan durable-agent bridge). The RTerm desktop app stays RTerm; neuralOS is the standalone backend daemon. Dual-published as rterm-backend. v3.0.4: monitor-status diagnostic over RPC + agent tool.",
5
5
  "main": "bin/gybackend.cjs",
6
6
  "bin": {
7
7
  "gybackend": "bin/gybackend.cjs",