@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.
Files changed (2) hide show
  1. package/dist/index.js +144 -72
  2. 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 messages for a session
4582
+ * Get queries for a session
4574
4583
  * @param sessionId - The session ID
4575
- * @returns Messages with pagination info
4584
+ * @returns Queries for the session
4576
4585
  */
4577
- async getSessionMessages(sessionId) {
4578
- const url = `${this.apiUrl}/v1/sessions/${sessionId}/messages`;
4579
- logger.debug(`Fetching messages for session: ${sessionId}`);
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.messages.length} messages for session: ${sessionId}`
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.12";
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(homedir(), ".supatest");
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
- reject(error);
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
- console.error("\n\u274C Login failed:", error.message, "\n");
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 homedir2 } from "os";
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 = homedir2();
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 = config2.supatestApiKey;
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.getSessionMessages(session.id);
8629
- const apiMessages = response.messages;
8630
- const toolResults = /* @__PURE__ */ new Map();
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"} (${apiMessages.length} messages loaded)`,
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 = options.supatestApiKey || config.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 = options.supatestApiKey || config.supatestApiKey;
9561
+ supatestApiKey = normalizeSupatestKey(
9562
+ options.supatestApiKey || config.supatestApiKey,
9563
+ { isHeadless: false }
9564
+ );
9493
9565
  }
9494
9566
  }
9495
9567
  let selectedModel;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supatest/cli",
3
- "version": "0.0.12",
3
+ "version": "0.0.13",
4
4
  "description": "Supatest CLI - AI-powered task automation for CI/CD",
5
5
  "type": "module",
6
6
  "bin": {