replicas-mcp 0.1.5 → 0.1.7

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/index.js CHANGED
@@ -5,6 +5,14 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
5
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
6
  import { z } from "zod";
7
7
 
8
+ // ../shared/src/sandbox.ts
9
+ var SANDBOX_LIFECYCLE = {
10
+ AUTO_STOP_MINUTES: 60,
11
+ AUTO_ARCHIVE_MINUTES: 60 * 24 * 7,
12
+ AUTO_DELETE_MINUTES: -1,
13
+ SSH_TOKEN_EXPIRATION_MINUTES: 3 * 60
14
+ };
15
+
8
16
  // ../shared/src/display-message/parsers/codex-parser.ts
9
17
  function safeJsonParse(str, fallback) {
10
18
  try {
@@ -16,8 +24,12 @@ function safeJsonParse(str, fallback) {
16
24
  function getStatusFromExitCode(exitCode) {
17
25
  return exitCode === 0 ? "completed" : "failed";
18
26
  }
19
- function findLastMessageByType(messages, type) {
20
- return messages.findLast((m) => m.type === type);
27
+ function parseShellOutput(raw) {
28
+ const exitCodeMatch = raw.match(/^Exit code: (\d+)/);
29
+ const exitCode = exitCodeMatch ? parseInt(exitCodeMatch[1], 10) : 0;
30
+ const outputIndex = raw.indexOf("Output:\n");
31
+ const output = outputIndex >= 0 ? raw.slice(outputIndex + "Output:\n".length) : raw;
32
+ return { exitCode, output };
21
33
  }
22
34
  function parsePatch(input) {
23
35
  const operations = [];
@@ -74,11 +86,12 @@ function parsePatch(input) {
74
86
  }
75
87
  function parseCodexEvents(events) {
76
88
  const messages = [];
89
+ const pendingCommands = /* @__PURE__ */ new Map();
77
90
  const pendingPatches = /* @__PURE__ */ new Map();
78
- events.forEach((event) => {
91
+ events.forEach((event, eventIndex) => {
79
92
  if (event.type === "event_msg" && event.payload?.type === "user_message") {
80
93
  messages.push({
81
- id: `user-${event.timestamp}`,
94
+ id: `user-${event.timestamp}-${eventIndex}`,
82
95
  type: "user",
83
96
  content: event.payload.message || "",
84
97
  timestamp: event.timestamp
@@ -86,7 +99,7 @@ function parseCodexEvents(events) {
86
99
  }
87
100
  if (event.type === "event_msg" && event.payload?.type === "agent_reasoning") {
88
101
  messages.push({
89
- id: `reasoning-${event.timestamp}-${messages.length}`,
102
+ id: `reasoning-${event.timestamp}-${eventIndex}-${messages.length}`,
90
103
  type: "reasoning",
91
104
  content: event.payload.text || "",
92
105
  status: "completed",
@@ -100,38 +113,40 @@ function parseCodexEvents(events) {
100
113
  const textContent = content.filter((c) => c.type === "output_text").map((c) => c.text || "").join("\n");
101
114
  if (textContent) {
102
115
  messages.push({
103
- id: `agent-${event.timestamp}`,
116
+ id: `agent-${event.timestamp}-${eventIndex}`,
104
117
  type: "agent",
105
118
  content: textContent,
106
119
  timestamp: event.timestamp
107
120
  });
108
121
  }
109
122
  }
110
- if (payloadType === "function_call" && event.payload?.name === "shell") {
123
+ if (payloadType === "function_call" && (event.payload?.name === "shell" || event.payload?.name === "shell_command")) {
124
+ const callId = event.payload.call_id;
111
125
  const args = safeJsonParse(event.payload.arguments || "{}", {});
112
126
  const command = Array.isArray(args.command) ? args.command.join(" ") : args.command || "";
113
- messages.push({
114
- id: `command-${event.timestamp}`,
127
+ const msg = {
128
+ id: `command-${callId || "no-call-id"}-${eventIndex}`,
115
129
  type: "command",
116
130
  command,
117
131
  output: "",
118
- // Will be filled by function_call_output
119
132
  status: "in_progress",
120
133
  timestamp: event.timestamp
121
- });
134
+ };
135
+ messages.push(msg);
136
+ if (callId) {
137
+ pendingCommands.set(callId, msg);
138
+ }
122
139
  }
123
140
  if (payloadType === "function_call_output") {
124
- const output = safeJsonParse(
125
- event.payload.output || "{}",
126
- {}
127
- );
128
- if (output.output !== void 0 && output.metadata) {
129
- const commandMsg = findLastMessageByType(messages, "command");
130
- if (commandMsg) {
131
- commandMsg.output = output.output || "";
132
- commandMsg.exitCode = output.metadata?.exit_code;
133
- commandMsg.status = getStatusFromExitCode(output.metadata?.exit_code);
134
- }
141
+ const callId = event.payload.call_id;
142
+ const rawOutput = event.payload.output || "";
143
+ const commandMsg = callId ? pendingCommands.get(callId) : void 0;
144
+ if (commandMsg) {
145
+ const { exitCode, output } = parseShellOutput(rawOutput);
146
+ commandMsg.output = output;
147
+ commandMsg.exitCode = exitCode;
148
+ commandMsg.status = getStatusFromExitCode(exitCode);
149
+ pendingCommands.delete(callId);
135
150
  }
136
151
  }
137
152
  if (payloadType === "custom_tool_call") {
@@ -144,7 +159,7 @@ function parseCodexEvents(events) {
144
159
  pendingPatches.set(callId, { input, status, timestamp: event.timestamp, operations });
145
160
  } else {
146
161
  messages.push({
147
- id: `toolcall-${event.timestamp}`,
162
+ id: `toolcall-${event.timestamp}-${eventIndex}`,
148
163
  type: "tool_call",
149
164
  server: "custom",
150
165
  tool: name,
@@ -162,7 +177,7 @@ function parseCodexEvents(events) {
162
177
  const pendingPatch = pendingPatches.get(callId);
163
178
  if (pendingPatch) {
164
179
  messages.push({
165
- id: `patch-${pendingPatch.timestamp}`,
180
+ id: `patch-${pendingPatch.timestamp}-${eventIndex}`,
166
181
  type: "patch",
167
182
  operations: pendingPatch.operations,
168
183
  output: output.output || "",
@@ -172,7 +187,7 @@ function parseCodexEvents(events) {
172
187
  });
173
188
  pendingPatches.delete(callId);
174
189
  } else {
175
- const toolCallMsg = findLastMessageByType(messages, "tool_call");
190
+ const toolCallMsg = messages.findLast((m) => m.type === "tool_call");
176
191
  if (toolCallMsg) {
177
192
  toolCallMsg.status = getStatusFromExitCode(output.metadata?.exit_code);
178
193
  }
@@ -189,7 +204,7 @@ function parseCodexEvents(events) {
189
204
  completed: item.status === "completed"
190
205
  }));
191
206
  messages.push({
192
- id: `todo-${event.timestamp}`,
207
+ id: `todo-${event.timestamp}-${eventIndex}`,
193
208
  type: "todo_list",
194
209
  items: todoItems,
195
210
  status: "completed",
@@ -469,8 +484,7 @@ registerTool(
469
484
  inputSchema: {
470
485
  name: z.string().regex(/^\S+$/, "Name cannot contain spaces"),
471
486
  message: z.string(),
472
- repository: z.string(),
473
- instance_type: z.enum(["standard", "large", "power"]).optional(),
487
+ repositories: z.array(z.string()).min(1),
474
488
  coding_agent: z.enum(["claude", "codex"]).optional()
475
489
  }
476
490
  },
@@ -479,9 +493,8 @@ registerTool(
479
493
  const body = {
480
494
  name: args.name,
481
495
  message: args.message,
482
- repository: args.repository
496
+ repositories: args.repositories
483
497
  };
484
- if (args.instance_type) body.instance_type = args.instance_type;
485
498
  if (args.coding_agent) body.coding_agent = args.coding_agent;
486
499
  return result(await api("POST", "/v1/replica", body));
487
500
  } catch (e) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replicas-mcp",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "license": "UNLICENSED",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
package/src/index.ts CHANGED
@@ -2,7 +2,7 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
2
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
3
  import { z } from 'zod';
4
4
  import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
5
- import { parseAgentEvents, type AgentType, type ReadReplicaHistoryResponse } from '@replicas/shared';
5
+ import { parseAgentEvents, type AgentProvider, type ReadReplicaHistoryResponse } from '@replicas/shared';
6
6
 
7
7
  const API_URL = process.env.REPLICAS_API_URL || 'https://api.replicas.dev';
8
8
  const API_KEY = process.env.REPLICAS_API_KEY;
@@ -69,8 +69,7 @@ registerTool(
69
69
  inputSchema: {
70
70
  name: z.string().regex(/^\S+$/, 'Name cannot contain spaces'),
71
71
  message: z.string(),
72
- repository: z.string(),
73
- instance_type: z.enum(['standard', 'large', 'power']).optional(),
72
+ repositories: z.array(z.string()).min(1),
74
73
  coding_agent: z.enum(['claude', 'codex']).optional(),
75
74
  },
76
75
  },
@@ -79,9 +78,8 @@ registerTool(
79
78
  const body: Record<string, unknown> = {
80
79
  name: args.name,
81
80
  message: args.message,
82
- repository: args.repository,
81
+ repositories: args.repositories,
83
82
  };
84
- if (args.instance_type) body.instance_type = args.instance_type;
85
83
  if (args.coding_agent) body.coding_agent = args.coding_agent;
86
84
  return result(await api('POST', '/v1/replica', body));
87
85
  } catch (e) {
@@ -207,7 +205,7 @@ registerTool(
207
205
  }
208
206
 
209
207
  // Parse raw events into DisplayMessage format
210
- const agentType = (response.coding_agent || 'claude') as AgentType;
208
+ const agentType = (response.coding_agent || 'claude') as AgentProvider;
211
209
  const messages = parseAgentEvents(response.events, agentType);
212
210
 
213
211
  return result({