@qatonic_innovations/qaios 0.2.0 → 0.3.0

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 (3) hide show
  1. package/README.md +46 -16
  2. package/dist/index.js +99 -11
  3. package/package.json +2 -4
package/README.md CHANGED
@@ -35,18 +35,20 @@ The published package is `@qatonic_innovations/qaios`; the installed **command i
35
35
 
36
36
  ### Requirements
37
37
 
38
- - **Node.js 20 LTS recommended.** QAIOS bundles a native SQLite module
39
- (better-sqlite3) for its local audit log; Node 20 LTS has the widest
40
- prebuilt-binary coverage. Newer Node usually works but may need to compile
41
- the binary (build tools + network access).
42
- - An **Anthropic API key** — get one at
43
- [console.anthropic.com](https://console.anthropic.com/settings/keys), then
44
- put it in your environment:
38
+ - **Node.js 22 LTS or newer.** QAIOS uses Node's **built-in** SQLite
39
+ (`node:sqlite`, stable in Node 22) for its local audit log **no native
40
+ module to compile**, so `npm install` is just a download. (On Node < 22 the
41
+ built-in isn't available; upgrade Node.)
42
+ - An **LLM provider API key.** QAIOS uses **Anthropic** by default — get a key
43
+ at [console.anthropic.com](https://console.anthropic.com/settings/keys) and
44
+ export it:
45
45
  ```bash
46
46
  export ANTHROPIC_API_KEY=sk-ant-... # macOS/Linux
47
47
  setx ANTHROPIC_API_KEY "sk-ant-..." # Windows (open a NEW shell after)
48
48
  ```
49
- The key is read from the environment and **never written to disk** by QAIOS.
49
+ Prefer **OpenAI**? Set `llm.provider: openai` and export `OPENAI_API_KEY`
50
+ instead — see [Choose your LLM provider](#choose-your-llm-provider) below.
51
+ Keys are read from the environment and **never written to disk** by QAIOS.
50
52
  - **Playwright** in your project, for `qaios run` / `snapshot` / `explore` / `a11y`:
51
53
  ```bash
52
54
  npm i -D @playwright/test && npx playwright install
@@ -57,14 +59,9 @@ The published package is `@qatonic_innovations/qaios`; the installed **command i
57
59
 
58
60
  ### Install troubleshooting
59
61
 
60
- If `qaios init` fails with a SQLite/native-binding error
61
- (`Could not load the native SQLite module`):
62
-
63
- - Confirm your Node version: `node -v` (prefer 20 LTS).
64
- - Rebuild the binary: `npm rebuild better-sqlite3` — or reinstall qaios.
65
- - Behind a proxy/firewall? The prebuilt binary is fetched from GitHub
66
- release assets; allow that host, or install C/C++ build tools so it can
67
- compile locally.
62
+ QAIOS has **no native dependencies** it uses Node's built-in SQLite — so
63
+ install is friction-free. If `qaios init` reports a SQLite/`node:sqlite` error,
64
+ your Node is too old: run `node -v` and upgrade to **Node 22 LTS or newer**.
68
65
 
69
66
  `qaios doctor` will tell you exactly which check failed and what to run.
70
67
 
@@ -204,6 +201,39 @@ provider's native guaranteed-schema mode (Anthropic forced tool-use / OpenAI
204
201
  strict function calling), so generated artifacts stay schema-valid. You can
205
202
  override the key's env-var name with `llm.apiKeyEnv` if needed.
206
203
 
204
+ #### Picking a model
205
+
206
+ `llm.model` is a free-form string passed straight to the provider — **any model
207
+ that provider's API accepts works**, not just the defaults. When `llm.model` is
208
+ omitted, QAIOS uses a sensible default per provider:
209
+
210
+ | Provider | Default | Other examples you can set |
211
+ | ----------- | ------------------- | ------------------------------------------------- |
212
+ | `anthropic` | `claude-sonnet-4-6` | `claude-opus-4-7`, `claude-haiku-4-5-20251001`, … |
213
+ | `openai` | `gpt-4o` | `gpt-4o-mini` (cheaper), `gpt-4.1`, … |
214
+
215
+ ```yaml
216
+ llm:
217
+ provider: openai
218
+ model: gpt-4o-mini # any OpenAI model id
219
+ ```
220
+
221
+ Two things to know about non-default models:
222
+
223
+ - **Cost tracking.** QAIOS knows exact pricing for a built-in set of models
224
+ (Sonnet/Opus/Haiku and gpt-4o/4o-mini/4.1/4.1-mini). A model **outside** that
225
+ set still runs fine, but the USD figure in the audit log is approximate — it
226
+ bills at a default rate and prints a one-time `no pricing for model …`
227
+ warning. The per-workflow call/cost **cap still applies** regardless.
228
+ - **Structured output.** QAIOS relies on the provider's strict tool/function
229
+ calling. The defaults are chosen for strong schema adherence; a much older or
230
+ unusual model may fail validation more often (the built-in retry loop
231
+ recovers from occasional misses).
232
+
233
+ The defaults are the **live-validated** starting points — every skill is
234
+ exercised end-to-end against them. Other models share the same code path but
235
+ aren't individually certified.
236
+
207
237
  ### Operating modes
208
238
 
209
239
  - **LITE** (default) — HIGH/CRITICAL risk pauses for review; routine work flows through.
package/dist/index.js CHANGED
@@ -6,8 +6,8 @@ import { realpathSync, readFileSync, existsSync, rmSync, mkdirSync, writeFileSyn
6
6
  import path12 from 'path';
7
7
  import { fileURLToPath } from 'url';
8
8
  import { Command, InvalidArgumentError } from 'commander';
9
- import Database from 'better-sqlite3';
10
9
  import { createHash } from 'crypto';
10
+ import { createRequire } from 'module';
11
11
  import { z, ZodError } from 'zod';
12
12
  import { monotonicFactory } from 'ulid';
13
13
  import Anthropic from '@anthropic-ai/sdk';
@@ -1134,6 +1134,92 @@ function ensureDirExists(dir) {
1134
1134
  throw err;
1135
1135
  }
1136
1136
  }
1137
+ var nodeRequire = createRequire(import.meta.url);
1138
+ var { DatabaseSync } = nodeRequire("node:sqlite");
1139
+ var StatementAdapter = class {
1140
+ constructor(stmt) {
1141
+ this.stmt = stmt;
1142
+ }
1143
+ stmt;
1144
+ run(...params) {
1145
+ return this.stmt.run(...params);
1146
+ }
1147
+ get(...params) {
1148
+ return this.stmt.get(...params);
1149
+ }
1150
+ all(...params) {
1151
+ return this.stmt.all(...params);
1152
+ }
1153
+ };
1154
+ var SqliteDb = class {
1155
+ db;
1156
+ /** Current nesting depth of `transaction()` — drives BEGIN vs SAVEPOINT. */
1157
+ txDepth = 0;
1158
+ constructor(location) {
1159
+ this.db = new DatabaseSync(location);
1160
+ }
1161
+ prepare(sql) {
1162
+ return new StatementAdapter(this.db.prepare(sql));
1163
+ }
1164
+ exec(sql) {
1165
+ this.db.exec(sql);
1166
+ }
1167
+ /**
1168
+ * better-sqlite3-compatible `pragma()`. Two call shapes are used in the repo:
1169
+ * - a SET: `pragma('journal_mode = WAL')` / `pragma('foreign_keys = ON')`
1170
+ * - a READ: `pragma('journal_mode', { simple: true })` → the scalar value
1171
+ * node:sqlite has no pragma helper, so route through exec/prepare. A statement
1172
+ * containing `=` is a set (no useful result); otherwise it's a read.
1173
+ */
1174
+ pragma(source, opts = {}) {
1175
+ const isSet = source.includes("=");
1176
+ if (isSet) {
1177
+ this.db.exec(`PRAGMA ${source}`);
1178
+ return void 0;
1179
+ }
1180
+ const rows = this.db.prepare(`PRAGMA ${source}`).all();
1181
+ if (opts.simple === true) {
1182
+ const first = rows[0];
1183
+ if (first === void 0) return void 0;
1184
+ const keys = Object.keys(first);
1185
+ return keys.length > 0 ? first[keys[0]] : void 0;
1186
+ }
1187
+ return rows;
1188
+ }
1189
+ /**
1190
+ * better-sqlite3-compatible `transaction(fn)`: returns a callable that runs
1191
+ * `fn` atomically, rolling back on throw. node:sqlite has no transaction
1192
+ * helper, so wrap explicitly — and like better-sqlite3, be **savepoint-aware**
1193
+ * so NESTED `transaction()` calls don't error with "cannot start a transaction
1194
+ * within a transaction". The outermost call uses BEGIN/COMMIT/ROLLBACK; a
1195
+ * nested call uses a uniquely-named SAVEPOINT / RELEASE / ROLLBACK TO.
1196
+ */
1197
+ transaction(fn) {
1198
+ return (...args) => {
1199
+ const nested = this.txDepth > 0;
1200
+ const savepoint = `qaios_sp_${this.txDepth}`;
1201
+ this.db.exec(nested ? `SAVEPOINT ${savepoint}` : "BEGIN");
1202
+ this.txDepth += 1;
1203
+ try {
1204
+ const result = fn(...args);
1205
+ this.db.exec(nested ? `RELEASE ${savepoint}` : "COMMIT");
1206
+ this.txDepth -= 1;
1207
+ return result;
1208
+ } catch (err) {
1209
+ this.txDepth -= 1;
1210
+ try {
1211
+ this.db.exec(nested ? `ROLLBACK TO ${savepoint}` : "ROLLBACK");
1212
+ if (nested) this.db.exec(`RELEASE ${savepoint}`);
1213
+ } catch {
1214
+ }
1215
+ throw err;
1216
+ }
1217
+ };
1218
+ }
1219
+ close() {
1220
+ this.db.close();
1221
+ }
1222
+ };
1137
1223
  var __dirname$1 = path12.dirname(fileURLToPath(import.meta.url));
1138
1224
  var DEFAULT_MIGRATIONS_DIR = path12.resolve(__dirname$1, "migrations");
1139
1225
  var Storage = class _Storage {
@@ -1150,7 +1236,7 @@ var Storage = class _Storage {
1150
1236
  * (recommended for tests).
1151
1237
  */
1152
1238
  static open(dbPath, opts = {}) {
1153
- const db = new Database(dbPath);
1239
+ const db = new SqliteDb(dbPath);
1154
1240
  try {
1155
1241
  if (!opts.disableWal && dbPath !== ":memory:") {
1156
1242
  db.pragma("journal_mode = WAL");
@@ -2083,6 +2169,12 @@ var PRICING = {
2083
2169
  cacheReadPerMTok: 0.3,
2084
2170
  cacheWritePerMTok: 3.75
2085
2171
  },
2172
+ "claude-opus-4-8": {
2173
+ inputPerMTok: 15,
2174
+ outputPerMTok: 75,
2175
+ cacheReadPerMTok: 1.5,
2176
+ cacheWritePerMTok: 18.75
2177
+ },
2086
2178
  "claude-opus-4-7": {
2087
2179
  inputPerMTok: 15,
2088
2180
  outputPerMTok: 75,
@@ -6802,7 +6894,7 @@ function directoryIsEmpty(projectRoot) {
6802
6894
  }
6803
6895
 
6804
6896
  // src/commands/doctor.ts
6805
- var MIN_NODE_MAJOR = 20;
6897
+ var MIN_NODE_MAJOR = 22;
6806
6898
  function runDoctorEnv() {
6807
6899
  return { exitCode: ExitCode.SUCCESS, envSnapshot: snapshotEnv() };
6808
6900
  }
@@ -9992,18 +10084,14 @@ function runInit(opts = {}) {
9992
10084
  } catch {
9993
10085
  }
9994
10086
  const raw = err.message ?? String(err);
9995
- const isBindingError = /bindings file|was compiled against|NODE_MODULE_VERSION|\.node/i.test(
9996
- raw
9997
- );
9998
- const message = isBindingError ? `Could not load the native SQLite module (better-sqlite3). This usually means the prebuilt binary didn't download or doesn't match your Node version.
9999
- \u2022 Ensure you're on Node 20 LTS (run \`node -v\`).
10000
- \u2022 Reinstall to fetch/rebuild the binary: \`npm rebuild better-sqlite3\` (or reinstall qaios).
10001
- \u2022 Behind a proxy/firewall? The binary is fetched from GitHub releases \u2014 allow that, or install build tools so it can compile.
10087
+ const isNodeTooOld = /node:sqlite|Cannot find module 'node:sqlite'|DatabaseSync/i.test(raw);
10088
+ const message = isNodeTooOld ? `QAIOS needs Node's built-in SQLite (Node 22 LTS or newer).
10089
+ \u2022 Check your version: \`node -v\` \u2014 upgrade to Node 22+ if it's older.
10002
10090
  Original error: ${raw}` : `Failed to initialise workflows.db: ${raw}`;
10003
10091
  return {
10004
10092
  exitCode: err instanceof StorageError ? ExitCode.INTERNAL : ExitCode.TOOL_ERROR,
10005
10093
  error: {
10006
- code: isBindingError ? "qaios.init.sqlite_binding_missing" : err instanceof StorageError ? err.code : "qaios.init.db_failed",
10094
+ code: isNodeTooOld ? "qaios.init.node_too_old" : err instanceof StorageError ? err.code : "qaios.init.db_failed",
10007
10095
  message
10008
10096
  },
10009
10097
  detection
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qatonic_innovations/qaios",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
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",
@@ -29,7 +29,7 @@
29
29
  "self-healing"
30
30
  ],
31
31
  "engines": {
32
- "node": ">=20.0.0"
32
+ "node": ">=22.0.0"
33
33
  },
34
34
  "publishConfig": {
35
35
  "access": "public"
@@ -46,7 +46,6 @@
46
46
  "dependencies": {
47
47
  "@anthropic-ai/sdk": "^0.40.0",
48
48
  "@modelcontextprotocol/sdk": "^1.29.0",
49
- "better-sqlite3": "^11.7.0",
50
49
  "commander": "^12.1.0",
51
50
  "openai": "^4.77.0",
52
51
  "ink": "^5.2.1",
@@ -62,7 +61,6 @@
62
61
  "zod-to-json-schema": "^3.24.0"
63
62
  },
64
63
  "devDependencies": {
65
- "@types/better-sqlite3": "^7.6.12",
66
64
  "@types/pixelmatch": "^5.2.6",
67
65
  "@types/pngjs": "^6.0.5",
68
66
  "@types/react": "^18.3.28",