cairn-engine 0.1.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 (62) hide show
  1. package/README.md +36 -0
  2. package/dist/adapters/context/inline.d.ts +8 -0
  3. package/dist/adapters/context/inline.js +10 -0
  4. package/dist/adapters/context/inline.js.map +1 -0
  5. package/dist/adapters/critics/assertion.d.ts +8 -0
  6. package/dist/adapters/critics/assertion.js +43 -0
  7. package/dist/adapters/critics/assertion.js.map +1 -0
  8. package/dist/adapters/critics/llm.d.ts +10 -0
  9. package/dist/adapters/critics/llm.js +68 -0
  10. package/dist/adapters/critics/llm.js.map +1 -0
  11. package/dist/adapters/drivers/chrome.d.ts +37 -0
  12. package/dist/adapters/drivers/chrome.js +195 -0
  13. package/dist/adapters/drivers/chrome.js.map +1 -0
  14. package/dist/adapters/drivers/fake.d.ts +24 -0
  15. package/dist/adapters/drivers/fake.js +37 -0
  16. package/dist/adapters/drivers/fake.js.map +1 -0
  17. package/dist/adapters/drivers/self-heal.d.ts +34 -0
  18. package/dist/adapters/drivers/self-heal.js +89 -0
  19. package/dist/adapters/drivers/self-heal.js.map +1 -0
  20. package/dist/adapters/llm/anthropic.d.ts +18 -0
  21. package/dist/adapters/llm/anthropic.js +41 -0
  22. package/dist/adapters/llm/anthropic.js.map +1 -0
  23. package/dist/adapters/llm/claude-code.d.ts +14 -0
  24. package/dist/adapters/llm/claude-code.js +48 -0
  25. package/dist/adapters/llm/claude-code.js.map +1 -0
  26. package/dist/adapters/llm/factory.d.ts +8 -0
  27. package/dist/adapters/llm/factory.js +9 -0
  28. package/dist/adapters/llm/factory.js.map +1 -0
  29. package/dist/adapters/planners/static.d.ts +8 -0
  30. package/dist/adapters/planners/static.js +10 -0
  31. package/dist/adapters/planners/static.js.map +1 -0
  32. package/dist/adapters/reporters/console.d.ts +6 -0
  33. package/dist/adapters/reporters/console.js +17 -0
  34. package/dist/adapters/reporters/console.js.map +1 -0
  35. package/dist/adapters/reporters/json.d.ts +7 -0
  36. package/dist/adapters/reporters/json.js +12 -0
  37. package/dist/adapters/reporters/json.js.map +1 -0
  38. package/dist/adapters/skills/file-store.d.ts +11 -0
  39. package/dist/adapters/skills/file-store.js +43 -0
  40. package/dist/adapters/skills/file-store.js.map +1 -0
  41. package/dist/cli.d.ts +2 -0
  42. package/dist/cli.js +158 -0
  43. package/dist/cli.js.map +1 -0
  44. package/dist/core/discover.d.ts +25 -0
  45. package/dist/core/discover.js +93 -0
  46. package/dist/core/discover.js.map +1 -0
  47. package/dist/core/pipeline.d.ts +8 -0
  48. package/dist/core/pipeline.js +49 -0
  49. package/dist/core/pipeline.js.map +1 -0
  50. package/dist/core/ports.d.ts +56 -0
  51. package/dist/core/ports.js +2 -0
  52. package/dist/core/ports.js.map +1 -0
  53. package/dist/core/types.d.ts +102 -0
  54. package/dist/core/types.js +3 -0
  55. package/dist/core/types.js.map +1 -0
  56. package/dist/index.d.ts +22 -0
  57. package/dist/index.js +20 -0
  58. package/dist/index.js.map +1 -0
  59. package/dist/run.d.ts +25 -0
  60. package/dist/run.js +53 -0
  61. package/dist/run.js.map +1 -0
  62. package/package.json +57 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"self-heal.js","sourceRoot":"","sources":["../../../src/adapters/drivers/self-heal.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * LlmClient backed by the Anthropic Messages API (ANTHROPIC_API_KEY) — the standalone
3
+ * default once a user supplies a key. Uses fetch to avoid an SDK dependency.
4
+ */
5
+ import type { CompleteOptions, LlmClient } from "../../core/ports.js";
6
+ export interface AnthropicOptions {
7
+ apiKey?: string;
8
+ model?: string;
9
+ baseUrl?: string;
10
+ }
11
+ export declare class AnthropicLlmClient implements LlmClient {
12
+ readonly id: string;
13
+ private readonly apiKey;
14
+ private readonly model;
15
+ private readonly baseUrl;
16
+ constructor(opts?: AnthropicOptions);
17
+ complete(prompt: string, opts?: CompleteOptions): Promise<string>;
18
+ }
@@ -0,0 +1,41 @@
1
+ export class AnthropicLlmClient {
2
+ id;
3
+ apiKey;
4
+ model;
5
+ baseUrl;
6
+ constructor(opts = {}) {
7
+ const apiKey = opts.apiKey ?? process.env.ANTHROPIC_API_KEY;
8
+ if (!apiKey)
9
+ throw new Error("AnthropicLlmClient requires ANTHROPIC_API_KEY");
10
+ this.apiKey = apiKey;
11
+ this.model = opts.model ?? "claude-sonnet-4-6";
12
+ this.baseUrl = opts.baseUrl ?? "https://api.anthropic.com";
13
+ this.id = `anthropic:${this.model}`;
14
+ }
15
+ async complete(prompt, opts = {}) {
16
+ const res = await fetch(`${this.baseUrl}/v1/messages`, {
17
+ method: "POST",
18
+ headers: {
19
+ "content-type": "application/json",
20
+ "x-api-key": this.apiKey,
21
+ "anthropic-version": "2023-06-01",
22
+ },
23
+ body: JSON.stringify({
24
+ model: this.model,
25
+ max_tokens: opts.maxTokens ?? 1024,
26
+ ...(opts.system ? { system: opts.system } : {}),
27
+ messages: [{ role: "user", content: prompt }],
28
+ }),
29
+ });
30
+ if (!res.ok) {
31
+ throw new Error(`Anthropic API ${res.status}: ${await res.text()}`);
32
+ }
33
+ const data = (await res.json());
34
+ return (data.content ?? [])
35
+ .filter((c) => c.type === "text" && typeof c.text === "string")
36
+ .map((c) => c.text)
37
+ .join("")
38
+ .trim();
39
+ }
40
+ }
41
+ //# sourceMappingURL=anthropic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anthropic.js","sourceRoot":"","sources":["../../../src/adapters/llm/anthropic.ts"],"names":[],"mappings":"AAgBA,MAAM,OAAO,kBAAkB;IACpB,EAAE,CAAS;IACH,MAAM,CAAS;IACf,KAAK,CAAS;IACd,OAAO,CAAS;IAEjC,YAAY,OAAyB,EAAE;QACrC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;QAC5D,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;QAC9E,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,mBAAmB,CAAC;QAC/C,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,2BAA2B,CAAC;QAC3D,IAAI,CAAC,EAAE,GAAG,aAAa,IAAI,CAAC,KAAK,EAAE,CAAC;IACtC,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,MAAc,EAAE,OAAwB,EAAE;QACvD,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,OAAO,cAAc,EAAE;YACrD,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;gBAClC,WAAW,EAAE,IAAI,CAAC,MAAM;gBACxB,mBAAmB,EAAE,YAAY;aAClC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,UAAU,EAAE,IAAI,CAAC,SAAS,IAAI,IAAI;gBAClC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC/C,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;aAC9C,CAAC;SACH,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CAAC,iBAAiB,GAAG,CAAC,MAAM,KAAK,MAAM,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACtE,CAAC;QACD,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAqB,CAAC;QACpD,OAAO,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC;aACxB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC;aAC9D,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;aAClB,IAAI,CAAC,EAAE,CAAC;aACR,IAAI,EAAE,CAAC;IACZ,CAAC;CACF"}
@@ -0,0 +1,14 @@
1
+ import type { CompleteOptions, LlmClient } from "../../core/ports.js";
2
+ export interface ClaudeCodeOptions {
3
+ model?: string;
4
+ bin?: string;
5
+ timeoutMs?: number;
6
+ }
7
+ export declare class ClaudeCodeLlmClient implements LlmClient {
8
+ readonly id: string;
9
+ private readonly model;
10
+ private readonly bin;
11
+ private readonly timeoutMs;
12
+ constructor(opts?: ClaudeCodeOptions);
13
+ complete(prompt: string, opts?: CompleteOptions): Promise<string>;
14
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * LlmClient backed by the local Claude Code CLI (`claude -p`) — the default when no API key
3
+ * is set: it reuses an installed Claude Code's auth, no extra credentials. Swappable for
4
+ * AnthropicLlmClient via `createLlmClient` (invariant #5).
5
+ */
6
+ import { spawn } from "node:child_process";
7
+ export class ClaudeCodeLlmClient {
8
+ id;
9
+ model;
10
+ bin;
11
+ timeoutMs;
12
+ constructor(opts = {}) {
13
+ this.model = opts.model ?? "sonnet";
14
+ this.bin = opts.bin ?? "claude";
15
+ this.timeoutMs = opts.timeoutMs ?? 120_000;
16
+ this.id = `claude-code:${this.model}`;
17
+ }
18
+ async complete(prompt, opts = {}) {
19
+ const args = ["-p", "--model", this.model];
20
+ if (opts.system)
21
+ args.push("--append-system-prompt", opts.system);
22
+ return new Promise((resolve, reject) => {
23
+ const child = spawn(this.bin, args, { stdio: ["pipe", "pipe", "pipe"] });
24
+ let stdout = "";
25
+ let stderr = "";
26
+ const timer = setTimeout(() => {
27
+ child.kill("SIGKILL");
28
+ reject(new Error(`claude -p timed out after ${this.timeoutMs}ms`));
29
+ }, this.timeoutMs);
30
+ child.stdout.on("data", (d) => (stdout += d));
31
+ child.stderr.on("data", (d) => (stderr += d));
32
+ child.on("error", (err) => {
33
+ clearTimeout(timer);
34
+ reject(err);
35
+ });
36
+ child.on("close", (code) => {
37
+ clearTimeout(timer);
38
+ if (code === 0)
39
+ resolve(stdout.trim());
40
+ else
41
+ reject(new Error(`claude -p exited ${code}: ${stderr.trim()}`));
42
+ });
43
+ child.stdin.write(prompt);
44
+ child.stdin.end();
45
+ });
46
+ }
47
+ }
48
+ //# sourceMappingURL=claude-code.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-code.js","sourceRoot":"","sources":["../../../src/adapters/llm/claude-code.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAS3C,MAAM,OAAO,mBAAmB;IACrB,EAAE,CAAS;IACH,KAAK,CAAS;IACd,GAAG,CAAS;IACZ,SAAS,CAAS;IAEnC,YAAY,OAA0B,EAAE;QACtC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,QAAQ,CAAC;QACpC,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,QAAQ,CAAC;QAChC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,OAAO,CAAC;QAC3C,IAAI,CAAC,EAAE,GAAG,eAAe,IAAI,CAAC,KAAK,EAAE,CAAC;IACxC,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,MAAc,EAAE,OAAwB,EAAE;QACvD,MAAM,IAAI,GAAG,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QAC3C,IAAI,IAAI,CAAC,MAAM;YAAE,IAAI,CAAC,IAAI,CAAC,wBAAwB,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QAElE,OAAO,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC7C,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;YACzE,IAAI,MAAM,GAAG,EAAE,CAAC;YAChB,IAAI,MAAM,GAAG,EAAE,CAAC;YAChB,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;gBAC5B,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;gBACtB,MAAM,CAAC,IAAI,KAAK,CAAC,6BAA6B,IAAI,CAAC,SAAS,IAAI,CAAC,CAAC,CAAC;YACrE,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;YAEnB,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC;YAC9C,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC;YAC9C,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;gBACxB,YAAY,CAAC,KAAK,CAAC,CAAC;gBACpB,MAAM,CAAC,GAAG,CAAC,CAAC;YACd,CAAC,CAAC,CAAC;YACH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE;gBACzB,YAAY,CAAC,KAAK,CAAC,CAAC;gBACpB,IAAI,IAAI,KAAK,CAAC;oBAAE,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;;oBAClC,MAAM,CAAC,IAAI,KAAK,CAAC,oBAAoB,IAAI,KAAK,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC;YACvE,CAAC,CAAC,CAAC;YAEH,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YAC1B,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;QACpB,CAAC,CAAC,CAAC;IACL,CAAC;CACF"}
@@ -0,0 +1,8 @@
1
+ /** Picks the LLM backend (invariant #5): the API if ANTHROPIC_API_KEY is set, else local Claude Code. */
2
+ import type { LlmClient } from "../../core/ports.js";
3
+ export interface LlmFactoryOptions {
4
+ /** Force a backend regardless of environment. */
5
+ backend?: "anthropic" | "claude-code";
6
+ model?: string;
7
+ }
8
+ export declare function createLlmClient(opts?: LlmFactoryOptions): LlmClient;
@@ -0,0 +1,9 @@
1
+ import { AnthropicLlmClient } from "./anthropic.js";
2
+ import { ClaudeCodeLlmClient } from "./claude-code.js";
3
+ export function createLlmClient(opts = {}) {
4
+ const backend = opts.backend ?? (process.env.ANTHROPIC_API_KEY ? "anthropic" : "claude-code");
5
+ return backend === "anthropic"
6
+ ? new AnthropicLlmClient({ model: opts.model })
7
+ : new ClaudeCodeLlmClient({ model: opts.model });
8
+ }
9
+ //# sourceMappingURL=factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"factory.js","sourceRoot":"","sources":["../../../src/adapters/llm/factory.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAQvD,MAAM,UAAU,eAAe,CAAC,OAA0B,EAAE;IAC1D,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC;IAC9F,OAAO,OAAO,KAAK,WAAW;QAC5B,CAAC,CAAC,IAAI,kBAAkB,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC;QAC/C,CAAC,CAAC,IAAI,mBAAmB,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;AACrD,CAAC"}
@@ -0,0 +1,8 @@
1
+ /** Deterministic Planner — returns a fixed scenario. The replay path's planner, no LLM (invariant #4). */
2
+ import type { Planner } from "../../core/ports.js";
3
+ import type { Context, Scenario } from "../../core/types.js";
4
+ export declare class StaticPlanner implements Planner {
5
+ private readonly scenario;
6
+ constructor(scenario: Scenario);
7
+ plan(_ctx: Context): Promise<Scenario>;
8
+ }
@@ -0,0 +1,10 @@
1
+ export class StaticPlanner {
2
+ scenario;
3
+ constructor(scenario) {
4
+ this.scenario = scenario;
5
+ }
6
+ async plan(_ctx) {
7
+ return this.scenario;
8
+ }
9
+ }
10
+ //# sourceMappingURL=static.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"static.js","sourceRoot":"","sources":["../../../src/adapters/planners/static.ts"],"names":[],"mappings":"AAIA,MAAM,OAAO,aAAa;IACK;IAA7B,YAA6B,QAAkB;QAAlB,aAAQ,GAAR,QAAQ,CAAU;IAAG,CAAC;IAEnD,KAAK,CAAC,IAAI,CAAC,IAAa;QACtB,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;CACF"}
@@ -0,0 +1,6 @@
1
+ /** Human-readable Reporter for CLI/CI. Mirrors the report sketch in docs/design.md §7. */
2
+ import type { Reporter } from "../../core/ports.js";
3
+ import type { Result } from "../../core/types.js";
4
+ export declare class ConsoleReporter implements Reporter {
5
+ emit(result: Result): Promise<void>;
6
+ }
@@ -0,0 +1,17 @@
1
+ export class ConsoleReporter {
2
+ async emit(result) {
3
+ const { scenario, evidence, verdict } = result;
4
+ const mark = (ok) => (ok ? "✓" : "✗");
5
+ console.log(`\n${scenario}`);
6
+ console.log(` ${mark(evidence.execution.navigated)} navigated → ${evidence.execution.finalUrl ?? "(none)"}`);
7
+ console.log(` · ${evidence.execution.actions.length} actions · ${evidence.logic.requests.length} requests · ${evidence.logic.console.length} console msgs`);
8
+ for (const r of verdict.results) {
9
+ console.log(` ${mark(r.passed)} ${r.assertion.kind}${r.detail ? ` — ${r.detail}` : ""}`);
10
+ }
11
+ const failed = verdict.results.filter((r) => !r.passed).length;
12
+ console.log(verdict.passed
13
+ ? `\n${mark(true)} pass — ${verdict.results.length} assertion(s)`
14
+ : `\n${mark(false)} ${failed} issue(s) — evidence captured`);
15
+ }
16
+ }
17
+ //# sourceMappingURL=console.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"console.js","sourceRoot":"","sources":["../../../src/adapters/reporters/console.ts"],"names":[],"mappings":"AAIA,MAAM,OAAO,eAAe;IAC1B,KAAK,CAAC,IAAI,CAAC,MAAc;QACvB,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC;QAC/C,MAAM,IAAI,GAAG,CAAC,EAAW,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QAE/C,OAAO,CAAC,GAAG,CAAC,KAAK,QAAQ,EAAE,CAAC,CAAC;QAC7B,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,SAAS,CAAC,gBAAgB,QAAQ,CAAC,SAAS,CAAC,QAAQ,IAAI,QAAQ,EAAE,CAAC,CAAC;QAC9G,OAAO,CAAC,GAAG,CAAC,OAAO,QAAQ,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,cAAc,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,eAAe,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,eAAe,CAAC,CAAC;QAE7J,KAAK,MAAM,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;YAChC,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC5F,CAAC;QAED,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC;QAC/D,OAAO,CAAC,GAAG,CACT,OAAO,CAAC,MAAM;YACZ,CAAC,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,WAAW,OAAO,CAAC,OAAO,CAAC,MAAM,eAAe;YACjE,CAAC,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,IAAI,MAAM,+BAA+B,CAC9D,CAAC;IACJ,CAAC;CACF"}
@@ -0,0 +1,7 @@
1
+ import type { Reporter } from "../../core/ports.js";
2
+ import type { Result } from "../../core/types.js";
3
+ export declare class JsonReporter implements Reporter {
4
+ private readonly path;
5
+ constructor(path: string);
6
+ emit(result: Result): Promise<void>;
7
+ }
@@ -0,0 +1,12 @@
1
+ /** Structured Reporter — writes the full result as JSON for CI consumption. */
2
+ import { writeFile } from "node:fs/promises";
3
+ export class JsonReporter {
4
+ path;
5
+ constructor(path) {
6
+ this.path = path;
7
+ }
8
+ async emit(result) {
9
+ await writeFile(this.path, JSON.stringify(result, null, 2), "utf8");
10
+ }
11
+ }
12
+ //# sourceMappingURL=json.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json.js","sourceRoot":"","sources":["../../../src/adapters/reporters/json.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAI7C,MAAM,OAAO,YAAY;IACM;IAA7B,YAA6B,IAAY;QAAZ,SAAI,GAAJ,IAAI,CAAQ;IAAG,CAAC;IAE7C,KAAK,CAAC,IAAI,CAAC,MAAc;QACvB,MAAM,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;IACtE,CAAC;CACF"}
@@ -0,0 +1,11 @@
1
+ import type { SkillStore, Skill } from "../../core/ports.js";
2
+ import type { Scenario } from "../../core/types.js";
3
+ export declare class FileSkillStore implements SkillStore {
4
+ private readonly dir;
5
+ constructor(dir: string);
6
+ private pathFor;
7
+ resolve(name: string): Promise<Skill | undefined>;
8
+ freeze(name: string, scenario: Scenario): Promise<string>;
9
+ }
10
+ /** Load a frozen skill by path, accepting either a full `{name,scenario}` or a bare scenario. */
11
+ export declare function loadSkillFile(path: string): Promise<Skill>;
@@ -0,0 +1,43 @@
1
+ /**
2
+ * File-backed SkillStore. A frozen skill is plain JSON `{ name, scenario }`; freezing turns
3
+ * an expensive LLM discovery into a cheap, repeatable, LLM-free replay (invariant #4).
4
+ */
5
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
6
+ import { dirname, join } from "node:path";
7
+ export class FileSkillStore {
8
+ dir;
9
+ constructor(dir) {
10
+ this.dir = dir;
11
+ }
12
+ pathFor(name) {
13
+ return join(this.dir, `${name}.json`);
14
+ }
15
+ async resolve(name) {
16
+ try {
17
+ const raw = await readFile(this.pathFor(name), "utf8");
18
+ return JSON.parse(raw);
19
+ }
20
+ catch (err) {
21
+ if (err.code === "ENOENT")
22
+ return undefined;
23
+ throw err;
24
+ }
25
+ }
26
+ async freeze(name, scenario) {
27
+ const path = this.pathFor(name);
28
+ await mkdir(dirname(path), { recursive: true });
29
+ const skill = { name, scenario };
30
+ await writeFile(path, JSON.stringify(skill, null, 2), "utf8");
31
+ return path;
32
+ }
33
+ }
34
+ /** Load a frozen skill by path, accepting either a full `{name,scenario}` or a bare scenario. */
35
+ export async function loadSkillFile(path) {
36
+ const raw = await readFile(path, "utf8");
37
+ const parsed = JSON.parse(raw);
38
+ if ("scenario" in parsed)
39
+ return parsed;
40
+ const scenario = parsed;
41
+ return { name: scenario.name, scenario };
42
+ }
43
+ //# sourceMappingURL=file-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-store.js","sourceRoot":"","sources":["../../../src/adapters/skills/file-store.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAI1C,MAAM,OAAO,cAAc;IACI;IAA7B,YAA6B,GAAW;QAAX,QAAG,GAAH,GAAG,CAAQ;IAAG,CAAC;IAEpC,OAAO,CAAC,IAAY;QAC1B,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,OAAO,CAAC,CAAC;IACxC,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,IAAY;QACxB,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC;YACvD,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAU,CAAC;QAClC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ;gBAAE,OAAO,SAAS,CAAC;YACvE,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,IAAY,EAAE,QAAkB;QAC3C,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAChC,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAChD,MAAM,KAAK,GAAU,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;QACxC,MAAM,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;QAC9D,OAAO,IAAI,CAAC;IACd,CAAC;CACF;AAED,iGAAiG;AACjG,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAAY;IAC9C,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACzC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAqB,CAAC;IACnD,IAAI,UAAU,IAAI,MAAM;QAAE,OAAO,MAAe,CAAC;IACjD,MAAM,QAAQ,GAAG,MAAkB,CAAC;IACpC,OAAO,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,EAAE,CAAC;AAC3C,CAAC"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,158 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * cairn CLI — a thin consumer of the cairn-engine library.
4
+ *
5
+ * cairn run --dogfood built-in example.com → first link → network
6
+ * cairn run --scenario s.json [--json out] run a scenario file (deterministic)
7
+ * cairn replay <skill.json> [--json out] replay a frozen skill (deterministic, no LLM)
8
+ * cairn replay <skill.json> --heal [--freeze f] repair broken steps via LLM, re-freeze
9
+ * cairn discover "<intent>" --url <u> LLM discover a scenario [--freeze f] [--model m]
10
+ *
11
+ * All orchestration lives in the library (`runScenario` / `discover`). This file only
12
+ * parses args, composes reporters, and maps the verdict to an exit code (1 = fail → CI
13
+ * gate). A desktop app or CI job imports the same library functions instead of this CLI.
14
+ */
15
+ import { readFile, writeFile } from "node:fs/promises";
16
+ import { runScenario, needsLlmCritic } from "./run.js";
17
+ import { discover } from "./core/discover.js";
18
+ import { ConsoleReporter } from "./adapters/reporters/console.js";
19
+ import { JsonReporter } from "./adapters/reporters/json.js";
20
+ import { ChromeDevToolsDriver } from "./adapters/drivers/chrome.js";
21
+ import { loadSkillFile } from "./adapters/skills/file-store.js";
22
+ import { createLlmClient } from "./adapters/llm/factory.js";
23
+ /** Reproduces the manual MCP verification: example.com → "Learn more" → observe network. */
24
+ const DOGFOOD = {
25
+ name: "example.com → first link → network",
26
+ steps: [
27
+ { kind: "goto", url: "https://example.com" },
28
+ { kind: "click", target: { text: "Learn more" } },
29
+ ],
30
+ assertions: [{ kind: "navigated" }, { kind: "no-failed-requests" }],
31
+ };
32
+ function parseArgs(argv) {
33
+ const positionals = [];
34
+ const flags = new Map();
35
+ for (let i = 0; i < argv.length; i++) {
36
+ const a = argv[i];
37
+ if (a.startsWith("--")) {
38
+ const key = a.slice(2);
39
+ const next = argv[i + 1];
40
+ if (next !== undefined && !next.startsWith("--")) {
41
+ flags.set(key, next);
42
+ i++;
43
+ }
44
+ else {
45
+ flags.set(key, true);
46
+ }
47
+ }
48
+ else {
49
+ positionals.push(a);
50
+ }
51
+ }
52
+ return { positionals, flags };
53
+ }
54
+ const flagStr = (flags, key) => {
55
+ const v = flags.get(key);
56
+ return typeof v === "string" ? v : undefined;
57
+ };
58
+ function reporterFor(flags) {
59
+ const reporters = [new ConsoleReporter()];
60
+ const jsonOut = flagStr(flags, "json");
61
+ if (jsonOut)
62
+ reporters.push(new JsonReporter(jsonOut));
63
+ return { emit: async (r) => void (await Promise.all(reporters.map((rep) => rep.emit(r)))) };
64
+ }
65
+ /** Run a scenario through the library and surface CLI-specific output (heal log, freeze). */
66
+ async function runScenarioCli(scenario, flags) {
67
+ if (needsLlmCritic(scenario))
68
+ console.log("scenario has 'expect' criteria → judging with LlmCritic");
69
+ const { result, heals, healedScenario } = await runScenario(scenario, {
70
+ reporter: reporterFor(flags),
71
+ model: flagStr(flags, "model"),
72
+ heal: Boolean(flags.get("heal")),
73
+ });
74
+ if (heals.length) {
75
+ console.log(`\nself-healed ${heals.length} step(s):`);
76
+ for (const h of heals)
77
+ console.log(` · "${h.original.text}" → "${h.healedText}"`);
78
+ const freeze = flagStr(flags, "freeze");
79
+ if (freeze && healedScenario) {
80
+ await writeFile(freeze, JSON.stringify({ name: healedScenario.name, scenario: healedScenario }, null, 2), "utf8");
81
+ console.log(` re-frozen → ${freeze}`);
82
+ }
83
+ }
84
+ return result.verdict.passed ? 0 : 1;
85
+ }
86
+ async function cmdRun(flags) {
87
+ let scenario;
88
+ if (flags.get("dogfood")) {
89
+ scenario = DOGFOOD;
90
+ }
91
+ else {
92
+ const path = flagStr(flags, "scenario");
93
+ if (!path)
94
+ throw new Error("provide --scenario <file.json> or --dogfood");
95
+ scenario = JSON.parse(await readFile(path, "utf8"));
96
+ }
97
+ return runScenarioCli(scenario, flags);
98
+ }
99
+ async function cmdReplay(positionals, flags) {
100
+ const file = positionals[0];
101
+ if (!file)
102
+ throw new Error("usage: cairn replay <skill.json> [--heal] [--json out]");
103
+ const skill = await loadSkillFile(file);
104
+ const mode = flags.get("heal") ? "self-heal on" : "deterministic, no LLM";
105
+ console.log(`replaying frozen skill "${skill.name}" — ${mode}`);
106
+ return runScenarioCli(skill.scenario, flags);
107
+ }
108
+ async function cmdDiscover(positionals, flags) {
109
+ const intent = positionals[0];
110
+ if (!intent)
111
+ throw new Error('usage: cairn discover "<intent>" --url <u> [--freeze f] [--model m]');
112
+ const url = flagStr(flags, "url");
113
+ const model = flagStr(flags, "model");
114
+ const driver = new ChromeDevToolsDriver();
115
+ const llm = createLlmClient(model ? { model } : {});
116
+ console.log(`discovering with ${llm.id} …`);
117
+ let scenario;
118
+ try {
119
+ scenario = await discover(intent, { driver, llm, baseUrl: url });
120
+ }
121
+ finally {
122
+ await driver.close();
123
+ }
124
+ console.log(`\ndiscovered scenario "${scenario.name}" — ${scenario.steps.length} steps:`);
125
+ for (const step of scenario.steps)
126
+ console.log(` · ${JSON.stringify(step)}`);
127
+ const freeze = flagStr(flags, "freeze");
128
+ if (freeze) {
129
+ await writeFile(freeze, JSON.stringify({ name: scenario.name, scenario }, null, 2), "utf8");
130
+ console.log(`\nfrozen → ${freeze} (replay with: cairn replay ${freeze})`);
131
+ }
132
+ return 0;
133
+ }
134
+ async function main() {
135
+ const [cmd, ...rest] = process.argv.slice(2);
136
+ const { positionals, flags } = parseArgs(rest);
137
+ let code;
138
+ switch (cmd) {
139
+ case "run":
140
+ code = await cmdRun(flags);
141
+ break;
142
+ case "replay":
143
+ code = await cmdReplay(positionals, flags);
144
+ break;
145
+ case "discover":
146
+ code = await cmdDiscover(positionals, flags);
147
+ break;
148
+ default:
149
+ console.error("usage: cairn <run|replay|discover> …");
150
+ code = 2;
151
+ }
152
+ process.exit(code);
153
+ }
154
+ main().catch((err) => {
155
+ console.error(err instanceof Error ? err.stack ?? err.message : err);
156
+ process.exit(1);
157
+ });
158
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * The discover loop — the only place the agent loops (invariant #3). It observes the page,
3
+ * asks the LLM for the next action, acts, and repeats until done, emitting a Scenario that
4
+ * later replays with no LLM (invariant #4). LLM is behind the LlmClient seam (invariant #5).
5
+ */
6
+ import type { Driver, LlmClient } from "./ports.js";
7
+ import type { Assertion, Scenario, Step } from "./types.js";
8
+ export interface DiscoverOptions {
9
+ driver: Driver;
10
+ llm: LlmClient;
11
+ baseUrl?: string;
12
+ maxSteps?: number;
13
+ onStep?: (decision: Decision, step?: Step) => void;
14
+ }
15
+ export interface Decision {
16
+ action: "click" | "type" | "goto" | "done";
17
+ text?: string;
18
+ value?: string;
19
+ url?: string;
20
+ reason?: string;
21
+ assertions?: Assertion[];
22
+ }
23
+ /** First JSON object in a model reply, tolerating code fences and surrounding prose. */
24
+ export declare function parseDecision(text: string): Decision;
25
+ export declare function discover(intent: string, opts: DiscoverOptions): Promise<Scenario>;
@@ -0,0 +1,93 @@
1
+ const SYSTEM = "You are a QA agent driving a web browser to satisfy a natural-language intent. " +
2
+ "At each turn you see the page's interactive elements and the actions taken so far. " +
3
+ "Respond with ONE next action as strict JSON, no prose, no code fences. " +
4
+ 'Schema: {"action":"click|type|goto|done","text":"<element name>","value":"<text to type>","url":"<url>","reason":"<short>"}. ' +
5
+ 'Use "click"/"type" with the exact element name shown. Use "done" when the intent is achieved; ' +
6
+ 'with "done" you may include "assertions": an array of {"kind":"navigated"} | {"kind":"no-failed-requests"} | ' +
7
+ '{"kind":"no-console-errors"} | {"kind":"request-status","urlIncludes":"...","status":200}.';
8
+ function buildPrompt(intent, elements, steps) {
9
+ const els = elements
10
+ .slice(0, 60)
11
+ .map((e) => `- [${e.role}] ${e.name}`)
12
+ .join("\n");
13
+ const history = steps.length
14
+ ? steps.map((s, i) => `${i + 1}. ${JSON.stringify(s)}`).join("\n")
15
+ : "(none yet)";
16
+ return [
17
+ `Intent: ${intent}`,
18
+ ``,
19
+ `Actions taken so far:`,
20
+ history,
21
+ ``,
22
+ `Interactive elements now on the page:`,
23
+ els || "(none)",
24
+ ``,
25
+ `What is the single next action? Respond with JSON only.`,
26
+ ].join("\n");
27
+ }
28
+ /** First JSON object in a model reply, tolerating code fences and surrounding prose. */
29
+ export function parseDecision(text) {
30
+ let s = text.trim();
31
+ s = s.replace(/^```(?:json)?\s*/i, "").replace(/\s*```$/i, "");
32
+ const start = s.indexOf("{");
33
+ const end = s.lastIndexOf("}");
34
+ if (start === -1 || end === -1 || end < start) {
35
+ throw new Error(`no JSON object in model reply: ${text.slice(0, 200)}`);
36
+ }
37
+ const obj = JSON.parse(s.slice(start, end + 1));
38
+ if (!obj.action)
39
+ throw new Error(`decision missing "action": ${s.slice(0, 200)}`);
40
+ return obj;
41
+ }
42
+ const KNOWN_KINDS = new Set(["navigated", "no-failed-requests", "no-console-errors", "request-status"]);
43
+ /** Drop assertions the deterministic critic can't evaluate; fall back to a safe default. */
44
+ function sanitizeAssertions(input) {
45
+ const valid = (input ?? []).filter((a) => a && KNOWN_KINDS.has(a.kind));
46
+ return valid.length ? valid : [{ kind: "no-failed-requests" }];
47
+ }
48
+ export async function discover(intent, opts) {
49
+ const { driver, llm, baseUrl, maxSteps = 8, onStep } = opts;
50
+ const steps = [];
51
+ if (baseUrl) {
52
+ await driver.goto(baseUrl);
53
+ steps.push({ kind: "goto", url: baseUrl });
54
+ }
55
+ for (let i = 0; i < maxSteps; i++) {
56
+ await driver.settle();
57
+ const elements = await driver.snapshot();
58
+ const reply = await llm.complete(buildPrompt(intent, elements, steps), { system: SYSTEM });
59
+ const decision = parseDecision(reply);
60
+ if (decision.action === "done") {
61
+ onStep?.(decision);
62
+ return { name: intent, steps, assertions: sanitizeAssertions(decision.assertions) };
63
+ }
64
+ let step;
65
+ switch (decision.action) {
66
+ case "click":
67
+ if (!decision.text)
68
+ throw new Error('click decision missing "text"');
69
+ await driver.click({ text: decision.text });
70
+ step = { kind: "click", target: { text: decision.text } };
71
+ break;
72
+ case "type":
73
+ if (!decision.text)
74
+ throw new Error('type decision missing "text"');
75
+ await driver.type({ text: decision.text }, decision.value ?? "");
76
+ step = { kind: "type", target: { text: decision.text }, text: decision.value ?? "" };
77
+ break;
78
+ case "goto":
79
+ if (!decision.url)
80
+ throw new Error('goto decision missing "url"');
81
+ await driver.goto(decision.url);
82
+ step = { kind: "goto", url: decision.url };
83
+ break;
84
+ default:
85
+ throw new Error(`unknown action: ${decision.action}`);
86
+ }
87
+ steps.push(step);
88
+ onStep?.(decision, step);
89
+ }
90
+ // Safety cap reached without an explicit "done".
91
+ return { name: intent, steps, assertions: [{ kind: "no-failed-requests" }] };
92
+ }
93
+ //# sourceMappingURL=discover.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discover.js","sourceRoot":"","sources":["../../src/core/discover.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * The execution body: Context → Plan → Execute → Judge → Report. Every variable behavior
3
+ * is injected (invariant #2); with a fixed-scenario Planner + deterministic Critic, no LLM
4
+ * runs (invariant #4).
5
+ */
6
+ import type { Harness } from "./ports.js";
7
+ import type { Result } from "./types.js";
8
+ export declare function runHarness(harness: Harness, task: string): Promise<Result>;