@supatest/cli 0.0.21 → 0.0.22

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 +73 -72
  2. package/package.json +2 -1
package/dist/index.js CHANGED
@@ -3904,7 +3904,7 @@ var CLI_VERSION;
3904
3904
  var init_version = __esm({
3905
3905
  "src/version.ts"() {
3906
3906
  "use strict";
3907
- CLI_VERSION = "0.0.21";
3907
+ CLI_VERSION = "0.0.22";
3908
3908
  }
3909
3909
  });
3910
3910
 
@@ -4195,12 +4195,24 @@ ${"=".repeat(80)}
4195
4195
  });
4196
4196
 
4197
4197
  // src/services/api-client.ts
4198
- var ApiError, ApiClient;
4198
+ import { Agent, setGlobalDispatcher } from "undici";
4199
+ var agent, ApiError, ApiClient;
4199
4200
  var init_api_client = __esm({
4200
4201
  "src/services/api-client.ts"() {
4201
4202
  "use strict";
4202
4203
  init_error_logger();
4203
4204
  init_logger();
4205
+ agent = new Agent({
4206
+ keepAliveTimeout: 3e4,
4207
+ // Keep connections alive for 30s
4208
+ keepAliveMaxTimeout: 6e4,
4209
+ // Max time to keep connection alive
4210
+ connections: 10,
4211
+ // Max connections per origin
4212
+ pipelining: 1
4213
+ // Enable HTTP pipelining
4214
+ });
4215
+ setGlobalDispatcher(agent);
4204
4216
  ApiError = class extends Error {
4205
4217
  status;
4206
4218
  isAuthError;
@@ -4729,7 +4741,7 @@ var init_agent = __esm({
4729
4741
  }
4730
4742
  async run(config2) {
4731
4743
  this.abortController = new AbortController();
4732
- await this.presenter.onStart(config2);
4744
+ this.presenter.onStart(config2);
4733
4745
  const claudeCodePath = await this.resolveClaudeCodePath();
4734
4746
  let prompt = config2.task;
4735
4747
  if (config2.logs) {
@@ -4746,9 +4758,9 @@ ${config2.logs}
4746
4758
  const customAgents = discoverAgents(cwd);
4747
4759
  let customAgentsPrompt;
4748
4760
  if (customAgents.length > 0) {
4749
- const agentList = customAgents.map((agent) => {
4750
- const modelInfo = agent.model ? ` (model: ${agent.model})` : "";
4751
- return `- **${agent.name}**${modelInfo}: ${agent.description || "No description"}`;
4761
+ const agentList = customAgents.map((agent2) => {
4762
+ const modelInfo = agent2.model ? ` (model: ${agent2.model})` : "";
4763
+ return `- **${agent2.name}**${modelInfo}: ${agent2.description || "No description"}`;
4752
4764
  }).join("\n");
4753
4765
  customAgentsPrompt = `
4754
4766
 
@@ -4798,21 +4810,14 @@ ${projectInstructions}`,
4798
4810
  pathToClaudeCodeExecutable: claudeCodePath,
4799
4811
  includePartialMessages: true,
4800
4812
  executable: "node",
4801
- // MCP servers for enhanced capabilities
4802
- // User-defined servers from .supatest/mcp.json can override defaults
4813
+ // MCP servers from .supatest/mcp.json
4814
+ // Users can add servers like Playwright if needed
4803
4815
  mcpServers: (() => {
4804
- const userServers = loadMcpServers(cwd);
4805
- const allServers = {
4806
- // Default Playwright MCP server for browser automation
4807
- playwright: {
4808
- command: "npx",
4809
- args: ["-y", "@playwright/mcp@latest"]
4810
- },
4811
- // User-defined servers override defaults (spread after)
4812
- ...userServers
4813
- };
4814
- this.presenter.onLog(`MCP servers loaded: ${Object.keys(allServers).join(", ")}`);
4815
- return allServers;
4816
+ const servers = loadMcpServers(cwd);
4817
+ if (Object.keys(servers).length > 0) {
4818
+ this.presenter.onLog(`MCP servers: ${Object.keys(servers).join(", ")}`);
4819
+ }
4820
+ return servers;
4816
4821
  })(),
4817
4822
  // Resume from previous session if providerSessionId is provided
4818
4823
  // This allows the agent to continue conversations with full context
@@ -4882,16 +4887,16 @@ ${projectInstructions}`,
4882
4887
  for (const block of content) {
4883
4888
  if (block.type === "text") {
4884
4889
  resultText += block.text + "\n";
4885
- await this.presenter.onAssistantText(block.text);
4890
+ this.presenter.onAssistantText(block.text);
4886
4891
  } else if (block.type === "thinking") {
4887
- await this.presenter.onThinking(block.thinking);
4892
+ this.presenter.onThinking(block.thinking);
4888
4893
  } else if (block.type === "tool_use") {
4889
4894
  const toolName = block.name;
4890
4895
  const input = block.input;
4891
4896
  if ((toolName === "Write" || toolName === "Edit") && input?.file_path) {
4892
4897
  filesModified.add(input.file_path);
4893
4898
  }
4894
- await this.presenter.onToolUse(toolName, input, block.id);
4899
+ this.presenter.onToolUse(toolName, input, block.id);
4895
4900
  }
4896
4901
  }
4897
4902
  }
@@ -4909,7 +4914,7 @@ ${projectInstructions}`,
4909
4914
  cacheReadTokens: cumulativeCacheReadTokens,
4910
4915
  cacheCreationTokens: cumulativeCacheCreationTokens
4911
4916
  };
4912
- await this.presenter.onUsageUpdate?.({
4917
+ this.presenter.onUsageUpdate?.({
4913
4918
  model: currentModel,
4914
4919
  inputTokens: cumulativeInputTokens,
4915
4920
  outputTokens: cumulativeOutputTokens,
@@ -4917,7 +4922,7 @@ ${projectInstructions}`,
4917
4922
  cacheCreationTokens: cumulativeCacheCreationTokens
4918
4923
  });
4919
4924
  }
4920
- await this.presenter.onTurnComplete(content);
4925
+ this.presenter.onTurnComplete(content);
4921
4926
  } else if (msg.type === "result") {
4922
4927
  iterations = msg.num_turns;
4923
4928
  const modelName = Object.keys(msg.modelUsage || {})[0] || "unknown";
@@ -4954,7 +4959,7 @@ ${projectInstructions}`,
4954
4959
  if ("errors" in msg && Array.isArray(msg.errors)) {
4955
4960
  errors.push(...msg.errors);
4956
4961
  for (const error of msg.errors) {
4957
- await this.presenter.onError(error);
4962
+ this.presenter.onError(error);
4958
4963
  }
4959
4964
  }
4960
4965
  }
@@ -4965,7 +4970,7 @@ ${projectInstructions}`,
4965
4970
  if (block.type === "tool_result" && block.tool_use_id) {
4966
4971
  if (this.presenter.onToolResult) {
4967
4972
  const resultContent = Array.isArray(block.content) ? block.content.map((c2) => c2.text || "").join("\n") : typeof block.content === "string" ? block.content : "";
4968
- await this.presenter.onToolResult(block.tool_use_id, resultContent);
4973
+ this.presenter.onToolResult(block.tool_use_id, resultContent);
4969
4974
  }
4970
4975
  }
4971
4976
  }
@@ -4982,7 +4987,7 @@ ${projectInstructions}`,
4982
4987
  wasInterrupted = true;
4983
4988
  } else if (config2.providerSessionId && isSessionExpiredError(errorMessage)) {
4984
4989
  const expiredMessage = "Can't continue conversation older than 30 days. Please start a new session.";
4985
- await this.presenter.onError(expiredMessage);
4990
+ this.presenter.onError(expiredMessage);
4986
4991
  hasError = true;
4987
4992
  errors.push(expiredMessage);
4988
4993
  } else {
@@ -4993,7 +4998,7 @@ ${projectInstructions}`,
4993
4998
  // Truncate for log size
4994
4999
  iteration: iterations
4995
5000
  });
4996
- await this.presenter.onError(errorMessage);
5001
+ this.presenter.onError(errorMessage);
4997
5002
  hasError = true;
4998
5003
  errors.push(errorMessage);
4999
5004
  }
@@ -5010,7 +5015,7 @@ ${projectInstructions}`,
5010
5015
  providerSessionId,
5011
5016
  usage: queryUsage
5012
5017
  };
5013
- await this.presenter.onComplete(result);
5018
+ this.presenter.onComplete(result);
5014
5019
  return result;
5015
5020
  }
5016
5021
  async resolveClaudeCodePath() {
@@ -5039,7 +5044,7 @@ ${projectInstructions}`,
5039
5044
  } catch {
5040
5045
  const error = `Claude Code executable not found at: ${claudeCodePath}
5041
5046
  Set SUPATEST_CLAUDE_CODE_PATH environment variable to override.`;
5042
- await this.presenter.onError(error);
5047
+ this.presenter.onError(error);
5043
5048
  throw new Error(error);
5044
5049
  }
5045
5050
  return claudeCodePath;
@@ -5079,6 +5084,10 @@ function getToolDescription(toolName, input) {
5079
5084
  return toolName;
5080
5085
  }
5081
5086
  }
5087
+ function streamEventAsync(apiClient, sessionId, event) {
5088
+ apiClient.streamEvent(sessionId, event).catch(() => {
5089
+ });
5090
+ }
5082
5091
  var CONTEXT_WINDOWS, ReactPresenter;
5083
5092
  var init_react = __esm({
5084
5093
  "src/presenters/react.ts"() {
@@ -5109,19 +5118,18 @@ var init_react = __esm({
5109
5118
  this.sessionId = sessionId;
5110
5119
  this.verbose = verbose;
5111
5120
  }
5112
- async onStart(config2) {
5113
- const event = {
5121
+ onStart(config2) {
5122
+ streamEventAsync(this.apiClient, this.sessionId, {
5114
5123
  type: "query_start",
5115
5124
  prompt: config2.task
5116
- };
5117
- await this.apiClient.streamEvent(this.sessionId, event);
5125
+ });
5118
5126
  }
5119
5127
  onLog(message) {
5120
5128
  if (this.verbose) {
5121
5129
  console.error(message);
5122
5130
  }
5123
5131
  }
5124
- async onAssistantText(text) {
5132
+ onAssistantText(text) {
5125
5133
  if (!this.hasAssistantMessage) {
5126
5134
  this.callbacks.addMessage({
5127
5135
  type: "assistant",
@@ -5136,13 +5144,12 @@ var init_react = __esm({
5136
5144
  content: this.currentAssistantText
5137
5145
  });
5138
5146
  }
5139
- const textEvent = {
5147
+ streamEventAsync(this.apiClient, this.sessionId, {
5140
5148
  type: "assistant_text",
5141
5149
  delta: text
5142
- };
5143
- await this.apiClient.streamEvent(this.sessionId, textEvent);
5150
+ });
5144
5151
  }
5145
- async onThinking(text) {
5152
+ onThinking(text) {
5146
5153
  if (!this.hasThinkingMessage) {
5147
5154
  this.callbacks.addMessage({
5148
5155
  type: "thinking",
@@ -5157,13 +5164,12 @@ var init_react = __esm({
5157
5164
  content: this.currentThinkingText
5158
5165
  });
5159
5166
  }
5160
- const thinkingEvent = {
5167
+ streamEventAsync(this.apiClient, this.sessionId, {
5161
5168
  type: "assistant_thinking",
5162
5169
  delta: text
5163
- };
5164
- await this.apiClient.streamEvent(this.sessionId, thinkingEvent);
5170
+ });
5165
5171
  }
5166
- async onToolUse(tool, input, toolId) {
5172
+ onToolUse(tool, input, toolId) {
5167
5173
  const message = {
5168
5174
  type: "tool",
5169
5175
  content: getToolDescription(tool, input),
@@ -5207,48 +5213,45 @@ var init_react = __esm({
5207
5213
  } else if (tool === "EnterPlanMode") {
5208
5214
  this.callbacks.onEnterPlanMode?.();
5209
5215
  }
5210
- const toolUseEvent = {
5216
+ streamEventAsync(this.apiClient, this.sessionId, {
5211
5217
  type: "tool_use",
5212
5218
  id: toolId,
5213
5219
  name: tool,
5214
5220
  input: input || {}
5215
- };
5216
- await this.apiClient.streamEvent(this.sessionId, toolUseEvent);
5221
+ });
5217
5222
  }
5218
- async onToolResult(toolId, result) {
5223
+ onToolResult(toolId, result) {
5219
5224
  this.callbacks.updateMessageByToolId(toolId, {
5220
5225
  toolResult: result
5221
5226
  });
5222
- const toolResultEvent = {
5227
+ streamEventAsync(this.apiClient, this.sessionId, {
5223
5228
  type: "tool_result",
5224
5229
  tool_use_id: toolId,
5225
5230
  content: result
5226
- };
5227
- await this.apiClient.streamEvent(this.sessionId, toolResultEvent);
5231
+ });
5228
5232
  }
5229
- async onTurnComplete(content) {
5230
- const messageCompleteEvent = {
5233
+ onTurnComplete(content) {
5234
+ streamEventAsync(this.apiClient, this.sessionId, {
5231
5235
  type: "message_complete",
5232
5236
  message: {
5233
5237
  role: "assistant",
5234
5238
  content
5235
5239
  }
5236
- };
5237
- await this.apiClient.streamEvent(this.sessionId, messageCompleteEvent);
5240
+ });
5238
5241
  this.callbacks.onTurnComplete?.();
5239
5242
  }
5240
- async onError(error) {
5243
+ onError(error) {
5241
5244
  this.callbacks.addMessage({
5242
5245
  type: "error",
5243
5246
  content: error,
5244
5247
  errorType: "error"
5245
5248
  });
5246
- await this.apiClient.streamEvent(this.sessionId, {
5249
+ streamEventAsync(this.apiClient, this.sessionId, {
5247
5250
  type: "session_error",
5248
5251
  error
5249
5252
  });
5250
5253
  }
5251
- async onUsageUpdate(usage) {
5254
+ onUsageUpdate(usage) {
5252
5255
  const contextWindow = CONTEXT_WINDOWS[usage.model] || 2e5;
5253
5256
  const contextTokens = usage.inputTokens + usage.cacheReadTokens + usage.cacheCreationTokens;
5254
5257
  const contextPct = contextTokens / contextWindow * 100;
@@ -5269,10 +5272,10 @@ var init_react = __esm({
5269
5272
  };
5270
5273
  });
5271
5274
  }
5272
- async onComplete(result) {
5275
+ onComplete(result) {
5273
5276
  if (result.usage) {
5274
5277
  const queryStatus = result.success ? "success" : result.error?.toLowerCase().includes("interrupt") ? "interrupted" : "error";
5275
- await this.apiClient.streamEvent(this.sessionId, {
5278
+ streamEventAsync(this.apiClient, this.sessionId, {
5276
5279
  type: "query_complete",
5277
5280
  usage: result.usage,
5278
5281
  result: {
@@ -5283,11 +5286,11 @@ var init_react = __esm({
5283
5286
  });
5284
5287
  }
5285
5288
  if (result.success) {
5286
- await this.apiClient.streamEvent(this.sessionId, {
5289
+ streamEventAsync(this.apiClient, this.sessionId, {
5287
5290
  type: "session_complete"
5288
5291
  });
5289
5292
  } else {
5290
- await this.apiClient.streamEvent(this.sessionId, {
5293
+ streamEventAsync(this.apiClient, this.sessionId, {
5291
5294
  type: "session_error",
5292
5295
  error: result.error || "Unknown error"
5293
5296
  });
@@ -9436,9 +9439,9 @@ var init_interactive = __esm({
9436
9439
  selectedModel,
9437
9440
  systemPromptAppend: agentMode === "plan" ? config.planSystemPrompt : config2.systemPromptAppend
9438
9441
  };
9439
- const agent = new CoreAgent(presenter, messageBridge);
9440
- agentRef.current = agent;
9441
- const result = await agent.run(runConfig);
9442
+ const agent2 = new CoreAgent(presenter, messageBridge);
9443
+ agentRef.current = agent2;
9444
+ const result = await agent2.run(runConfig);
9442
9445
  if (isMounted) {
9443
9446
  onComplete(result.success, result.providerSessionId);
9444
9447
  }
@@ -9752,9 +9755,9 @@ var HeadlessAgentRunner = ({ config: config2, sessionId, apiClient, onComplete }
9752
9755
  sessionId,
9753
9756
  config2.verbose
9754
9757
  );
9755
- const agent = new CoreAgent(presenter);
9756
- agentRef.current = agent;
9757
- const result = await agent.run(config2);
9758
+ const agent2 = new CoreAgent(presenter);
9759
+ agentRef.current = agent2;
9760
+ const result = await agent2.run(config2);
9758
9761
  if (isMounted) {
9759
9762
  onComplete(result.success, result.providerSessionId);
9760
9763
  }
@@ -9825,10 +9828,8 @@ var HeadlessAppContent = ({
9825
9828
  const handleComplete = (success, providerSessionId) => {
9826
9829
  if (hasCompletedRef.current) return;
9827
9830
  hasCompletedRef.current = true;
9828
- setTimeout(() => {
9829
- onComplete(success, providerSessionId);
9830
- exit();
9831
- }, 100);
9831
+ onComplete(success, providerSessionId);
9832
+ exit();
9832
9833
  };
9833
9834
  return /* @__PURE__ */ React13.createElement(Box12, { flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React13.createElement(
9834
9835
  MessageList,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supatest/cli",
3
- "version": "0.0.21",
3
+ "version": "0.0.22",
4
4
  "description": "Supatest CLI - AI-powered task automation for CI/CD",
5
5
  "type": "module",
6
6
  "bin": {
@@ -63,6 +63,7 @@
63
63
  "react": "^19.0.0",
64
64
  "string-width": "^8.1.0",
65
65
  "strip-ansi": "^7.1.2",
66
+ "undici": "^7.16.0",
66
67
  "wrap-ansi": "^9.0.2"
67
68
  },
68
69
  "devDependencies": {