@supatest/cli 0.0.21 → 0.0.23
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 +206 -86
- package/package.json +6 -2
package/dist/index.js
CHANGED
|
@@ -3817,6 +3817,34 @@ function installChromium() {
|
|
|
3817
3817
|
};
|
|
3818
3818
|
}
|
|
3819
3819
|
}
|
|
3820
|
+
function createSupatestConfig(cwd) {
|
|
3821
|
+
const supatestDir = path.join(cwd, ".supatest");
|
|
3822
|
+
const mcpJsonPath = path.join(supatestDir, "mcp.json");
|
|
3823
|
+
try {
|
|
3824
|
+
if (fs.existsSync(mcpJsonPath)) {
|
|
3825
|
+
return {
|
|
3826
|
+
ok: true,
|
|
3827
|
+
message: "mcp.json already exists",
|
|
3828
|
+
created: false
|
|
3829
|
+
};
|
|
3830
|
+
}
|
|
3831
|
+
if (!fs.existsSync(supatestDir)) {
|
|
3832
|
+
fs.mkdirSync(supatestDir, { recursive: true });
|
|
3833
|
+
}
|
|
3834
|
+
fs.writeFileSync(mcpJsonPath, JSON.stringify(DEFAULT_MCP_CONFIG, null, 2) + "\n", "utf-8");
|
|
3835
|
+
return {
|
|
3836
|
+
ok: true,
|
|
3837
|
+
message: "Created .supatest/mcp.json with Playwright MCP server configuration",
|
|
3838
|
+
created: true
|
|
3839
|
+
};
|
|
3840
|
+
} catch (error) {
|
|
3841
|
+
return {
|
|
3842
|
+
ok: false,
|
|
3843
|
+
message: `Failed to create mcp.json: ${error instanceof Error ? error.message : String(error)}`,
|
|
3844
|
+
created: false
|
|
3845
|
+
};
|
|
3846
|
+
}
|
|
3847
|
+
}
|
|
3820
3848
|
function getVersionSummary() {
|
|
3821
3849
|
const nodeVersion = getNodeVersion();
|
|
3822
3850
|
const playwrightVersion = getPlaywrightVersion();
|
|
@@ -3828,7 +3856,7 @@ function getVersionSummary() {
|
|
|
3828
3856
|
lines.push(` Chromium: ${chromiumVersion ? `build ${chromiumVersion}` : "Not installed"}`);
|
|
3829
3857
|
return lines.join("\n");
|
|
3830
3858
|
}
|
|
3831
|
-
async function setupCommand() {
|
|
3859
|
+
async function setupCommand(options) {
|
|
3832
3860
|
const output = [];
|
|
3833
3861
|
const log = (msg) => {
|
|
3834
3862
|
output.push(msg);
|
|
@@ -3876,6 +3904,18 @@ async function setupCommand() {
|
|
|
3876
3904
|
result.errors.push(chromiumResult.message);
|
|
3877
3905
|
}
|
|
3878
3906
|
}
|
|
3907
|
+
log("\n3. Setting up MCP configuration...");
|
|
3908
|
+
const configResult = createSupatestConfig(options.cwd);
|
|
3909
|
+
if (configResult.ok) {
|
|
3910
|
+
if (configResult.created) {
|
|
3911
|
+
log(` \u2705 ${configResult.message}`);
|
|
3912
|
+
} else {
|
|
3913
|
+
log(` \u2705 ${configResult.message}`);
|
|
3914
|
+
}
|
|
3915
|
+
} else {
|
|
3916
|
+
log(` \u274C ${configResult.message}`);
|
|
3917
|
+
result.errors.push(configResult.message);
|
|
3918
|
+
}
|
|
3879
3919
|
const versionSummary = getVersionSummary();
|
|
3880
3920
|
log(versionSummary);
|
|
3881
3921
|
log("\n" + "\u2500".repeat(50));
|
|
@@ -3891,11 +3931,19 @@ async function setupCommand() {
|
|
|
3891
3931
|
result.output = output.join("\n");
|
|
3892
3932
|
return result;
|
|
3893
3933
|
}
|
|
3894
|
-
var MINIMUM_NODE_VERSION;
|
|
3934
|
+
var MINIMUM_NODE_VERSION, DEFAULT_MCP_CONFIG;
|
|
3895
3935
|
var init_setup = __esm({
|
|
3896
3936
|
"src/commands/setup.ts"() {
|
|
3897
3937
|
"use strict";
|
|
3898
3938
|
MINIMUM_NODE_VERSION = 18;
|
|
3939
|
+
DEFAULT_MCP_CONFIG = {
|
|
3940
|
+
mcpServers: {
|
|
3941
|
+
playwright: {
|
|
3942
|
+
command: "npx",
|
|
3943
|
+
args: ["@playwright/mcp@latest"]
|
|
3944
|
+
}
|
|
3945
|
+
}
|
|
3946
|
+
};
|
|
3899
3947
|
}
|
|
3900
3948
|
});
|
|
3901
3949
|
|
|
@@ -3904,7 +3952,7 @@ var CLI_VERSION;
|
|
|
3904
3952
|
var init_version = __esm({
|
|
3905
3953
|
"src/version.ts"() {
|
|
3906
3954
|
"use strict";
|
|
3907
|
-
CLI_VERSION = "0.0.
|
|
3955
|
+
CLI_VERSION = "0.0.23";
|
|
3908
3956
|
}
|
|
3909
3957
|
});
|
|
3910
3958
|
|
|
@@ -4195,12 +4243,24 @@ ${"=".repeat(80)}
|
|
|
4195
4243
|
});
|
|
4196
4244
|
|
|
4197
4245
|
// src/services/api-client.ts
|
|
4198
|
-
|
|
4246
|
+
import { Agent, setGlobalDispatcher } from "undici";
|
|
4247
|
+
var agent, ApiError, ApiClient;
|
|
4199
4248
|
var init_api_client = __esm({
|
|
4200
4249
|
"src/services/api-client.ts"() {
|
|
4201
4250
|
"use strict";
|
|
4202
4251
|
init_error_logger();
|
|
4203
4252
|
init_logger();
|
|
4253
|
+
agent = new Agent({
|
|
4254
|
+
keepAliveTimeout: 3e4,
|
|
4255
|
+
// Keep connections alive for 30s
|
|
4256
|
+
keepAliveMaxTimeout: 6e4,
|
|
4257
|
+
// Max time to keep connection alive
|
|
4258
|
+
connections: 10,
|
|
4259
|
+
// Max connections per origin
|
|
4260
|
+
pipelining: 1
|
|
4261
|
+
// Enable HTTP pipelining
|
|
4262
|
+
});
|
|
4263
|
+
setGlobalDispatcher(agent);
|
|
4204
4264
|
ApiError = class extends Error {
|
|
4205
4265
|
status;
|
|
4206
4266
|
isAuthError;
|
|
@@ -4729,7 +4789,7 @@ var init_agent = __esm({
|
|
|
4729
4789
|
}
|
|
4730
4790
|
async run(config2) {
|
|
4731
4791
|
this.abortController = new AbortController();
|
|
4732
|
-
|
|
4792
|
+
this.presenter.onStart(config2);
|
|
4733
4793
|
const claudeCodePath = await this.resolveClaudeCodePath();
|
|
4734
4794
|
let prompt = config2.task;
|
|
4735
4795
|
if (config2.logs) {
|
|
@@ -4746,9 +4806,9 @@ ${config2.logs}
|
|
|
4746
4806
|
const customAgents = discoverAgents(cwd);
|
|
4747
4807
|
let customAgentsPrompt;
|
|
4748
4808
|
if (customAgents.length > 0) {
|
|
4749
|
-
const agentList = customAgents.map((
|
|
4750
|
-
const modelInfo =
|
|
4751
|
-
return `- **${
|
|
4809
|
+
const agentList = customAgents.map((agent2) => {
|
|
4810
|
+
const modelInfo = agent2.model ? ` (model: ${agent2.model})` : "";
|
|
4811
|
+
return `- **${agent2.name}**${modelInfo}: ${agent2.description || "No description"}`;
|
|
4752
4812
|
}).join("\n");
|
|
4753
4813
|
customAgentsPrompt = `
|
|
4754
4814
|
|
|
@@ -4798,21 +4858,14 @@ ${projectInstructions}`,
|
|
|
4798
4858
|
pathToClaudeCodeExecutable: claudeCodePath,
|
|
4799
4859
|
includePartialMessages: true,
|
|
4800
4860
|
executable: "node",
|
|
4801
|
-
// MCP servers
|
|
4802
|
-
//
|
|
4861
|
+
// MCP servers from .supatest/mcp.json
|
|
4862
|
+
// Users can add servers like Playwright if needed
|
|
4803
4863
|
mcpServers: (() => {
|
|
4804
|
-
const
|
|
4805
|
-
|
|
4806
|
-
|
|
4807
|
-
|
|
4808
|
-
|
|
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;
|
|
4864
|
+
const servers = loadMcpServers(cwd);
|
|
4865
|
+
if (Object.keys(servers).length > 0) {
|
|
4866
|
+
this.presenter.onLog(`MCP servers: ${Object.keys(servers).join(", ")}`);
|
|
4867
|
+
}
|
|
4868
|
+
return servers;
|
|
4816
4869
|
})(),
|
|
4817
4870
|
// Resume from previous session if providerSessionId is provided
|
|
4818
4871
|
// This allows the agent to continue conversations with full context
|
|
@@ -4882,16 +4935,16 @@ ${projectInstructions}`,
|
|
|
4882
4935
|
for (const block of content) {
|
|
4883
4936
|
if (block.type === "text") {
|
|
4884
4937
|
resultText += block.text + "\n";
|
|
4885
|
-
|
|
4938
|
+
this.presenter.onAssistantText(block.text);
|
|
4886
4939
|
} else if (block.type === "thinking") {
|
|
4887
|
-
|
|
4940
|
+
this.presenter.onThinking(block.thinking);
|
|
4888
4941
|
} else if (block.type === "tool_use") {
|
|
4889
4942
|
const toolName = block.name;
|
|
4890
4943
|
const input = block.input;
|
|
4891
4944
|
if ((toolName === "Write" || toolName === "Edit") && input?.file_path) {
|
|
4892
4945
|
filesModified.add(input.file_path);
|
|
4893
4946
|
}
|
|
4894
|
-
|
|
4947
|
+
this.presenter.onToolUse(toolName, input, block.id);
|
|
4895
4948
|
}
|
|
4896
4949
|
}
|
|
4897
4950
|
}
|
|
@@ -4909,7 +4962,7 @@ ${projectInstructions}`,
|
|
|
4909
4962
|
cacheReadTokens: cumulativeCacheReadTokens,
|
|
4910
4963
|
cacheCreationTokens: cumulativeCacheCreationTokens
|
|
4911
4964
|
};
|
|
4912
|
-
|
|
4965
|
+
this.presenter.onUsageUpdate?.({
|
|
4913
4966
|
model: currentModel,
|
|
4914
4967
|
inputTokens: cumulativeInputTokens,
|
|
4915
4968
|
outputTokens: cumulativeOutputTokens,
|
|
@@ -4917,7 +4970,7 @@ ${projectInstructions}`,
|
|
|
4917
4970
|
cacheCreationTokens: cumulativeCacheCreationTokens
|
|
4918
4971
|
});
|
|
4919
4972
|
}
|
|
4920
|
-
|
|
4973
|
+
this.presenter.onTurnComplete(content);
|
|
4921
4974
|
} else if (msg.type === "result") {
|
|
4922
4975
|
iterations = msg.num_turns;
|
|
4923
4976
|
const modelName = Object.keys(msg.modelUsage || {})[0] || "unknown";
|
|
@@ -4954,7 +5007,7 @@ ${projectInstructions}`,
|
|
|
4954
5007
|
if ("errors" in msg && Array.isArray(msg.errors)) {
|
|
4955
5008
|
errors.push(...msg.errors);
|
|
4956
5009
|
for (const error of msg.errors) {
|
|
4957
|
-
|
|
5010
|
+
this.presenter.onError(error);
|
|
4958
5011
|
}
|
|
4959
5012
|
}
|
|
4960
5013
|
}
|
|
@@ -4965,7 +5018,7 @@ ${projectInstructions}`,
|
|
|
4965
5018
|
if (block.type === "tool_result" && block.tool_use_id) {
|
|
4966
5019
|
if (this.presenter.onToolResult) {
|
|
4967
5020
|
const resultContent = Array.isArray(block.content) ? block.content.map((c2) => c2.text || "").join("\n") : typeof block.content === "string" ? block.content : "";
|
|
4968
|
-
|
|
5021
|
+
this.presenter.onToolResult(block.tool_use_id, resultContent);
|
|
4969
5022
|
}
|
|
4970
5023
|
}
|
|
4971
5024
|
}
|
|
@@ -4982,7 +5035,7 @@ ${projectInstructions}`,
|
|
|
4982
5035
|
wasInterrupted = true;
|
|
4983
5036
|
} else if (config2.providerSessionId && isSessionExpiredError(errorMessage)) {
|
|
4984
5037
|
const expiredMessage = "Can't continue conversation older than 30 days. Please start a new session.";
|
|
4985
|
-
|
|
5038
|
+
this.presenter.onError(expiredMessage);
|
|
4986
5039
|
hasError = true;
|
|
4987
5040
|
errors.push(expiredMessage);
|
|
4988
5041
|
} else {
|
|
@@ -4993,7 +5046,7 @@ ${projectInstructions}`,
|
|
|
4993
5046
|
// Truncate for log size
|
|
4994
5047
|
iteration: iterations
|
|
4995
5048
|
});
|
|
4996
|
-
|
|
5049
|
+
this.presenter.onError(errorMessage);
|
|
4997
5050
|
hasError = true;
|
|
4998
5051
|
errors.push(errorMessage);
|
|
4999
5052
|
}
|
|
@@ -5010,7 +5063,7 @@ ${projectInstructions}`,
|
|
|
5010
5063
|
providerSessionId,
|
|
5011
5064
|
usage: queryUsage
|
|
5012
5065
|
};
|
|
5013
|
-
|
|
5066
|
+
this.presenter.onComplete(result);
|
|
5014
5067
|
return result;
|
|
5015
5068
|
}
|
|
5016
5069
|
async resolveClaudeCodePath() {
|
|
@@ -5039,7 +5092,7 @@ ${projectInstructions}`,
|
|
|
5039
5092
|
} catch {
|
|
5040
5093
|
const error = `Claude Code executable not found at: ${claudeCodePath}
|
|
5041
5094
|
Set SUPATEST_CLAUDE_CODE_PATH environment variable to override.`;
|
|
5042
|
-
|
|
5095
|
+
this.presenter.onError(error);
|
|
5043
5096
|
throw new Error(error);
|
|
5044
5097
|
}
|
|
5045
5098
|
return claudeCodePath;
|
|
@@ -5079,6 +5132,10 @@ function getToolDescription(toolName, input) {
|
|
|
5079
5132
|
return toolName;
|
|
5080
5133
|
}
|
|
5081
5134
|
}
|
|
5135
|
+
function streamEventAsync(apiClient, sessionId, event) {
|
|
5136
|
+
apiClient.streamEvent(sessionId, event).catch(() => {
|
|
5137
|
+
});
|
|
5138
|
+
}
|
|
5082
5139
|
var CONTEXT_WINDOWS, ReactPresenter;
|
|
5083
5140
|
var init_react = __esm({
|
|
5084
5141
|
"src/presenters/react.ts"() {
|
|
@@ -5109,19 +5166,18 @@ var init_react = __esm({
|
|
|
5109
5166
|
this.sessionId = sessionId;
|
|
5110
5167
|
this.verbose = verbose;
|
|
5111
5168
|
}
|
|
5112
|
-
|
|
5113
|
-
|
|
5169
|
+
onStart(config2) {
|
|
5170
|
+
streamEventAsync(this.apiClient, this.sessionId, {
|
|
5114
5171
|
type: "query_start",
|
|
5115
5172
|
prompt: config2.task
|
|
5116
|
-
};
|
|
5117
|
-
await this.apiClient.streamEvent(this.sessionId, event);
|
|
5173
|
+
});
|
|
5118
5174
|
}
|
|
5119
5175
|
onLog(message) {
|
|
5120
5176
|
if (this.verbose) {
|
|
5121
5177
|
console.error(message);
|
|
5122
5178
|
}
|
|
5123
5179
|
}
|
|
5124
|
-
|
|
5180
|
+
onAssistantText(text) {
|
|
5125
5181
|
if (!this.hasAssistantMessage) {
|
|
5126
5182
|
this.callbacks.addMessage({
|
|
5127
5183
|
type: "assistant",
|
|
@@ -5136,13 +5192,12 @@ var init_react = __esm({
|
|
|
5136
5192
|
content: this.currentAssistantText
|
|
5137
5193
|
});
|
|
5138
5194
|
}
|
|
5139
|
-
|
|
5195
|
+
streamEventAsync(this.apiClient, this.sessionId, {
|
|
5140
5196
|
type: "assistant_text",
|
|
5141
5197
|
delta: text
|
|
5142
|
-
};
|
|
5143
|
-
await this.apiClient.streamEvent(this.sessionId, textEvent);
|
|
5198
|
+
});
|
|
5144
5199
|
}
|
|
5145
|
-
|
|
5200
|
+
onThinking(text) {
|
|
5146
5201
|
if (!this.hasThinkingMessage) {
|
|
5147
5202
|
this.callbacks.addMessage({
|
|
5148
5203
|
type: "thinking",
|
|
@@ -5157,13 +5212,12 @@ var init_react = __esm({
|
|
|
5157
5212
|
content: this.currentThinkingText
|
|
5158
5213
|
});
|
|
5159
5214
|
}
|
|
5160
|
-
|
|
5215
|
+
streamEventAsync(this.apiClient, this.sessionId, {
|
|
5161
5216
|
type: "assistant_thinking",
|
|
5162
5217
|
delta: text
|
|
5163
|
-
};
|
|
5164
|
-
await this.apiClient.streamEvent(this.sessionId, thinkingEvent);
|
|
5218
|
+
});
|
|
5165
5219
|
}
|
|
5166
|
-
|
|
5220
|
+
onToolUse(tool, input, toolId) {
|
|
5167
5221
|
const message = {
|
|
5168
5222
|
type: "tool",
|
|
5169
5223
|
content: getToolDescription(tool, input),
|
|
@@ -5207,48 +5261,45 @@ var init_react = __esm({
|
|
|
5207
5261
|
} else if (tool === "EnterPlanMode") {
|
|
5208
5262
|
this.callbacks.onEnterPlanMode?.();
|
|
5209
5263
|
}
|
|
5210
|
-
|
|
5264
|
+
streamEventAsync(this.apiClient, this.sessionId, {
|
|
5211
5265
|
type: "tool_use",
|
|
5212
5266
|
id: toolId,
|
|
5213
5267
|
name: tool,
|
|
5214
5268
|
input: input || {}
|
|
5215
|
-
};
|
|
5216
|
-
await this.apiClient.streamEvent(this.sessionId, toolUseEvent);
|
|
5269
|
+
});
|
|
5217
5270
|
}
|
|
5218
|
-
|
|
5271
|
+
onToolResult(toolId, result) {
|
|
5219
5272
|
this.callbacks.updateMessageByToolId(toolId, {
|
|
5220
5273
|
toolResult: result
|
|
5221
5274
|
});
|
|
5222
|
-
|
|
5275
|
+
streamEventAsync(this.apiClient, this.sessionId, {
|
|
5223
5276
|
type: "tool_result",
|
|
5224
5277
|
tool_use_id: toolId,
|
|
5225
5278
|
content: result
|
|
5226
|
-
};
|
|
5227
|
-
await this.apiClient.streamEvent(this.sessionId, toolResultEvent);
|
|
5279
|
+
});
|
|
5228
5280
|
}
|
|
5229
|
-
|
|
5230
|
-
|
|
5281
|
+
onTurnComplete(content) {
|
|
5282
|
+
streamEventAsync(this.apiClient, this.sessionId, {
|
|
5231
5283
|
type: "message_complete",
|
|
5232
5284
|
message: {
|
|
5233
5285
|
role: "assistant",
|
|
5234
5286
|
content
|
|
5235
5287
|
}
|
|
5236
|
-
};
|
|
5237
|
-
await this.apiClient.streamEvent(this.sessionId, messageCompleteEvent);
|
|
5288
|
+
});
|
|
5238
5289
|
this.callbacks.onTurnComplete?.();
|
|
5239
5290
|
}
|
|
5240
|
-
|
|
5291
|
+
onError(error) {
|
|
5241
5292
|
this.callbacks.addMessage({
|
|
5242
5293
|
type: "error",
|
|
5243
5294
|
content: error,
|
|
5244
5295
|
errorType: "error"
|
|
5245
5296
|
});
|
|
5246
|
-
|
|
5297
|
+
streamEventAsync(this.apiClient, this.sessionId, {
|
|
5247
5298
|
type: "session_error",
|
|
5248
5299
|
error
|
|
5249
5300
|
});
|
|
5250
5301
|
}
|
|
5251
|
-
|
|
5302
|
+
onUsageUpdate(usage) {
|
|
5252
5303
|
const contextWindow = CONTEXT_WINDOWS[usage.model] || 2e5;
|
|
5253
5304
|
const contextTokens = usage.inputTokens + usage.cacheReadTokens + usage.cacheCreationTokens;
|
|
5254
5305
|
const contextPct = contextTokens / contextWindow * 100;
|
|
@@ -5269,10 +5320,10 @@ var init_react = __esm({
|
|
|
5269
5320
|
};
|
|
5270
5321
|
});
|
|
5271
5322
|
}
|
|
5272
|
-
|
|
5323
|
+
onComplete(result) {
|
|
5273
5324
|
if (result.usage) {
|
|
5274
5325
|
const queryStatus = result.success ? "success" : result.error?.toLowerCase().includes("interrupt") ? "interrupted" : "error";
|
|
5275
|
-
|
|
5326
|
+
streamEventAsync(this.apiClient, this.sessionId, {
|
|
5276
5327
|
type: "query_complete",
|
|
5277
5328
|
usage: result.usage,
|
|
5278
5329
|
result: {
|
|
@@ -5283,11 +5334,11 @@ var init_react = __esm({
|
|
|
5283
5334
|
});
|
|
5284
5335
|
}
|
|
5285
5336
|
if (result.success) {
|
|
5286
|
-
|
|
5337
|
+
streamEventAsync(this.apiClient, this.sessionId, {
|
|
5287
5338
|
type: "session_complete"
|
|
5288
5339
|
});
|
|
5289
5340
|
} else {
|
|
5290
|
-
|
|
5341
|
+
streamEventAsync(this.apiClient, this.sessionId, {
|
|
5291
5342
|
type: "session_error",
|
|
5292
5343
|
error: result.error || "Unknown error"
|
|
5293
5344
|
});
|
|
@@ -5412,6 +5463,9 @@ var init_SessionContext = __esm({
|
|
|
5412
5463
|
});
|
|
5413
5464
|
setStaticRemountKey((prev) => prev + 1);
|
|
5414
5465
|
}, []);
|
|
5466
|
+
const refreshStatic = useCallback(() => {
|
|
5467
|
+
setStaticRemountKey((prev) => prev + 1);
|
|
5468
|
+
}, []);
|
|
5415
5469
|
const updateStats = useCallback((updates) => {
|
|
5416
5470
|
setStats((prev) => ({ ...prev, ...updates }));
|
|
5417
5471
|
}, []);
|
|
@@ -5457,7 +5511,8 @@ var init_SessionContext = __esm({
|
|
|
5457
5511
|
setPlanFilePath,
|
|
5458
5512
|
selectedModel,
|
|
5459
5513
|
setSelectedModel,
|
|
5460
|
-
staticRemountKey
|
|
5514
|
+
staticRemountKey,
|
|
5515
|
+
refreshStatic
|
|
5461
5516
|
};
|
|
5462
5517
|
return /* @__PURE__ */ React.createElement(SessionContext.Provider, { value }, children);
|
|
5463
5518
|
};
|
|
@@ -6731,7 +6786,7 @@ var init_message_bridge = __esm({
|
|
|
6731
6786
|
});
|
|
6732
6787
|
|
|
6733
6788
|
// src/commands/login.ts
|
|
6734
|
-
import { spawn } from "child_process";
|
|
6789
|
+
import { spawn as spawn2 } from "child_process";
|
|
6735
6790
|
import crypto2 from "crypto";
|
|
6736
6791
|
import http from "http";
|
|
6737
6792
|
import { platform } from "os";
|
|
@@ -6767,7 +6822,7 @@ function openBrowser(url) {
|
|
|
6767
6822
|
default:
|
|
6768
6823
|
command = "xdg-open";
|
|
6769
6824
|
}
|
|
6770
|
-
|
|
6825
|
+
spawn2(command, [url], { detached: true, stdio: "ignore" }).unref();
|
|
6771
6826
|
}
|
|
6772
6827
|
function startCallbackServer(port, expectedState) {
|
|
6773
6828
|
return new Promise((resolve2, reject) => {
|
|
@@ -8678,9 +8733,9 @@ var init_useOverlayEscapeGuard = __esm({
|
|
|
8678
8733
|
});
|
|
8679
8734
|
|
|
8680
8735
|
// src/ui/App.tsx
|
|
8681
|
-
import { execSync as
|
|
8736
|
+
import { execSync as execSync5 } from "child_process";
|
|
8682
8737
|
import { homedir as homedir5 } from "os";
|
|
8683
|
-
import { Box as Box20, Text as Text18, useApp as useApp2 } from "ink";
|
|
8738
|
+
import { Box as Box20, Text as Text18, useApp as useApp2, useStdout as useStdout2 } from "ink";
|
|
8684
8739
|
import Spinner3 from "ink-spinner";
|
|
8685
8740
|
import React23, { useEffect as useEffect10, useRef as useRef4, useState as useState10 } from "react";
|
|
8686
8741
|
var getGitBranch2, getCurrentFolder2, AppContent, App;
|
|
@@ -8710,7 +8765,7 @@ var init_App = __esm({
|
|
|
8710
8765
|
init_theme();
|
|
8711
8766
|
getGitBranch2 = () => {
|
|
8712
8767
|
try {
|
|
8713
|
-
return
|
|
8768
|
+
return execSync5("git rev-parse --abbrev-ref HEAD", { encoding: "utf8" }).trim();
|
|
8714
8769
|
} catch {
|
|
8715
8770
|
return "";
|
|
8716
8771
|
}
|
|
@@ -8725,7 +8780,8 @@ var init_App = __esm({
|
|
|
8725
8780
|
};
|
|
8726
8781
|
AppContent = ({ config: config2, sessionId, webUrl, queuedTasks = [], onExit, onSubmitTask, apiClient, onResumeSession, onClearSession }) => {
|
|
8727
8782
|
const { exit } = useApp2();
|
|
8728
|
-
const {
|
|
8783
|
+
const { stdout } = useStdout2();
|
|
8784
|
+
const { addMessage, clearMessages, isAgentRunning, messages, setSessionId, setWebUrl, setShouldInterruptAgent, setIsAgentRunning, toggleAllToolOutputs, allToolsExpanded, selectedModel, setSelectedModel, refreshStatic } = useSession();
|
|
8729
8785
|
useModeToggle();
|
|
8730
8786
|
const [terminalWidth, setTerminalWidth] = useState10(process.stdout.columns || 80);
|
|
8731
8787
|
const [showHelp, setShowHelp] = useState10(false);
|
|
@@ -8878,7 +8934,7 @@ var init_App = __esm({
|
|
|
8878
8934
|
content: "Running setup..."
|
|
8879
8935
|
});
|
|
8880
8936
|
try {
|
|
8881
|
-
const result = await setupCommand();
|
|
8937
|
+
const result = await setupCommand({ cwd: config2.cwd || process.cwd() });
|
|
8882
8938
|
addMessage({
|
|
8883
8939
|
type: "assistant",
|
|
8884
8940
|
content: result.output
|
|
@@ -9002,6 +9058,7 @@ var init_App = __esm({
|
|
|
9002
9058
|
markOverlayClosed();
|
|
9003
9059
|
setShowHelp(false);
|
|
9004
9060
|
};
|
|
9061
|
+
const isInitialMount = useRef4(true);
|
|
9005
9062
|
useEffect10(() => {
|
|
9006
9063
|
const handleResize = () => {
|
|
9007
9064
|
setTerminalWidth(process.stdout.columns || 80);
|
|
@@ -9011,6 +9068,19 @@ var init_App = __esm({
|
|
|
9011
9068
|
process.stdout.off("resize", handleResize);
|
|
9012
9069
|
};
|
|
9013
9070
|
}, []);
|
|
9071
|
+
useEffect10(() => {
|
|
9072
|
+
if (isInitialMount.current) {
|
|
9073
|
+
isInitialMount.current = false;
|
|
9074
|
+
return;
|
|
9075
|
+
}
|
|
9076
|
+
const handler = setTimeout(() => {
|
|
9077
|
+
stdout?.write("\x1B[3J\x1B[H\x1B[2J");
|
|
9078
|
+
refreshStatic();
|
|
9079
|
+
}, 300);
|
|
9080
|
+
return () => {
|
|
9081
|
+
clearTimeout(handler);
|
|
9082
|
+
};
|
|
9083
|
+
}, [terminalWidth, refreshStatic, stdout]);
|
|
9014
9084
|
useKeypress(
|
|
9015
9085
|
(key) => {
|
|
9016
9086
|
if (key.name === "escape" && isAgentRunning && !isOverlayOpen) {
|
|
@@ -9436,9 +9506,9 @@ var init_interactive = __esm({
|
|
|
9436
9506
|
selectedModel,
|
|
9437
9507
|
systemPromptAppend: agentMode === "plan" ? config.planSystemPrompt : config2.systemPromptAppend
|
|
9438
9508
|
};
|
|
9439
|
-
const
|
|
9440
|
-
agentRef.current =
|
|
9441
|
-
const result = await
|
|
9509
|
+
const agent2 = new CoreAgent(presenter, messageBridge);
|
|
9510
|
+
agentRef.current = agent2;
|
|
9511
|
+
const result = await agent2.run(runConfig);
|
|
9442
9512
|
if (isMounted) {
|
|
9443
9513
|
onComplete(result.success, result.providerSessionId);
|
|
9444
9514
|
}
|
|
@@ -9752,9 +9822,9 @@ var HeadlessAgentRunner = ({ config: config2, sessionId, apiClient, onComplete }
|
|
|
9752
9822
|
sessionId,
|
|
9753
9823
|
config2.verbose
|
|
9754
9824
|
);
|
|
9755
|
-
const
|
|
9756
|
-
agentRef.current =
|
|
9757
|
-
const result = await
|
|
9825
|
+
const agent2 = new CoreAgent(presenter);
|
|
9826
|
+
agentRef.current = agent2;
|
|
9827
|
+
const result = await agent2.run(config2);
|
|
9758
9828
|
if (isMounted) {
|
|
9759
9829
|
onComplete(result.success, result.providerSessionId);
|
|
9760
9830
|
}
|
|
@@ -9825,10 +9895,8 @@ var HeadlessAppContent = ({
|
|
|
9825
9895
|
const handleComplete = (success, providerSessionId) => {
|
|
9826
9896
|
if (hasCompletedRef.current) return;
|
|
9827
9897
|
hasCompletedRef.current = true;
|
|
9828
|
-
|
|
9829
|
-
|
|
9830
|
-
exit();
|
|
9831
|
-
}, 100);
|
|
9898
|
+
onComplete(success, providerSessionId);
|
|
9899
|
+
exit();
|
|
9832
9900
|
};
|
|
9833
9901
|
return /* @__PURE__ */ React13.createElement(Box12, { flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React13.createElement(
|
|
9834
9902
|
MessageList,
|
|
@@ -9940,6 +10008,57 @@ async function runAgent(config2) {
|
|
|
9940
10008
|
});
|
|
9941
10009
|
}
|
|
9942
10010
|
|
|
10011
|
+
// src/utils/auto-update.ts
|
|
10012
|
+
init_error_logger();
|
|
10013
|
+
init_version();
|
|
10014
|
+
import { execSync as execSync3, spawn } from "child_process";
|
|
10015
|
+
import latestVersion from "latest-version";
|
|
10016
|
+
import { gt as gt2 } from "semver";
|
|
10017
|
+
var UPDATE_CHECK_TIMEOUT = 3e3;
|
|
10018
|
+
var INSTALL_TIMEOUT = 6e4;
|
|
10019
|
+
async function checkAndAutoUpdate() {
|
|
10020
|
+
if (process.env.NODE_ENV === "development") {
|
|
10021
|
+
return;
|
|
10022
|
+
}
|
|
10023
|
+
if (process.env.SUPATEST_SKIP_UPDATE === "1") {
|
|
10024
|
+
return;
|
|
10025
|
+
}
|
|
10026
|
+
try {
|
|
10027
|
+
const latest = await Promise.race([
|
|
10028
|
+
latestVersion("@supatest/cli"),
|
|
10029
|
+
new Promise(
|
|
10030
|
+
(_2, reject) => setTimeout(() => reject(new Error("timeout")), UPDATE_CHECK_TIMEOUT)
|
|
10031
|
+
)
|
|
10032
|
+
]);
|
|
10033
|
+
if (!gt2(latest, CLI_VERSION)) {
|
|
10034
|
+
return;
|
|
10035
|
+
}
|
|
10036
|
+
console.log(`
|
|
10037
|
+
Updating Supatest CLI ${CLI_VERSION} \u2192 ${latest}...`);
|
|
10038
|
+
try {
|
|
10039
|
+
execSync3("npm install -g @supatest/cli@latest", {
|
|
10040
|
+
stdio: "inherit",
|
|
10041
|
+
timeout: INSTALL_TIMEOUT
|
|
10042
|
+
});
|
|
10043
|
+
console.log("\u2713 Updated successfully\n");
|
|
10044
|
+
const child = spawn(process.argv[0], process.argv.slice(1), {
|
|
10045
|
+
stdio: "inherit",
|
|
10046
|
+
detached: false
|
|
10047
|
+
});
|
|
10048
|
+
child.on("exit", (code) => process.exit(code ?? 0));
|
|
10049
|
+
await new Promise(() => {
|
|
10050
|
+
});
|
|
10051
|
+
} catch (installError) {
|
|
10052
|
+
logError(installError, { source: "auto-update", action: "install" });
|
|
10053
|
+
const errMsg = installError instanceof Error ? installError.message : String(installError);
|
|
10054
|
+
console.error("Failed to update:", errMsg);
|
|
10055
|
+
console.log("Continuing with current version...\n");
|
|
10056
|
+
}
|
|
10057
|
+
} catch (error) {
|
|
10058
|
+
logError(error, { source: "auto-update", action: "check" });
|
|
10059
|
+
}
|
|
10060
|
+
}
|
|
10061
|
+
|
|
9943
10062
|
// src/index.ts
|
|
9944
10063
|
init_banner();
|
|
9945
10064
|
init_error_logger();
|
|
@@ -9947,7 +10066,7 @@ init_logger();
|
|
|
9947
10066
|
|
|
9948
10067
|
// src/utils/node-version.ts
|
|
9949
10068
|
init_logger();
|
|
9950
|
-
import { execSync as
|
|
10069
|
+
import { execSync as execSync4 } from "child_process";
|
|
9951
10070
|
var MINIMUM_NODE_VERSION2 = 18;
|
|
9952
10071
|
function parseVersion2(versionString) {
|
|
9953
10072
|
const cleaned = versionString.trim().replace(/^v/, "");
|
|
@@ -9964,7 +10083,7 @@ function parseVersion2(versionString) {
|
|
|
9964
10083
|
}
|
|
9965
10084
|
function getNodeVersion2() {
|
|
9966
10085
|
try {
|
|
9967
|
-
const versionOutput =
|
|
10086
|
+
const versionOutput = execSync4("node --version", {
|
|
9968
10087
|
encoding: "utf-8",
|
|
9969
10088
|
stdio: ["ignore", "pipe", "ignore"]
|
|
9970
10089
|
});
|
|
@@ -10084,6 +10203,7 @@ program.name("supatest").description(
|
|
|
10084
10203
|
).option("--supatest-api-key <key>", "Supatest API key (or use SUPATEST_API_KEY env)").option("--supatest-api-url <url>", "Supatest API URL (or use SUPATEST_API_URL env, defaults to https://code-api.supatest.ai)").option("--headless", "Run in headless mode (for CI/CD, minimal output)").option("--verbose", "Enable verbose logging").option("--model <model>", "Claude model to use (or use ANTHROPIC_MODEL_NAME env)").action(async (task, options) => {
|
|
10085
10204
|
try {
|
|
10086
10205
|
checkNodeVersion2();
|
|
10206
|
+
await checkAndAutoUpdate();
|
|
10087
10207
|
const isHeadlessMode = options.headless || process.stdin.isTTY === false;
|
|
10088
10208
|
if (options.verbose) {
|
|
10089
10209
|
logger.setVerbose(true);
|
|
@@ -10192,9 +10312,9 @@ program.name("supatest").description(
|
|
|
10192
10312
|
process.exit(1);
|
|
10193
10313
|
}
|
|
10194
10314
|
});
|
|
10195
|
-
program.command("setup").description("Check prerequisites and set up required tools (Node.js, Playwright MCP)").action(async () => {
|
|
10315
|
+
program.command("setup").description("Check prerequisites and set up required tools (Node.js, Playwright MCP)").option("-C, --cwd <path>", "Working directory for setup", process.cwd()).action(async (options) => {
|
|
10196
10316
|
try {
|
|
10197
|
-
const result = await setupCommand();
|
|
10317
|
+
const result = await setupCommand({ cwd: options.cwd });
|
|
10198
10318
|
process.exit(result.errors.length === 0 ? 0 : 1);
|
|
10199
10319
|
} catch (error) {
|
|
10200
10320
|
logError(error, { source: "setup" });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@supatest/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.23",
|
|
4
4
|
"description": "Supatest CLI - AI-powered task automation for CI/CD",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -63,10 +63,14 @@
|
|
|
63
63
|
"react": "^19.0.0",
|
|
64
64
|
"string-width": "^8.1.0",
|
|
65
65
|
"strip-ansi": "^7.1.2",
|
|
66
|
-
"
|
|
66
|
+
"undici": "^7.16.0",
|
|
67
|
+
"wrap-ansi": "^9.0.2",
|
|
68
|
+
"latest-version": "^9.0.0",
|
|
69
|
+
"semver": "^7.6.0"
|
|
67
70
|
},
|
|
68
71
|
"devDependencies": {
|
|
69
72
|
"@types/node": "^20.12.12",
|
|
73
|
+
"@types/semver": "^7.5.8",
|
|
70
74
|
"@types/react": "^19.0.0",
|
|
71
75
|
"nodemon": "^3.1.11",
|
|
72
76
|
"tsup": "^8.5.1",
|