autokap 1.8.8 → 1.8.9

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.
@@ -58,3 +58,4 @@ export interface CLIRunResult {
58
58
  }
59
59
  export declare function runCapture(options: CLIRunnerOptions): Promise<CLIRunResult>;
60
60
  export declare function buildVideoClipMetadata(videoId: string, result: RunResult, program?: ExecutionProgram, runId?: string): VideoClipMetadata[];
61
+ export declare function resolveEffectiveCliArtifactPlan(artifactPlan: ExecutionProgram['artifactPlan'], deviceFrame?: string | null): ExecutionProgram['artifactPlan'];
@@ -25,7 +25,7 @@ import { parseProgram } from './execution-schema.js';
25
25
  import { buildCursorOverlayScript } from './cursor-overlay-script.js';
26
26
  import { CLI_VERSION_HEADER, } from './cli-contract.js';
27
27
  import { postProcessClipRecording } from './clip-postprocess.js';
28
- import { applyDeviceFrame, seedDeviceConfigs } from './mockup.js';
28
+ import { applyDeviceFrame, resolveVariantFrameOptions, seedDeviceConfigs } from './mockup.js';
29
29
  import { transformBrowserUrl } from './transform-browser-url.js';
30
30
  import { localizeStatusBar } from './status-bar-l10n.js';
31
31
  import { logger } from './logger.js';
@@ -164,7 +164,12 @@ export async function runCapture(options) {
164
164
  catch (error) {
165
165
  return { success: false, runId, error: error instanceof Error ? error.message : String(error) };
166
166
  }
167
- if (!options.program && program.mediaMode === 'video') {
167
+ // TTS synthesis is real, billed work — it belongs to a real run only. A dry run validates
168
+ // navigation/opcodes (capture opcodes + upload are skipped below) and must NOT synthesize speech:
169
+ // doing so would bill TTS credits while the run reports "0 credits charged". The rewritten SLEEP
170
+ // durations + audio assets are only consumed on the upload path (signalVideoComplete), which a dry
171
+ // run never reaches, so skipping prep here is side-effect-free for dry.
172
+ if (!options.dryRun && !options.program && program.mediaMode === 'video') {
168
173
  const prepareResult = await prepareVideoSpeechForRun(config, options.presetId, runId, options.regenerateTts ?? false);
169
174
  if (!prepareResult.success) {
170
175
  return { success: false, runId, error: prepareResult.error };
@@ -962,6 +967,9 @@ async function uploadArtifactMultipart(config, program, runId, job, filename, pr
962
967
  if (variantSpec?.deviceFrame) {
963
968
  formData.append('deviceFrame', variantSpec.deviceFrame);
964
969
  }
970
+ if (variantSpec?.mockupOptions) {
971
+ formData.append('mockupOptions', JSON.stringify(variantSpec.mockupOptions));
972
+ }
965
973
  const requestedDeviceScaleFactor = variantSpec?.deviceScaleFactor ?? program.outputScale ?? 2;
966
974
  const isFrameCapture = artifact.mediaMode === 'clip' || artifact.mediaMode === 'video';
967
975
  const deviceScaleFactor = isFrameCapture && Number.isFinite(requestedDeviceScaleFactor)
@@ -1103,22 +1111,24 @@ async function prepareDirectUploadParts(params) {
1103
1111
  async function prepareScreenshotBufferForDirectUpload(input, metadata, program, variantSpec, tabIcon) {
1104
1112
  let output = input;
1105
1113
  const artifactPlan = resolveEffectiveCliArtifactPlan(program.artifactPlan, variantSpec?.deviceFrame ?? null);
1106
- if (artifactPlan?.applyStatusBar && !artifactPlan?.applyMockup) {
1107
- throw new Error('applyStatusBar requires applyMockup with a device frame');
1108
- }
1109
- if (artifactPlan?.applyMockup) {
1110
- if (!variantSpec?.deviceFrame) {
1111
- throw new Error('applyMockup requires a deviceFrame on the variant');
1112
- }
1113
- output = await applyDeviceFrame(output, variantSpec.deviceFrame, {
1114
+ // The variant's deviceFrame is the sole gate. Its mockupOptions (orientation, status bar,
1115
+ // safe areas, …) defined by the user on the preset — drive the frame; the legacy
1116
+ // program-level applyStatusBar only survives as a fallback for old programs without options.
1117
+ if (variantSpec?.deviceFrame) {
1118
+ const mockup = variantSpec.mockupOptions;
1119
+ const frame = resolveVariantFrameOptions(mockup, {
1114
1120
  orientation: inferCliOrientation(metadata.viewport ?? null),
1121
+ showStatusBar: artifactPlan?.applyStatusBar,
1122
+ });
1123
+ output = await applyDeviceFrame(output, variantSpec.deviceFrame, {
1124
+ ...mockup,
1125
+ ...frame,
1115
1126
  viewport: metadata.viewport ?? undefined,
1116
1127
  colorScheme: metadata.theme,
1117
1128
  outputScale: normalizeCliDeviceScaleFactor(metadata.deviceScaleFactor)
1118
1129
  ?? normalizeCliDeviceScaleFactor(program.outputScale)
1119
1130
  ?? 2,
1120
- showStatusBar: artifactPlan.applyStatusBar ?? false,
1121
- statusBar: localizeStatusBar({}, metadata.lang),
1131
+ statusBar: localizeStatusBar(mockup?.statusBar ?? {}, metadata.lang),
1122
1132
  browserBar: buildCliBrowserBar(metadata.captureUrl, metadata.theme, tabIcon, { publicUrl: program.publicUrl, pageTitle: metadata.pageTitle ?? null }),
1123
1133
  });
1124
1134
  }
@@ -1181,10 +1191,11 @@ async function prepareClipBuffersForDirectUpload(artifact, metadata) {
1181
1191
  await fs.rm(tempDir, { recursive: true, force: true }).catch(() => undefined);
1182
1192
  }
1183
1193
  }
1184
- function resolveEffectiveCliArtifactPlan(artifactPlan, deviceFrame) {
1194
+ export function resolveEffectiveCliArtifactPlan(artifactPlan, deviceFrame) {
1195
+ // Device mockups are gated SOLELY by the variant's `deviceFrame` (user-defined, deterministic).
1196
+ // A present deviceFrame always renders its frame; `applyMockup` is a derived value, never a
1197
+ // kill-switch (the legacy `applyMockup: false` toggle is intentionally ignored here).
1185
1198
  if (deviceFrame) {
1186
- if (artifactPlan.applyMockup === false)
1187
- return artifactPlan;
1188
1199
  return artifactPlan.applyMockup ? artifactPlan : { ...artifactPlan, applyMockup: true };
1189
1200
  }
1190
1201
  if (!artifactPlan.applyMockup && !artifactPlan.applyStatusBar)
@@ -1053,7 +1053,7 @@ export declare const VariantSpecSchema: z.ZodObject<{
1053
1053
  light: "light";
1054
1054
  dark: "dark";
1055
1055
  }>>;
1056
- }, z.core.$strict>>;
1056
+ }, z.core.$strip>>;
1057
1057
  }, z.core.$strict>;
1058
1058
  export declare const PreconditionSpecSchema: z.ZodObject<{
1059
1059
  credentialsId: z.ZodOptional<z.ZodString>;
@@ -1205,7 +1205,7 @@ export declare const ExecutionProgramSchema: z.ZodObject<{
1205
1205
  light: "light";
1206
1206
  dark: "dark";
1207
1207
  }>>;
1208
- }, z.core.$strict>>;
1208
+ }, z.core.$strip>>;
1209
1209
  }, z.core.$strict>>;
1210
1210
  preconditions: z.ZodObject<{
1211
1211
  credentialsId: z.ZodOptional<z.ZodString>;
@@ -553,7 +553,13 @@ export const VariantSpecSchema = z.object({
553
553
  radius: z.number(),
554
554
  }).strict().optional(),
555
555
  colorScheme: z.enum(['light', 'dark']).optional(),
556
- }).strict().optional(),
556
+ // STRIP (not strict): per-variant mockupOptions are reconciled verbatim from the preset's
557
+ // CaptureTarget, whose UI-side MockupOptions carries render-irrelevant keys the engine never
558
+ // consumes (showDock, dockScale, dockMode, userAppIcon, autoBrowserBar, viewport). Strict would
559
+ // make parseProgram THROW on those — stranding every Mac/browser preset at CLI fetch time.
560
+ // Dropping unknown keys post-parse is safe: applyDeviceFrame ignores them, and the full set
561
+ // stays on config.targets for the UI/preview.
562
+ }).optional(),
557
563
  }).strict();
558
564
  const cookieSchema = z.object({
559
565
  name: z.string().min(1),
@@ -535,9 +535,16 @@ export interface ArtifactSpec {
535
535
  cursorTheme?: VideoCursorTheme;
536
536
  /** Max clip duration in seconds. Clips are trimmed if they exceed this. Default: 8. Ignored when `mediaMode='video'`. */
537
537
  maxClipDurationSec?: number;
538
- /** Whether to apply device frame mockup. Default: false */
538
+ /**
539
+ * @deprecated Device mockups are gated SOLELY by a variant's `deviceFrame` (user-defined,
540
+ * deterministic). The render paths derive mockup application from `deviceFrame` and ignore this
541
+ * flag as a gate. Kept optional for back-compat with stored programs; do not author it.
542
+ */
539
543
  applyMockup?: boolean;
540
- /** Whether to add status bar. Default: false */
544
+ /**
545
+ * @deprecated Per-variant `mockupOptions.showStatusBar` drives the status bar now. Retained only
546
+ * as a fallback for legacy programs that lack per-variant `mockupOptions`. Do not author it.
547
+ */
541
548
  applyStatusBar?: boolean;
542
549
  }
543
550
  export interface ExecutionProgram {
package/dist/mockup.d.ts CHANGED
@@ -204,6 +204,19 @@ export interface MockupOptions {
204
204
  height: number;
205
205
  };
206
206
  }
207
+ /**
208
+ * Resolve the two per-variant frame decisions shared by both render paths (CLI direct-upload
209
+ * framing and the cloud legacy-multipart route): a variant's own `mockupOptions` wins, falling
210
+ * back to the viewport-inferred orientation and the deprecated program-level `applyStatusBar`.
211
+ * Pure + exported so the precedence is tested once instead of in two duplicated call sites.
212
+ */
213
+ export declare function resolveVariantFrameOptions(mockupOptions: MockupOptions | undefined, fallback: {
214
+ orientation?: MockupOrientation;
215
+ showStatusBar?: boolean;
216
+ }): {
217
+ orientation?: MockupOrientation;
218
+ showStatusBar: boolean;
219
+ };
207
220
  export interface ResolvedDeviceFrameDescriptor {
208
221
  id: string;
209
222
  name: string;
package/dist/mockup.js CHANGED
@@ -17,6 +17,18 @@ function getSupabaseMockupConfig() {
17
17
  serviceKey: process.env.SUPABASE_SERVICE_ROLE_KEY,
18
18
  };
19
19
  }
20
+ /**
21
+ * Resolve the two per-variant frame decisions shared by both render paths (CLI direct-upload
22
+ * framing and the cloud legacy-multipart route): a variant's own `mockupOptions` wins, falling
23
+ * back to the viewport-inferred orientation and the deprecated program-level `applyStatusBar`.
24
+ * Pure + exported so the precedence is tested once instead of in two duplicated call sites.
25
+ */
26
+ export function resolveVariantFrameOptions(mockupOptions, fallback) {
27
+ return {
28
+ orientation: mockupOptions?.orientation ?? fallback.orientation,
29
+ showStatusBar: mockupOptions?.showStatusBar ?? fallback.showStatusBar ?? false,
30
+ };
31
+ }
20
32
  const DEFAULT_MOCKUP_OPTIONS = {
21
33
  orientation: 'portrait',
22
34
  outputScale: 2,
@@ -111,7 +111,7 @@ export declare const SignedExecutionProgramEnvelopeSchema: z.ZodObject<{
111
111
  light: "light";
112
112
  dark: "dark";
113
113
  }>>;
114
- }, z.core.$strict>>;
114
+ }, z.core.$strip>>;
115
115
  }, z.core.$strict>>;
116
116
  preconditions: z.ZodObject<{
117
117
  credentialsId: z.ZodOptional<z.ZodString>;
@@ -19,6 +19,7 @@ const PRESET_SKILL_SOURCE = {
19
19
  { relativePath: 'OPCODE-REFERENCE.md', title: 'Opcode Reference', anchor: 'reference-opcode-reference' },
20
20
  { relativePath: 'references/STANDARDS.md', title: 'Prompt Charter & Quality Standards', anchor: 'reference-prompt-standards' },
21
21
  { relativePath: 'references/mock-data.md', title: 'Mock Data Injection', anchor: 'reference-mock-data-injection' },
22
+ { relativePath: 'references/video-workflow.md', title: 'Demo Video Workflow', anchor: 'reference-demo-video-workflow' },
22
23
  { relativePath: 'references/examples.md', title: 'Complete Examples', anchor: 'reference-complete-examples' },
23
24
  ],
24
25
  };
@@ -130,7 +130,7 @@ export declare const VideoIngestPayloadSchema: z.ZodObject<{
130
130
  light: "light";
131
131
  dark: "dark";
132
132
  }>>;
133
- }, z.core.$strict>>;
133
+ }, z.core.$strip>>;
134
134
  }, z.core.$strict>>;
135
135
  preconditions: z.ZodObject<{
136
136
  credentialsId: z.ZodOptional<z.ZodString>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "autokap",
3
- "version": "1.8.8",
3
+ "version": "1.8.9",
4
4
  "description": "AI-powered CLI tool for capturing clean screenshots of websites",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",