@aperant/framework 0.8.4 → 0.8.7
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/CHANGELOG.md +185 -0
- package/agents/apt-planner.md +34 -3
- package/dist/cli/commands/coverage-check.d.mts.map +1 -1
- package/dist/cli/commands/coverage-check.mjs +74 -7
- package/dist/cli/commands/coverage-check.mjs.map +1 -1
- package/dist/cli/commands/detect-runtime.d.mts.map +1 -1
- package/dist/cli/commands/detect-runtime.mjs +18 -12
- package/dist/cli/commands/detect-runtime.mjs.map +1 -1
- package/dist/cli/coverage-check/user-outcomes.d.mts +84 -0
- package/dist/cli/coverage-check/user-outcomes.d.mts.map +1 -0
- package/dist/cli/coverage-check/user-outcomes.mjs +265 -0
- package/dist/cli/coverage-check/user-outcomes.mjs.map +1 -0
- package/dist/cli/host/detect.mjs +1 -1
- package/dist/cli/host/detect.mjs.map +1 -1
- package/dist/cli/install/install-from-source.mjs +3 -3
- package/dist/cli/install/legacy-paths.d.mts.map +1 -1
- package/dist/cli/install/legacy-paths.mjs +2 -0
- package/dist/cli/install/legacy-paths.mjs.map +1 -1
- package/dist/cli/install/manifest.d.mts +13 -0
- package/dist/cli/install/manifest.d.mts.map +1 -1
- package/dist/cli/install/manifest.mjs +5 -0
- package/dist/cli/install/manifest.mjs.map +1 -1
- package/dist/cli/install/pipeline.d.mts +15 -0
- package/dist/cli/install/pipeline.d.mts.map +1 -1
- package/dist/cli/install/pipeline.mjs +27 -0
- package/dist/cli/install/pipeline.mjs.map +1 -1
- package/dist/cli/install/transforms/codex.d.mts +1 -1
- package/dist/cli/install/transforms/codex.d.mts.map +1 -1
- package/dist/cli/install/transforms/codex.mjs +43 -2
- package/dist/cli/install/transforms/codex.mjs.map +1 -1
- package/dist/cli/install/version-header.d.mts.map +1 -1
- package/dist/cli/install/version-header.mjs +7 -1
- package/dist/cli/install/version-header.mjs.map +1 -1
- package/dist/cli/verify-proof/idl/index.d.mts +1 -1
- package/dist/cli/verify-proof/idl/index.mjs +7 -8
- package/dist/cli/verify-proof/idl/index.mjs.map +1 -1
- package/dist/cli/verify-proof/idl/types.d.ts +5 -4
- package/dist/cli/verify-proof/idl/types.d.ts.map +1 -1
- package/dist/cli/verify-proof/idl/types.js +4 -3
- package/dist/cli/verify-proof/idl/types.js.map +1 -1
- package/dist/cli/verify-proof/resolver.d.mts +63 -1
- package/dist/cli/verify-proof/resolver.d.mts.map +1 -1
- package/dist/cli/verify-proof/resolver.mjs +164 -4
- package/dist/cli/verify-proof/resolver.mjs.map +1 -1
- package/dist/cli/verify-proof/runtime-detect.d.mts +6 -3
- package/dist/cli/verify-proof/runtime-detect.d.mts.map +1 -1
- package/dist/cli/verify-proof/runtime-detect.mjs +104 -11
- package/dist/cli/verify-proof/runtime-detect.mjs.map +1 -1
- package/dist/cli/verify-proof/trust.d.mts +1 -1
- package/dist/cli/verify-proof/trust.d.mts.map +1 -1
- package/dist/cli/verify-proof/trust.mjs +1 -1
- package/dist/driver-sdk/conformance.d.ts +41 -0
- package/dist/driver-sdk/conformance.d.ts.map +1 -0
- package/dist/driver-sdk/conformance.js +123 -0
- package/dist/driver-sdk/conformance.js.map +1 -0
- package/dist/driver-sdk/errors.d.ts +75 -0
- package/dist/driver-sdk/errors.d.ts.map +1 -0
- package/dist/driver-sdk/errors.js +80 -0
- package/dist/driver-sdk/errors.js.map +1 -0
- package/dist/driver-sdk/idl.d.ts +221 -0
- package/dist/driver-sdk/idl.d.ts.map +1 -0
- package/dist/driver-sdk/idl.js +140 -0
- package/dist/driver-sdk/idl.js.map +1 -0
- package/dist/driver-sdk/index.d.ts +28 -0
- package/dist/driver-sdk/index.d.ts.map +1 -0
- package/dist/driver-sdk/index.js +28 -0
- package/dist/driver-sdk/index.js.map +1 -0
- package/dist/driver-sdk/manifest.d.ts +93 -0
- package/dist/driver-sdk/manifest.d.ts.map +1 -0
- package/dist/driver-sdk/manifest.js +12 -0
- package/dist/driver-sdk/manifest.js.map +1 -0
- package/dist/driver-sdk/retry.d.ts +33 -0
- package/dist/driver-sdk/retry.d.ts.map +1 -0
- package/dist/driver-sdk/retry.js +50 -0
- package/dist/driver-sdk/retry.js.map +1 -0
- package/dist/plugin/.claude-plugin/plugin.json +2 -1
- package/dist/plugin/agents/apt-planner.md +34 -3
- package/dist/plugin/skills/apt-plan/SKILL.md +50 -0
- package/dist/plugin/skills/apt-pr-review/SKILL.md +1 -1
- package/dist/plugin/skills/apt-research/SKILL.md +526 -0
- package/dist/plugin/skills/apt-research/appendices/budget-loop.md +397 -0
- package/dist/plugin/skills/apt-research/appendices/claim-graph-schema.md +206 -0
- package/dist/plugin/skills/apt-research/appendices/domain-tools.md +236 -0
- package/dist/plugin/skills/apt-spar/SKILL.md +19 -7
- package/dist/plugin/skills/apt-verify-proof/SKILL.md +41 -2
- package/dist/schemas/quick-task.d.ts +17 -17
- package/drivers/.gitkeep +0 -0
- package/drivers/api/README.md +40 -0
- package/drivers/api/driver.mjs +59 -0
- package/drivers/api/manifest.json +26 -0
- package/drivers/browser/README.md +105 -0
- package/drivers/browser/driver.mjs +134 -0
- package/drivers/browser/manifest.json +35 -0
- package/drivers/cli/README.md +44 -0
- package/drivers/cli/driver.mjs +62 -0
- package/drivers/cli/manifest.json +28 -0
- package/drivers/electron/README.md +64 -0
- package/drivers/electron/driver.mjs +87 -0
- package/drivers/electron/manifest.json +37 -0
- package/package.json +7 -3
- package/skills/apt-plan/SKILL.md +50 -0
- package/skills/apt-planner.md +16 -0
- package/skills/apt-pr-review/SKILL.md +1 -1
- package/skills/apt-research/SKILL.md +526 -0
- package/skills/apt-research/appendices/budget-loop.md +397 -0
- package/skills/apt-research/appendices/claim-graph-schema.md +206 -0
- package/skills/apt-research/appendices/domain-tools.md +236 -0
- package/skills/apt-spar/SKILL.md +19 -7
- package/skills/apt-verify-proof/SKILL.md +41 -2
- package/src/cli/commands/coverage-check.mjs +126 -44
- package/src/cli/commands/detect-runtime.mjs +23 -14
- package/src/cli/coverage-check/user-outcomes.mjs +273 -0
- package/src/cli/host/detect.mjs +1 -1
- package/src/cli/install/install-from-source.mjs +3 -3
- package/src/cli/install/legacy-paths.mjs +2 -0
- package/src/cli/install/manifest.mjs +5 -0
- package/src/cli/install/pipeline.mjs +28 -0
- package/src/cli/install/transforms/codex.mjs +51 -2
- package/src/cli/install/version-header.mjs +7 -1
- package/src/cli/verify-proof/idl/index.mjs +7 -8
- package/src/cli/verify-proof/idl/types.ts +5 -4
- package/src/cli/verify-proof/manifest-schema.json +2 -1
- package/src/cli/verify-proof/resolver.mjs +171 -4
- package/src/cli/verify-proof/runtime-detect.mjs +99 -10
- package/src/cli/verify-proof/trust.mjs +1 -1
- package/templates/proof-verification.md +32 -3
- package/workflows/verify-proof.md +78 -9
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Browser Driver
|
|
2
|
+
|
|
3
|
+
Bundled, first-party verify-proof driver targeting Chromium / Firefox /
|
|
4
|
+
WebKit via two transports: the Puppeteer MCP server OR the
|
|
5
|
+
`agent-browser` skill (Claude Code / OpenCode hosts).
|
|
6
|
+
|
|
7
|
+
## When Aperant picks this driver
|
|
8
|
+
|
|
9
|
+
`/apt:verify-proof` auto-selects `browser` when:
|
|
10
|
+
|
|
11
|
+
- The project's `package.json` declares a web framework (Next.js, React,
|
|
12
|
+
Vue, Svelte, Angular, Solid) AND the project is NOT an Electron /
|
|
13
|
+
Tauri / React Native container.
|
|
14
|
+
- The required IDL capabilities are a subset of: `navigate`, `click`,
|
|
15
|
+
`type`, `screenshot`, `assert_visible`, `assert_text`, `wait_for_idle`,
|
|
16
|
+
`execute_js`.
|
|
17
|
+
|
|
18
|
+
## Capabilities
|
|
19
|
+
|
|
20
|
+
The driver maps each IDL verb to BOTH transports. The workflow runner
|
|
21
|
+
picks the transport at dispatch time via `chooseTransport({has_skill_tool})`;
|
|
22
|
+
the chosen transport is recorded on each VerbResult as `transport: "skill" | "mcp"`.
|
|
23
|
+
|
|
24
|
+
| IDL verb | Puppeteer MCP tool | agent-browser CLI |
|
|
25
|
+
|----------|--------------------|--------------------|
|
|
26
|
+
| `navigate` | `mcp__puppeteer__puppeteer_navigate` | `agent-browser open <url>` |
|
|
27
|
+
| `click` | `mcp__puppeteer__puppeteer_click` | `agent-browser click @e<N>` |
|
|
28
|
+
| `type` | `mcp__puppeteer__puppeteer_fill` | `agent-browser fill @e<N> <text>` |
|
|
29
|
+
| `screenshot` | `mcp__puppeteer__puppeteer_screenshot` | `agent-browser screenshot` |
|
|
30
|
+
| `assert_visible` | `mcp__puppeteer__puppeteer_evaluate` (selector check) | `agent-browser snapshot` + `agent-browser get role <sel>` |
|
|
31
|
+
| `assert_text` | `mcp__puppeteer__puppeteer_evaluate` (textContent match) | `agent-browser snapshot` + `agent-browser get text <sel>` |
|
|
32
|
+
| `wait_for_idle` | `mcp__puppeteer__puppeteer_evaluate` (networkidle check) | `agent-browser wait --load networkidle` |
|
|
33
|
+
| `execute_js` | `mcp__puppeteer__puppeteer_evaluate` | `agent-browser eval <script>` |
|
|
34
|
+
|
|
35
|
+
## Transport selection
|
|
36
|
+
|
|
37
|
+
The driver carries TWO transport declarations on its manifest:
|
|
38
|
+
|
|
39
|
+
```json
|
|
40
|
+
"transport": { "mcp_server_id": "puppeteer", "skill_id": "agent-browser" }
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The workflow runner picks between them via the driver's exported
|
|
44
|
+
`chooseTransport({has_skill_tool})` helper. The selection rule:
|
|
45
|
+
|
|
46
|
+
- **`Skill` tool exposed by host CLI** (Claude Code, OpenCode) → `'skill'`.
|
|
47
|
+
Dispatches via `Skill({skill: "agent-browser", args: ...})`. One fewer
|
|
48
|
+
external dep (no MCP server install).
|
|
49
|
+
- **No Skill tool** (legacy host, MCP-only environment) → `'mcp'`.
|
|
50
|
+
Dispatches via `mcp__puppeteer__*` (the existing path).
|
|
51
|
+
|
|
52
|
+
The resolver's "one driver per runtime tag" invariant is preserved —
|
|
53
|
+
`browser` stays one driver with two transport options. No new
|
|
54
|
+
driverId; no resolver disambiguation needed.
|
|
55
|
+
|
|
56
|
+
## Manifest
|
|
57
|
+
|
|
58
|
+
- **Priority:** 100 (highest among bundled drivers — browser is the most
|
|
59
|
+
common target).
|
|
60
|
+
- **Stability:** `ga`.
|
|
61
|
+
- **Trust:** `first_party_signed` (default-allow).
|
|
62
|
+
- **Transport:** `{ mcp_server_id: "puppeteer", skill_id: "agent-browser" }`.
|
|
63
|
+
The `McpRegistry` handles consent gating for the MCP path per AC21;
|
|
64
|
+
the existing skill-permissions registry handles consent for the
|
|
65
|
+
Skill path.
|
|
66
|
+
|
|
67
|
+
## V1 scope — adapter is thin
|
|
68
|
+
|
|
69
|
+
This driver's `driver.mjs` is a manifest-conformant **adapter shim** in
|
|
70
|
+
v1. Each verb validates its args against `IDL_VERB_ARGS`, then returns
|
|
71
|
+
a VerbResult stub. **The actual transport dispatch — invoking
|
|
72
|
+
`mcp__puppeteer__*` tools OR `Skill({skill: "agent-browser", ...})` —
|
|
73
|
+
happens at the workflow-runner layer**, not inside this file: the model
|
|
74
|
+
loads the workflow prose, the workflow names IDL verbs, and the model
|
|
75
|
+
calls the tool whose name the driver manifest binds via
|
|
76
|
+
`transport.mcp_server_id` (MCP path) or `transport.skill_id` (Skill
|
|
77
|
+
path). The split exists because both tool surfaces are the model's, not
|
|
78
|
+
the driver's runtime — drivers in v1 are the validator + manifest +
|
|
79
|
+
resolver-anchor, not the executor.
|
|
80
|
+
|
|
81
|
+
The driver's adapter shim DOES record the chosen transport on the
|
|
82
|
+
returned VerbResult (`transport: "skill" | "mcp"`) so the workflow
|
|
83
|
+
runner can populate the corresponding field on each
|
|
84
|
+
`verification.json:tests[]` entry for audit purposes.
|
|
85
|
+
|
|
86
|
+
Follow-up `verify-proof-driver-transport-wiring-v2` will lift transport
|
|
87
|
+
dispatch into the driver process itself for drivers that want
|
|
88
|
+
out-of-band execution (e.g. node-side fetch for the api driver, or
|
|
89
|
+
direct Puppeteer.connect for headless CI).
|
|
90
|
+
|
|
91
|
+
## Overriding
|
|
92
|
+
|
|
93
|
+
To force the resolver to pick a different driver:
|
|
94
|
+
|
|
95
|
+
```json
|
|
96
|
+
// .aperant/config.json
|
|
97
|
+
{
|
|
98
|
+
"verification": {
|
|
99
|
+
"runtimes": ["electron", "cli"]
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
The resolver places listed driver IDs first; the highest-coverage / most-
|
|
105
|
+
compatible / highest-priority driver still wins within the override list.
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* browser/driver.mjs — bundled browser driver.
|
|
3
|
+
*
|
|
4
|
+
* Maps IDL verbs to one of two transports: the Puppeteer MCP server or
|
|
5
|
+
* the `agent-browser` skill. The manifest declares both:
|
|
6
|
+
* transport.mcp_server_id = "puppeteer" (legacy / non-Skill hosts)
|
|
7
|
+
* transport.skill_id = "agent-browser" (Claude Code / OpenCode)
|
|
8
|
+
*
|
|
9
|
+
* Transport selection lives in the WORKFLOW RUNNER layer — that's the
|
|
10
|
+
* site that knows whether the host CLI exposes the Skill tool. This
|
|
11
|
+
* driver exposes `chooseTransport({has_skill_tool})` as a helper so the
|
|
12
|
+
* workflow runner can ask the driver which transport it would pick;
|
|
13
|
+
* the driver records the chosen transport on each VerbResult via the
|
|
14
|
+
* optional `transport: 'skill' | 'mcp'` field (additive — existing
|
|
15
|
+
* consumers that ignore the field keep working).
|
|
16
|
+
*
|
|
17
|
+
* Per ID-01 — the workflow prose NEVER names mcp__puppeteer__* tools
|
|
18
|
+
* OR concrete `agent-browser` CLI subcommands. Both mappings live ONLY
|
|
19
|
+
* here in driver.mjs.
|
|
20
|
+
*
|
|
21
|
+
* IDL verb → MCP tool mapping (legacy transport):
|
|
22
|
+
* navigate → mcp__puppeteer__puppeteer_navigate
|
|
23
|
+
* click → mcp__puppeteer__puppeteer_click
|
|
24
|
+
* type → mcp__puppeteer__puppeteer_fill
|
|
25
|
+
* screenshot → mcp__puppeteer__puppeteer_screenshot
|
|
26
|
+
* assert_visible → mcp__puppeteer__puppeteer_evaluate (DOM selector)
|
|
27
|
+
* assert_text → mcp__puppeteer__puppeteer_evaluate (textContent match)
|
|
28
|
+
* wait_for_idle → mcp__puppeteer__puppeteer_evaluate (networkidle check)
|
|
29
|
+
* execute_js → mcp__puppeteer__puppeteer_evaluate
|
|
30
|
+
*
|
|
31
|
+
* IDL verb → agent-browser CLI mapping (Skill transport):
|
|
32
|
+
* navigate → agent-browser open <url>
|
|
33
|
+
* click → agent-browser click @e<N>
|
|
34
|
+
* type → agent-browser fill @e<N> <text>
|
|
35
|
+
* screenshot → agent-browser screenshot
|
|
36
|
+
* assert_visible → agent-browser snapshot + agent-browser get role <selector>
|
|
37
|
+
* assert_text → agent-browser snapshot + agent-browser get text <selector>
|
|
38
|
+
* wait_for_idle → agent-browser wait --load networkidle
|
|
39
|
+
* execute_js → agent-browser eval <script>
|
|
40
|
+
*
|
|
41
|
+
* The driver is a thin adapter: it validates input shape via the IDL
|
|
42
|
+
* arg-metadata, builds the transport-specific args, and returns a
|
|
43
|
+
* VerbResult. The actual transport dispatch happens at the workflow
|
|
44
|
+
* runner layer (which holds the model's tool surface — `Skill(...)` for
|
|
45
|
+
* skill transport, `mcp__puppeteer__*` for MCP transport). For
|
|
46
|
+
* conformance testing we expose the verb signatures as identity stubs
|
|
47
|
+
* returning VerbResult — the SDK conformance kit then validates the
|
|
48
|
+
* shape contract.
|
|
49
|
+
*/
|
|
50
|
+
|
|
51
|
+
import { validateVerbArgs } from '../../src/cli/verify-proof/idl/index.mjs'
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Pick a transport given the host environment's capabilities. When the
|
|
55
|
+
* host exposes the Skill tool (Claude Code, OpenCode), prefer the
|
|
56
|
+
* `agent-browser` skill — one fewer external dependency (no Puppeteer
|
|
57
|
+
* MCP server install). Otherwise fall back to the MCP transport.
|
|
58
|
+
*
|
|
59
|
+
* @param {{has_skill_tool: boolean}} env
|
|
60
|
+
* @returns {('skill'|'mcp')}
|
|
61
|
+
*/
|
|
62
|
+
export function chooseTransport(env) {
|
|
63
|
+
if (env && env.has_skill_tool === true) return 'skill'
|
|
64
|
+
return 'mcp'
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Build a VerbResult given the actual transport-layer result. */
|
|
68
|
+
function ok(evidence_paths = [], transport) {
|
|
69
|
+
const result = { status: 'success', evidence_paths, duration_ms: 0, retry_count: 0 }
|
|
70
|
+
if (transport) result.transport = transport
|
|
71
|
+
return result
|
|
72
|
+
}
|
|
73
|
+
function fail(error, transport) {
|
|
74
|
+
const result = { status: 'fail', evidence_paths: [], duration_ms: 0, retry_count: 0, error }
|
|
75
|
+
if (transport) result.transport = transport
|
|
76
|
+
return result
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Validate args + return a VerbResult shape stub. Real transport
|
|
81
|
+
* invocation happens at the workflow-runner layer (model holds the tool
|
|
82
|
+
* surface — `Skill({skill: "agent-browser", args})` OR
|
|
83
|
+
* `mcp__puppeteer__*` depending on host capabilities).
|
|
84
|
+
*
|
|
85
|
+
* @param {string} verb
|
|
86
|
+
* @param {Record<string, unknown>} args
|
|
87
|
+
* @param {('skill'|'mcp')} [transport] — optional, recorded on the result for audit
|
|
88
|
+
* @returns {{status: 'success'|'fail'|'skip', evidence_paths: string[], duration_ms: number, retry_count: number, error?: string, transport?: 'skill'|'mcp'}}
|
|
89
|
+
*/
|
|
90
|
+
function adaptVerb(verb, args, transport) {
|
|
91
|
+
const check = validateVerbArgs(verb, args)
|
|
92
|
+
if (!check.valid) {
|
|
93
|
+
return fail(`browser.${verb}: missing required args: ${check.missing.join(', ')}`, transport)
|
|
94
|
+
}
|
|
95
|
+
// Identity stub for the conformance test surface. Production execution
|
|
96
|
+
// dispatches to Skill({skill: "agent-browser", ...}) OR mcp__puppeteer__*
|
|
97
|
+
// at the workflow-runner layer.
|
|
98
|
+
if (verb === 'screenshot' && typeof args.output_path === 'string') {
|
|
99
|
+
return ok([args.output_path], transport)
|
|
100
|
+
}
|
|
101
|
+
return ok([], transport)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export async function navigate(args) {
|
|
105
|
+
return adaptVerb('navigate', args)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export async function click(args) {
|
|
109
|
+
return adaptVerb('click', args)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export async function type(args) {
|
|
113
|
+
return adaptVerb('type', args)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export async function screenshot(args) {
|
|
117
|
+
return adaptVerb('screenshot', args)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export async function assert_visible(args) {
|
|
121
|
+
return adaptVerb('assert_visible', args)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export async function assert_text(args) {
|
|
125
|
+
return adaptVerb('assert_text', args)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export async function wait_for_idle(args) {
|
|
129
|
+
return adaptVerb('wait_for_idle', args)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export async function execute_js(args) {
|
|
133
|
+
return adaptVerb('execute_js', args)
|
|
134
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"driverId": "browser",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"targets": ["browser@chrome", "browser@firefox", "browser@webkit"],
|
|
5
|
+
"capabilities": [
|
|
6
|
+
"navigate",
|
|
7
|
+
"click",
|
|
8
|
+
"type",
|
|
9
|
+
"screenshot",
|
|
10
|
+
"assert_visible",
|
|
11
|
+
"assert_text",
|
|
12
|
+
"wait_for_idle",
|
|
13
|
+
"execute_js"
|
|
14
|
+
],
|
|
15
|
+
"evidence_capabilities": ["screenshot", "dom_dump", "capture_logs"],
|
|
16
|
+
"frameworkApiRange": "^1.0.0",
|
|
17
|
+
"priority": 100,
|
|
18
|
+
"stability": "ga",
|
|
19
|
+
"trust": {
|
|
20
|
+
"tier": "first_party_signed",
|
|
21
|
+
"publisher": {
|
|
22
|
+
"name": "Aperant",
|
|
23
|
+
"npm_scope": "@aperant",
|
|
24
|
+
"github_org": "Mikalsen-AI",
|
|
25
|
+
"verified": true
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"security": {
|
|
29
|
+
"sandbox_profile": "workspace_write",
|
|
30
|
+
"network": "allowlist",
|
|
31
|
+
"requires_user_approval": false
|
|
32
|
+
},
|
|
33
|
+
"audit": { "log_invocations": true, "log_manifest_hash": true },
|
|
34
|
+
"transport": { "mcp_server_id": "puppeteer", "skill_id": "agent-browser" }
|
|
35
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# CLI Driver
|
|
2
|
+
|
|
3
|
+
Bundled headless verify-proof driver for CI / non-GUI environments.
|
|
4
|
+
Doesn't open a browser, doesn't render anything — asserts on command
|
|
5
|
+
output, captured logs, and runtime state.
|
|
6
|
+
|
|
7
|
+
## When Aperant picks this driver
|
|
8
|
+
|
|
9
|
+
`/apt:verify-proof` auto-selects `cli` when:
|
|
10
|
+
|
|
11
|
+
- The project has no GUI runtime (no Electron, no Tauri, no web frontend).
|
|
12
|
+
- The required IDL capabilities are: `execute_js`, `capture_logs`,
|
|
13
|
+
`assert_output`, `assert_text`, `dump_state`.
|
|
14
|
+
- Used as a fallback for CI runs where the higher-priority browser /
|
|
15
|
+
electron drivers can't be reached.
|
|
16
|
+
|
|
17
|
+
## Capabilities
|
|
18
|
+
|
|
19
|
+
| IDL verb | Behavior |
|
|
20
|
+
|----------|----------|
|
|
21
|
+
| `execute_js` | Run a Node snippet via `execFile('node', ['-e', script])` |
|
|
22
|
+
| `capture_logs` | Append stdout/stderr to an evidence file |
|
|
23
|
+
| `assert_output` | Match a pattern against captured stdout/stderr |
|
|
24
|
+
| `assert_text` | Match a pattern against a captured text artifact |
|
|
25
|
+
| `dump_state` | Write a JSON dump of runner CWD + env keys |
|
|
26
|
+
|
|
27
|
+
## Manifest
|
|
28
|
+
|
|
29
|
+
- **Priority:** 60 (below browser=100 / electron=90 / api=50).
|
|
30
|
+
- **Stability:** `ga`.
|
|
31
|
+
- **Trust:** `first_party_signed`.
|
|
32
|
+
- **Network posture:** `deny`.
|
|
33
|
+
- **Transport:** `{ builtin: true }` — the driver IS the transport.
|
|
34
|
+
- **Security:** `allow_shell: false`. The driver routes every spawn
|
|
35
|
+
through `execFile` (no shell, no pipes, no redirects) per ID-03.
|
|
36
|
+
|
|
37
|
+
## V1 scope — adapter is thin
|
|
38
|
+
|
|
39
|
+
Like the other bundled drivers, `driver.mjs` is a v1 manifest-conformant
|
|
40
|
+
adapter shim: each verb validates its args via `IDL_VERB_ARGS` and
|
|
41
|
+
returns a VerbResult stub. Actual Node-side execution lands in
|
|
42
|
+
`verify-proof-driver-transport-wiring-v2` (follow-up). The CLI driver
|
|
43
|
+
is the most natural candidate for first direct-dispatch since
|
|
44
|
+
`{ builtin: true }` already means "driver process owns the transport."
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cli/driver.mjs — bundled headless CLI driver.
|
|
3
|
+
*
|
|
4
|
+
* Targets non-GUI CI verify-proof runs. Asserts on command stdout / stderr
|
|
5
|
+
* and on captured log streams. The transport is `{ builtin: true }` — the
|
|
6
|
+
* driver itself shells out via packages/framework/src/cli/verify-proof/exec.mjs
|
|
7
|
+
* (execFile, no shell) for the rare cases it needs to spawn a tool.
|
|
8
|
+
*
|
|
9
|
+
* Capabilities (5, the AC8 minimum):
|
|
10
|
+
* - execute_js — run a Node snippet via execFile('node', ['-e', script])
|
|
11
|
+
* - capture_logs — append stdout/stderr to an evidence file
|
|
12
|
+
* - assert_output — match an expected pattern against stdout/stderr
|
|
13
|
+
* - assert_text — match against a captured text artifact
|
|
14
|
+
* - dump_state — write a JSON describing the runner's CWD + env keys
|
|
15
|
+
*
|
|
16
|
+
* Per ID-03 — `transport: { builtin: true }` means the driver IS the
|
|
17
|
+
* transport; it never shells out to free-form strings. Any `execute_js`
|
|
18
|
+
* invocation routes through exec.mjs which uses execFile (no shell).
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { validateVerbArgs } from '../../src/cli/verify-proof/idl/index.mjs'
|
|
22
|
+
|
|
23
|
+
function ok(evidence_paths = []) {
|
|
24
|
+
return { status: 'success', evidence_paths, duration_ms: 0, retry_count: 0 }
|
|
25
|
+
}
|
|
26
|
+
function fail(error) {
|
|
27
|
+
return { status: 'fail', evidence_paths: [], duration_ms: 0, retry_count: 0, error }
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function adaptVerb(verb, args) {
|
|
31
|
+
const check = validateVerbArgs(verb, args)
|
|
32
|
+
if (!check.valid) {
|
|
33
|
+
return fail(`cli.${verb}: missing required args: ${check.missing.join(', ')}`)
|
|
34
|
+
}
|
|
35
|
+
if (verb === 'capture_logs' && typeof args.output_path === 'string') {
|
|
36
|
+
return ok([args.output_path])
|
|
37
|
+
}
|
|
38
|
+
if (verb === 'dump_state' && typeof args.output_path === 'string') {
|
|
39
|
+
return ok([args.output_path])
|
|
40
|
+
}
|
|
41
|
+
return ok()
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export async function execute_js(args) {
|
|
45
|
+
return adaptVerb('execute_js', args)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export async function capture_logs(args) {
|
|
49
|
+
return adaptVerb('capture_logs', args)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export async function assert_output(args) {
|
|
53
|
+
return adaptVerb('assert_output', args)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export async function assert_text(args) {
|
|
57
|
+
return adaptVerb('assert_text', args)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export async function dump_state(args) {
|
|
61
|
+
return adaptVerb('dump_state', args)
|
|
62
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"driverId": "cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"targets": ["cli", "node"],
|
|
5
|
+
"capabilities": ["execute_js", "capture_logs", "assert_output", "assert_text", "dump_state"],
|
|
6
|
+
"evidence_capabilities": ["capture_logs", "dump_state"],
|
|
7
|
+
"frameworkApiRange": "^1.0.0",
|
|
8
|
+
"priority": 60,
|
|
9
|
+
"stability": "ga",
|
|
10
|
+
"trust": {
|
|
11
|
+
"tier": "first_party_signed",
|
|
12
|
+
"publisher": {
|
|
13
|
+
"name": "Aperant",
|
|
14
|
+
"npm_scope": "@aperant",
|
|
15
|
+
"github_org": "Mikalsen-AI",
|
|
16
|
+
"verified": true
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"security": {
|
|
20
|
+
"sandbox_profile": "workspace_write",
|
|
21
|
+
"network": "deny",
|
|
22
|
+
"allowed_executables": ["node", "sh", "bash"],
|
|
23
|
+
"allow_shell": false,
|
|
24
|
+
"requires_user_approval": false
|
|
25
|
+
},
|
|
26
|
+
"audit": { "log_invocations": true, "log_manifest_hash": true },
|
|
27
|
+
"transport": { "builtin": true }
|
|
28
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Electron Driver
|
|
2
|
+
|
|
3
|
+
Bundled, first-party verify-proof driver for Electron-based desktop
|
|
4
|
+
applications. This driver is the one Aperant itself uses internally
|
|
5
|
+
(this monorepo is an Electron app).
|
|
6
|
+
|
|
7
|
+
## When Aperant picks this driver
|
|
8
|
+
|
|
9
|
+
`/apt:verify-proof` auto-selects `electron` when:
|
|
10
|
+
|
|
11
|
+
- The project's `package.json` declares `electron` (or `@electron/*`) as
|
|
12
|
+
a dependency.
|
|
13
|
+
- The required IDL capabilities are a subset of: `click`, `type`, `key`,
|
|
14
|
+
`navigate`, `screenshot`, `assert_visible`, `assert_text`,
|
|
15
|
+
`wait_for_idle`, `execute_js`, `dump_state`.
|
|
16
|
+
|
|
17
|
+
## Capabilities
|
|
18
|
+
|
|
19
|
+
| IDL verb | Maps to Electron MCP tool |
|
|
20
|
+
|----------|---------------------------|
|
|
21
|
+
| `click` | `mcp__electron__send_command_to_electron` (button_click) |
|
|
22
|
+
| `type` | `mcp__electron__send_command_to_electron` (fill_input) |
|
|
23
|
+
| `key` | `mcp__electron__send_command_to_electron` (send_keyboard_shortcut) |
|
|
24
|
+
| `navigate` | `mcp__electron__send_command_to_electron` (navigate) |
|
|
25
|
+
| `screenshot` | `mcp__electron__take_screenshot` |
|
|
26
|
+
| `assert_visible` | `mcp__electron__send_command_to_electron` (selector check) |
|
|
27
|
+
| `assert_text` | `mcp__electron__send_command_to_electron` (textContent match) |
|
|
28
|
+
| `wait_for_idle` | `mcp__electron__send_command_to_electron` (renderer-idle) |
|
|
29
|
+
| `execute_js` | `mcp__electron__send_command_to_electron` (evaluate) |
|
|
30
|
+
| `dump_state` | `mcp__electron__get_electron_window_info` |
|
|
31
|
+
|
|
32
|
+
## Manifest
|
|
33
|
+
|
|
34
|
+
- **Priority:** 90 (second-highest among bundled drivers — Electron is
|
|
35
|
+
rarer than browser but covers the desktop-app case).
|
|
36
|
+
- **Stability:** `ga`.
|
|
37
|
+
- **Trust:** `first_party_signed` (default-allow).
|
|
38
|
+
- **Network posture:** `deny` (Electron apps shouldn't be hitting the
|
|
39
|
+
network during proof verification).
|
|
40
|
+
- **Transport:** `{ mcp_server_id: "electron" }`.
|
|
41
|
+
|
|
42
|
+
## Why hardcoded MCP tool names live here
|
|
43
|
+
|
|
44
|
+
Per [ID-01](../../docs/frameworks/spec-gaps.md): the verify-proof workflow
|
|
45
|
+
prose refers ONLY to IDL verbs (`driver.click(target)`,
|
|
46
|
+
`driver.screenshot(path)`). The mapping from a verb to a concrete
|
|
47
|
+
`mcp__electron__*` tool name exists in exactly one place — this driver
|
|
48
|
+
file. The grep guard in
|
|
49
|
+
`packages/framework/src/__tests__/verify-proof-workflow-grep-guard.test.ts`
|
|
50
|
+
asserts zero `mcp__electron__*` matches in
|
|
51
|
+
`workflows/verify-proof.md`. Adding a Tauri driver requires zero edits
|
|
52
|
+
to the workflow.
|
|
53
|
+
|
|
54
|
+
## V1 scope — adapter is thin
|
|
55
|
+
|
|
56
|
+
This driver's `driver.mjs` is a manifest-conformant **adapter shim** in
|
|
57
|
+
v1: each verb validates its args against `IDL_VERB_ARGS` and returns a
|
|
58
|
+
VerbResult stub. The actual `mcp__electron__*` tool invocations happen
|
|
59
|
+
at the workflow-runner layer (the model holds the MCP tool surface,
|
|
60
|
+
NOT the driver process). The verb→tool mapping table above is the
|
|
61
|
+
contract the workflow runner consults; the driver itself is the
|
|
62
|
+
validator + manifest + resolver-anchor in v1. See
|
|
63
|
+
`verify-proof-driver-transport-wiring-v2` (follow-up) for direct-
|
|
64
|
+
dispatch evolution.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* electron/driver.mjs — bundled Electron driver.
|
|
3
|
+
*
|
|
4
|
+
* Maps IDL verbs to the Electron MCP server's tool surface. The
|
|
5
|
+
* transport.mcp_server_id="electron" string in manifest.json names the
|
|
6
|
+
* MCP server config the resolver hands off to.
|
|
7
|
+
*
|
|
8
|
+
* Per ID-01 — the workflow prose NEVER names mcp__electron__* tools.
|
|
9
|
+
* That mapping lives ONLY here in driver.mjs.
|
|
10
|
+
*
|
|
11
|
+
* IDL verb → MCP tool mapping (one place, one purpose):
|
|
12
|
+
* click → mcp__electron__send_command_to_electron (button_click)
|
|
13
|
+
* type → mcp__electron__send_command_to_electron (fill_input)
|
|
14
|
+
* key → mcp__electron__send_command_to_electron (send_keyboard_shortcut)
|
|
15
|
+
* navigate → mcp__electron__send_command_to_electron (navigate)
|
|
16
|
+
* screenshot → mcp__electron__take_screenshot
|
|
17
|
+
* assert_visible → mcp__electron__send_command_to_electron (selector check)
|
|
18
|
+
* assert_text → mcp__electron__send_command_to_electron (textContent match)
|
|
19
|
+
* wait_for_idle → mcp__electron__send_command_to_electron (renderer-idle check)
|
|
20
|
+
* execute_js → mcp__electron__send_command_to_electron (evaluate)
|
|
21
|
+
* dump_state → mcp__electron__get_electron_window_info
|
|
22
|
+
*
|
|
23
|
+
* The driver is a thin adapter — same shape as browser/driver.mjs.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { validateVerbArgs } from '../../src/cli/verify-proof/idl/index.mjs'
|
|
27
|
+
|
|
28
|
+
function ok(evidence_paths = []) {
|
|
29
|
+
return { status: 'success', evidence_paths, duration_ms: 0, retry_count: 0 }
|
|
30
|
+
}
|
|
31
|
+
function fail(error) {
|
|
32
|
+
return { status: 'fail', evidence_paths: [], duration_ms: 0, retry_count: 0, error }
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function adaptVerb(verb, args) {
|
|
36
|
+
const check = validateVerbArgs(verb, args)
|
|
37
|
+
if (!check.valid) {
|
|
38
|
+
return fail(`electron.${verb}: missing required args: ${check.missing.join(', ')}`)
|
|
39
|
+
}
|
|
40
|
+
if (verb === 'screenshot' && typeof args.output_path === 'string') {
|
|
41
|
+
return ok([args.output_path])
|
|
42
|
+
}
|
|
43
|
+
if (verb === 'dump_state' && typeof args.output_path === 'string') {
|
|
44
|
+
return ok([args.output_path])
|
|
45
|
+
}
|
|
46
|
+
return ok()
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export async function click(args) {
|
|
50
|
+
return adaptVerb('click', args)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export async function type(args) {
|
|
54
|
+
return adaptVerb('type', args)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export async function key(args) {
|
|
58
|
+
return adaptVerb('key', args)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export async function navigate(args) {
|
|
62
|
+
return adaptVerb('navigate', args)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export async function screenshot(args) {
|
|
66
|
+
return adaptVerb('screenshot', args)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export async function assert_visible(args) {
|
|
70
|
+
return adaptVerb('assert_visible', args)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export async function assert_text(args) {
|
|
74
|
+
return adaptVerb('assert_text', args)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export async function wait_for_idle(args) {
|
|
78
|
+
return adaptVerb('wait_for_idle', args)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export async function execute_js(args) {
|
|
82
|
+
return adaptVerb('execute_js', args)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export async function dump_state(args) {
|
|
86
|
+
return adaptVerb('dump_state', args)
|
|
87
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"driverId": "electron",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"targets": ["electron@>=25"],
|
|
5
|
+
"capabilities": [
|
|
6
|
+
"click",
|
|
7
|
+
"type",
|
|
8
|
+
"key",
|
|
9
|
+
"navigate",
|
|
10
|
+
"screenshot",
|
|
11
|
+
"assert_visible",
|
|
12
|
+
"assert_text",
|
|
13
|
+
"wait_for_idle",
|
|
14
|
+
"execute_js",
|
|
15
|
+
"dump_state"
|
|
16
|
+
],
|
|
17
|
+
"evidence_capabilities": ["screenshot", "dom_dump", "capture_logs"],
|
|
18
|
+
"frameworkApiRange": "^1.0.0",
|
|
19
|
+
"priority": 90,
|
|
20
|
+
"stability": "ga",
|
|
21
|
+
"trust": {
|
|
22
|
+
"tier": "first_party_signed",
|
|
23
|
+
"publisher": {
|
|
24
|
+
"name": "Aperant",
|
|
25
|
+
"npm_scope": "@aperant",
|
|
26
|
+
"github_org": "Mikalsen-AI",
|
|
27
|
+
"verified": true
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"security": {
|
|
31
|
+
"sandbox_profile": "workspace_write",
|
|
32
|
+
"network": "deny",
|
|
33
|
+
"requires_user_approval": false
|
|
34
|
+
},
|
|
35
|
+
"audit": { "log_invocations": true, "log_manifest_hash": true },
|
|
36
|
+
"transport": { "mcp_server_id": "electron" }
|
|
37
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aperant/framework",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.7",
|
|
4
4
|
"description": "AI coding framework — composable skills for planning, executing, verifying, and reviewing code with any LLM provider. Works as Claude Code commands, Codex tasks, or programmatically.",
|
|
5
5
|
"author": "Mikalsen AI <hello@mikalsen.ai>",
|
|
6
6
|
"type": "module",
|
|
@@ -36,6 +36,10 @@
|
|
|
36
36
|
"types": "./dist/standalone/index.d.ts",
|
|
37
37
|
"import": "./dist/standalone/index.js"
|
|
38
38
|
},
|
|
39
|
+
"./driver-sdk": {
|
|
40
|
+
"types": "./dist/driver-sdk/index.d.ts",
|
|
41
|
+
"import": "./dist/driver-sdk/index.js"
|
|
42
|
+
},
|
|
39
43
|
"./coordination/event-schema": {
|
|
40
44
|
"types": "./src/cli/coordination/event-schema.d.ts",
|
|
41
45
|
"import": "./src/cli/coordination/event-schema.mjs"
|
|
@@ -66,6 +70,7 @@
|
|
|
66
70
|
"templates/",
|
|
67
71
|
"context/",
|
|
68
72
|
"workflows/",
|
|
73
|
+
"drivers/",
|
|
69
74
|
"examples/",
|
|
70
75
|
"LICENSE",
|
|
71
76
|
"README.md",
|
|
@@ -104,8 +109,7 @@
|
|
|
104
109
|
"@clack/prompts": "^1.2.0",
|
|
105
110
|
"proper-lockfile": "^4.1.2",
|
|
106
111
|
"yaml": "^2.8.3",
|
|
107
|
-
"zod": "^4.3.6"
|
|
108
|
-
"@aperant/driver-sdk": "0.1.0"
|
|
112
|
+
"zod": "^4.3.6"
|
|
109
113
|
},
|
|
110
114
|
"optionalDependencies": {
|
|
111
115
|
"@babel/parser": "^7.29.2",
|