@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.
- package/README.md +46 -16
- package/dist/index.js +99 -11
- 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
|
|
39
|
-
(
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
- An **
|
|
43
|
-
[console.anthropic.com](https://console.anthropic.com/settings/keys)
|
|
44
|
-
|
|
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
|
-
|
|
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
|
-
|
|
61
|
-
|
|
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
|
|
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 =
|
|
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
|
|
9996
|
-
|
|
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:
|
|
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.
|
|
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": ">=
|
|
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",
|