@qatonic_innovations/qaios 0.3.0 → 0.3.1

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/README.md CHANGED
@@ -123,7 +123,7 @@ Run `qaios <command> --help` for the full option list of any command.
123
123
 
124
124
  ```bash
125
125
  # Generate API tests from an OpenAPI spec
126
- qaios test --type api --spec ./openapi.yaml "exercise the /orders endpoints"
126
+ qaios test --type api --api-spec ./openapi.yaml "exercise the /orders endpoints"
127
127
 
128
128
  # Run a suite; QAIOS classifies failures and self-heals locator drift
129
129
  qaios run
package/dist/index.js CHANGED
@@ -843,6 +843,14 @@ var McpServerConfig = z.object({
843
843
  var QaiosConfig = z.object({
844
844
  version: z.literal(1),
845
845
  mode: Mode.default("LITE"),
846
+ // The application under test. `baseUrl` is read by run / snapshot / fix /
847
+ // test (CLI `--base-url` overrides it per run). OPTIONAL, not defaulted — a
848
+ // `.default({})` would make `qaios init` serialize an empty `app: {}` stub
849
+ // into every config, and a user later hand-adding an `app:` block would then
850
+ // hit a duplicate-key YAML error. Callers use `config?.app?.baseUrl`.
851
+ app: z.object({
852
+ baseUrl: z.string().url().optional()
853
+ }).optional(),
846
854
  llm: z.object({
847
855
  // Which LLM provider backs every skill. Default stays anthropic so
848
856
  // existing projects are unchanged. Set `openai` to use OpenAI instead;
@@ -2673,6 +2681,20 @@ function tempForTier(tier) {
2673
2681
  return 0.1;
2674
2682
  }
2675
2683
  }
2684
+ var DEFAULT_LLM_TIMEOUT_MS = 12e4;
2685
+ function llmTimeoutMs() {
2686
+ const raw = process.env["QAIOS_LLM_TIMEOUT_MS"];
2687
+ if (raw === void 0) return DEFAULT_LLM_TIMEOUT_MS;
2688
+ const n = Number.parseInt(raw, 10);
2689
+ return Number.isFinite(n) && n >= 0 ? n : DEFAULT_LLM_TIMEOUT_MS;
2690
+ }
2691
+ function buildCallSignal(cancelSignal) {
2692
+ const ms = llmTimeoutMs();
2693
+ if (ms <= 0) return cancelSignal;
2694
+ const timeout = AbortSignal.timeout(ms);
2695
+ if (cancelSignal === void 0) return timeout;
2696
+ return AbortSignal.any([timeout, cancelSignal]);
2697
+ }
2676
2698
  function schemaToJsonSchema(schema) {
2677
2699
  const probe = schema;
2678
2700
  if (typeof probe.toJSONSchema === "function") {
@@ -2742,7 +2764,8 @@ var SkillRunner = class {
2742
2764
  maxTokens: 16384,
2743
2765
  temperature: tempForTier(skill.modelTier)
2744
2766
  };
2745
- if (ctx.cancelSignal !== void 0) callOpts.signal = ctx.cancelSignal;
2767
+ const signal = buildCallSignal(ctx.cancelSignal);
2768
+ if (signal !== void 0) callOpts.signal = signal;
2746
2769
  response = await ctx.llm.call(callOpts);
2747
2770
  } catch (err) {
2748
2771
  ctx.auditLogger.append({
@@ -10514,6 +10537,13 @@ async function runMcp(opts) {
10514
10537
  if (ownsStorage) storage.close();
10515
10538
  }
10516
10539
  }
10540
+ function withTimeout(promise, ms, message) {
10541
+ let timer;
10542
+ const timeout = new Promise((_resolve, reject) => {
10543
+ timer = setTimeout(() => reject(new McpError("qaios.mcp.test_timeout", message)), ms);
10544
+ });
10545
+ return Promise.race([promise, timeout]).finally(() => clearTimeout(timer));
10546
+ }
10517
10547
  function listServers(repo, opts, writeOut) {
10518
10548
  const servers = repo.list();
10519
10549
  if (opts.json === true) {
@@ -10653,8 +10683,13 @@ async function testServer(repo, opts, writeOut) {
10653
10683
  servers: [{ ...config, enabled: true }]
10654
10684
  });
10655
10685
  const ownsClient = opts.mcpClient === void 0;
10686
+ const MCP_TEST_TIMEOUT_MS = 15e3;
10656
10687
  try {
10657
- const tools = await client.listTools(opts.name);
10688
+ const tools = await withTimeout(
10689
+ client.listTools(opts.name),
10690
+ MCP_TEST_TIMEOUT_MS,
10691
+ `MCP server "${opts.name}" did not respond within ${MCP_TEST_TIMEOUT_MS / 1e3}s \u2014 is it a valid MCP server?`
10692
+ );
10658
10693
  writeOut(`\u2713 Connected to "${opts.name}". Tools (${tools.length}):`);
10659
10694
  for (const t of tools) {
10660
10695
  writeOut(` - ${t.name}${t.description !== void 0 ? ` \u2014 ${t.description}` : ""}`);
@@ -12278,7 +12313,11 @@ function buildProgram() {
12278
12313
  }
12279
12314
  process.exit(result.exitCode);
12280
12315
  });
12281
- program.command("explore <url>").description("Run an exploratory testing session against a URL").option("--duration <seconds>", "time budget in seconds (default 600)", (v) => parseInt(v, 10)).option("--focus <text>", "optional natural-language focus hint").option("--charter-only", "generate charter and stop; no findings, no defects").option("--no-defects", "skip defect.create + filing for findings").option("--defect-target <target>", "where to file defects: stdout | github | jira").option("--defect-repo <repo>", "github repo (owner/repo) when --defect-target=github").option("--defect-project <project>", "jira project key when --defect-target=jira").action(async (url, cmdOpts, command) => {
12316
+ program.command("explore <url>").description("Run an exploratory testing session against a URL").option(
12317
+ "--duration <seconds>",
12318
+ "time budget in seconds (min 60, default 600)",
12319
+ (v) => parseInt(v, 10)
12320
+ ).option("--focus <text>", "optional natural-language focus hint").option("--charter-only", "generate charter and stop; no findings, no defects").option("--no-defects", "skip defect.create + filing for findings").option("--defect-target <target>", "where to file defects: stdout | github | jira").option("--defect-repo <repo>", "github repo (owner/repo) when --defect-target=github").option("--defect-project <project>", "jira project key when --defect-target=jira").action(async (url, cmdOpts, command) => {
12282
12321
  const globalOpts = command.parent?.opts() ?? {};
12283
12322
  const opts = {
12284
12323
  url,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qatonic_innovations/qaios",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "type": "module",
5
5
  "description": "AI QA engineer in your terminal — designs, writes, runs, heals, and explores tests for web UI and APIs with audit-grade traceability.",
6
6
  "license": "MIT",