@appliqation/scriptgen 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +70 -0
- package/dist/cli/audit.js +25 -0
- package/dist/cli/audit.js.map +1 -0
- package/dist/cli/index.js +137 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/output.js +29 -0
- package/dist/cli/output.js.map +1 -0
- package/dist/config/env.js +49 -0
- package/dist/config/env.js.map +1 -0
- package/dist/orchestrator/generate.js +77 -0
- package/dist/orchestrator/generate.js.map +1 -0
- package/dist/tools/codingTools.js +173 -0
- package/dist/tools/codingTools.js.map +1 -0
- package/dist/tools/commandGate.js +67 -0
- package/dist/tools/commandGate.js.map +1 -0
- package/dist/tools/safety.js +25 -0
- package/dist/tools/safety.js.map +1 -0
- package/package.json +37 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Appliqation Pty Ltd
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Appliqation Scriptgen
|
|
2
|
+
|
|
3
|
+
**Drafts and verifies an enterprise-grade Playwright script for one test case — and never claims it passes without actually running it.**
|
|
4
|
+
|
|
5
|
+
Point it at a test case that has no canonical automation yet, and it investigates the surrounding context (scenario intent, sibling test flakiness, linked defects, prior execution evidence), writes a real Playwright spec into your repo, and iterates against a real `npx playwright test` run until it's genuinely green — not until the model *says* it's green.
|
|
6
|
+
|
|
7
|
+
## Why this exists
|
|
8
|
+
|
|
9
|
+
Most "AI writes your tests" tools generate a script and hope. This one is judged by a single rule: **`testRun.ok` is only ever `true` if a real, `execFile`-reported Playwright process exited 0, and that run happened *after* the most recent file write.** An earlier passing run that predates the latest edit doesn't count — that's a stale result, not a verification. If the generated script never actually ran, the exit code reflects that honestly, every time.
|
|
10
|
+
|
|
11
|
+
## How it works
|
|
12
|
+
|
|
13
|
+
```mermaid
|
|
14
|
+
flowchart TD
|
|
15
|
+
A[test case UUID] --> B[gather context:<br/>scenario, sibling flakiness,<br/>linked defects, evidence]
|
|
16
|
+
B --> C[draft a Playwright spec]
|
|
17
|
+
C --> D[npx playwright test]
|
|
18
|
+
D --> E{passed?}
|
|
19
|
+
E -- no --> F[read the real failure,<br/>revise the spec]
|
|
20
|
+
F --> D
|
|
21
|
+
E -- yes --> G[report: verified: true<br/>+ the written file path]
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
The context-gathering and drafting *methodology* itself lives in Appliqation's own `appq:automate` MCP prompt — this repo is deliberately thin: it just gives that workflow two tool surfaces (read-only Appliqation context tools, and real filesystem + an allowlisted shell) and lets it do the work.
|
|
25
|
+
|
|
26
|
+
- **No appq write tool, no git operation.** This agent writes local files only — nothing is synced back to Appliqation and nothing is committed. [`appliqation-pr-raise`](https://github.com/appliqation/appliqation-pr-raise) handles turning the result into a real PR.
|
|
27
|
+
- **A hardcoded, non-negotiable shell allowlist.** `npm init/install -D`, `npx playwright install/--version/test`, `node --version`, `git status/diff` — nothing else can run, checked before execution, spawned via `execFile` with an explicit argv array (never a shell string).
|
|
28
|
+
|
|
29
|
+
## Quick start
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm install -g @appliqation/scriptgen
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Create a `.env` file (in whatever directory you'll run it from) with:
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
APPQ_API_KEY=your-appliqation-api-key
|
|
39
|
+
ANTHROPIC_API_KEY=your-anthropic-key # or OPENAI_API_KEY — pick one
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
appliqation-scriptgen generate \
|
|
44
|
+
--test-case-uuid <uuid> \
|
|
45
|
+
--repo-path /path/to/your/checkout
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Add `--environment <name>` if the target repo needs a fresh Playwright config bootstrapped (its `baseURL` context), `--role <name>` to authenticate as a specific role (otherwise inferred per-TC automatically), `--autotest-run-id <id>` to ground the draft in a prior [`appliqation-autotest`](https://github.com/appliqation/appliqation-autotest) run's real execution evidence, and `--json`/`--ci` for a structured summary + CI-friendly exit code.
|
|
49
|
+
|
|
50
|
+
## Configuration
|
|
51
|
+
|
|
52
|
+
Copy `.env.example` to `.env`. Requires `APPQ_API_KEY` and one of `ANTHROPIC_API_KEY`/`OPENAI_API_KEY`.
|
|
53
|
+
|
|
54
|
+
## Development
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
git clone https://github.com/appliqation/appliqation-scriptgen.git
|
|
58
|
+
cd appliqation-scriptgen
|
|
59
|
+
npm install
|
|
60
|
+
cp .env.example .env # fill in APPQ_API_KEY and one LLM provider key
|
|
61
|
+
npm run dev -- generate --test-case-uuid <uuid> --repo-path <path>
|
|
62
|
+
npm run typecheck
|
|
63
|
+
npm test
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
See `CLAUDE.md` for a map of this repo if you're working in it with an AI coding assistant.
|
|
67
|
+
|
|
68
|
+
## License
|
|
69
|
+
|
|
70
|
+
MIT — see [LICENSE](./LICENSE).
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// Extracted out of cli/index.ts so this is testable without triggering that
|
|
2
|
+
// file's top-level program.parseAsync(process.argv) side effect — same
|
|
3
|
+
// reasoning as appliqation-autotest's cli/resolvers.ts.
|
|
4
|
+
import { safeRecord } from '@appliqation/agent-core';
|
|
5
|
+
import { exitCodeFor } from './output.js';
|
|
6
|
+
export async function recordGenerateRun(args) {
|
|
7
|
+
const { sink, startedAt, endedAt, model, usage, testCaseUuid, result } = args;
|
|
8
|
+
const summary = result
|
|
9
|
+
? { testCaseUuid, writtenPaths: result.writtenPaths, testRan: result.testRun.ran, verified: result.testRun.ok, report: result.report }
|
|
10
|
+
: undefined;
|
|
11
|
+
await safeRecord(sink, {
|
|
12
|
+
agent: 'appliqation-scriptgen',
|
|
13
|
+
subcommand: 'generate',
|
|
14
|
+
startedAt,
|
|
15
|
+
endedAt,
|
|
16
|
+
durationMillis: endedAt - startedAt,
|
|
17
|
+
model,
|
|
18
|
+
usage,
|
|
19
|
+
turns: result?.turns,
|
|
20
|
+
budgetExceeded: result?.budgetExceeded,
|
|
21
|
+
exitCode: summary ? exitCodeFor(summary) : 1,
|
|
22
|
+
outcome: summary ? { ...summary } : { testCaseUuid, error: true },
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=audit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/cli/audit.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,uEAAuE;AACvE,wDAAwD;AAExD,OAAO,EAAE,UAAU,EAAoC,MAAM,yBAAyB,CAAC;AAEvF,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAc1C,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,IAA2B;IACjE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAC9E,MAAM,OAAO,GAAgC,MAAM;QACjD,CAAC,CAAC,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,CAAC,YAAY,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,QAAQ,EAAE,MAAM,CAAC,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE;QACtI,CAAC,CAAC,SAAS,CAAC;IAEd,MAAM,UAAU,CAAC,IAAI,EAAE;QACrB,KAAK,EAAE,uBAAuB;QAC9B,UAAU,EAAE,UAAU;QACtB,SAAS;QACT,OAAO;QACP,cAAc,EAAE,OAAO,GAAG,SAAS;QACnC,KAAK;QACL,KAAK;QACL,KAAK,EAAE,MAAM,EAAE,KAAK;QACpB,cAAc,EAAE,MAAM,EAAE,cAAc;QACtC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;QAC5C,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,KAAK,EAAE,IAAI,EAAE;KAClE,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// `generate`: draft + verify a Playwright script for one test case, via
|
|
3
|
+
// appq's (enriched) appq:automate workflow given real filesystem + shell
|
|
4
|
+
// tools. See src/orchestrator/generate.ts for the actual mechanism.
|
|
5
|
+
import { Command } from 'commander';
|
|
6
|
+
import { createMcpClient, createAnthropicAdapter, createOpenAiAdapter, createUsageAccumulator, resolveScenarioId, fetchScenarioInfo, resolveUrl, knownRolesForProject, inferRole, } from '@appliqation/agent-core';
|
|
7
|
+
import { config, resolveProvider, resolveModel } from '../config/env.js';
|
|
8
|
+
import { generate } from '../orchestrator/generate.js';
|
|
9
|
+
import { recordGenerateRun } from './audit.js';
|
|
10
|
+
import { printJsonSummary, printHumanSummary, exitCodeFor } from './output.js';
|
|
11
|
+
const client = createMcpClient({ origin: config.appqOrigin, apiKey: config.appqApiKey() });
|
|
12
|
+
function buildAdapter() {
|
|
13
|
+
const provider = resolveProvider();
|
|
14
|
+
const model = resolveModel();
|
|
15
|
+
return provider === 'anthropic'
|
|
16
|
+
? createAnthropicAdapter(config.anthropicApiKey, model, config.anthropicMaxTokens)
|
|
17
|
+
: createOpenAiAdapter(config.openaiApiKey, model, config.openaiMaxOutputTokens);
|
|
18
|
+
}
|
|
19
|
+
function logEvent(prefix) {
|
|
20
|
+
return (e) => {
|
|
21
|
+
if (e.type === 'assistant') {
|
|
22
|
+
const text = (e.detail ?? '').trim();
|
|
23
|
+
if (text)
|
|
24
|
+
console.error(`${prefix}[thinking] ${text}`);
|
|
25
|
+
}
|
|
26
|
+
else if (e.type === 'tool') {
|
|
27
|
+
const d = e.detail;
|
|
28
|
+
console.error(`${prefix}[tool] ${d.name} -> ${d.result.slice(0, 300)}`);
|
|
29
|
+
}
|
|
30
|
+
else if (e.type === 'log') {
|
|
31
|
+
console.error(`${prefix}[log] ${e.detail}`);
|
|
32
|
+
}
|
|
33
|
+
else if (e.type === 'usage') {
|
|
34
|
+
const u = e.detail;
|
|
35
|
+
const cacheNote = u.cacheReadTokens
|
|
36
|
+
? ` (${u.cacheReadTokens} from cache)`
|
|
37
|
+
: u.cacheWriteTokens
|
|
38
|
+
? ` (${u.cacheWriteTokens} written to cache)`
|
|
39
|
+
: '';
|
|
40
|
+
console.error(`${prefix}[usage] in=${u.inputTokens} out=${u.outputTokens}${cacheNote}`);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
const program = new Command();
|
|
45
|
+
program
|
|
46
|
+
.name('appliqation-scriptgen')
|
|
47
|
+
.description('Draft and verify an enterprise-grade Playwright script for one Appliqation test case.');
|
|
48
|
+
program
|
|
49
|
+
.command('generate')
|
|
50
|
+
.description("Compose a Playwright test for one TC via appq's appq:automate workflow (context: scenario, sibling-TC " +
|
|
51
|
+
'flakiness, linked defects, manual/agentic execution evidence, canonical script if present), given real ' +
|
|
52
|
+
'filesystem + an allowlisted shell so it can bootstrap the target repo and actually run the result — ' +
|
|
53
|
+
'`npx playwright test` is what decides pass/fail, never the model\'s own claim. Writes local files only: ' +
|
|
54
|
+
'no appq write tool call, no git operation. scenario_id/project_id are always derived from ' +
|
|
55
|
+
'--test-case-uuid, never accepted as separate inputs.')
|
|
56
|
+
.requiredOption('--test-case-uuid <uuid>', 'test case UUID to generate a script for')
|
|
57
|
+
.option('--environment <name>', 'environment name — its URL (from get_project_settings) is offered as context for the target app\'s ' +
|
|
58
|
+
'baseURL, only if the target repo needs a Playwright config bootstrapped from scratch')
|
|
59
|
+
.option('--role <name>', 'authenticate as this role in the generated script (setupAuth). Omit for per-TC inference from the TC\'s ' +
|
|
60
|
+
'own tag/name (same mechanism appliqation-autotest uses), or for ungated projects where neither applies.')
|
|
61
|
+
.option('--autotest-run-id <id>', 'a prior appliqation-autotest run for this TC. Passed straight through as appq:automate\'s autotest_run_id ' +
|
|
62
|
+
"arg — pulls that run's execution evidence and switches to autonomous mode (no confirmation steps, no " +
|
|
63
|
+
'live-browsing phase).')
|
|
64
|
+
.option('--repo-path <path>', 'target repo root every file/command tool call is scoped to', process.cwd())
|
|
65
|
+
.option('--file-path <path>', 'local spec file to extend or create. Omit to let the AI discover/propose one.')
|
|
66
|
+
.option('--max-turns <n>', 'override BUDGET_MAX_TURNS for this run')
|
|
67
|
+
.option('--json', 'print a single structured JSON summary on stdout instead of a human-readable report')
|
|
68
|
+
.option('--ci', 'shorthand for --json; exit code already reflects the real, execFile-verified outcome either way')
|
|
69
|
+
.action(async (opts) => {
|
|
70
|
+
const json = (opts.json ?? false) || (opts.ci ?? false);
|
|
71
|
+
const adapter = buildAdapter();
|
|
72
|
+
const scenarioId = resolveScenarioId({ testCaseUuid: opts.testCaseUuid });
|
|
73
|
+
const { projectId, tcs } = await fetchScenarioInfo(client, scenarioId);
|
|
74
|
+
const environmentUrl = opts.environment ? await resolveUrl(client, opts.environment, projectId) : undefined;
|
|
75
|
+
let role = opts.role;
|
|
76
|
+
if (!role) {
|
|
77
|
+
const knownRoles = knownRolesForProject(projectId);
|
|
78
|
+
const tcInfo = tcs.find((t) => t.testCaseUuid === opts.testCaseUuid);
|
|
79
|
+
const inferred = tcInfo ? inferRole(tcInfo, knownRoles) : null;
|
|
80
|
+
if (inferred) {
|
|
81
|
+
role = inferred;
|
|
82
|
+
console.error(`[setup] authenticated as role "${inferred}" (inferred)`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const budget = { ...config.budget, ...(opts.maxTurns ? { maxTurns: Number(opts.maxTurns) } : {}) };
|
|
86
|
+
const startedAt = Date.now();
|
|
87
|
+
const usage = createUsageAccumulator();
|
|
88
|
+
const baseLog = logEvent('');
|
|
89
|
+
let result;
|
|
90
|
+
try {
|
|
91
|
+
result = await generate({
|
|
92
|
+
client,
|
|
93
|
+
adapter,
|
|
94
|
+
projectId,
|
|
95
|
+
scenarioId,
|
|
96
|
+
testCaseUuid: opts.testCaseUuid,
|
|
97
|
+
repoPath: opts.repoPath,
|
|
98
|
+
budget,
|
|
99
|
+
commandTimeoutMs: config.commandTimeoutMs,
|
|
100
|
+
filePath: opts.filePath,
|
|
101
|
+
autotestRunId: opts.autotestRunId,
|
|
102
|
+
role,
|
|
103
|
+
environmentUrl,
|
|
104
|
+
onEvent: (e) => {
|
|
105
|
+
baseLog(e);
|
|
106
|
+
if (e.type === 'usage')
|
|
107
|
+
usage.onUsage(e.detail);
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
finally {
|
|
112
|
+
// Audit write happens whether the run succeeded or threw — see
|
|
113
|
+
// @appliqation/agent-core's audit/sink.ts: safeRecord() (used
|
|
114
|
+
// inside recordGenerateRun) never lets a failed/unreachable audit
|
|
115
|
+
// sink affect this process's real outcome.
|
|
116
|
+
await recordGenerateRun({ sink: config.auditSink, startedAt, endedAt: Date.now(), model: resolveModel(), usage: usage.totals(), testCaseUuid: opts.testCaseUuid, result });
|
|
117
|
+
}
|
|
118
|
+
if (!json) {
|
|
119
|
+
console.log('\n=== Report ===\n');
|
|
120
|
+
console.log(result.report);
|
|
121
|
+
console.error(`\n(${result.turns} turns, budget exceeded: ${result.budgetExceeded})`);
|
|
122
|
+
}
|
|
123
|
+
const summary = {
|
|
124
|
+
testCaseUuid: opts.testCaseUuid,
|
|
125
|
+
writtenPaths: result.writtenPaths,
|
|
126
|
+
testRan: result.testRun.ran,
|
|
127
|
+
verified: result.testRun.ok,
|
|
128
|
+
report: result.report,
|
|
129
|
+
};
|
|
130
|
+
if (json)
|
|
131
|
+
printJsonSummary(summary);
|
|
132
|
+
else
|
|
133
|
+
printHumanSummary(summary);
|
|
134
|
+
process.exitCode = exitCodeFor(summary);
|
|
135
|
+
});
|
|
136
|
+
program.parseAsync(process.argv);
|
|
137
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// --json/--ci's renderer, matching appliqation-autotest's output.ts shape.
|
|
2
|
+
// exitCodeFor() never trusts the model's own report text — only
|
|
3
|
+
// GenerateResult.testRun.ok, which is derived from a real execFile exit
|
|
4
|
+
// code, decides success. A file that was never actually run — or was run
|
|
5
|
+
// before its last edit — is not a pass, no matter what the report claims.
|
|
6
|
+
export function printJsonSummary(summary) {
|
|
7
|
+
console.log(JSON.stringify(summary, null, 2));
|
|
8
|
+
}
|
|
9
|
+
export function printHumanSummary(summary) {
|
|
10
|
+
console.log(`\n=== Test case ${summary.testCaseUuid} ===\n`);
|
|
11
|
+
if (summary.writtenPaths.length === 0) {
|
|
12
|
+
console.log(' No files were written.');
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
for (const p of summary.writtenPaths)
|
|
16
|
+
console.log(` wrote ${p}`);
|
|
17
|
+
}
|
|
18
|
+
if (!summary.testRan) {
|
|
19
|
+
console.log('\n Never actually ran `npx playwright test` — not verified.');
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
console.log(`\n Verification: ${summary.verified ? 'PASSED' : 'FAILED (or stale — run predates the last edit)'}`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/** 1 unless the file was actually run — via a real, execFile-reported exit code — after its last edit, and passed. */
|
|
26
|
+
export function exitCodeFor(summary) {
|
|
27
|
+
return summary.testRan && summary.verified ? 0 : 1;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.js","sourceRoot":"","sources":["../../src/cli/output.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,gEAAgE;AAChE,wEAAwE;AACxE,yEAAyE;AACzE,0EAA0E;AAU1E,MAAM,UAAU,gBAAgB,CAAC,OAAwB;IACvD,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AAChD,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,OAAwB;IACxD,OAAO,CAAC,GAAG,CAAC,mBAAmB,OAAO,CAAC,YAAY,QAAQ,CAAC,CAAC;IAC7D,IAAI,OAAO,CAAC,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtC,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAC;IAC1C,CAAC;SAAM,CAAC;QACN,KAAK,MAAM,CAAC,IAAI,OAAO,CAAC,YAAY;YAAE,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;IACrE,CAAC;IACD,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;QACrB,OAAO,CAAC,GAAG,CAAC,8DAA8D,CAAC,CAAC;IAC9E,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,qBAAqB,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,gDAAgD,EAAE,CAAC,CAAC;IACrH,CAAC;AACH,CAAC;AAED,sHAAsH;AACtH,MAAM,UAAU,WAAW,CAAC,OAAwB;IAClD,OAAO,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACrD,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import 'dotenv/config';
|
|
2
|
+
import { DEFAULT_ANTHROPIC_MODEL, DEFAULT_OPENAI_MODEL } from '@appliqation/agent-core/providers';
|
|
3
|
+
import { required, optional } from '@appliqation/agent-core/config';
|
|
4
|
+
import { resolveAuditSink } from '@appliqation/agent-core/audit';
|
|
5
|
+
export const config = {
|
|
6
|
+
appqOrigin: optional('APPQ_ORIGIN') ?? 'https://appq.appliqation.io',
|
|
7
|
+
appqApiKey: () => required('APPQ_API_KEY'),
|
|
8
|
+
anthropicApiKey: optional('ANTHROPIC_API_KEY'),
|
|
9
|
+
openaiApiKey: optional('OPENAI_API_KEY'),
|
|
10
|
+
anthropicModel: optional('ANTHROPIC_MODEL'),
|
|
11
|
+
openaiModel: optional('OPENAI_MODEL'),
|
|
12
|
+
anthropicMaxTokens: Number(optional('ANTHROPIC_MAX_TOKENS') ?? 8192),
|
|
13
|
+
openaiMaxOutputTokens: Number(optional('OPENAI_MAX_OUTPUT_TOKENS') ?? 8192),
|
|
14
|
+
// A single, generous budget — unlike appliqation-autotest there is no
|
|
15
|
+
// executor/validator split here, just one drafting-and-verifying pass that
|
|
16
|
+
// may need several rounds of "run the test, read the failure, fix it".
|
|
17
|
+
budget: {
|
|
18
|
+
maxCalls: Number(optional('BUDGET_MAX_CALLS') ?? 60),
|
|
19
|
+
// This agent never calls browser_navigate (no browser tools offered at
|
|
20
|
+
// all), so BudgetTracker's page counter never increments — a large,
|
|
21
|
+
// effectively-unreachable cap rather than 0, since 0 would trip
|
|
22
|
+
// immediately (exceeded() checks pages >= maxPages, and 0 >= 0 is true).
|
|
23
|
+
maxPages: Number(optional('BUDGET_MAX_PAGES') ?? 999_999),
|
|
24
|
+
maxMillis: Number(optional('BUDGET_MAX_MILLIS') ?? 20 * 60 * 1000),
|
|
25
|
+
maxTurns: Number(optional('BUDGET_MAX_TURNS') ?? 60),
|
|
26
|
+
},
|
|
27
|
+
// Wall-clock cap per run_command invocation (npm install / playwright test
|
|
28
|
+
// can each legitimately take a while) — separate from the overall budget.
|
|
29
|
+
commandTimeoutMs: Number(optional('COMMAND_TIMEOUT_MS') ?? 5 * 60 * 1000),
|
|
30
|
+
// Observability, entirely opt-in — see @appliqation/agent-core's audit/sink.ts.
|
|
31
|
+
auditSink: resolveAuditSink({
|
|
32
|
+
auditMongoUri: optional('AUDIT_MONGO_URI'),
|
|
33
|
+
auditMongoDb: optional('AUDIT_MONGO_DB'),
|
|
34
|
+
auditMongoCollection: optional('AUDIT_MONGO_COLLECTION'),
|
|
35
|
+
auditJsonlPath: optional('AUDIT_JSONL_PATH'),
|
|
36
|
+
}),
|
|
37
|
+
};
|
|
38
|
+
export function resolveProvider() {
|
|
39
|
+
if (config.anthropicApiKey)
|
|
40
|
+
return 'anthropic';
|
|
41
|
+
if (config.openaiApiKey)
|
|
42
|
+
return 'openai';
|
|
43
|
+
throw new Error('Set ANTHROPIC_API_KEY or OPENAI_API_KEY');
|
|
44
|
+
}
|
|
45
|
+
export function resolveModel() {
|
|
46
|
+
const provider = resolveProvider();
|
|
47
|
+
return provider === 'anthropic' ? (config.anthropicModel ?? DEFAULT_ANTHROPIC_MODEL) : (config.openaiModel ?? DEFAULT_OPENAI_MODEL);
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=env.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env.js","sourceRoot":"","sources":["../../src/config/env.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAC;AACvB,OAAO,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAClG,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAEjE,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,UAAU,EAAE,QAAQ,CAAC,aAAa,CAAC,IAAI,6BAA6B;IACpE,UAAU,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,cAAc,CAAC;IAC1C,eAAe,EAAE,QAAQ,CAAC,mBAAmB,CAAC;IAC9C,YAAY,EAAE,QAAQ,CAAC,gBAAgB,CAAC;IACxC,cAAc,EAAE,QAAQ,CAAC,iBAAiB,CAAC;IAC3C,WAAW,EAAE,QAAQ,CAAC,cAAc,CAAC;IACrC,kBAAkB,EAAE,MAAM,CAAC,QAAQ,CAAC,sBAAsB,CAAC,IAAI,IAAI,CAAC;IACpE,qBAAqB,EAAE,MAAM,CAAC,QAAQ,CAAC,0BAA0B,CAAC,IAAI,IAAI,CAAC;IAC3E,sEAAsE;IACtE,2EAA2E;IAC3E,uEAAuE;IACvE,MAAM,EAAE;QACN,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,kBAAkB,CAAC,IAAI,EAAE,CAAC;QACpD,uEAAuE;QACvE,oEAAoE;QACpE,gEAAgE;QAChE,yEAAyE;QACzE,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,kBAAkB,CAAC,IAAI,OAAO,CAAC;QACzD,SAAS,EAAE,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;QAClE,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,kBAAkB,CAAC,IAAI,EAAE,CAAC;KACrD;IACD,2EAA2E;IAC3E,0EAA0E;IAC1E,gBAAgB,EAAE,MAAM,CAAC,QAAQ,CAAC,oBAAoB,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;IAEzE,gFAAgF;IAChF,SAAS,EAAE,gBAAgB,CAAC;QAC1B,aAAa,EAAE,QAAQ,CAAC,iBAAiB,CAAC;QAC1C,YAAY,EAAE,QAAQ,CAAC,gBAAgB,CAAC;QACxC,oBAAoB,EAAE,QAAQ,CAAC,wBAAwB,CAAC;QACxD,cAAc,EAAE,QAAQ,CAAC,kBAAkB,CAAC;KAC7C,CAAC;CACH,CAAC;AAEF,MAAM,UAAU,eAAe;IAC7B,IAAI,MAAM,CAAC,eAAe;QAAE,OAAO,WAAW,CAAC;IAC/C,IAAI,MAAM,CAAC,YAAY;QAAE,OAAO,QAAQ,CAAC;IACzC,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;AAC7D,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,MAAM,QAAQ,GAAG,eAAe,EAAE,CAAC;IACnC,OAAO,QAAQ,KAAK,WAAW,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,cAAc,IAAI,uBAAuB,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,IAAI,oBAAoB,CAAC,CAAC;AACtI,CAAC"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// Calls the (now enriched, see appq's AutomatePrompt.php) appq:automate
|
|
2
|
+
// workflow through the shared engine, offering it exactly two tool
|
|
3
|
+
// surfaces: read-only appq context tools (get_scenario, get_run_evidence,
|
|
4
|
+
// get_defect_context, ...) and real coding tools (read_file/write_file/
|
|
5
|
+
// list_directory/run_command, scoped to one repo). No appq write tool, no
|
|
6
|
+
// git operation, no browser — the prompt's own Phase 0/3/5/6 already
|
|
7
|
+
// describe exactly the bootstrap-draft-verify loop needed, and Phase 4
|
|
8
|
+
// (optional live browsing) is skipped outright once autotest_run_id is
|
|
9
|
+
// passed (see AutomatePrompt.php's autonomous mode).
|
|
10
|
+
import { fetchAppqToolDefs, createGatedAppqDispatcher, runWorkflow, } from '@appliqation/agent-core';
|
|
11
|
+
import { READONLY_CONTEXT_TOOLS } from '../tools/safety.js';
|
|
12
|
+
import { CODING_TOOL_DEFS, CodingTools } from '../tools/codingTools.js';
|
|
13
|
+
function seedMessage(opts) {
|
|
14
|
+
const lines = [
|
|
15
|
+
`Project ID: ${opts.projectId}`,
|
|
16
|
+
`Scenario ID: ${opts.scenarioId}`,
|
|
17
|
+
`Test case UUID: ${opts.testCaseUuid}`,
|
|
18
|
+
`Target repo root (every file/command tool call is scoped here): ${opts.repoPath}`,
|
|
19
|
+
];
|
|
20
|
+
if (opts.filePath)
|
|
21
|
+
lines.push(`Target spec file: ${opts.filePath}`);
|
|
22
|
+
if (opts.role) {
|
|
23
|
+
lines.push(`This TC authenticates as role "${opts.role}" — if the target app is gated, the generated script ` +
|
|
24
|
+
`should call setupAuth({ project_id: ${opts.projectId}, role: "${opts.role}" }) per the contract rule.`);
|
|
25
|
+
}
|
|
26
|
+
if (opts.environmentUrl) {
|
|
27
|
+
lines.push(`Target app base URL (for playwright.config's baseURL, only if you need to bootstrap one): ${opts.environmentUrl}`);
|
|
28
|
+
}
|
|
29
|
+
lines.push('Begin now — start with get_scenario.');
|
|
30
|
+
return lines.join('\n');
|
|
31
|
+
}
|
|
32
|
+
export async function generate(opts) {
|
|
33
|
+
const coding = new CodingTools(opts.repoPath, opts.commandTimeoutMs);
|
|
34
|
+
const appqToolDefs = await fetchAppqToolDefs(opts.client, READONLY_CONTEXT_TOOLS);
|
|
35
|
+
const gatedAppq = createGatedAppqDispatcher(opts.client, READONLY_CONTEXT_TOOLS);
|
|
36
|
+
const codingToolNames = new Set(CODING_TOOL_DEFS.map((t) => t.name));
|
|
37
|
+
const dispatch = async (name, args) => {
|
|
38
|
+
if (codingToolNames.has(name))
|
|
39
|
+
return coding.dispatch(name, args);
|
|
40
|
+
return gatedAppq(name, args);
|
|
41
|
+
};
|
|
42
|
+
const promptArgs = {
|
|
43
|
+
project_id: opts.projectId,
|
|
44
|
+
scenario_id: opts.scenarioId,
|
|
45
|
+
test_case_uuid: opts.testCaseUuid,
|
|
46
|
+
};
|
|
47
|
+
if (opts.filePath)
|
|
48
|
+
promptArgs.file_path = opts.filePath;
|
|
49
|
+
if (opts.autotestRunId)
|
|
50
|
+
promptArgs.autotest_run_id = opts.autotestRunId;
|
|
51
|
+
const loopResult = await runWorkflow({
|
|
52
|
+
source: { kind: 'appq', name: 'appq:automate', args: promptArgs },
|
|
53
|
+
fetchPrompt: opts.client.fetchPrompt,
|
|
54
|
+
seedMessage: seedMessage(opts),
|
|
55
|
+
tools: [...appqToolDefs, ...CODING_TOOL_DEFS],
|
|
56
|
+
dispatch,
|
|
57
|
+
adapter: opts.adapter,
|
|
58
|
+
budget: opts.budget,
|
|
59
|
+
onEvent: opts.onEvent,
|
|
60
|
+
});
|
|
61
|
+
const writtenPaths = coding.getWrittenPaths();
|
|
62
|
+
const lastTestRun = coding.lastPlaywrightTestRun();
|
|
63
|
+
const lastWriteAt = writtenPaths.size > 0 ? Math.max(...writtenPaths.values()) : 0;
|
|
64
|
+
const verifiedAfterLastWrite = lastTestRun !== null && lastTestRun.ok && lastTestRun.timestamp >= lastWriteAt;
|
|
65
|
+
return {
|
|
66
|
+
report: loopResult.report,
|
|
67
|
+
turns: loopResult.turns,
|
|
68
|
+
budgetExceeded: loopResult.budgetExceeded,
|
|
69
|
+
writtenPaths: [...writtenPaths.keys()],
|
|
70
|
+
testRun: {
|
|
71
|
+
ran: lastTestRun !== null,
|
|
72
|
+
ok: verifiedAfterLastWrite,
|
|
73
|
+
exitCode: lastTestRun?.exitCode ?? null,
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=generate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate.js","sourceRoot":"","sources":["../../src/orchestrator/generate.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,mEAAmE;AACnE,0EAA0E;AAC1E,wEAAwE;AACxE,0EAA0E;AAC1E,qEAAqE;AACrE,uEAAuE;AACvE,uEAAuE;AACvE,qDAAqD;AAErD,OAAO,EACL,iBAAiB,EACjB,yBAAyB,EACzB,WAAW,GAKZ,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AA0CxE,SAAS,WAAW,CAAC,IAAqB;IACxC,MAAM,KAAK,GAAG;QACZ,eAAe,IAAI,CAAC,SAAS,EAAE;QAC/B,gBAAgB,IAAI,CAAC,UAAU,EAAE;QACjC,mBAAmB,IAAI,CAAC,YAAY,EAAE;QACtC,mEAAmE,IAAI,CAAC,QAAQ,EAAE;KACnF,CAAC;IACF,IAAI,IAAI,CAAC,QAAQ;QAAE,KAAK,CAAC,IAAI,CAAC,qBAAqB,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;IACpE,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACd,KAAK,CAAC,IAAI,CACR,kCAAkC,IAAI,CAAC,IAAI,uDAAuD;YAChG,uCAAuC,IAAI,CAAC,SAAS,YAAY,IAAI,CAAC,IAAI,6BAA6B,CAC1G,CAAC;IACJ,CAAC;IACD,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;QACxB,KAAK,CAAC,IAAI,CAAC,6FAA6F,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC;IACjI,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,sCAAsC,CAAC,CAAC;IACnD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,IAAqB;IAClD,MAAM,MAAM,GAAG,IAAI,WAAW,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,gBAAgB,CAAC,CAAC;IACrE,MAAM,YAAY,GAAG,MAAM,iBAAiB,CAAC,IAAI,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC;IAClF,MAAM,SAAS,GAAG,yBAAyB,CAAC,IAAI,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC;IAEjF,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IACrE,MAAM,QAAQ,GAAmB,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE;QACpD,IAAI,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,OAAO,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAClE,OAAO,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAC/B,CAAC,CAAC;IAEF,MAAM,UAAU,GAA4B;QAC1C,UAAU,EAAE,IAAI,CAAC,SAAS;QAC1B,WAAW,EAAE,IAAI,CAAC,UAAU;QAC5B,cAAc,EAAE,IAAI,CAAC,YAAY;KAClC,CAAC;IACF,IAAI,IAAI,CAAC,QAAQ;QAAE,UAAU,CAAC,SAAS,GAAG,IAAI,CAAC,QAAQ,CAAC;IACxD,IAAI,IAAI,CAAC,aAAa;QAAE,UAAU,CAAC,eAAe,GAAG,IAAI,CAAC,aAAa,CAAC;IAExE,MAAM,UAAU,GAAG,MAAM,WAAW,CAAC;QACnC,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,UAAU,EAAE;QACjE,WAAW,EAAE,IAAI,CAAC,MAAM,CAAC,WAAW;QACpC,WAAW,EAAE,WAAW,CAAC,IAAI,CAAC;QAC9B,KAAK,EAAE,CAAC,GAAG,YAAY,EAAE,GAAG,gBAAgB,CAAC;QAC7C,QAAQ;QACR,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,OAAO,EAAE,IAAI,CAAC,OAAO;KACtB,CAAC,CAAC;IAEH,MAAM,YAAY,GAAG,MAAM,CAAC,eAAe,EAAE,CAAC;IAC9C,MAAM,WAAW,GAAG,MAAM,CAAC,qBAAqB,EAAE,CAAC;IACnD,MAAM,WAAW,GAAG,YAAY,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,YAAY,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACnF,MAAM,sBAAsB,GAAG,WAAW,KAAK,IAAI,IAAI,WAAW,CAAC,EAAE,IAAI,WAAW,CAAC,SAAS,IAAI,WAAW,CAAC;IAE9G,OAAO;QACL,MAAM,EAAE,UAAU,CAAC,MAAM;QACzB,KAAK,EAAE,UAAU,CAAC,KAAK;QACvB,cAAc,EAAE,UAAU,CAAC,cAAc;QACzC,YAAY,EAAE,CAAC,GAAG,YAAY,CAAC,IAAI,EAAE,CAAC;QACtC,OAAO,EAAE;YACP,GAAG,EAAE,WAAW,KAAK,IAAI;YACzB,EAAE,EAAE,sBAAsB;YAC1B,QAAQ,EAAE,WAAW,EAAE,QAAQ,IAAI,IAAI;SACxC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
// The capability appq:automate's own prompt (Phase 0/3/5/6) assumes a full
|
|
2
|
+
// coding-agent session has: read/write a local repo, and run real shell
|
|
3
|
+
// commands to bootstrap Playwright and actually verify the generated spec.
|
|
4
|
+
// Every path is scoped to repoPath (no traversal out of it); every command
|
|
5
|
+
// goes through commandGate.ts's allowlist AND is spawned via execFile with
|
|
6
|
+
// an explicit argv array — never a shell string — so the OS never parses
|
|
7
|
+
// arguments as shell syntax in the first place.
|
|
8
|
+
import { readFile, writeFile, mkdir, readdir } from 'node:fs/promises';
|
|
9
|
+
import { resolve, dirname, relative, isAbsolute } from 'node:path';
|
|
10
|
+
import { execFile } from 'node:child_process';
|
|
11
|
+
import { assertCommandAllowed } from './commandGate.js';
|
|
12
|
+
// Hand-rolled rather than util.promisify(execFile) — Node's real execFile
|
|
13
|
+
// resolves {stdout, stderr} via an internal util.promisify.custom symbol
|
|
14
|
+
// that a mocked module in tests won't have, so promisify(execFile) silently
|
|
15
|
+
// degrades to generic single-value promisify (stdout only, stderr dropped)
|
|
16
|
+
// under mocking. This wrapper has one obvious behavior either way.
|
|
17
|
+
function execFileAsync(command, args, options) {
|
|
18
|
+
return new Promise((resolvePromise, rejectPromise) => {
|
|
19
|
+
execFile(command, args, options, (error, stdout, stderr) => {
|
|
20
|
+
if (error) {
|
|
21
|
+
const failure = error;
|
|
22
|
+
failure.stdout = String(stdout ?? '');
|
|
23
|
+
failure.stderr = String(stderr ?? '');
|
|
24
|
+
rejectPromise(failure);
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
resolvePromise({ stdout: String(stdout ?? ''), stderr: String(stderr ?? '') });
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
export const CODING_TOOL_DEFS = [
|
|
33
|
+
{
|
|
34
|
+
name: 'read_file',
|
|
35
|
+
description: 'Read a text file, relative to the target repo root.',
|
|
36
|
+
inputSchema: { type: 'object', properties: { path: { type: 'string' } }, required: ['path'] },
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: 'write_file',
|
|
40
|
+
description: 'Write (create or overwrite) a text file, relative to the target repo root. Creates parent ' +
|
|
41
|
+
'directories as needed. This is how you write the generated spec and any config files.',
|
|
42
|
+
inputSchema: {
|
|
43
|
+
type: 'object',
|
|
44
|
+
properties: { path: { type: 'string' }, content: { type: 'string' } },
|
|
45
|
+
required: ['path', 'content'],
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: 'list_directory',
|
|
50
|
+
description: 'List entries (files and directories) in a directory, relative to the target repo root.',
|
|
51
|
+
inputSchema: { type: 'object', properties: { path: { type: 'string' } }, required: [] },
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: 'run_command',
|
|
55
|
+
description: 'Run an allowlisted shell command (npm init/install -D, npx playwright install/--version/test, ' +
|
|
56
|
+
'node --version, git status/diff) in the target repo. Anything outside that allowlist is refused ' +
|
|
57
|
+
'before it runs. Use this for Phase 0 bootstrap and Phase 6 verification — never claim a test ' +
|
|
58
|
+
'passed without actually running it here.',
|
|
59
|
+
inputSchema: {
|
|
60
|
+
type: 'object',
|
|
61
|
+
properties: {
|
|
62
|
+
command: { type: 'string', description: 'The binary, e.g. "npm", "npx", "node", "git".' },
|
|
63
|
+
args: { type: 'array', items: { type: 'string' }, description: 'Argv, one element per argument.' },
|
|
64
|
+
},
|
|
65
|
+
required: ['command', 'args'],
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
];
|
|
69
|
+
/** Wraps a real filesystem + shell surface, scoped to one repo root, tracking what actually happened. */
|
|
70
|
+
export class CodingTools {
|
|
71
|
+
repoPath;
|
|
72
|
+
commandTimeoutMs;
|
|
73
|
+
writtenPaths = new Map(); // relative path -> last-written timestamp
|
|
74
|
+
commandHistory = [];
|
|
75
|
+
constructor(repoPath, commandTimeoutMs) {
|
|
76
|
+
this.repoPath = repoPath;
|
|
77
|
+
this.commandTimeoutMs = commandTimeoutMs;
|
|
78
|
+
}
|
|
79
|
+
resolveScoped(relPath) {
|
|
80
|
+
const resolved = resolve(this.repoPath, relPath);
|
|
81
|
+
const rel = relative(this.repoPath, resolved);
|
|
82
|
+
if (rel.startsWith('..') || isAbsolute(rel)) {
|
|
83
|
+
throw new Error(`Path "${relPath}" escapes the target repo root — refusing.`);
|
|
84
|
+
}
|
|
85
|
+
return resolved;
|
|
86
|
+
}
|
|
87
|
+
/** Relative paths written so far, each with the timestamp of its most recent write. */
|
|
88
|
+
getWrittenPaths() {
|
|
89
|
+
return new Map(this.writtenPaths);
|
|
90
|
+
}
|
|
91
|
+
getCommandHistory() {
|
|
92
|
+
return [...this.commandHistory];
|
|
93
|
+
}
|
|
94
|
+
/** The most recent `npx playwright test` invocation's real, execFile-reported outcome — or null if none ran. */
|
|
95
|
+
lastPlaywrightTestRun() {
|
|
96
|
+
for (let i = this.commandHistory.length - 1; i >= 0; i--) {
|
|
97
|
+
const r = this.commandHistory[i];
|
|
98
|
+
if (r.command === 'npx' && r.args[0] === 'playwright' && r.args[1] === 'test')
|
|
99
|
+
return r;
|
|
100
|
+
}
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
async dispatch(name, args) {
|
|
104
|
+
switch (name) {
|
|
105
|
+
case 'read_file': {
|
|
106
|
+
const rawPath = String(args.path ?? '');
|
|
107
|
+
try {
|
|
108
|
+
const content = await readFile(this.resolveScoped(rawPath), 'utf-8');
|
|
109
|
+
return {
|
|
110
|
+
ok: true,
|
|
111
|
+
text: content.length > 50_000 ? `${content.slice(0, 50_000)}\n... (truncated)` : content,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
catch (err) {
|
|
115
|
+
return { ok: false, text: `Could not read "${rawPath}": ${err.message}` };
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
case 'write_file': {
|
|
119
|
+
const rawPath = String(args.path ?? '');
|
|
120
|
+
const content = String(args.content ?? '');
|
|
121
|
+
const resolved = this.resolveScoped(rawPath);
|
|
122
|
+
await mkdir(dirname(resolved), { recursive: true });
|
|
123
|
+
await writeFile(resolved, content, 'utf-8');
|
|
124
|
+
this.writtenPaths.set(rawPath, Date.now());
|
|
125
|
+
return { ok: true, text: `Wrote ${content.length} bytes to ${rawPath}` };
|
|
126
|
+
}
|
|
127
|
+
case 'list_directory': {
|
|
128
|
+
const rawPath = String(args.path ?? '.');
|
|
129
|
+
try {
|
|
130
|
+
const entries = await readdir(this.resolveScoped(rawPath), { withFileTypes: true });
|
|
131
|
+
const lines = entries.map((e) => `${e.isDirectory() ? 'dir ' : 'file'} ${e.name}`).sort();
|
|
132
|
+
return { ok: true, text: lines.join('\n') || '(empty directory)' };
|
|
133
|
+
}
|
|
134
|
+
catch (err) {
|
|
135
|
+
return { ok: false, text: `Could not list "${rawPath}": ${err.message}` };
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
case 'run_command': {
|
|
139
|
+
const command = String(args.command ?? '');
|
|
140
|
+
const cmdArgs = Array.isArray(args.args) ? args.args.map(String) : [];
|
|
141
|
+
try {
|
|
142
|
+
assertCommandAllowed(command, cmdArgs);
|
|
143
|
+
}
|
|
144
|
+
catch (err) {
|
|
145
|
+
return { ok: false, text: err.message };
|
|
146
|
+
}
|
|
147
|
+
return this.runAllowed(command, cmdArgs);
|
|
148
|
+
}
|
|
149
|
+
default:
|
|
150
|
+
return { ok: false, text: `Unknown coding tool "${name}"` };
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
async runAllowed(command, cmdArgs) {
|
|
154
|
+
try {
|
|
155
|
+
const { stdout, stderr } = await execFileAsync(command, cmdArgs, {
|
|
156
|
+
cwd: this.repoPath,
|
|
157
|
+
timeout: this.commandTimeoutMs,
|
|
158
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
159
|
+
});
|
|
160
|
+
this.commandHistory.push({ command, args: cmdArgs, exitCode: 0, ok: true, timestamp: Date.now() });
|
|
161
|
+
const out = `${stdout}${stderr ? `\n[stderr]\n${stderr}` : ''}`.trim();
|
|
162
|
+
return { ok: true, text: out.length > 20_000 ? out.slice(-20_000) : out || '(no output, exit 0)' };
|
|
163
|
+
}
|
|
164
|
+
catch (err) {
|
|
165
|
+
const e = err;
|
|
166
|
+
const exitCode = typeof e.code === 'number' ? e.code : null;
|
|
167
|
+
this.commandHistory.push({ command, args: cmdArgs, exitCode, ok: false, timestamp: Date.now() });
|
|
168
|
+
const out = `${e.stdout ?? ''}\n[stderr]\n${e.stderr ?? e.message}`.trim();
|
|
169
|
+
return { ok: false, text: out.length > 20_000 ? out.slice(-20_000) : out };
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
//# sourceMappingURL=codingTools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codingTools.js","sourceRoot":"","sources":["../../src/tools/codingTools.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// This agent's ONE genuinely new, more-dangerous capability compared to the
|
|
2
|
+
// rest of the family: it can run real shell commands (npm/npx/git), needed
|
|
3
|
+
// to bootstrap a target repo's Playwright setup and actually execute the
|
|
4
|
+
// generated spec — appq:automate's Phase 0/5/6 assume exactly this.
|
|
5
|
+
//
|
|
6
|
+
// Hardcoded, non-negotiable invariant (same class of thing as
|
|
7
|
+
// destructiveActionGate.ts / the appq tool allowlists elsewhere in this
|
|
8
|
+
// family): an explicit allowlist of (command, argv-shape), checked BEFORE
|
|
9
|
+
// execution, never widened by prompt text. The actual execution path
|
|
10
|
+
// (codingTools.ts) uses child_process.execFile with an argv array — never a
|
|
11
|
+
// shell string — so even an allowed command can't smuggle a second command
|
|
12
|
+
// via `;`/`&&`/backticks; the OS never parses the arguments as shell syntax
|
|
13
|
+
// in the first place. The allowlist is defense in depth on top of that, not
|
|
14
|
+
// the only layer.
|
|
15
|
+
const PACKAGE_SPEC_RE = /^@?[a-z0-9][a-z0-9._/-]*(@[\w.\-^~]+)?$/i;
|
|
16
|
+
function isPlainPackageSpec(spec) {
|
|
17
|
+
return PACKAGE_SPEC_RE.test(spec) && !spec.includes('..') && !spec.startsWith('-');
|
|
18
|
+
}
|
|
19
|
+
/** A path-shaped argument (a test file glob/relative path) — no traversal, no shell metacharacters. */
|
|
20
|
+
function isSafePathArg(arg) {
|
|
21
|
+
return !arg.includes('..') && !/[;&|`$(){}<>]/.test(arg) && !arg.startsWith('-');
|
|
22
|
+
}
|
|
23
|
+
const ALLOWED_COMMANDS = {
|
|
24
|
+
npm: (args) => {
|
|
25
|
+
if (args.length === 2 && args[0] === 'init' && args[1] === '-y')
|
|
26
|
+
return true;
|
|
27
|
+
// npm install -D <package...> — dev deps only, plain package specs only.
|
|
28
|
+
if (args.length >= 3 && args[0] === 'install' && args[1] === '-D') {
|
|
29
|
+
return args.slice(2).every(isPlainPackageSpec);
|
|
30
|
+
}
|
|
31
|
+
return false;
|
|
32
|
+
},
|
|
33
|
+
npx: (args) => {
|
|
34
|
+
if (args[0] !== 'playwright')
|
|
35
|
+
return false;
|
|
36
|
+
if (args[1] === '--version' && args.length === 2)
|
|
37
|
+
return true;
|
|
38
|
+
if (args[1] === 'install') {
|
|
39
|
+
// optional browser names (chromium/firefox/webkit) or --with-deps
|
|
40
|
+
return args.slice(2).every((a) => /^(chromium|firefox|webkit|--with-deps)$/.test(a));
|
|
41
|
+
}
|
|
42
|
+
if (args[1] === 'test') {
|
|
43
|
+
// optional --grep "<title>" and/or a relative spec file path
|
|
44
|
+
return args.slice(2).every((a) => isSafePathArg(a) || a === '--grep');
|
|
45
|
+
}
|
|
46
|
+
return false;
|
|
47
|
+
},
|
|
48
|
+
node: (args) => args.length === 1 && args[0] === '--version',
|
|
49
|
+
git: (args) => {
|
|
50
|
+
if (args.length === 1 && args[0] === 'status')
|
|
51
|
+
return true;
|
|
52
|
+
if (args.length === 1 && args[0] === 'diff')
|
|
53
|
+
return true;
|
|
54
|
+
if (args.length === 2 && args[0] === 'diff' && args[1] === '--stat')
|
|
55
|
+
return true;
|
|
56
|
+
return false;
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
export function assertCommandAllowed(command, args) {
|
|
60
|
+
const validator = ALLOWED_COMMANDS[command];
|
|
61
|
+
if (!validator || !validator(args)) {
|
|
62
|
+
throw new Error(`Command "${command} ${args.join(' ')}" is not in the allowlist. This is a hardcoded ` +
|
|
63
|
+
`boundary — no workflow prompt can widen it. Allowed: npm init -y, npm install -D <pkgs>, ` +
|
|
64
|
+
`npx playwright install/--version/test, node --version, git status/diff.`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=commandGate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commandGate.js","sourceRoot":"","sources":["../../src/tools/commandGate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// This agent's own domain knowledge of which appq tools it may touch — the
|
|
2
|
+
// enforcement mechanism (assertToolAllowed / the gated dispatcher) lives in
|
|
3
|
+
// @appliqation/agent-core, shared with every sibling agent; only the
|
|
4
|
+
// allowlist content is local. Zero write tools — not gated behind a flag,
|
|
5
|
+
// genuinely absent from the set. This agent never calls an appq write tool
|
|
6
|
+
// and never performs a git operation; it writes local files only, and a
|
|
7
|
+
// separate future agent is responsible for reviewing/committing/pushing
|
|
8
|
+
// them.
|
|
9
|
+
export const READONLY_CONTEXT_TOOLS = new Set([
|
|
10
|
+
'get_scenario',
|
|
11
|
+
'get_defect_context',
|
|
12
|
+
'get_analytics',
|
|
13
|
+
'get_failure_patterns',
|
|
14
|
+
'get_execution_evidence',
|
|
15
|
+
'get_run_evidence',
|
|
16
|
+
'get_automation_readiness',
|
|
17
|
+
'get_coverage_analysis',
|
|
18
|
+
'get_evidence_summary',
|
|
19
|
+
'get_test_results',
|
|
20
|
+
'get_project_settings',
|
|
21
|
+
'get_validation_targets',
|
|
22
|
+
'search_tests',
|
|
23
|
+
'get_project_test_data',
|
|
24
|
+
]);
|
|
25
|
+
//# sourceMappingURL=safety.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"safety.js","sourceRoot":"","sources":["../../src/tools/safety.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,4EAA4E;AAC5E,qEAAqE;AACrE,0EAA0E;AAC1E,2EAA2E;AAC3E,wEAAwE;AACxE,wEAAwE;AACxE,QAAQ;AAER,MAAM,CAAC,MAAM,sBAAsB,GAAG,IAAI,GAAG,CAAC;IAC5C,cAAc;IACd,oBAAoB;IACpB,eAAe;IACf,sBAAsB;IACtB,wBAAwB;IACxB,kBAAkB;IAClB,0BAA0B;IAC1B,uBAAuB;IACvB,sBAAsB;IACtB,kBAAkB;IAClB,sBAAsB;IACtB,wBAAwB;IACxB,cAAc;IACd,uBAAuB;CACxB,CAAC,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@appliqation/scriptgen",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Standalone agent that generates enterprise-grade Playwright automation scripts for Appliqation test cases, grounded in every available signal (manual/agentic execution evidence, defects, flakiness, sibling test cases) and verified by actually running the result.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"appliqation-scriptgen": "./dist/cli/index.js"
|
|
8
|
+
},
|
|
9
|
+
"engines": {
|
|
10
|
+
"node": ">=20"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist/",
|
|
14
|
+
"README.md",
|
|
15
|
+
"LICENSE"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "tsc -p tsconfig.build.json",
|
|
19
|
+
"dev": "tsx src/cli/index.ts",
|
|
20
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
21
|
+
"lint": "eslint src --ext .ts",
|
|
22
|
+
"test": "vitest run",
|
|
23
|
+
"test:watch": "vitest"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@appliqation/agent-core": "^0.1.0",
|
|
27
|
+
"@appliqation/automation-sdk": "^2.7.0",
|
|
28
|
+
"commander": "^13.1.0",
|
|
29
|
+
"dotenv": "^16.4.7"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/node": "^22.13.10",
|
|
33
|
+
"tsx": "^4.19.3",
|
|
34
|
+
"typescript": "^5.8.2",
|
|
35
|
+
"vitest": "^3.2.7"
|
|
36
|
+
}
|
|
37
|
+
}
|