@supatest/cli 0.0.12 → 0.0.13
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 +144 -72
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3872,6 +3872,7 @@ var init_project_instructions = __esm({
|
|
|
3872
3872
|
|
|
3873
3873
|
// src/core/agent.ts
|
|
3874
3874
|
import { createRequire } from "module";
|
|
3875
|
+
import { homedir } from "os";
|
|
3875
3876
|
import { dirname, join as join2 } from "path";
|
|
3876
3877
|
import { query } from "@anthropic-ai/claude-agent-sdk";
|
|
3877
3878
|
var CoreAgent;
|
|
@@ -3932,6 +3933,8 @@ ${projectInstructions}`
|
|
|
3932
3933
|
cleanEnv[key] = value;
|
|
3933
3934
|
}
|
|
3934
3935
|
}
|
|
3936
|
+
const claudeConfigDir = process.env.CLAUDE_CONFIG_DIR || join2(homedir(), ".supatest", "claude-config");
|
|
3937
|
+
cleanEnv.CLAUDE_CONFIG_DIR = claudeConfigDir;
|
|
3935
3938
|
cleanEnv.ANTHROPIC_API_KEY = config2.supatestApiKey || "";
|
|
3936
3939
|
cleanEnv.ANTHROPIC_BASE_URL = process.env.ANTHROPIC_BASE_URL || "";
|
|
3937
3940
|
cleanEnv.ANTHROPIC_AUTH_TOKEN = "";
|
|
@@ -4447,6 +4450,12 @@ var init_api_client = __esm({
|
|
|
4447
4450
|
hasApiKey() {
|
|
4448
4451
|
return !!this.apiKey;
|
|
4449
4452
|
}
|
|
4453
|
+
/**
|
|
4454
|
+
* Get the current API key (used to refresh auth during a session)
|
|
4455
|
+
*/
|
|
4456
|
+
getApiKey() {
|
|
4457
|
+
return this.apiKey;
|
|
4458
|
+
}
|
|
4450
4459
|
/**
|
|
4451
4460
|
* Create a new CLI session on the backend
|
|
4452
4461
|
* @param title - The session title
|
|
@@ -4570,13 +4579,13 @@ var init_api_client = __esm({
|
|
|
4570
4579
|
return data;
|
|
4571
4580
|
}
|
|
4572
4581
|
/**
|
|
4573
|
-
* Get
|
|
4582
|
+
* Get queries for a session
|
|
4574
4583
|
* @param sessionId - The session ID
|
|
4575
|
-
* @returns
|
|
4584
|
+
* @returns Queries for the session
|
|
4576
4585
|
*/
|
|
4577
|
-
async
|
|
4578
|
-
const url = `${this.apiUrl}/v1/sessions/${sessionId}/
|
|
4579
|
-
logger.debug(`Fetching
|
|
4586
|
+
async getSessionQueries(sessionId) {
|
|
4587
|
+
const url = `${this.apiUrl}/v1/sessions/${sessionId}/queries`;
|
|
4588
|
+
logger.debug(`Fetching queries for session: ${sessionId}`);
|
|
4580
4589
|
const response = await fetch(url, {
|
|
4581
4590
|
method: "GET",
|
|
4582
4591
|
headers: {
|
|
@@ -4589,7 +4598,7 @@ var init_api_client = __esm({
|
|
|
4589
4598
|
}
|
|
4590
4599
|
const data = await response.json();
|
|
4591
4600
|
logger.debug(
|
|
4592
|
-
`Fetched ${data.
|
|
4601
|
+
`Fetched ${data.queries.length} queries for session: ${sessionId}`
|
|
4593
4602
|
);
|
|
4594
4603
|
return data;
|
|
4595
4604
|
}
|
|
@@ -4658,7 +4667,7 @@ var CLI_VERSION;
|
|
|
4658
4667
|
var init_version = __esm({
|
|
4659
4668
|
"src/version.ts"() {
|
|
4660
4669
|
"use strict";
|
|
4661
|
-
CLI_VERSION = "0.0.
|
|
4670
|
+
CLI_VERSION = "0.0.13";
|
|
4662
4671
|
}
|
|
4663
4672
|
});
|
|
4664
4673
|
|
|
@@ -4733,7 +4742,7 @@ var init_encryption = __esm({
|
|
|
4733
4742
|
|
|
4734
4743
|
// src/utils/token-storage.ts
|
|
4735
4744
|
import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, unlinkSync, writeFileSync } from "fs";
|
|
4736
|
-
import { homedir } from "os";
|
|
4745
|
+
import { homedir as homedir2 } from "os";
|
|
4737
4746
|
import { join as join4 } from "path";
|
|
4738
4747
|
function getTokenFilePath() {
|
|
4739
4748
|
const apiUrl = process.env.SUPATEST_API_URL || PRODUCTION_API_URL;
|
|
@@ -4808,7 +4817,7 @@ var init_token_storage = __esm({
|
|
|
4808
4817
|
"src/utils/token-storage.ts"() {
|
|
4809
4818
|
"use strict";
|
|
4810
4819
|
init_encryption();
|
|
4811
|
-
CONFIG_DIR = join4(
|
|
4820
|
+
CONFIG_DIR = join4(homedir2(), ".supatest");
|
|
4812
4821
|
PRODUCTION_API_URL = "https://code-api.supatest.ai";
|
|
4813
4822
|
STORAGE_VERSION = 2;
|
|
4814
4823
|
TOKEN_FILE = join4(CONFIG_DIR, "token.json");
|
|
@@ -5170,7 +5179,15 @@ function startCallbackServer(port, expectedState) {
|
|
|
5170
5179
|
}
|
|
5171
5180
|
});
|
|
5172
5181
|
server.on("error", (error) => {
|
|
5173
|
-
|
|
5182
|
+
if (error.code === "EADDRINUSE") {
|
|
5183
|
+
const portError = new Error(
|
|
5184
|
+
"Something went wrong. Please restart the CLI and try again."
|
|
5185
|
+
);
|
|
5186
|
+
portError.code = "EADDRINUSE";
|
|
5187
|
+
reject(portError);
|
|
5188
|
+
} else {
|
|
5189
|
+
reject(error);
|
|
5190
|
+
}
|
|
5174
5191
|
});
|
|
5175
5192
|
const timeout = setTimeout(() => {
|
|
5176
5193
|
server.close();
|
|
@@ -5424,7 +5441,13 @@ ${loginUrl}
|
|
|
5424
5441
|
console.log("\n\u2705 Login successful!\n");
|
|
5425
5442
|
return result;
|
|
5426
5443
|
} catch (error) {
|
|
5427
|
-
|
|
5444
|
+
const err = error;
|
|
5445
|
+
if (err.code === "EADDRINUSE") {
|
|
5446
|
+
console.error("\n\u274C Login failed: Something went wrong.");
|
|
5447
|
+
console.error(" Please restart the CLI and try again.\n");
|
|
5448
|
+
} else {
|
|
5449
|
+
console.error("\n\u274C Login failed:", error.message, "\n");
|
|
5450
|
+
}
|
|
5428
5451
|
throw error;
|
|
5429
5452
|
}
|
|
5430
5453
|
}
|
|
@@ -7913,7 +7936,7 @@ var init_useOverlayEscapeGuard = __esm({
|
|
|
7913
7936
|
|
|
7914
7937
|
// src/ui/App.tsx
|
|
7915
7938
|
import { execSync as execSync3 } from "child_process";
|
|
7916
|
-
import { homedir as
|
|
7939
|
+
import { homedir as homedir3 } from "os";
|
|
7917
7940
|
import { Box as Box18, Text as Text17, useApp } from "ink";
|
|
7918
7941
|
import React20, { useEffect as useEffect7, useRef as useRef3, useState as useState6 } from "react";
|
|
7919
7942
|
var getGitBranch, getCurrentFolder, AppContent, App;
|
|
@@ -7947,7 +7970,7 @@ var init_App = __esm({
|
|
|
7947
7970
|
};
|
|
7948
7971
|
getCurrentFolder = () => {
|
|
7949
7972
|
const cwd = process.cwd();
|
|
7950
|
-
const home =
|
|
7973
|
+
const home = homedir3();
|
|
7951
7974
|
if (cwd.startsWith(home)) {
|
|
7952
7975
|
return `~${cwd.slice(home.length)}`;
|
|
7953
7976
|
}
|
|
@@ -8344,6 +8367,69 @@ function getToolDescription2(toolName, input) {
|
|
|
8344
8367
|
return toolName;
|
|
8345
8368
|
}
|
|
8346
8369
|
}
|
|
8370
|
+
function convertQueriesToUIMessages(queries) {
|
|
8371
|
+
const uiMessages = [];
|
|
8372
|
+
const sortedQueries = [...queries].sort((a, b2) => a.sequence - b2.sequence);
|
|
8373
|
+
for (const query2 of sortedQueries) {
|
|
8374
|
+
const timestamp = new Date(query2.startedAt).getTime();
|
|
8375
|
+
if (query2.content?.userMessage) {
|
|
8376
|
+
for (const block of query2.content.userMessage) {
|
|
8377
|
+
if (block.type === "text") {
|
|
8378
|
+
uiMessages.push({
|
|
8379
|
+
id: `${query2.id}-user-${uiMessages.length}`,
|
|
8380
|
+
type: "user",
|
|
8381
|
+
content: block.text,
|
|
8382
|
+
timestamp
|
|
8383
|
+
});
|
|
8384
|
+
}
|
|
8385
|
+
}
|
|
8386
|
+
}
|
|
8387
|
+
if (query2.content?.turns) {
|
|
8388
|
+
const toolResults = /* @__PURE__ */ new Map();
|
|
8389
|
+
for (const turn of query2.content.turns) {
|
|
8390
|
+
if (turn.toolResults) {
|
|
8391
|
+
for (const block of turn.toolResults) {
|
|
8392
|
+
if (block.type === "tool_result" && block.tool_use_id) {
|
|
8393
|
+
toolResults.set(block.tool_use_id, block.content);
|
|
8394
|
+
}
|
|
8395
|
+
}
|
|
8396
|
+
}
|
|
8397
|
+
}
|
|
8398
|
+
for (const turn of query2.content.turns) {
|
|
8399
|
+
for (const block of turn.assistant) {
|
|
8400
|
+
if (block.type === "text") {
|
|
8401
|
+
uiMessages.push({
|
|
8402
|
+
id: `${query2.id}-assistant-${uiMessages.length}`,
|
|
8403
|
+
type: "assistant",
|
|
8404
|
+
content: block.text,
|
|
8405
|
+
timestamp
|
|
8406
|
+
});
|
|
8407
|
+
} else if (block.type === "thinking") {
|
|
8408
|
+
uiMessages.push({
|
|
8409
|
+
id: `${query2.id}-thinking-${uiMessages.length}`,
|
|
8410
|
+
type: "thinking",
|
|
8411
|
+
content: block.thinking,
|
|
8412
|
+
timestamp
|
|
8413
|
+
});
|
|
8414
|
+
} else if (block.type === "tool_use") {
|
|
8415
|
+
const toolResult = toolResults.get(block.id);
|
|
8416
|
+
uiMessages.push({
|
|
8417
|
+
id: `${query2.id}-tool-${uiMessages.length}`,
|
|
8418
|
+
type: "tool",
|
|
8419
|
+
content: getToolDescription2(block.name, block.input),
|
|
8420
|
+
toolName: block.name,
|
|
8421
|
+
toolInput: block.input,
|
|
8422
|
+
toolResult,
|
|
8423
|
+
toolUseId: block.id,
|
|
8424
|
+
timestamp
|
|
8425
|
+
});
|
|
8426
|
+
}
|
|
8427
|
+
}
|
|
8428
|
+
}
|
|
8429
|
+
}
|
|
8430
|
+
}
|
|
8431
|
+
return uiMessages;
|
|
8432
|
+
}
|
|
8347
8433
|
async function runInteractive(config2) {
|
|
8348
8434
|
let success = false;
|
|
8349
8435
|
let unmountFn = null;
|
|
@@ -8467,10 +8553,11 @@ var init_interactive = __esm({
|
|
|
8467
8553
|
const runAgent2 = async () => {
|
|
8468
8554
|
setIsAgentRunning(true);
|
|
8469
8555
|
try {
|
|
8556
|
+
const supatestApiKey = apiClient.getApiKey?.() || config2.supatestApiKey || "";
|
|
8470
8557
|
const proxyUrl = config2.supatestApiUrl || "https://code-api.supatest.ai";
|
|
8471
8558
|
const baseUrl = `${proxyUrl}/v1/sessions/${sessionId}/anthropic`;
|
|
8472
8559
|
process.env.ANTHROPIC_BASE_URL = baseUrl;
|
|
8473
|
-
process.env.ANTHROPIC_API_KEY =
|
|
8560
|
+
process.env.ANTHROPIC_API_KEY = supatestApiKey;
|
|
8474
8561
|
const presenter = new ReactPresenter(
|
|
8475
8562
|
{
|
|
8476
8563
|
addMessage: (msg) => {
|
|
@@ -8502,6 +8589,7 @@ var init_interactive = __esm({
|
|
|
8502
8589
|
);
|
|
8503
8590
|
const runConfig = {
|
|
8504
8591
|
...config2,
|
|
8592
|
+
supatestApiKey,
|
|
8505
8593
|
mode: agentMode,
|
|
8506
8594
|
planFilePath,
|
|
8507
8595
|
selectedModel,
|
|
@@ -8625,61 +8713,9 @@ var init_interactive = __esm({
|
|
|
8625
8713
|
});
|
|
8626
8714
|
return;
|
|
8627
8715
|
}
|
|
8628
|
-
const response = await apiClient.
|
|
8629
|
-
const
|
|
8630
|
-
const
|
|
8631
|
-
for (const msg of apiMessages) {
|
|
8632
|
-
const contentBlocks = msg.content;
|
|
8633
|
-
if (!contentBlocks) continue;
|
|
8634
|
-
for (const block of contentBlocks) {
|
|
8635
|
-
if (block.type === "tool_result" && block.tool_use_id) {
|
|
8636
|
-
let resultText = "";
|
|
8637
|
-
if (typeof block.content === "string") {
|
|
8638
|
-
resultText = block.content;
|
|
8639
|
-
} else if (Array.isArray(block.content)) {
|
|
8640
|
-
resultText = block.content.map((c2) => c2.text || "").join("\n");
|
|
8641
|
-
}
|
|
8642
|
-
toolResults.set(block.tool_use_id, resultText);
|
|
8643
|
-
}
|
|
8644
|
-
}
|
|
8645
|
-
}
|
|
8646
|
-
const uiMessages = apiMessages.flatMap((msg) => {
|
|
8647
|
-
const messages = [];
|
|
8648
|
-
const contentBlocks = msg.content;
|
|
8649
|
-
if (!contentBlocks || contentBlocks.length === 0) {
|
|
8650
|
-
return [];
|
|
8651
|
-
}
|
|
8652
|
-
for (const block of contentBlocks) {
|
|
8653
|
-
if (block.type === "text") {
|
|
8654
|
-
messages.push({
|
|
8655
|
-
id: `${msg.id}-${messages.length}`,
|
|
8656
|
-
type: msg.role,
|
|
8657
|
-
content: block.text,
|
|
8658
|
-
timestamp: new Date(msg.createdAt).getTime()
|
|
8659
|
-
});
|
|
8660
|
-
} else if (block.type === "thinking") {
|
|
8661
|
-
messages.push({
|
|
8662
|
-
id: `${msg.id}-${messages.length}`,
|
|
8663
|
-
type: "thinking",
|
|
8664
|
-
content: block.thinking,
|
|
8665
|
-
timestamp: new Date(msg.createdAt).getTime()
|
|
8666
|
-
});
|
|
8667
|
-
} else if (block.type === "tool_use") {
|
|
8668
|
-
const toolResult = toolResults.get(block.id);
|
|
8669
|
-
messages.push({
|
|
8670
|
-
id: `${msg.id}-${messages.length}`,
|
|
8671
|
-
type: "tool",
|
|
8672
|
-
content: getToolDescription2(block.name, block.input),
|
|
8673
|
-
toolName: block.name,
|
|
8674
|
-
toolInput: block.input,
|
|
8675
|
-
toolResult,
|
|
8676
|
-
toolUseId: block.id,
|
|
8677
|
-
timestamp: new Date(msg.createdAt).getTime()
|
|
8678
|
-
});
|
|
8679
|
-
}
|
|
8680
|
-
}
|
|
8681
|
-
return messages;
|
|
8682
|
-
});
|
|
8716
|
+
const response = await apiClient.getSessionQueries(session.id);
|
|
8717
|
+
const queries = response.queries;
|
|
8718
|
+
const uiMessages = convertQueriesToUIMessages(queries);
|
|
8683
8719
|
setSessionId(session.id);
|
|
8684
8720
|
setContextSessionId(session.id);
|
|
8685
8721
|
if (session.providerSessionId) {
|
|
@@ -8705,7 +8741,7 @@ var init_interactive = __esm({
|
|
|
8705
8741
|
uiMessages.push({
|
|
8706
8742
|
id: `resume-info-${Date.now()}`,
|
|
8707
8743
|
type: "error",
|
|
8708
|
-
content: `Resumed session: ${session.title || "Untitled"} (${
|
|
8744
|
+
content: `Resumed session: ${session.title || "Untitled"} (${queries.length} queries loaded)`,
|
|
8709
8745
|
errorType: "info",
|
|
8710
8746
|
timestamp: Date.now()
|
|
8711
8747
|
});
|
|
@@ -9432,6 +9468,36 @@ async function readStdin() {
|
|
|
9432
9468
|
// src/index.ts
|
|
9433
9469
|
init_token_storage();
|
|
9434
9470
|
init_version();
|
|
9471
|
+
var looksLikeAnthropicKey = (key) => !!key && key.startsWith("sk-ant-");
|
|
9472
|
+
var normalizeSupatestKey = (key, { isHeadless }) => {
|
|
9473
|
+
if (!key) return void 0;
|
|
9474
|
+
if (looksLikeAnthropicKey(key)) {
|
|
9475
|
+
const message = "Detected an Anthropic API key. Use a Supatest API key (sk_test_/sk_live_) or run `supatest login` for a CLI token.";
|
|
9476
|
+
if (isHeadless) {
|
|
9477
|
+
logger.error(message);
|
|
9478
|
+
process.exit(1);
|
|
9479
|
+
} else {
|
|
9480
|
+
logger.warn(`${message} Ignoring the provided key and prompting for login.`);
|
|
9481
|
+
return void 0;
|
|
9482
|
+
}
|
|
9483
|
+
}
|
|
9484
|
+
return key;
|
|
9485
|
+
};
|
|
9486
|
+
process.on("uncaughtException", (error) => {
|
|
9487
|
+
logger.error(`Uncaught exception: ${error.message}`);
|
|
9488
|
+
if (error.stack) {
|
|
9489
|
+
console.error(error.stack);
|
|
9490
|
+
}
|
|
9491
|
+
process.exit(1);
|
|
9492
|
+
});
|
|
9493
|
+
process.on("unhandledRejection", (reason, promise) => {
|
|
9494
|
+
const errorMessage = reason instanceof Error ? reason.message : String(reason);
|
|
9495
|
+
logger.error(`Unhandled promise rejection: ${errorMessage}`);
|
|
9496
|
+
if (reason instanceof Error && reason.stack) {
|
|
9497
|
+
console.error(reason.stack);
|
|
9498
|
+
}
|
|
9499
|
+
process.exit(1);
|
|
9500
|
+
});
|
|
9435
9501
|
var program = new Command();
|
|
9436
9502
|
program.name("supatest").description(
|
|
9437
9503
|
"AI-powered task automation CLI for CI/CD - fix tests, lint issues, and more"
|
|
@@ -9475,7 +9541,10 @@ program.name("supatest").description(
|
|
|
9475
9541
|
let supatestApiKey;
|
|
9476
9542
|
const supatestApiUrl = options.supatestApiUrl || config.supatestApiUrl;
|
|
9477
9543
|
if (isHeadlessMode) {
|
|
9478
|
-
supatestApiKey =
|
|
9544
|
+
supatestApiKey = normalizeSupatestKey(
|
|
9545
|
+
options.supatestApiKey || config.supatestApiKey,
|
|
9546
|
+
{ isHeadless: true }
|
|
9547
|
+
);
|
|
9479
9548
|
if (!supatestApiKey) {
|
|
9480
9549
|
logger.error(
|
|
9481
9550
|
"API key required in CI/headless mode. Please either:"
|
|
@@ -9489,7 +9558,10 @@ program.name("supatest").description(
|
|
|
9489
9558
|
if (cliToken) {
|
|
9490
9559
|
supatestApiKey = cliToken;
|
|
9491
9560
|
} else {
|
|
9492
|
-
supatestApiKey =
|
|
9561
|
+
supatestApiKey = normalizeSupatestKey(
|
|
9562
|
+
options.supatestApiKey || config.supatestApiKey,
|
|
9563
|
+
{ isHeadless: false }
|
|
9564
|
+
);
|
|
9493
9565
|
}
|
|
9494
9566
|
}
|
|
9495
9567
|
let selectedModel;
|