replicas-cli 0.2.322 → 0.2.324

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/dist/index.mjs +79 -15
  2. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -8026,7 +8026,7 @@ Prints the live noVNC viewer URL (\`https://6080-<hash>.tryreplicas.com/\`) for
8026
8026
  If invoked very early in the workspace lifecycle, \`info\` will poll briefly while the engine finishes registering the preview, then error if it's still not available.
8027
8027
 
8028
8028
  ### \`replicas computer status\`
8029
- Prints which desktop services are running and the active preview URL (if any). Useful for debugging when a tool call seems to be doing nothing.
8029
+ Checks and repairs the desktop bridge, then prints which desktop services are running and the active preview URL (if any). Useful for debugging when a tool call seems to be doing nothing.
8030
8030
 
8031
8031
  ### \`replicas computer screenshot <path> [--raw] [--grid [px]]\`
8032
8032
  Captures the current desktop to a PNG at the given path.
@@ -8143,7 +8143,7 @@ Then embed the printed \`![\u2026](\u2026)\` line in your chat reply. See \`MEDI
8143
8143
  ## Failure modes
8144
8144
 
8145
8145
  - **"Desktop services script missing"**: workspace image is older than this skill. Tell the user - nothing you can do from the CLI side.
8146
- - **\`xdotool ... failed: Can't open display\`**: Xvfb didn't come up. \`replicas computer status\` will show which service is dead. Re-running any CLI command auto-attempts to start it.
8146
+ - **\`xdotool ... failed: Can't open display\`**: Xvfb didn't come up. \`replicas computer status\` will show which service is dead and auto-repair the desktop bridge.
8147
8147
  - **Browser doesn't appear after \`launch chrome\`**: run \`replicas computer observe /tmp/state.png\`. Chrome cold-start on the virtual display takes ~500ms but bigger pages take longer.
8148
8148
  - **Live preview shows static / black screen**: the browser may have crashed. \`replicas computer status\` should show no Chrome process - re-launch.
8149
8149
  - **\`replicas computer info\` errors with "not registered"**: engine couldn't register the preview at startup (transient monolith error, or warming mode). Re-running the engine usually fixes it. Until it's registered, the Desktop tab will show a placeholder.
@@ -9536,7 +9536,7 @@ var HOOK_EXEC_MAX_BUFFER_BYTES = 10 * 1024 * 1024;
9536
9536
  var REPLICAS_CONFIG_FILENAMES = ["replicas.json", "replicas.yaml", "replicas.yml"];
9537
9537
 
9538
9538
  // ../shared/src/cli-version.ts
9539
- var CLI_VERSION = "0.2.322";
9539
+ var CLI_VERSION = "0.2.324";
9540
9540
 
9541
9541
  // ../shared/src/engine/environment.ts
9542
9542
  var DESKTOP_NOVNC_PORT = 6080;
@@ -10486,6 +10486,11 @@ function parseClaudeEvents(events, parentToolUseId) {
10486
10486
  const toolCallMap = /* @__PURE__ */ new Map();
10487
10487
  const taskMessageMap = /* @__PURE__ */ new Map();
10488
10488
  const partialIndexes = /* @__PURE__ */ new Map();
10489
+ const completedStreamIds = /* @__PURE__ */ new Set();
10490
+ const supersededStreamIds = /* @__PURE__ */ new Set();
10491
+ let liveStreamId = null;
10492
+ const assistantThinking = /* @__PURE__ */ new Map();
10493
+ const assistantTextCounts = /* @__PURE__ */ new Map();
10489
10494
  const taskAccumulator = new TaskAccumulator();
10490
10495
  const taskSnapshot = () => taskAccumulator.getTasks().map((task) => ({
10491
10496
  text: task.subject,
@@ -10496,6 +10501,10 @@ function parseClaudeEvents(events, parentToolUseId) {
10496
10501
  if (event.type === CLAUDE_PARTIAL_MESSAGE_EVENT_TYPE) {
10497
10502
  const payload = coerceClaudePartialMessagePayload(event.payload);
10498
10503
  if (!payload) return;
10504
+ if (liveStreamId !== null && liveStreamId !== payload.streamId) {
10505
+ supersededStreamIds.add(liveStreamId);
10506
+ }
10507
+ liveStreamId = payload.streamId;
10499
10508
  const existing = partialIndexes.get(payload.streamId) ?? {};
10500
10509
  if (payload.thinking) {
10501
10510
  const reasoningMessage = {
@@ -10558,24 +10567,51 @@ function parseClaudeEvents(events, parentToolUseId) {
10558
10567
  if (event.type === "claude-assistant") {
10559
10568
  const messageId = event.payload.message?.id;
10560
10569
  const contentBlocks = normalizeContentBlocks(event.payload.message?.content);
10561
- const thinkingText = contentBlocks.filter((block) => block.type === "thinking" && block.thinking).map((block) => block.thinking).join("\n\n");
10562
- if (thinkingText) {
10563
- upsertDisplayMessage(messages, {
10564
- id: `reasoning-${messageId || event.timestamp}-thinking`,
10570
+ const messageKey = messageId || event.timestamp;
10571
+ const streamRefs = messageId ? partialIndexes.get(messageId) : void 0;
10572
+ if (messageId) {
10573
+ completedStreamIds.add(messageId);
10574
+ if (liveStreamId !== null && liveStreamId !== messageId) {
10575
+ supersededStreamIds.add(liveStreamId);
10576
+ }
10577
+ liveStreamId = null;
10578
+ }
10579
+ const thinkingBlocks = contentBlocks.flatMap(
10580
+ (block) => block.type === "thinking" && typeof block.thinking === "string" ? [block.thinking] : []
10581
+ );
10582
+ if (thinkingBlocks.length > 0) {
10583
+ const allThinking = [...assistantThinking.get(messageKey) ?? [], ...thinkingBlocks];
10584
+ assistantThinking.set(messageKey, allThinking);
10585
+ const reasoningMessage = {
10586
+ id: `reasoning-${messageKey}-thinking`,
10565
10587
  type: "reasoning",
10566
- content: thinkingText,
10588
+ content: allThinking.join("\n\n"),
10567
10589
  status: "completed",
10568
10590
  timestamp: event.timestamp
10569
- });
10591
+ };
10592
+ if (streamRefs?.reasoning !== void 0) {
10593
+ messages[streamRefs.reasoning] = reasoningMessage;
10594
+ streamRefs.reasoning = void 0;
10595
+ } else {
10596
+ upsertDisplayMessage(messages, reasoningMessage);
10597
+ }
10570
10598
  }
10571
- contentBlocks.forEach((block, blockIndex) => {
10599
+ contentBlocks.forEach((block) => {
10572
10600
  if (block.type === "text" && block.text) {
10573
- upsertDisplayMessage(messages, {
10574
- id: `agent-${messageId || event.timestamp}-${blockIndex}`,
10601
+ const textIndex = assistantTextCounts.get(messageKey) ?? 0;
10602
+ assistantTextCounts.set(messageKey, textIndex + 1);
10603
+ const agentMessage = {
10604
+ id: `agent-${messageKey}-${textIndex}`,
10575
10605
  type: "agent",
10576
10606
  content: block.text,
10577
10607
  timestamp: event.timestamp
10578
- });
10608
+ };
10609
+ if (streamRefs?.agent !== void 0) {
10610
+ messages[streamRefs.agent] = agentMessage;
10611
+ streamRefs.agent = void 0;
10612
+ } else {
10613
+ upsertDisplayMessage(messages, agentMessage);
10614
+ }
10579
10615
  }
10580
10616
  if (block.type === "tool_use" && block.id) {
10581
10617
  const toolName = block.name || "unknown";
@@ -10847,7 +10883,14 @@ function parseClaudeEvents(events, parentToolUseId) {
10847
10883
  }
10848
10884
  }
10849
10885
  });
10850
- return messages;
10886
+ const staleIndexes = /* @__PURE__ */ new Set();
10887
+ for (const streamId of [...completedStreamIds, ...supersededStreamIds]) {
10888
+ const refs = partialIndexes.get(streamId);
10889
+ if (refs?.reasoning !== void 0) staleIndexes.add(refs.reasoning);
10890
+ if (refs?.agent !== void 0) staleIndexes.add(refs.agent);
10891
+ }
10892
+ if (staleIndexes.size === 0) return messages;
10893
+ return messages.filter((_, index) => !staleIndexes.has(index));
10851
10894
  }
10852
10895
 
10853
10896
  // ../shared/src/agent-event-utils.ts
@@ -14482,6 +14525,24 @@ function parseScreenCoord(value, label, size) {
14482
14525
  function resolvePath(p) {
14483
14526
  return isAbsolute(p) ? p : resolve(process.cwd(), p);
14484
14527
  }
14528
+ function desktopBridgeStatus() {
14529
+ const r = spawnSync("bash", [SERVICES_SCRIPT, "--status-json"], { stdio: "pipe" });
14530
+ if (r.status !== 0) return null;
14531
+ try {
14532
+ return JSON.parse(r.stdout.toString());
14533
+ } catch {
14534
+ return null;
14535
+ }
14536
+ }
14537
+ function bridgeStatus(details, includeBacklog = false, rootsOnly = false) {
14538
+ const pids = rootsOnly ? details.rootPids ?? [] : details.listenerPids ?? [];
14539
+ const listenerCount = rootsOnly ? details.rootListeners ?? pids.length : details.listeners ?? pids.length;
14540
+ const listenerLabel = rootsOnly ? "root listener" : "listener";
14541
+ const parts = [`${listenerCount} ${listenerLabel}${listenerCount === 1 ? "" : "s"} on ${details.port}`];
14542
+ if (includeBacklog) parts.push(`backlog ${details.backlog ?? 0}`);
14543
+ if (pids.length > 0) parts.push(`pid${pids.length === 1 ? "" : "s"} ${pids.join(",")}`);
14544
+ return ` (${parts.join(", ")})`;
14545
+ }
14485
14546
  var sleep = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
14486
14547
  async function lookupDesktopViewerUrl() {
14487
14548
  try {
@@ -14512,11 +14573,14 @@ async function computerInfoCommand() {
14512
14573
  console.error(chalk20.dim(`Share this URL with the user to let them watch the desktop live.`));
14513
14574
  }
14514
14575
  async function computerStatusCommand() {
14576
+ ensureServicesRunning();
14577
+ const bridge = desktopBridgeStatus();
14515
14578
  const procs = ["Xvfb", "openbox", "tint2", "x11vnc", "websockify"];
14516
14579
  for (const p of procs) {
14517
14580
  const r = spawnSync("pgrep", ["-af", p], { stdio: "pipe" });
14518
14581
  const running = r.status === 0 && !!r.stdout?.toString().trim();
14519
- console.log(` ${running ? chalk20.green("\u25CF") : chalk20.red("\u25CB")} ${p}`);
14582
+ const suffix = p === "x11vnc" && bridge ? bridgeStatus(bridge.x11vnc, true) : p === "websockify" && bridge ? bridgeStatus(bridge.websockify, false, true) : "";
14583
+ console.log(` ${running ? chalk20.green("\u25CF") : chalk20.red("\u25CB")} ${p}${suffix}`);
14520
14584
  }
14521
14585
  const viewerUrl = await lookupDesktopViewerUrl();
14522
14586
  if (viewerUrl) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replicas-cli",
3
- "version": "0.2.322",
3
+ "version": "0.2.324",
4
4
  "description": "CLI for managing Replicas workspaces - SSH into cloud dev environments with automatic port forwarding",
5
5
  "main": "dist/index.mjs",
6
6
  "bin": {