autokap 1.9.10 → 2.0.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.
@@ -174,7 +174,7 @@ export async function runCapture(options) {
174
174
  // durations + audio assets are only consumed on the upload path (signalVideoComplete), which a dry
175
175
  // run never reaches, so skipping prep here is side-effect-free for dry.
176
176
  if (!options.dryRun && !options.program && program.mediaMode === 'video') {
177
- const prepareResult = await prepareVideoSpeechForRun(config, options.presetId, runId, options.regenerateTts ?? false, sessionId);
177
+ const prepareResult = await prepareVideoSpeechForRun(config, options.presetId, runId, options.regenerateTts ?? false, sessionId, options.onProgress);
178
178
  if (!prepareResult.success) {
179
179
  return { success: false, runId, error: prepareResult.error };
180
180
  }
@@ -524,7 +524,7 @@ async function fetchProgram(config, presetId, environmentName) {
524
524
  }
525
525
  return { success: false, error: 'failed to fetch program: retry attempts exhausted' };
526
526
  }
527
- async function prepareVideoSpeechForRun(config, videoId, runId, regenerateTts, sessionId) {
527
+ async function prepareVideoSpeechForRun(config, videoId, runId, regenerateTts, sessionId, onProgress) {
528
528
  if (regenerateTts) {
529
529
  logger.info('[capture] Forcing TTS regeneration — all cached segments will be re-synthesized and billed.');
530
530
  }
@@ -578,6 +578,17 @@ async function prepareVideoSpeechForRun(config, videoId, runId, regenerateTts, s
578
578
  }
579
579
  if (event.type === 'tts_progress') {
580
580
  logTtsProgress(event);
581
+ // Forward to the caller so cloud runs can surface the narration phase as
582
+ // checkpoints (local runs only ever logged it — the original blind spot).
583
+ onProgress?.({
584
+ type: 'tts_progress',
585
+ variantId: 'tts',
586
+ message: '',
587
+ stage: event.stage,
588
+ index: event.index,
589
+ total: event.total,
590
+ stepId: event.stepId,
591
+ });
581
592
  return;
582
593
  }
583
594
  if (event.type === 'error') {
package/dist/cli.d.ts CHANGED
@@ -1,3 +1,24 @@
1
1
  #!/usr/bin/env node
2
2
  import { Command } from 'commander';
3
+ import type { ProgressEvent } from './opcode-runner.js';
3
4
  export declare const program: Command;
5
+ type CloudCheckpointType = 'preset_progress' | 'upload_start' | 'upload_end';
6
+ /** Interval below which consecutive `granular` checkpoints are coalesced. */
7
+ export declare const GRANULAR_CHECKPOINT_THROTTLE_MS = 1200;
8
+ export declare function cloudCaptureProgressCheckpoint(event: ProgressEvent): {
9
+ type: CloudCheckpointType;
10
+ message: string;
11
+ status?: 'running';
12
+ /** Fine-grained intra-preset event (per-opcode / per-narration) — throttled
13
+ * so a long video's step stream can't bury the coarse structural events in
14
+ * the server's bounded timeline window. Structural events are never marked. */
15
+ granular?: boolean;
16
+ } | null;
17
+ /**
18
+ * Stateful gate that admits at most one `granular` checkpoint per throttle
19
+ * window; structural (non-granular) checkpoints always pass. Create one per
20
+ * preset so each preset streams its own opcode/narration cadence. `now` is
21
+ * injectable for tests.
22
+ */
23
+ export declare function createGranularCheckpointThrottle(throttleMs?: number, now?: () => number): (granular: boolean) => boolean;
24
+ export {};
package/dist/cli.js CHANGED
@@ -51,7 +51,9 @@ function displayPresetName(preset) {
51
51
  const name = preset.name?.trim();
52
52
  return name && name.length > 0 ? name : preset.id;
53
53
  }
54
- function cloudCaptureProgressCheckpoint(event) {
54
+ /** Interval below which consecutive `granular` checkpoints are coalesced. */
55
+ export const GRANULAR_CHECKPOINT_THROTTLE_MS = 1200;
56
+ export function cloudCaptureProgressCheckpoint(event) {
55
57
  switch (event.type) {
56
58
  case 'variant_start':
57
59
  return {
@@ -66,6 +68,26 @@ function cloudCaptureProgressCheckpoint(event) {
66
68
  : `Capture failed: ${event.variantId}`,
67
69
  status: event.status === 'failed' ? 'running' : undefined,
68
70
  };
71
+ case 'opcode_start': {
72
+ const position = (event.opcodeIndex ?? 0) + 1;
73
+ const total = event.opcodeTotal ? `/${event.opcodeTotal}` : '';
74
+ const label = event.message || event.opcodeKind || '';
75
+ return {
76
+ type: 'preset_progress',
77
+ granular: true,
78
+ message: label ? `Step ${position}${total}: ${label}` : `Step ${position}${total}`,
79
+ };
80
+ }
81
+ case 'tts_progress': {
82
+ const position = event.index ?? '?';
83
+ const total = event.total ?? '?';
84
+ const suffix = event.stage === 'uploading' ? ' · uploading' : event.stage === 'done' ? ' · done' : '';
85
+ return {
86
+ type: 'preset_progress',
87
+ granular: true,
88
+ message: `Narration ${position}/${total}${suffix}`,
89
+ };
90
+ }
69
91
  case 'upload_start':
70
92
  return {
71
93
  type: 'upload_start',
@@ -81,6 +103,24 @@ function cloudCaptureProgressCheckpoint(event) {
81
103
  return null;
82
104
  }
83
105
  }
106
+ /**
107
+ * Stateful gate that admits at most one `granular` checkpoint per throttle
108
+ * window; structural (non-granular) checkpoints always pass. Create one per
109
+ * preset so each preset streams its own opcode/narration cadence. `now` is
110
+ * injectable for tests.
111
+ */
112
+ export function createGranularCheckpointThrottle(throttleMs = GRANULAR_CHECKPOINT_THROTTLE_MS, now = Date.now) {
113
+ let lastGranularAt = null;
114
+ return (granular) => {
115
+ if (!granular)
116
+ return true;
117
+ const current = now();
118
+ if (lastGranularAt !== null && current - lastGranularAt < throttleMs)
119
+ return false;
120
+ lastGranularAt = current;
121
+ return true;
122
+ };
123
+ }
84
124
  // ── login command ───────────────────────────────────────────────────
85
125
  program
86
126
  .command('login <key>')
@@ -420,6 +460,9 @@ program
420
460
  failedPresets: failures.length,
421
461
  message: `Preset started: ${presetDisplayName}`,
422
462
  });
463
+ // Fresh throttle per preset so each preset streams its own opcode/
464
+ // narration cadence and the window resets at each preset boundary.
465
+ const admitCheckpoint = createGranularCheckpointThrottle();
423
466
  const result = await runCapture({
424
467
  presetId: preset.id,
425
468
  env: opts.env,
@@ -437,6 +480,10 @@ program
437
480
  const checkpoint = cloudCaptureProgressCheckpoint(event);
438
481
  if (!checkpoint)
439
482
  return;
483
+ // Coalesce fine-grained (opcode / narration) checkpoints; structural
484
+ // ones (capture/export start/end) always pass through.
485
+ if (!admitCheckpoint(checkpoint.granular ?? false))
486
+ return;
440
487
  void postCloudCheckpoint({
441
488
  type: checkpoint.type,
442
489
  presetId: preset.id,