autokap 1.5.7 → 1.6.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/dist/cli-runner.js
CHANGED
|
@@ -686,6 +686,7 @@ async function uploadResults(config, program, result, runId = randomUUID()) {
|
|
|
686
686
|
healerPatches: sanitizedHealerPatches,
|
|
687
687
|
runResult: strippedRunResult,
|
|
688
688
|
totalDurationMs: result.totalDurationMs,
|
|
689
|
+
detectedAppVersion: result.detectedAppVersion ?? undefined,
|
|
689
690
|
variantSummaries: result.variantResults.map(v => ({
|
|
690
691
|
variantId: v.variantId,
|
|
691
692
|
success: v.success,
|
package/dist/cli.js
CHANGED
|
@@ -240,12 +240,54 @@ program
|
|
|
240
240
|
}
|
|
241
241
|
process.exit(0);
|
|
242
242
|
});
|
|
243
|
+
async function runOutdatedPresetsLocally(opts) {
|
|
244
|
+
if (opts.output) {
|
|
245
|
+
fatal('`--output` is not supported with `--outdated`; run an individual preset when local copies are needed.');
|
|
246
|
+
}
|
|
247
|
+
const config = await requireConfig();
|
|
248
|
+
const search = new URLSearchParams({ freshness: 'outdated' });
|
|
249
|
+
const data = await requestJson(config, `/api/cli/projects/${opts.project}/auto-recapture-presets`, { headers: authHeaders(config) }, 'Failed to list outdated presets', search);
|
|
250
|
+
if (data.presets.length === 0) {
|
|
251
|
+
logger.info(`[capture] No outdated presets found for project ${opts.project}`);
|
|
252
|
+
process.exit(0);
|
|
253
|
+
}
|
|
254
|
+
const { runCapture } = await import('./cli-runner.js');
|
|
255
|
+
const failures = [];
|
|
256
|
+
logger.info(`[capture] Running ${data.presets.length} outdated preset(s)`);
|
|
257
|
+
for (const preset of data.presets) {
|
|
258
|
+
const label = preset.name ? `${preset.name} (${preset.id})` : preset.id;
|
|
259
|
+
logger.info(`[capture] Running outdated preset ${label}`);
|
|
260
|
+
const result = await runCapture({
|
|
261
|
+
presetId: preset.id,
|
|
262
|
+
env: opts.env,
|
|
263
|
+
headed: opts.headed,
|
|
264
|
+
allowUploadFailure: opts.allowUploadFailure,
|
|
265
|
+
dryRun: opts.dry,
|
|
266
|
+
regenerateTts: opts.regenerateTts,
|
|
267
|
+
});
|
|
268
|
+
if (!result.success) {
|
|
269
|
+
failures.push({
|
|
270
|
+
id: preset.id,
|
|
271
|
+
name: preset.name,
|
|
272
|
+
error: result.error ?? result.runResult?.error ?? 'capture failed',
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
if (failures.length > 0) {
|
|
277
|
+
logger.error(`[capture] ${failures.length}/${data.presets.length} outdated preset(s) failed: ${failures.map((failure) => failure.name ?? failure.id).join(', ')}`);
|
|
278
|
+
process.exit(1);
|
|
279
|
+
}
|
|
280
|
+
logger.success(`[capture] ${data.presets.length} outdated preset(s) captured successfully`);
|
|
281
|
+
process.exit(0);
|
|
282
|
+
}
|
|
243
283
|
// ── run command (deterministic capture engine) ───────────────────────
|
|
244
284
|
program
|
|
245
|
-
.command('run
|
|
285
|
+
.command('run [preset-id]')
|
|
246
286
|
.description('Run a capture using the deterministic opcode engine (local Playwright)')
|
|
247
287
|
.option('--headed', 'Show browser window for debugging', false)
|
|
248
288
|
.option('--local', `Use the local AutoKap dev server (${LOCAL_API_BASE_URL})`, false)
|
|
289
|
+
.option('--project <id>', 'Project ID. Required with --outdated.')
|
|
290
|
+
.option('--outdated', 'Run all outdated presets in the project', false)
|
|
249
291
|
.option('--env <name>', "Project environment to capture against. Falls back to the project's default environment when omitted.")
|
|
250
292
|
.option('--allow-upload-failure', 'Keep a successful capture exit code even if artifact upload fails', false)
|
|
251
293
|
.option('--output <dir>', 'Optional output directory for local artifact copies')
|
|
@@ -264,6 +306,19 @@ program
|
|
|
264
306
|
process.env[WS_URL_ENV_VAR] = LOCAL_WS_URL;
|
|
265
307
|
logger.info(`Using local AutoKap dev server: ${LOCAL_API_BASE_URL}`);
|
|
266
308
|
}
|
|
309
|
+
if (opts.outdated) {
|
|
310
|
+
if (!opts.project) {
|
|
311
|
+
fatal('Missing --project <id> for `autokap run --outdated`.');
|
|
312
|
+
}
|
|
313
|
+
if (opts.program) {
|
|
314
|
+
fatal('`--program` cannot be combined with `--outdated`.');
|
|
315
|
+
}
|
|
316
|
+
await runOutdatedPresetsLocally(opts);
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
if (!presetId) {
|
|
320
|
+
fatal('Missing preset ID. Use `autokap run <preset-id>` or `autokap run --outdated --project <project-id>`.');
|
|
321
|
+
}
|
|
267
322
|
const { runLocal } = await import('./cli-runner-local.js');
|
|
268
323
|
await runLocal(presetId, opts);
|
|
269
324
|
});
|
|
@@ -279,6 +334,7 @@ program
|
|
|
279
334
|
.option('--debug', 'Verbose logging: per-substep timing, opcode dumps, recovery strategy traces', false)
|
|
280
335
|
.option('--cloud', 'Cloud runner mode: signals 4+ vCPU available, unblocks the conservative Linux FPS default (8 → 30)', false)
|
|
281
336
|
.option('--preset-ids <ids>', 'Comma-separated preset IDs to capture. When omitted, captures all presets with auto_recapture_enabled=true.')
|
|
337
|
+
.option('--outdated', 'Capture only outdated auto-recapture presets.', false)
|
|
282
338
|
.action(async (opts) => {
|
|
283
339
|
if (opts.debug) {
|
|
284
340
|
const { setDebugEnabled } = await import('./logger.js');
|
|
@@ -393,9 +449,10 @@ program
|
|
|
393
449
|
// When `--preset-ids` is provided, restrict the run to that subset
|
|
394
450
|
// (per-preset recapture launched from the dashboard).
|
|
395
451
|
const presetIdsArg = opts.presetIds?.trim();
|
|
452
|
+
const freshnessSuffix = opts.outdated ? '?freshness=outdated' : '';
|
|
396
453
|
const presetsPath = presetIdsArg
|
|
397
454
|
? `/api/cli/projects/${opts.project}/auto-recapture-presets?preset_ids=${encodeURIComponent(presetIdsArg)}`
|
|
398
|
-
: `/api/cli/projects/${opts.project}/auto-recapture-presets`;
|
|
455
|
+
: `/api/cli/projects/${opts.project}/auto-recapture-presets${freshnessSuffix}`;
|
|
399
456
|
let data;
|
|
400
457
|
try {
|
|
401
458
|
const response = await fetch(buildApiUrl(config, presetsPath), {
|
|
@@ -605,6 +605,13 @@ export interface VariantResult {
|
|
|
605
605
|
durationMs: number;
|
|
606
606
|
/** Artifact buffers produced */
|
|
607
607
|
artifacts: ArtifactResult[];
|
|
608
|
+
/**
|
|
609
|
+
* App version detected on the captured page (meta tag, window global, or
|
|
610
|
+
* data attribute). Sent to the server with telemetry so freshness tracking
|
|
611
|
+
* picks up the version live during the capture instead of waiting for the
|
|
612
|
+
* hourly cron.
|
|
613
|
+
*/
|
|
614
|
+
detectedAppVersion?: string | null;
|
|
608
615
|
error?: string;
|
|
609
616
|
}
|
|
610
617
|
export interface ArtifactResult {
|
|
@@ -725,6 +732,12 @@ export interface RunResult {
|
|
|
725
732
|
* array for `screenshot` and `clip` modes.
|
|
726
733
|
*/
|
|
727
734
|
opcodeTimings: OpcodeTiming[];
|
|
735
|
+
/**
|
|
736
|
+
* First non-null `detectedAppVersion` from the variants. Used by the server
|
|
737
|
+
* telemetry route to bypass the cron-detected version and stamp the preset
|
|
738
|
+
* with the version actually captured on the page.
|
|
739
|
+
*/
|
|
740
|
+
detectedAppVersion?: string | null;
|
|
728
741
|
error?: string;
|
|
729
742
|
}
|
|
730
743
|
export interface WaitCondition {
|
|
@@ -842,6 +855,12 @@ export interface RuntimeAdapter {
|
|
|
842
855
|
buffer: Buffer;
|
|
843
856
|
mimeType: string;
|
|
844
857
|
} | null>;
|
|
858
|
+
/**
|
|
859
|
+
* Read the captured app's version from the live page (meta tag, window
|
|
860
|
+
* global, or data attribute). Mirrors `extractAppVersionFromHtml` server-side
|
|
861
|
+
* and `__AUTOKAP_VERSION__` lookup. Returns null if no marker is present.
|
|
862
|
+
*/
|
|
863
|
+
detectAppVersion?(): Promise<string | null>;
|
|
845
864
|
close(): Promise<void>;
|
|
846
865
|
/** Click an element by semantic target. Falls back to selector if target not found. */
|
|
847
866
|
clickByTarget?(opts: ClickByTargetOptions): Promise<void>;
|
package/dist/opcode-runner.js
CHANGED
|
@@ -147,6 +147,7 @@ export async function executeProgram(program, createAdapter, options = {}) {
|
|
|
147
147
|
const completedVariantResults = variantResults.filter((result) => Boolean(result));
|
|
148
148
|
const aborted = options.abortSignal?.aborted && completedVariantResults.length < program.variants.length;
|
|
149
149
|
const success = !aborted && completedVariantResults.length > 0 && completedVariantResults.every(v => v.success);
|
|
150
|
+
const detectedAppVersion = completedVariantResults.reduce((acc, variantResult) => acc ?? (variantResult.detectedAppVersion ?? null), null);
|
|
150
151
|
return {
|
|
151
152
|
programId: program.presetId,
|
|
152
153
|
success,
|
|
@@ -155,6 +156,7 @@ export async function executeProgram(program, createAdapter, options = {}) {
|
|
|
155
156
|
healerPatches: success ? healerPatches : [], // Only propagate patches on success
|
|
156
157
|
opcodeTimings,
|
|
157
158
|
totalDurationMs: Date.now() - startTime,
|
|
159
|
+
detectedAppVersion,
|
|
158
160
|
error: aborted ? 'aborted' : (success ? undefined : completedVariantResults.find(v => !v.success)?.error),
|
|
159
161
|
};
|
|
160
162
|
}
|
|
@@ -234,12 +236,25 @@ async function executeVariant(program, variant, createAdapter, recoveryChain, te
|
|
|
234
236
|
};
|
|
235
237
|
}
|
|
236
238
|
}
|
|
239
|
+
// Best-effort: read the app version from the live page so the server can
|
|
240
|
+
// stamp the preset with what we actually captured (instead of falling back
|
|
241
|
+
// to whatever the hourly cron last detected).
|
|
242
|
+
let detectedAppVersion = null;
|
|
243
|
+
if (adapter.detectAppVersion) {
|
|
244
|
+
try {
|
|
245
|
+
detectedAppVersion = await adapter.detectAppVersion();
|
|
246
|
+
}
|
|
247
|
+
catch {
|
|
248
|
+
detectedAppVersion = null;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
237
251
|
return {
|
|
238
252
|
variantId: variant.id,
|
|
239
253
|
success: true,
|
|
240
254
|
opcodeResults,
|
|
241
255
|
durationMs: Date.now() - startTime,
|
|
242
256
|
artifacts,
|
|
257
|
+
detectedAppVersion,
|
|
243
258
|
};
|
|
244
259
|
}
|
|
245
260
|
catch (err) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { SupabaseClient } from '@supabase/supabase-js';
|
|
2
|
-
export type CreditUsageType = 'screenshot' | 'clip' | 'video' | '
|
|
2
|
+
export type CreditUsageType = 'screenshot' | 'clip' | 'video' | 'cloud_recapture' | 'ai_chat' | 'studio_creation' | 'studio_iteration';
|
|
3
3
|
export declare function recordCreditUsage(supabase: SupabaseClient, params: {
|
|
4
4
|
userId: string;
|
|
5
5
|
projectId: string | null;
|
|
@@ -27,6 +27,7 @@ export declare class WebPlaywrightLocal implements RuntimeAdapter {
|
|
|
27
27
|
constructor(browser: Browser, recordingDir?: string | undefined);
|
|
28
28
|
navigate(url: string): Promise<void>;
|
|
29
29
|
getCurrentUrl(): Promise<string>;
|
|
30
|
+
detectAppVersion(): Promise<string | null>;
|
|
30
31
|
getAKTree(): Promise<AKTree>;
|
|
31
32
|
getPageSignals(): Promise<VideoPageSignals>;
|
|
32
33
|
click(selector: string, options?: ClickOptions): Promise<void>;
|
|
Binary file
|