autokap 1.3.2 → 1.3.3
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-config.d.ts +2 -1
- package/dist/cli-config.js +13 -1
- package/dist/cli-contract.js +1 -1
- package/dist/cli-runner-local.js +11 -0
- package/dist/cli-runner.d.ts +1 -0
- package/dist/cli-runner.js +33 -4
- package/dist/cli.js +118 -2
- package/dist/clip-capture-loop.js +1 -1
- package/dist/opcode-runner.d.ts +1 -1
- package/dist/server-credit-usage.d.ts +1 -1
- package/dist/web-playwright-local.js +1 -1
- package/package.json +1 -1
- package/readme.md +16 -5
package/dist/cli-config.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ declare const DEFAULT_WS_URL = "wss://autokap.app/ws";
|
|
|
8
8
|
declare const LOCAL_API_BASE_URL = "http://localhost:3000";
|
|
9
9
|
declare const LOCAL_WS_URL = "ws://localhost:3000/ws";
|
|
10
10
|
declare const API_KEY_ENV_VAR = "AUTOKAP_API_KEY";
|
|
11
|
+
declare const RUN_TOKEN_ENV_VAR = "AUTOKAP_RUN_TOKEN";
|
|
11
12
|
declare const API_BASE_URL_ENV_VAR = "AUTOKAP_API_BASE_URL";
|
|
12
13
|
declare const WS_URL_ENV_VAR = "AUTOKAP_WS_URL";
|
|
13
14
|
declare const ALLOW_UNSAFE_SERVER_ORIGIN_ENV_VAR = "AUTOKAP_ALLOW_UNSAFE_SERVER_ORIGIN";
|
|
@@ -19,4 +20,4 @@ export declare function readConfig(): Promise<AutokapConfig | null>;
|
|
|
19
20
|
export declare function writeConfig(config: AutokapConfig): Promise<void>;
|
|
20
21
|
export declare function deleteConfig(): Promise<void>;
|
|
21
22
|
export declare function requireConfig(): Promise<AutokapConfig>;
|
|
22
|
-
export { DEFAULT_API_BASE_URL, DEFAULT_WS_URL, LOCAL_API_BASE_URL, LOCAL_WS_URL, API_KEY_ENV_VAR, API_BASE_URL_ENV_VAR, WS_URL_ENV_VAR, ALLOW_UNSAFE_SERVER_ORIGIN_ENV_VAR, };
|
|
23
|
+
export { DEFAULT_API_BASE_URL, DEFAULT_WS_URL, LOCAL_API_BASE_URL, LOCAL_WS_URL, API_KEY_ENV_VAR, RUN_TOKEN_ENV_VAR, API_BASE_URL_ENV_VAR, WS_URL_ENV_VAR, ALLOW_UNSAFE_SERVER_ORIGIN_ENV_VAR, };
|
package/dist/cli-config.js
CHANGED
|
@@ -7,6 +7,7 @@ const DEFAULT_WS_URL = 'wss://autokap.app/ws';
|
|
|
7
7
|
const LOCAL_API_BASE_URL = 'http://localhost:3000';
|
|
8
8
|
const LOCAL_WS_URL = 'ws://localhost:3000/ws';
|
|
9
9
|
const API_KEY_ENV_VAR = 'AUTOKAP_API_KEY';
|
|
10
|
+
const RUN_TOKEN_ENV_VAR = 'AUTOKAP_RUN_TOKEN';
|
|
10
11
|
const API_BASE_URL_ENV_VAR = 'AUTOKAP_API_BASE_URL';
|
|
11
12
|
const WS_URL_ENV_VAR = 'AUTOKAP_WS_URL';
|
|
12
13
|
const ALLOW_UNSAFE_SERVER_ORIGIN_ENV_VAR = 'AUTOKAP_ALLOW_UNSAFE_SERVER_ORIGIN';
|
|
@@ -23,6 +24,17 @@ export function getDefaultWsUrl(apiBaseUrl = getDefaultApiBaseUrl()) {
|
|
|
23
24
|
return normalizeUrl(process.env[WS_URL_ENV_VAR]) ?? deriveWsUrl(apiBaseUrl);
|
|
24
25
|
}
|
|
25
26
|
export async function readConfig() {
|
|
27
|
+
const envRunToken = normalizeApiKey(process.env[RUN_TOKEN_ENV_VAR]);
|
|
28
|
+
if (envRunToken) {
|
|
29
|
+
const apiBaseUrl = getDefaultApiBaseUrl();
|
|
30
|
+
const wsUrl = getDefaultWsUrl(apiBaseUrl);
|
|
31
|
+
assertAllowedApiOrigin(apiBaseUrl, DEFAULT_API_BASE_URL, API_BASE_URL_ENV_VAR);
|
|
32
|
+
return {
|
|
33
|
+
apiKey: envRunToken,
|
|
34
|
+
apiBaseUrl,
|
|
35
|
+
wsUrl,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
26
38
|
const envApiKey = normalizeApiKey(process.env[API_KEY_ENV_VAR]);
|
|
27
39
|
if (envApiKey) {
|
|
28
40
|
const apiBaseUrl = getDefaultApiBaseUrl();
|
|
@@ -159,5 +171,5 @@ function assertAllowedApiOrigin(candidateUrl, baselineUrl, envVar) {
|
|
|
159
171
|
}
|
|
160
172
|
throw new Error(`Refusing unsafe server override to ${candidateOrigin}. Set ${ALLOW_UNSAFE_SERVER_ORIGIN_ENV_VAR}=1 to allow ${envVar ?? 'this override'} explicitly.`);
|
|
161
173
|
}
|
|
162
|
-
export { DEFAULT_API_BASE_URL, DEFAULT_WS_URL, LOCAL_API_BASE_URL, LOCAL_WS_URL, API_KEY_ENV_VAR, API_BASE_URL_ENV_VAR, WS_URL_ENV_VAR, ALLOW_UNSAFE_SERVER_ORIGIN_ENV_VAR, };
|
|
174
|
+
export { DEFAULT_API_BASE_URL, DEFAULT_WS_URL, LOCAL_API_BASE_URL, LOCAL_WS_URL, API_KEY_ENV_VAR, RUN_TOKEN_ENV_VAR, API_BASE_URL_ENV_VAR, WS_URL_ENV_VAR, ALLOW_UNSAFE_SERVER_ORIGIN_ENV_VAR, };
|
|
163
175
|
//# sourceMappingURL=cli-config.js.map
|
package/dist/cli-contract.js
CHANGED
|
@@ -52,7 +52,7 @@ export const CLI_PUBLIC_COMMANDS = [
|
|
|
52
52
|
{
|
|
53
53
|
id: "auto-recapture",
|
|
54
54
|
command: "autokap auto-recapture --project <project-id> --env local",
|
|
55
|
-
summary: "Run every preset enabled for
|
|
55
|
+
summary: "Run every preset enabled for Recapture Cloud in a project",
|
|
56
56
|
docsDescriptionKey: "cliCmdAutoRecapture",
|
|
57
57
|
},
|
|
58
58
|
{
|
package/dist/cli-runner-local.js
CHANGED
|
@@ -57,6 +57,17 @@ export async function runLocal(presetId, opts) {
|
|
|
57
57
|
case 'breaker_trip':
|
|
58
58
|
logger.error(`${prefix} CIRCUIT BREAKER: ${event.message}`);
|
|
59
59
|
break;
|
|
60
|
+
case 'upload_start':
|
|
61
|
+
logger.info('[capture] Uploading artifacts and telemetry');
|
|
62
|
+
break;
|
|
63
|
+
case 'upload_end':
|
|
64
|
+
if (event.status === 'failed') {
|
|
65
|
+
logger.error(`[capture] Upload failed: ${event.message}`);
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
logger.info('[capture] Upload complete');
|
|
69
|
+
}
|
|
70
|
+
break;
|
|
60
71
|
}
|
|
61
72
|
},
|
|
62
73
|
});
|
package/dist/cli-runner.d.ts
CHANGED
package/dist/cli-runner.js
CHANGED
|
@@ -156,12 +156,12 @@ export async function runCapture(options) {
|
|
|
156
156
|
assertProgramNavigationScope(program, resolvedProgram.security);
|
|
157
157
|
}
|
|
158
158
|
catch (error) {
|
|
159
|
-
return { success: false, error: error instanceof Error ? error.message : String(error) };
|
|
159
|
+
return { success: false, runId, error: error instanceof Error ? error.message : String(error) };
|
|
160
160
|
}
|
|
161
161
|
if (!options.program && program.mediaMode === 'video') {
|
|
162
162
|
const prepareResult = await prepareVideoSpeechForRun(config, options.presetId, runId);
|
|
163
163
|
if (!prepareResult.success) {
|
|
164
|
-
return { success: false, error: prepareResult.error };
|
|
164
|
+
return { success: false, runId, error: prepareResult.error };
|
|
165
165
|
}
|
|
166
166
|
program = applyVideoSpeechDurations(program, prepareResult.durationsByStepId);
|
|
167
167
|
videoAudioAssets = prepareResult.audioAssets;
|
|
@@ -170,7 +170,7 @@ export async function runCapture(options) {
|
|
|
170
170
|
program = normalizeVideoCaptureProgram(parseProgram(program));
|
|
171
171
|
}
|
|
172
172
|
catch (err) {
|
|
173
|
-
return { success: false, error: `prepared video program validation failed: ${err instanceof Error ? err.message : String(err)}` };
|
|
173
|
+
return { success: false, runId, error: `prepared video program validation failed: ${err instanceof Error ? err.message : String(err)}` };
|
|
174
174
|
}
|
|
175
175
|
}
|
|
176
176
|
logger.info(`[capture] Running preset "${options.presetId}" — ${program.steps.length} opcodes, ${program.variants.length} variant(s)`);
|
|
@@ -264,19 +264,37 @@ export async function runCapture(options) {
|
|
|
264
264
|
}
|
|
265
265
|
try {
|
|
266
266
|
logger.info('[capture] Saving captures, might take a few seconds...');
|
|
267
|
+
options.onProgress?.({
|
|
268
|
+
type: 'upload_start',
|
|
269
|
+
variantId: 'run',
|
|
270
|
+
message: 'saving captures',
|
|
271
|
+
});
|
|
267
272
|
const uploadOutcome = await uploadResults(config, program, runResult, runId);
|
|
268
273
|
if (program.mediaMode === 'video' && runResult.success) {
|
|
269
274
|
await signalVideoComplete(config, program, runResult, uploadOutcome.runId, videoAudioAssets, videoAudioAssetsByLocale);
|
|
270
275
|
}
|
|
271
276
|
const totalDurationSec = ((Date.now() - captureStart) / 1000).toFixed(1);
|
|
272
277
|
logger.info(`[capture] Captures saved successfully — total ${totalDurationSec}s`);
|
|
278
|
+
options.onProgress?.({
|
|
279
|
+
type: 'upload_end',
|
|
280
|
+
variantId: 'run',
|
|
281
|
+
status: 'ok',
|
|
282
|
+
message: 'captures saved',
|
|
283
|
+
});
|
|
273
284
|
}
|
|
274
285
|
catch (err) {
|
|
275
286
|
const message = err instanceof Error ? err.message : String(err);
|
|
276
287
|
logger.error(`[capture] Failed to upload results: ${message}`);
|
|
288
|
+
options.onProgress?.({
|
|
289
|
+
type: 'upload_end',
|
|
290
|
+
variantId: 'run',
|
|
291
|
+
status: 'failed',
|
|
292
|
+
message,
|
|
293
|
+
});
|
|
277
294
|
if (!options.allowUploadFailure) {
|
|
278
295
|
return {
|
|
279
296
|
success: false,
|
|
297
|
+
runId,
|
|
280
298
|
runResult,
|
|
281
299
|
error: runResult.success
|
|
282
300
|
? `upload failed: ${message}`
|
|
@@ -285,7 +303,7 @@ export async function runCapture(options) {
|
|
|
285
303
|
}
|
|
286
304
|
logger.warn('[capture] Continuing after upload failure because --allow-upload-failure was set');
|
|
287
305
|
}
|
|
288
|
-
return { success: runResult.success, runResult };
|
|
306
|
+
return { success: runResult.success, runId, runResult };
|
|
289
307
|
}
|
|
290
308
|
// ── Server communication ────────────────────────────────────────────
|
|
291
309
|
async function fetchProgram(config, presetId, environmentName) {
|
|
@@ -1067,6 +1085,17 @@ function logProgress(event) {
|
|
|
1067
1085
|
case 'breaker_trip':
|
|
1068
1086
|
logger.error(`${prefix} Circuit breaker tripped: ${event.message}`);
|
|
1069
1087
|
break;
|
|
1088
|
+
case 'upload_start':
|
|
1089
|
+
logger.info('[capture] Uploading artifacts and telemetry');
|
|
1090
|
+
break;
|
|
1091
|
+
case 'upload_end':
|
|
1092
|
+
if (event.status === 'failed') {
|
|
1093
|
+
logger.error(`[capture] Upload failed: ${event.message}`);
|
|
1094
|
+
}
|
|
1095
|
+
else {
|
|
1096
|
+
logger.info('[capture] Upload complete');
|
|
1097
|
+
}
|
|
1098
|
+
break;
|
|
1070
1099
|
}
|
|
1071
1100
|
}
|
|
1072
1101
|
//# sourceMappingURL=cli-runner.js.map
|
package/dist/cli.js
CHANGED
|
@@ -233,7 +233,7 @@ program
|
|
|
233
233
|
// ── auto-recapture command ─────────────────────────────────────────
|
|
234
234
|
program
|
|
235
235
|
.command('auto-recapture')
|
|
236
|
-
.description('Run every preset enabled for
|
|
236
|
+
.description('Run every preset enabled for Recapture Cloud in a project')
|
|
237
237
|
.requiredOption('--project <id>', 'Project ID')
|
|
238
238
|
.option('--env <name>', "Project environment to capture against. Falls back to the project's default environment when omitted.")
|
|
239
239
|
.option('--headed', 'Show browser window for debugging', false)
|
|
@@ -262,6 +262,34 @@ program
|
|
|
262
262
|
// so the `capture_runs` row flips out of `queued` — without this the
|
|
263
263
|
// backend rate-limiter blocks future cloud recaptures.
|
|
264
264
|
const cloudRunId = process.env.AUTOKAP_RUN_ID;
|
|
265
|
+
const checkpointUrl = cloudRunId
|
|
266
|
+
? buildApiUrl(config, `/api/cli/cloud-recapture/${cloudRunId}/checkpoint`)
|
|
267
|
+
: null;
|
|
268
|
+
let lastProgressCheckpointAt = 0;
|
|
269
|
+
const postCloudCheckpoint = async (body, options = {}) => {
|
|
270
|
+
if (!checkpointUrl)
|
|
271
|
+
return;
|
|
272
|
+
if (options.throttle) {
|
|
273
|
+
const now = Date.now();
|
|
274
|
+
if (now - lastProgressCheckpointAt < 750)
|
|
275
|
+
return;
|
|
276
|
+
lastProgressCheckpointAt = now;
|
|
277
|
+
}
|
|
278
|
+
try {
|
|
279
|
+
const response = await fetch(checkpointUrl, {
|
|
280
|
+
method: 'POST',
|
|
281
|
+
headers: { ...authHeaders(config), 'Content-Type': 'application/json' },
|
|
282
|
+
body: JSON.stringify(body),
|
|
283
|
+
});
|
|
284
|
+
if (!response.ok) {
|
|
285
|
+
const bodyText = await response.text().catch(() => response.statusText);
|
|
286
|
+
logger.warn(`[auto-recapture] Cloud checkpoint non-OK (${response.status}): ${bodyText}`);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
catch (err) {
|
|
290
|
+
logger.warn(`[auto-recapture] Cloud checkpoint failed (best-effort): ${err.message}`);
|
|
291
|
+
}
|
|
292
|
+
};
|
|
265
293
|
/**
|
|
266
294
|
* Best-effort: tell the AutoKap backend whether this cloud run finished
|
|
267
295
|
* cleanly. Failures here do NOT change the CLI exit code — the artifact
|
|
@@ -289,6 +317,13 @@ program
|
|
|
289
317
|
}
|
|
290
318
|
};
|
|
291
319
|
const data = await requestJson(config, `/api/cli/projects/${opts.project}/auto-recapture-presets`, { headers: authHeaders(config) }, 'Failed to list auto-recapture presets');
|
|
320
|
+
await postCloudCheckpoint({
|
|
321
|
+
type: 'run_plan',
|
|
322
|
+
totalPresets: data.presets.length,
|
|
323
|
+
completedPresets: 0,
|
|
324
|
+
failedPresets: 0,
|
|
325
|
+
message: `planned ${data.presets.length} preset(s)`,
|
|
326
|
+
});
|
|
292
327
|
if (data.presets.length === 0) {
|
|
293
328
|
logger.info(`[auto-recapture] No presets enabled for project ${opts.project}`);
|
|
294
329
|
await notifyCloudCallback('completed', { totalPresets: 0, failedPresets: 0 });
|
|
@@ -296,19 +331,83 @@ program
|
|
|
296
331
|
}
|
|
297
332
|
const { runCapture } = await import('./cli-runner.js');
|
|
298
333
|
const failures = [];
|
|
299
|
-
|
|
334
|
+
await postCloudCheckpoint({
|
|
335
|
+
type: 'run_start',
|
|
336
|
+
totalPresets: data.presets.length,
|
|
337
|
+
completedPresets: 0,
|
|
338
|
+
failedPresets: 0,
|
|
339
|
+
message: `starting ${data.presets.length} preset(s)`,
|
|
340
|
+
});
|
|
341
|
+
for (const [index, preset] of data.presets.entries()) {
|
|
300
342
|
const label = preset.name ? `${preset.name} (${preset.id})` : preset.id;
|
|
301
343
|
logger.info(`[auto-recapture] Running ${label}`);
|
|
344
|
+
await postCloudCheckpoint({
|
|
345
|
+
type: 'preset_start',
|
|
346
|
+
presetId: preset.id,
|
|
347
|
+
presetName: preset.name ?? null,
|
|
348
|
+
presetIndex: index,
|
|
349
|
+
totalPresets: data.presets.length,
|
|
350
|
+
completedPresets: index,
|
|
351
|
+
failedPresets: failures.length,
|
|
352
|
+
message: `running ${preset.name ?? preset.id}`,
|
|
353
|
+
});
|
|
302
354
|
const result = await runCapture({
|
|
303
355
|
presetId: preset.id,
|
|
304
356
|
env: opts.env,
|
|
305
357
|
headed: opts.headed,
|
|
306
358
|
allowUploadFailure: opts.allowUploadFailure,
|
|
359
|
+
onProgress: (event) => {
|
|
360
|
+
const checkpointType = event.type === 'upload_start'
|
|
361
|
+
? 'upload_start'
|
|
362
|
+
: event.type === 'upload_end'
|
|
363
|
+
? 'upload_end'
|
|
364
|
+
: 'preset_progress';
|
|
365
|
+
void postCloudCheckpoint({
|
|
366
|
+
type: checkpointType,
|
|
367
|
+
presetId: preset.id,
|
|
368
|
+
presetName: preset.name ?? null,
|
|
369
|
+
presetIndex: index,
|
|
370
|
+
totalPresets: data.presets.length,
|
|
371
|
+
completedPresets: index,
|
|
372
|
+
failedPresets: failures.length,
|
|
373
|
+
message: event.message,
|
|
374
|
+
progressEvent: event,
|
|
375
|
+
status: event.status === 'failed' ? 'running' : undefined,
|
|
376
|
+
}, { throttle: checkpointType === 'preset_progress' });
|
|
377
|
+
},
|
|
307
378
|
});
|
|
379
|
+
const childRunId = result.runId;
|
|
308
380
|
if (!result.success) {
|
|
309
381
|
const error = result.error ?? result.runResult?.error ?? 'capture failed';
|
|
310
382
|
failures.push({ id: preset.id, name: preset.name, error });
|
|
311
383
|
logger.error(`[auto-recapture] Failed ${label}: ${error}`);
|
|
384
|
+
await postCloudCheckpoint({
|
|
385
|
+
type: 'preset_end',
|
|
386
|
+
presetId: preset.id,
|
|
387
|
+
presetName: preset.name ?? null,
|
|
388
|
+
presetIndex: index,
|
|
389
|
+
totalPresets: data.presets.length,
|
|
390
|
+
completedPresets: index + 1,
|
|
391
|
+
failedPresets: failures.length,
|
|
392
|
+
childRunId,
|
|
393
|
+
status: 'failed',
|
|
394
|
+
errorMessage: error,
|
|
395
|
+
message: `failed ${preset.name ?? preset.id}`,
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
else {
|
|
399
|
+
await postCloudCheckpoint({
|
|
400
|
+
type: 'preset_end',
|
|
401
|
+
presetId: preset.id,
|
|
402
|
+
presetName: preset.name ?? null,
|
|
403
|
+
presetIndex: index,
|
|
404
|
+
totalPresets: data.presets.length,
|
|
405
|
+
completedPresets: index + 1,
|
|
406
|
+
failedPresets: failures.length,
|
|
407
|
+
childRunId,
|
|
408
|
+
status: 'completed',
|
|
409
|
+
message: `completed ${preset.name ?? preset.id}`,
|
|
410
|
+
});
|
|
312
411
|
}
|
|
313
412
|
}
|
|
314
413
|
if (failures.length > 0) {
|
|
@@ -316,6 +415,15 @@ program
|
|
|
316
415
|
.map((failure) => failure.name ?? failure.id)
|
|
317
416
|
.join(', ')}`;
|
|
318
417
|
logger.error(`[auto-recapture] ${errorMessage}`);
|
|
418
|
+
await postCloudCheckpoint({
|
|
419
|
+
type: 'error',
|
|
420
|
+
totalPresets: data.presets.length,
|
|
421
|
+
completedPresets: data.presets.length,
|
|
422
|
+
failedPresets: failures.length,
|
|
423
|
+
status: 'failed',
|
|
424
|
+
errorMessage,
|
|
425
|
+
message: errorMessage,
|
|
426
|
+
});
|
|
319
427
|
await notifyCloudCallback('failed', {
|
|
320
428
|
totalPresets: data.presets.length,
|
|
321
429
|
failedPresets: failures.length,
|
|
@@ -324,6 +432,14 @@ program
|
|
|
324
432
|
process.exit(1);
|
|
325
433
|
}
|
|
326
434
|
logger.success(`[auto-recapture] ${data.presets.length} preset(s) recaptured successfully`);
|
|
435
|
+
await postCloudCheckpoint({
|
|
436
|
+
type: 'run_end',
|
|
437
|
+
totalPresets: data.presets.length,
|
|
438
|
+
completedPresets: data.presets.length,
|
|
439
|
+
failedPresets: 0,
|
|
440
|
+
status: 'completed',
|
|
441
|
+
message: 'cloud recapture completed',
|
|
442
|
+
});
|
|
327
443
|
await notifyCloudCallback('completed', {
|
|
328
444
|
totalPresets: data.presets.length,
|
|
329
445
|
failedPresets: 0,
|
|
@@ -37,7 +37,7 @@ export class ClipCaptureLoop {
|
|
|
37
37
|
// Linux default is 8 fps to stay safe on 2 vCPU CI runners. Cloud runners
|
|
38
38
|
// (AUTOKAP_CLOUD_RUNNER=1, set by the Fly.io image and the `--cloud` CLI
|
|
39
39
|
// flag) get the same 15 fps default as macOS/Windows since they have
|
|
40
|
-
//
|
|
40
|
+
// dedicated 8 vCPU capacity. Callers can still override via opts.targetFps.
|
|
41
41
|
const isCloudRunner = process.env.AUTOKAP_CLOUD_RUNNER === '1';
|
|
42
42
|
const linuxDefault = isCloudRunner ? 15 : 8;
|
|
43
43
|
const platformDefault = process.platform === 'linux' ? linuxDefault : 15;
|
package/dist/opcode-runner.d.ts
CHANGED
|
@@ -43,7 +43,7 @@ export interface RunOptions {
|
|
|
43
43
|
presetName?: string;
|
|
44
44
|
}
|
|
45
45
|
export interface ProgressEvent {
|
|
46
|
-
type: 'variant_start' | 'variant_end' | 'opcode_start' | 'opcode_end' | 'recovery' | 'breaker_trip';
|
|
46
|
+
type: 'variant_start' | 'variant_end' | 'opcode_start' | 'opcode_end' | 'recovery' | 'breaker_trip' | 'upload_start' | 'upload_end';
|
|
47
47
|
variantId: string;
|
|
48
48
|
opcodeIndex?: number;
|
|
49
49
|
opcodeKind?: string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { SupabaseClient } from '@supabase/supabase-js';
|
|
2
|
-
export type CreditUsageType = 'screenshot' | 'clip' | 'video' | 'preset_analysis' | 'ai_chat' | 'studio_creation' | 'studio_iteration';
|
|
2
|
+
export type CreditUsageType = 'screenshot' | 'clip' | 'video' | 'preset_analysis' | '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;
|
|
@@ -344,7 +344,7 @@ export class WebPlaywrightLocal {
|
|
|
344
344
|
await fs.mkdir(framesDir, { recursive: true });
|
|
345
345
|
// Linux defaults are conservative because GitHub Actions free runners
|
|
346
346
|
// (2 vCPU) can't sustain 30 fps. AUTOKAP_CLOUD_RUNNER=1 signals that the
|
|
347
|
-
// process is running on managed cloud infra (Fly.io machines,
|
|
347
|
+
// process is running on managed cloud infra (Fly.io machines, 8 vCPU)
|
|
348
348
|
// where the cap can safely lift to 30 fps for clips too. Set by the
|
|
349
349
|
// `--cloud` flag and by the cloud-runner Docker image.
|
|
350
350
|
const isCloudRunner = process.env.AUTOKAP_CLOUD_RUNNER === '1';
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -16,19 +16,30 @@ cp .env.example .env.local
|
|
|
16
16
|
npm run dev
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
##
|
|
19
|
+
## Recapture Cloud in CI/CD
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
The official CI/CD flow is Recapture Cloud. Generate a signed webhook secret
|
|
22
|
+
from the project Recapture page, store it in your CI secret manager, then call
|
|
23
|
+
the webhook on deploy. CI never needs an AutoKap CLI key.
|
|
23
24
|
|
|
24
25
|
```bash
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
BODY='{"env":"staging"}'
|
|
27
|
+
SIG=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$AUTOKAP_WEBHOOK_SECRET" -binary | xxd -p -c 256)
|
|
28
|
+
curl -X POST "https://autokap.app/api/webhooks/cloud-recapture/<project-id>" \
|
|
29
|
+
-H "Content-Type: application/json" \
|
|
30
|
+
-H "X-AutoKap-Signature: sha256=$SIG" \
|
|
31
|
+
-d "$BODY"
|
|
27
32
|
```
|
|
28
33
|
|
|
29
34
|
Use project environments in the dashboard to map `local`, `staging`, and
|
|
30
35
|
`production` to different base URLs without duplicating presets.
|
|
31
36
|
|
|
37
|
+
The local CLI remains available for advanced debugging:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
AUTOKAP_API_KEY=ak_cli_... autokap run <preset-id> --env staging
|
|
41
|
+
```
|
|
42
|
+
|
|
32
43
|
## Auth Setup (Supabase)
|
|
33
44
|
|
|
34
45
|
AutoKap uses Supabase Auth for user authentication. You need to configure the following providers in your Supabase dashboard:
|