replicas-cli 0.2.29 → 0.2.30

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.js +60 -48
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1662,8 +1662,12 @@ function safeJsonParse(str, fallback) {
1662
1662
  function getStatusFromExitCode(exitCode) {
1663
1663
  return exitCode === 0 ? "completed" : "failed";
1664
1664
  }
1665
- function findLastMessageByType(messages, type) {
1666
- return messages.findLast((m) => m.type === type);
1665
+ function parseShellOutput(raw) {
1666
+ const exitCodeMatch = raw.match(/^Exit code: (\d+)/);
1667
+ const exitCode = exitCodeMatch ? parseInt(exitCodeMatch[1], 10) : 0;
1668
+ const outputIndex = raw.indexOf("Output:\n");
1669
+ const output = outputIndex >= 0 ? raw.slice(outputIndex + "Output:\n".length) : raw;
1670
+ return { exitCode, output };
1667
1671
  }
1668
1672
  function parsePatch(input) {
1669
1673
  const operations = [];
@@ -1720,11 +1724,12 @@ function parsePatch(input) {
1720
1724
  }
1721
1725
  function parseCodexEvents(events) {
1722
1726
  const messages = [];
1727
+ const pendingCommands = /* @__PURE__ */ new Map();
1723
1728
  const pendingPatches = /* @__PURE__ */ new Map();
1724
- events.forEach((event) => {
1729
+ events.forEach((event, eventIndex) => {
1725
1730
  if (event.type === "event_msg" && event.payload?.type === "user_message") {
1726
1731
  messages.push({
1727
- id: `user-${event.timestamp}`,
1732
+ id: `user-${event.timestamp}-${eventIndex}`,
1728
1733
  type: "user",
1729
1734
  content: event.payload.message || "",
1730
1735
  timestamp: event.timestamp
@@ -1732,7 +1737,7 @@ function parseCodexEvents(events) {
1732
1737
  }
1733
1738
  if (event.type === "event_msg" && event.payload?.type === "agent_reasoning") {
1734
1739
  messages.push({
1735
- id: `reasoning-${event.timestamp}-${messages.length}`,
1740
+ id: `reasoning-${event.timestamp}-${eventIndex}-${messages.length}`,
1736
1741
  type: "reasoning",
1737
1742
  content: event.payload.text || "",
1738
1743
  status: "completed",
@@ -1746,38 +1751,40 @@ function parseCodexEvents(events) {
1746
1751
  const textContent = content.filter((c) => c.type === "output_text").map((c) => c.text || "").join("\n");
1747
1752
  if (textContent) {
1748
1753
  messages.push({
1749
- id: `agent-${event.timestamp}`,
1754
+ id: `agent-${event.timestamp}-${eventIndex}`,
1750
1755
  type: "agent",
1751
1756
  content: textContent,
1752
1757
  timestamp: event.timestamp
1753
1758
  });
1754
1759
  }
1755
1760
  }
1756
- if (payloadType === "function_call" && event.payload?.name === "shell") {
1761
+ if (payloadType === "function_call" && (event.payload?.name === "shell" || event.payload?.name === "shell_command")) {
1762
+ const callId = event.payload.call_id;
1757
1763
  const args = safeJsonParse(event.payload.arguments || "{}", {});
1758
1764
  const command = Array.isArray(args.command) ? args.command.join(" ") : args.command || "";
1759
- messages.push({
1760
- id: `command-${event.timestamp}`,
1765
+ const msg = {
1766
+ id: `command-${callId || "no-call-id"}-${eventIndex}`,
1761
1767
  type: "command",
1762
1768
  command,
1763
1769
  output: "",
1764
- // Will be filled by function_call_output
1765
1770
  status: "in_progress",
1766
1771
  timestamp: event.timestamp
1767
- });
1772
+ };
1773
+ messages.push(msg);
1774
+ if (callId) {
1775
+ pendingCommands.set(callId, msg);
1776
+ }
1768
1777
  }
1769
1778
  if (payloadType === "function_call_output") {
1770
- const output = safeJsonParse(
1771
- event.payload.output || "{}",
1772
- {}
1773
- );
1774
- if (output.output !== void 0 && output.metadata) {
1775
- const commandMsg = findLastMessageByType(messages, "command");
1776
- if (commandMsg) {
1777
- commandMsg.output = output.output || "";
1778
- commandMsg.exitCode = output.metadata?.exit_code;
1779
- commandMsg.status = getStatusFromExitCode(output.metadata?.exit_code);
1780
- }
1779
+ const callId = event.payload.call_id;
1780
+ const rawOutput = event.payload.output || "";
1781
+ const commandMsg = callId ? pendingCommands.get(callId) : void 0;
1782
+ if (commandMsg) {
1783
+ const { exitCode, output } = parseShellOutput(rawOutput);
1784
+ commandMsg.output = output;
1785
+ commandMsg.exitCode = exitCode;
1786
+ commandMsg.status = getStatusFromExitCode(exitCode);
1787
+ pendingCommands.delete(callId);
1781
1788
  }
1782
1789
  }
1783
1790
  if (payloadType === "custom_tool_call") {
@@ -1790,7 +1797,7 @@ function parseCodexEvents(events) {
1790
1797
  pendingPatches.set(callId, { input, status, timestamp: event.timestamp, operations });
1791
1798
  } else {
1792
1799
  messages.push({
1793
- id: `toolcall-${event.timestamp}`,
1800
+ id: `toolcall-${event.timestamp}-${eventIndex}`,
1794
1801
  type: "tool_call",
1795
1802
  server: "custom",
1796
1803
  tool: name,
@@ -1808,7 +1815,7 @@ function parseCodexEvents(events) {
1808
1815
  const pendingPatch = pendingPatches.get(callId);
1809
1816
  if (pendingPatch) {
1810
1817
  messages.push({
1811
- id: `patch-${pendingPatch.timestamp}`,
1818
+ id: `patch-${pendingPatch.timestamp}-${eventIndex}`,
1812
1819
  type: "patch",
1813
1820
  operations: pendingPatch.operations,
1814
1821
  output: output.output || "",
@@ -1818,7 +1825,7 @@ function parseCodexEvents(events) {
1818
1825
  });
1819
1826
  pendingPatches.delete(callId);
1820
1827
  } else {
1821
- const toolCallMsg = findLastMessageByType(messages, "tool_call");
1828
+ const toolCallMsg = messages.findLast((m) => m.type === "tool_call");
1822
1829
  if (toolCallMsg) {
1823
1830
  toolCallMsg.status = getStatusFromExitCode(output.metadata?.exit_code);
1824
1831
  }
@@ -1835,7 +1842,7 @@ function parseCodexEvents(events) {
1835
1842
  completed: item.status === "completed"
1836
1843
  }));
1837
1844
  messages.push({
1838
- id: `todo-${event.timestamp}`,
1845
+ id: `todo-${event.timestamp}-${eventIndex}`,
1839
1846
  type: "todo_list",
1840
1847
  items: todoItems,
1841
1848
  status: "completed",
@@ -2195,15 +2202,15 @@ Replicas (Page ${response.page} of ${response.totalPages}, Total: ${response.tot
2195
2202
  for (const replica of response.replicas) {
2196
2203
  console.log(import_chalk15.default.white(` ${replica.name}`));
2197
2204
  console.log(import_chalk15.default.gray(` ID: ${replica.id}`));
2198
- if (replica.repository) {
2199
- console.log(import_chalk15.default.gray(` Repository: ${replica.repository}`));
2205
+ if (replica.repositories.length > 0) {
2206
+ console.log(import_chalk15.default.gray(` Repositories: ${replica.repositories.map((repository) => repository.name).join(", ")}`));
2200
2207
  }
2201
2208
  console.log(import_chalk15.default.gray(` Status: ${formatStatus(replica.status)}`));
2202
2209
  console.log(import_chalk15.default.gray(` Created: ${formatDate(replica.created_at)}`));
2203
2210
  if (replica.pull_requests && replica.pull_requests.length > 0) {
2204
2211
  console.log(import_chalk15.default.gray(` Pull Requests:`));
2205
2212
  for (const pr of replica.pull_requests) {
2206
- console.log(import_chalk15.default.cyan(` - #${pr.number}: ${pr.url}`));
2213
+ console.log(import_chalk15.default.cyan(` - ${pr.repository} #${pr.number}: ${pr.url}`));
2207
2214
  }
2208
2215
  }
2209
2216
  console.log();
@@ -2225,8 +2232,8 @@ async function replicaGetCommand(id) {
2225
2232
  Replica: ${replica.name}
2226
2233
  `));
2227
2234
  console.log(import_chalk15.default.gray(` ID: ${replica.id}`));
2228
- if (replica.repository) {
2229
- console.log(import_chalk15.default.gray(` Repository: ${replica.repository}`));
2235
+ if (replica.repositories.length > 0) {
2236
+ console.log(import_chalk15.default.gray(` Repositories: ${replica.repositories.map((repository) => repository.name).join(", ")}`));
2230
2237
  }
2231
2238
  console.log(import_chalk15.default.gray(` Status: ${formatStatus(replica.status)}`));
2232
2239
  console.log(import_chalk15.default.gray(` Created: ${formatDate(replica.created_at)}`));
@@ -2236,17 +2243,18 @@ Replica: ${replica.name}
2236
2243
  if (replica.coding_agent) {
2237
2244
  console.log(import_chalk15.default.gray(` Coding Agent: ${replica.coding_agent}`));
2238
2245
  }
2239
- if (replica.branch) {
2240
- console.log(import_chalk15.default.gray(` Branch: ${replica.branch}`));
2241
- }
2242
- if (replica.git_diff) {
2243
- console.log(import_chalk15.default.gray(` Changes: ${import_chalk15.default.green(`+${replica.git_diff.added}`)} / ${import_chalk15.default.red(`-${replica.git_diff.removed}`)}`));
2246
+ if (replica.repository_statuses && replica.repository_statuses.length > 0) {
2247
+ console.log(import_chalk15.default.gray(" Repository Statuses:"));
2248
+ for (const repositoryStatus of replica.repository_statuses) {
2249
+ const changeText = repositoryStatus.git_diff ? ` (${import_chalk15.default.green(`+${repositoryStatus.git_diff.added}`)} / ${import_chalk15.default.red(`-${repositoryStatus.git_diff.removed}`)})` : "";
2250
+ console.log(import_chalk15.default.gray(` - ${repositoryStatus.repository}: ${repositoryStatus.branch || "unknown"}${changeText}`));
2251
+ }
2244
2252
  }
2245
2253
  }
2246
2254
  if (replica.pull_requests && replica.pull_requests.length > 0) {
2247
2255
  console.log(import_chalk15.default.gray(` Pull Requests:`));
2248
2256
  for (const pr of replica.pull_requests) {
2249
- console.log(import_chalk15.default.cyan(` - #${pr.number}: ${pr.url}`));
2257
+ console.log(import_chalk15.default.cyan(` - ${pr.repository} #${pr.number}: ${pr.url}`));
2250
2258
  }
2251
2259
  }
2252
2260
  console.log();
@@ -2269,7 +2277,7 @@ async function replicaCreateCommand(name, options) {
2269
2277
  }
2270
2278
  let replicaName = name;
2271
2279
  let message = options.message;
2272
- let repository = options.repository;
2280
+ let selectedRepositories = options.repositories?.split(",").map((repository) => repository.trim()).filter((repository) => repository.length > 0);
2273
2281
  let codingAgent = options.agent;
2274
2282
  if (replicaName && /\s/.test(replicaName)) {
2275
2283
  console.log(import_chalk15.default.red("Replica name cannot contain spaces."));
@@ -2292,22 +2300,23 @@ async function replicaCreateCommand(name, options) {
2292
2300
  }
2293
2301
  replicaName = response2.name;
2294
2302
  }
2295
- if (!repository) {
2303
+ if (!selectedRepositories || selectedRepositories.length === 0) {
2296
2304
  const response2 = await (0, import_prompts3.default)({
2297
- type: "select",
2298
- name: "repository",
2299
- message: "Select a repository:",
2305
+ type: "multiselect",
2306
+ name: "repositories",
2307
+ message: "Select repositories:",
2300
2308
  choices: repositories.map((repo) => ({
2301
2309
  title: repo.name,
2302
2310
  value: repo.name,
2303
2311
  description: repo.url
2304
- }))
2312
+ })),
2313
+ min: 1
2305
2314
  });
2306
- if (!response2.repository) {
2315
+ if (!response2.repositories || response2.repositories.length === 0) {
2307
2316
  console.log(import_chalk15.default.yellow("\nCancelled."));
2308
2317
  return;
2309
2318
  }
2310
- repository = response2.repository;
2319
+ selectedRepositories = response2.repositories;
2311
2320
  }
2312
2321
  if (!message) {
2313
2322
  const response2 = await (0, import_prompts3.default)({
@@ -2346,7 +2355,7 @@ async function replicaCreateCommand(name, options) {
2346
2355
  const body = {
2347
2356
  name: replicaName,
2348
2357
  message,
2349
- repository,
2358
+ repositories: selectedRepositories,
2350
2359
  coding_agent: codingAgent
2351
2360
  };
2352
2361
  console.log(import_chalk15.default.gray("\nCreating replica..."));
@@ -2359,6 +2368,9 @@ async function replicaCreateCommand(name, options) {
2359
2368
  Created replica: ${replica.name}`));
2360
2369
  console.log(import_chalk15.default.gray(` ID: ${replica.id}`));
2361
2370
  console.log(import_chalk15.default.gray(` Status: ${formatStatus(replica.status)}`));
2371
+ if (replica.repositories.length > 0) {
2372
+ console.log(import_chalk15.default.gray(` Repositories: ${replica.repositories.map((repository) => repository.name).join(", ")}`));
2373
+ }
2362
2374
  console.log();
2363
2375
  } catch (error) {
2364
2376
  console.error(import_chalk15.default.red(`Error: ${error instanceof Error ? error.message : "Unknown error"}`));
@@ -2529,7 +2541,7 @@ Repositories (${response.repositories.length}):
2529
2541
  }
2530
2542
 
2531
2543
  // src/index.ts
2532
- var CLI_VERSION = "0.2.29";
2544
+ var CLI_VERSION = "0.2.30";
2533
2545
  var program = new import_commander.Command();
2534
2546
  program.name("replicas").description("CLI for managing Replicas workspaces").version(CLI_VERSION);
2535
2547
  program.command("login").description("Authenticate with your Replicas account").action(async () => {
@@ -2726,7 +2738,7 @@ program.command("get <id>").description("Get replica details by ID").action(asyn
2726
2738
  process.exit(1);
2727
2739
  }
2728
2740
  });
2729
- program.command("create [name]").description("Create a new replica").option("-m, --message <message>", "Initial message for the replica").option("-r, --repository <repository>", "Repository name").option("-a, --agent <agent>", "Coding agent (claude, codex)").action(async (name, options) => {
2741
+ program.command("create [name]").description("Create a new replica").option("-m, --message <message>", "Initial message for the replica").option("-r, --repositories <repositories>", "Comma-separated repository names").option("-a, --agent <agent>", "Coding agent (claude, codex)").action(async (name, options) => {
2730
2742
  try {
2731
2743
  await replicaCreateCommand(name, options);
2732
2744
  } catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replicas-cli",
3
- "version": "0.2.29",
3
+ "version": "0.2.30",
4
4
  "description": "CLI for managing Replicas workspaces - SSH into cloud dev environments with automatic port forwarding",
5
5
  "main": "dist/index.js",
6
6
  "bin": {