agent-relay-orchestrator 0.12.1 → 0.12.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-relay-orchestrator",
3
- "version": "0.12.1",
3
+ "version": "0.12.2",
4
4
  "description": "Agent Relay orchestrator — manages agent lifecycle across hosts",
5
5
  "type": "module",
6
6
  "bin": {
package/src/api.ts CHANGED
@@ -5,7 +5,7 @@ import { proxyArtifactRequest } from "./artifact-proxy";
5
5
  import type { OrchestratorConfig } from "./config";
6
6
  import type { ProviderProbeCache } from "./provider-probe";
7
7
  import type { RelayClient } from "./relay";
8
- import { captureSession, captureTerminal, createTerminalGuest, listSessions, sendTerminalInput, resizeTerminal, stopTerminalGuest } from "./spawn";
8
+ import { captureSession, captureSessionMirror, captureTerminal, createTerminalGuest, listSessions, sendTerminalInput, resizeTerminal, stopTerminalGuest } from "./spawn";
9
9
  import { acquireTerminalStream, type TerminalStreamHandle, type TerminalStreamSubscriber } from "./terminal-stream";
10
10
  import { VERSION, runtimeMetadata } from "./version";
11
11
  import { previewWorkspaceMerge, probeWorkspace, workspaceDiff, workspaceGitState } from "./workspace-probe";
@@ -545,6 +545,11 @@ export function startApiServer(config: OrchestratorConfig, probeCache: ProviderP
545
545
  try {
546
546
  const session = decodeURIComponent(logMatch[1]!);
547
547
  const lines = Number(url.searchParams.get("lines") || "100");
548
+ // ?stream=mirror returns the clean session-mirror diagnostics log instead
549
+ // of the provider's ANSI TUI capture.
550
+ if (url.searchParams.get("stream") === "mirror") {
551
+ return json(captureSessionMirror(session, config, Number.isFinite(lines) ? lines : 200));
552
+ }
548
553
  return json(captureSession(session, config, Number.isFinite(lines) ? lines : 100, {
549
554
  raw: url.searchParams.get("raw") === "1",
550
555
  }));
package/src/spawn.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { chmodSync, closeSync, existsSync, mkdirSync, openSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
2
2
  import { homedir } from "node:os";
3
- import { isAbsolute, join, relative, resolve } from "node:path";
3
+ import { dirname, isAbsolute, join, relative, resolve } from "node:path";
4
4
  import { artifactProxyBaseUrl } from "./artifact-proxy";
5
5
  import type { OrchestratorConfig } from "./config";
6
6
  import type { ManagedAgentReport, ManagedSessionExitDiagnostics } from "./relay";
@@ -951,6 +951,39 @@ export function captureSession(
951
951
  };
952
952
  }
953
953
 
954
+ // Mirrors the runner's safeLogName so the orchestrator resolves the same filename.
955
+ function safeMirrorLogName(value: string): string {
956
+ return value.replace(/[^a-zA-Z0-9_.-]+/g, "_").slice(0, 180);
957
+ }
958
+
959
+ // Read the clean, ANSI-free session-mirror diagnostics log for a managed agent.
960
+ // Accepts either the tmux session name or the agent id; the mirror log is keyed by
961
+ // agent id. Returns the same shape as captureSession so the proxy is uniform.
962
+ export function captureSessionMirror(
963
+ name: string,
964
+ _config: OrchestratorConfig,
965
+ lines = 200,
966
+ ): { session: string; lines: string[]; running: boolean; mirror: true } {
967
+ const records = loadState();
968
+ const record = records.find((r) => r.name === name) ?? records.find((r) => r.agentId === name);
969
+ const agentId = record?.agentId ?? name;
970
+ const running = record ? isSessionRecordAlive(record) : false;
971
+ // The mirror log lives in the same directory as the provider log (both written
972
+ // by the same user on this host). Derive from the record's logFile when known so
973
+ // it tracks any per-session log relocation.
974
+ const logDir = record?.logFile ? dirname(record.logFile) : LOG_DIR;
975
+ const mirrorPath = join(logDir, `session-mirror-${safeMirrorLogName(agentId)}.log`);
976
+ let content: string;
977
+ try {
978
+ content = readFileSync(mirrorPath, "utf8");
979
+ } catch {
980
+ return { session: name, lines: [], running, mirror: true };
981
+ }
982
+ const allLines = content.split(/\r?\n/).filter(Boolean);
983
+ const safeLines = Math.min(Math.max(lines, 1), 2000);
984
+ return { session: name, lines: allLines.slice(-safeLines), running, mirror: true };
985
+ }
986
+
954
987
  export function captureTerminal(name: string, config: OrchestratorConfig): TerminalSnapshot {
955
988
  if (!name.startsWith(`${config.tmuxPrefix}-`)) throw new Error("session is not managed by this orchestrator");
956
989