@vostride/agent-qa-runner-local 0.2.3 → 0.2.4
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/dist/index.cjs +78 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -31
- package/dist/index.d.ts +14 -31
- package/dist/index.js +79 -24
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.d.ts
CHANGED
|
@@ -17,15 +17,9 @@ interface CachePolicy {
|
|
|
17
17
|
/** `--disable-cache` — short-circuit both reads and writes. */
|
|
18
18
|
disableCache: boolean;
|
|
19
19
|
}
|
|
20
|
-
/**
|
|
21
|
-
* Writes are skipped when the current branch is protected (default ∪ configured)
|
|
22
|
-
* unless `--save-cache` or CI; `--disable-cache` forbids all writes. Reads are not
|
|
23
|
-
* governed here (D-14).
|
|
24
|
-
*/
|
|
25
20
|
declare function shouldWrite(policy: CachePolicy): boolean;
|
|
26
21
|
|
|
27
|
-
|
|
28
|
-
declare const CACHE_SCHEMA_VERSION = 2;
|
|
22
|
+
declare const CACHE_SCHEMA_VERSION = 3;
|
|
29
23
|
interface FsActionCacheConfig {
|
|
30
24
|
/** Cache root (e.g. DEFAULT_AGENT_QA_CACHE_DIR = '.agent-qa/cache'). */
|
|
31
25
|
root: string;
|
|
@@ -35,14 +29,22 @@ interface FsActionCacheConfig {
|
|
|
35
29
|
declare class FsActionCache implements ActionPlanCache {
|
|
36
30
|
private readonly root;
|
|
37
31
|
private readonly policy;
|
|
32
|
+
private readonly skipped;
|
|
38
33
|
constructor(config: FsActionCacheConfig);
|
|
39
34
|
get(key: CacheKey): Promise<CachedPlanValue | null>;
|
|
40
35
|
set(key: CacheKey, value: CachedPlanValue): Promise<void>;
|
|
41
36
|
invalidate(key: CacheKey): Promise<void>;
|
|
37
|
+
private writeEnvelope;
|
|
42
38
|
private branchDir;
|
|
43
39
|
private entryPath;
|
|
44
40
|
private readEntry;
|
|
45
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* Removes every `<root>/<branch-slug>/<16-hex>.json` entry, and a branch directory only when that
|
|
44
|
+
* leaves it empty. Nothing else under `root` is touched, so a cache root that also holds other
|
|
45
|
+
* files keeps them. Returns the number of entries removed.
|
|
46
|
+
*/
|
|
47
|
+
declare function purgeActionCache(root: string): Promise<number>;
|
|
46
48
|
|
|
47
49
|
/** Subdirectory under the runtime root holding the raw storage-state blobs. */
|
|
48
50
|
declare const STORAGE_STATES_SUBDIR = "storage-states";
|
|
@@ -113,11 +115,6 @@ declare class FsHandoffStore implements HandoffStorePort {
|
|
|
113
115
|
constructor(config: FsHandoffStoreConfig);
|
|
114
116
|
save(handoff: CompletedRunHandoff): void;
|
|
115
117
|
}
|
|
116
|
-
/**
|
|
117
|
-
* The fs read leg of the handoff version discipline (D-19): read the single document, parse it,
|
|
118
|
-
* and delegate to the schema package's `guardCompletedRunHandoff`. A newer-than-known schemaVersion
|
|
119
|
-
* surfaces the schema's typed `HandoffVersionError` — this leg re-implements no version rule.
|
|
120
|
-
*/
|
|
121
118
|
declare function loadCompletedRunHandoff(filePath: string): CompletedRunHandoff;
|
|
122
119
|
|
|
123
120
|
/** The single JSON document each run writes its run artifact to. */
|
|
@@ -182,12 +179,6 @@ interface CompressResult {
|
|
|
182
179
|
imageWidth: number;
|
|
183
180
|
imageHeight: number;
|
|
184
181
|
}
|
|
185
|
-
/**
|
|
186
|
-
* The runner `ScreenshotCompressor` port impl (D-08 / Q10). `compress` covers the LLM-planner
|
|
187
|
-
* compression call site; `dimensions` covers the action-space-width metadata fallback done inline
|
|
188
|
-
* via `sharp(screenshot).metadata()` at v1 loop.ts. The loop applies `stampCaptureMode` itself
|
|
189
|
-
* (runner-owned since 352), so this stays purely pixel-level and returns v1's CompressResult shape.
|
|
190
|
-
*/
|
|
191
182
|
declare class SharpScreenshotCompressor implements ScreenshotCompressor {
|
|
192
183
|
private readonly logger?;
|
|
193
184
|
constructor(logger?: ScreenshotCompressorLogger | undefined);
|
|
@@ -212,8 +203,6 @@ interface ImageDimensions {
|
|
|
212
203
|
width: number;
|
|
213
204
|
height: number;
|
|
214
205
|
}
|
|
215
|
-
/** Read image buffer width/height without decoding pixels. Used by mobile adapters for
|
|
216
|
-
* action-space drift assertions (D-06/D-07). */
|
|
217
206
|
declare function getImageDimensions(buffer: Buffer): Promise<ImageDimensions | undefined>;
|
|
218
207
|
declare function flattenOntoWhite(buffer: Buffer): Promise<Buffer>;
|
|
219
208
|
|
|
@@ -288,24 +277,18 @@ declare function readBuildTagMap(opts: {
|
|
|
288
277
|
}): BuildTagMap;
|
|
289
278
|
|
|
290
279
|
interface DockerHookExecutorConfig {
|
|
280
|
+
version: string;
|
|
291
281
|
dockerBin?: string;
|
|
292
282
|
pullPolicy?: 'always' | 'if-not-present' | 'never';
|
|
293
283
|
}
|
|
294
|
-
/**
|
|
295
|
-
* The D-06 hook-execution impl. Constructed once at the CLI composition root and threaded
|
|
296
|
-
* into the runner `runHooks` orchestration behind the {@link HookExecutorPort}. A per-hook
|
|
297
|
-
* timeout resolves a FAILED {@link HookResult} (exit-124-style error), never a throw.
|
|
298
|
-
*/
|
|
299
284
|
declare class DockerHookExecutor implements HookExecutorPort {
|
|
285
|
+
private readonly version;
|
|
300
286
|
private readonly dockerBin;
|
|
301
287
|
private readonly pullPolicy;
|
|
302
|
-
constructor(config
|
|
288
|
+
constructor(config: DockerHookExecutorConfig);
|
|
303
289
|
execute(hook: HookDefinition, opts: HookExecuteOptions): Promise<HookResult>;
|
|
304
290
|
}
|
|
305
|
-
/**
|
|
306
|
-
* Docker availability probe (re-homed from core/src/hooks/sandbox-runner.ts). The CLI
|
|
307
|
-
* `doctor` command consumes this to report whether the hook sandbox can run.
|
|
308
|
-
*/
|
|
291
|
+
/** Docker availability probe; `doctor` reports from it whether the hook sandbox can run. */
|
|
309
292
|
declare function checkDockerAvailable(dockerBin?: string): Promise<boolean>;
|
|
310
293
|
|
|
311
|
-
export { ARTIFACT_FILE, type AlignOptions, BUILD_ORDINALS_FILE, BUILD_TAGS_FILE, type BranchResolverOptions, type BuildOrdinalReservation, BuildOrdinalReservationError, type BuildOrdinalReservationErrorCode, type BuildTagMap, CACHE_SCHEMA_VERSION, type CachePolicy, type CompressOptions, type CompressResult, DockerHookExecutor, type DockerHookExecutorConfig, EVENTS_FILE, FsActionCache, type FsActionCacheConfig, FsEventLog, type FsEventLogConfig, FsEvidenceArtifactStore, type FsEvidenceArtifactStoreConfig, FsHandoffStore, type FsHandoffStoreConfig, FsRunArtifactStore, type FsRunArtifactStoreConfig, FsStorageStateStore, type FsStorageStateStoreConfig, HANDOFF_FILE, type ImageDimensions, type NextBuildOrdinalOptions, RUNS_SUBDIR, RUN_EVIDENCE_BLOBS_SUBDIR, type ReserveBuildOrdinalForProductsOptions, type ReserveBuildOrdinalForProductsResult, type ResolvedBranch, STORAGE_STATES_SUBDIR, type ScreenshotCompressorLogger, SharpScreenshotCompressor, alignToWindow, checkDockerAvailable, flattenOntoWhite, getImageDimensions, latestBuildOrdinal, loadCompletedRunHandoff, nextBuildOrdinal, readBuildTagMap, recordBuildTag, reserveBuildOrdinalForProducts, resolveBranch, resolveBuildTag, shouldWrite };
|
|
294
|
+
export { ARTIFACT_FILE, type AlignOptions, BUILD_ORDINALS_FILE, BUILD_TAGS_FILE, type BranchResolverOptions, type BuildOrdinalReservation, BuildOrdinalReservationError, type BuildOrdinalReservationErrorCode, type BuildTagMap, CACHE_SCHEMA_VERSION, type CachePolicy, type CompressOptions, type CompressResult, DockerHookExecutor, type DockerHookExecutorConfig, EVENTS_FILE, FsActionCache, type FsActionCacheConfig, FsEventLog, type FsEventLogConfig, FsEvidenceArtifactStore, type FsEvidenceArtifactStoreConfig, FsHandoffStore, type FsHandoffStoreConfig, FsRunArtifactStore, type FsRunArtifactStoreConfig, FsStorageStateStore, type FsStorageStateStoreConfig, HANDOFF_FILE, type ImageDimensions, type NextBuildOrdinalOptions, RUNS_SUBDIR, RUN_EVIDENCE_BLOBS_SUBDIR, type ReserveBuildOrdinalForProductsOptions, type ReserveBuildOrdinalForProductsResult, type ResolvedBranch, STORAGE_STATES_SUBDIR, type ScreenshotCompressorLogger, SharpScreenshotCompressor, alignToWindow, checkDockerAvailable, flattenOntoWhite, getImageDimensions, latestBuildOrdinal, loadCompletedRunHandoff, nextBuildOrdinal, purgeActionCache, readBuildTagMap, recordBuildTag, reserveBuildOrdinalForProducts, resolveBranch, resolveBuildTag, shouldWrite };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/fs-action-cache.ts
|
|
2
|
-
import { mkdir, readFile, unlink, writeFile } from "fs/promises";
|
|
2
|
+
import { mkdir, readdir, readFile, rmdir, unlink, writeFile } from "fs/promises";
|
|
3
3
|
import { join as join2 } from "path";
|
|
4
4
|
|
|
5
5
|
// src/branch-resolver.ts
|
|
@@ -79,39 +79,56 @@ function shouldWrite(policy) {
|
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
// src/fs-action-cache.ts
|
|
82
|
-
var CACHE_SCHEMA_VERSION =
|
|
82
|
+
var CACHE_SCHEMA_VERSION = 3;
|
|
83
|
+
var CACHE_ENTRY_FILE_RE = /^[0-9a-f]{16}\.json$/;
|
|
83
84
|
var FsActionCache = class {
|
|
84
85
|
root;
|
|
85
86
|
policy;
|
|
87
|
+
skipped = /* @__PURE__ */ new Set();
|
|
86
88
|
constructor(config) {
|
|
87
89
|
this.root = config.root;
|
|
88
90
|
this.policy = config.policy;
|
|
89
91
|
}
|
|
90
92
|
async get(key) {
|
|
91
|
-
if (this.policy.disableCache) return null;
|
|
93
|
+
if (this.policy.disableCache || this.skipped.has(key)) return null;
|
|
92
94
|
const onCurrent = await this.readEntry(this.policy.currentBranch, key);
|
|
93
|
-
if (onCurrent
|
|
95
|
+
if (onCurrent?.kind === "suppressed") return null;
|
|
96
|
+
if (onCurrent?.kind === "value") return onCurrent.value;
|
|
94
97
|
if (this.policy.currentBranch !== this.policy.defaultBranch) {
|
|
95
|
-
|
|
98
|
+
const onDefault = await this.readEntry(this.policy.defaultBranch, key);
|
|
99
|
+
return onDefault?.kind === "value" ? onDefault.value : null;
|
|
96
100
|
}
|
|
97
101
|
return null;
|
|
98
102
|
}
|
|
99
103
|
async set(key, value) {
|
|
100
104
|
if (!shouldWrite(this.policy)) return;
|
|
101
|
-
|
|
102
|
-
await mkdir(dir, { recursive: true });
|
|
103
|
-
const envelope = { schemaVersion: CACHE_SCHEMA_VERSION, value };
|
|
104
|
-
await writeFile(this.entryPath(dir, key), `${JSON.stringify(envelope, null, 2)}
|
|
105
|
-
`, "utf-8");
|
|
105
|
+
await this.writeEnvelope(key, { schemaVersion: CACHE_SCHEMA_VERSION, value });
|
|
106
106
|
}
|
|
107
107
|
async invalidate(key) {
|
|
108
|
-
|
|
108
|
+
if (!shouldWrite(this.policy)) {
|
|
109
|
+
this.skipped.add(key);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const { currentBranch, defaultBranch } = this.policy;
|
|
113
|
+
if (currentBranch !== defaultBranch) {
|
|
114
|
+
const onDefault = await this.readEntry(defaultBranch, key);
|
|
115
|
+
if (onDefault?.kind === "value") {
|
|
116
|
+
await this.writeEnvelope(key, { schemaVersion: CACHE_SCHEMA_VERSION, suppressed: true });
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
109
120
|
try {
|
|
110
|
-
await unlink(this.entryPath(
|
|
121
|
+
await unlink(this.entryPath(this.branchDir(currentBranch), key));
|
|
111
122
|
} catch (err) {
|
|
112
|
-
if (err
|
|
123
|
+
if (!isMissing(err)) throw err;
|
|
113
124
|
}
|
|
114
125
|
}
|
|
126
|
+
async writeEnvelope(key, envelope) {
|
|
127
|
+
const dir = this.branchDir(this.policy.currentBranch);
|
|
128
|
+
await mkdir(dir, { recursive: true });
|
|
129
|
+
await writeFile(this.entryPath(dir, key), `${JSON.stringify(envelope, null, 2)}
|
|
130
|
+
`, "utf-8");
|
|
131
|
+
}
|
|
115
132
|
branchDir(branch) {
|
|
116
133
|
return join2(this.root, slugBranch(branch));
|
|
117
134
|
}
|
|
@@ -123,7 +140,7 @@ var FsActionCache = class {
|
|
|
123
140
|
try {
|
|
124
141
|
raw = await readFile(this.entryPath(this.branchDir(branch), key), "utf-8");
|
|
125
142
|
} catch (err) {
|
|
126
|
-
if (err
|
|
143
|
+
if (isMissing(err)) return null;
|
|
127
144
|
throw err;
|
|
128
145
|
}
|
|
129
146
|
let envelope;
|
|
@@ -133,9 +150,44 @@ var FsActionCache = class {
|
|
|
133
150
|
return null;
|
|
134
151
|
}
|
|
135
152
|
if (envelope?.schemaVersion !== CACHE_SCHEMA_VERSION) return null;
|
|
136
|
-
|
|
153
|
+
if (envelope.suppressed === true) return { kind: "suppressed" };
|
|
154
|
+
return envelope.value ? { kind: "value", value: envelope.value } : null;
|
|
137
155
|
}
|
|
138
156
|
};
|
|
157
|
+
function isMissing(err) {
|
|
158
|
+
return err.code === "ENOENT";
|
|
159
|
+
}
|
|
160
|
+
async function purgeActionCache(root) {
|
|
161
|
+
let branches;
|
|
162
|
+
try {
|
|
163
|
+
branches = await readdir(root, { withFileTypes: true });
|
|
164
|
+
} catch (err) {
|
|
165
|
+
if (isMissing(err)) return 0;
|
|
166
|
+
throw err;
|
|
167
|
+
}
|
|
168
|
+
let purged = 0;
|
|
169
|
+
for (const branch of branches) {
|
|
170
|
+
if (!branch.isDirectory() || slugBranch(branch.name) !== branch.name) continue;
|
|
171
|
+
const dir = join2(root, branch.name);
|
|
172
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
173
|
+
let removed = 0;
|
|
174
|
+
for (const entry of entries) {
|
|
175
|
+
if (!entry.isFile() || !CACHE_ENTRY_FILE_RE.test(entry.name)) continue;
|
|
176
|
+
try {
|
|
177
|
+
await unlink(join2(dir, entry.name));
|
|
178
|
+
} catch (err) {
|
|
179
|
+
if (!isMissing(err)) throw err;
|
|
180
|
+
}
|
|
181
|
+
removed++;
|
|
182
|
+
}
|
|
183
|
+
purged += removed;
|
|
184
|
+
if (removed > 0 && removed === entries.length) {
|
|
185
|
+
await rmdir(dir).catch(() => {
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return purged;
|
|
190
|
+
}
|
|
139
191
|
|
|
140
192
|
// src/storage-state-store.ts
|
|
141
193
|
import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
|
@@ -849,7 +901,7 @@ import { readFile as readFile3, mkdtemp, mkdir as mkdir3, rm, cp } from "fs/prom
|
|
|
849
901
|
import { join as join10, basename } from "path";
|
|
850
902
|
import { tmpdir } from "os";
|
|
851
903
|
import {
|
|
852
|
-
|
|
904
|
+
hookRunnerImage,
|
|
853
905
|
parseEnvFile,
|
|
854
906
|
AUTH_STATE_HOOK_CONTAINER_STORAGE_STATE_PATH,
|
|
855
907
|
AUTH_STATE_HOOK_STORAGE_STATE_FILENAME,
|
|
@@ -860,9 +912,11 @@ import {
|
|
|
860
912
|
} from "@vostride/agent-qa-runner";
|
|
861
913
|
var DEFAULT_DOCKER_BIN = "docker";
|
|
862
914
|
var DockerHookExecutor = class {
|
|
915
|
+
version;
|
|
863
916
|
dockerBin;
|
|
864
917
|
pullPolicy;
|
|
865
|
-
constructor(config
|
|
918
|
+
constructor(config) {
|
|
919
|
+
this.version = config.version;
|
|
866
920
|
this.dockerBin = config.dockerBin ?? DEFAULT_DOCKER_BIN;
|
|
867
921
|
this.pullPolicy = config.pullPolicy ?? "if-not-present";
|
|
868
922
|
}
|
|
@@ -870,15 +924,15 @@ var DockerHookExecutor = class {
|
|
|
870
924
|
const dockerBin = this.dockerBin;
|
|
871
925
|
const pullPolicy = this.pullPolicy;
|
|
872
926
|
const start = Date.now();
|
|
873
|
-
const image =
|
|
874
|
-
if (pullPolicy === "always" || pullPolicy === "if-not-present") {
|
|
875
|
-
const needsPull = pullPolicy === "always" || !await imageExists(dockerBin, image);
|
|
876
|
-
if (needsPull) {
|
|
877
|
-
await pullImage(dockerBin, image);
|
|
878
|
-
}
|
|
879
|
-
}
|
|
927
|
+
const image = hookRunnerImage(hook.runtime, this.version);
|
|
880
928
|
let workDir;
|
|
881
929
|
try {
|
|
930
|
+
if (pullPolicy === "always" || pullPolicy === "if-not-present") {
|
|
931
|
+
const needsPull = pullPolicy === "always" || !await imageExists(dockerBin, image);
|
|
932
|
+
if (needsPull) {
|
|
933
|
+
await pullImage(dockerBin, image);
|
|
934
|
+
}
|
|
935
|
+
}
|
|
882
936
|
workDir = await mkdtemp(join10(tmpdir(), "agent-qa-hook-"));
|
|
883
937
|
await mkdir3(join10(workDir, "tmp"), { recursive: true });
|
|
884
938
|
await cp(hook.file, join10(workDir, basename(hook.file)));
|
|
@@ -1062,6 +1116,7 @@ export {
|
|
|
1062
1116
|
latestBuildOrdinal,
|
|
1063
1117
|
loadCompletedRunHandoff,
|
|
1064
1118
|
nextBuildOrdinal,
|
|
1119
|
+
purgeActionCache,
|
|
1065
1120
|
readBuildTagMap,
|
|
1066
1121
|
recordBuildTag,
|
|
1067
1122
|
reserveBuildOrdinalForProducts,
|