autokap 1.6.1 → 1.6.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.
Files changed (41) hide show
  1. package/dist/browser-pool.d.ts +1 -0
  2. package/dist/browser-pool.js +2 -0
  3. package/dist/browser.js +13 -1
  4. package/dist/cli-config.js +69 -25
  5. package/dist/cli-contract.d.ts +25 -5
  6. package/dist/cli-contract.js +55 -151
  7. package/dist/cli-doctor.d.ts +1 -1
  8. package/dist/cli-doctor.js +67 -82
  9. package/dist/cli-runner.d.ts +1 -1
  10. package/dist/cli-runner.js +23 -10
  11. package/dist/cli.js +25 -1163
  12. package/dist/execution-schema.d.ts +9 -3
  13. package/dist/execution-schema.js +12 -0
  14. package/dist/execution-types.d.ts +33 -2
  15. package/dist/mockup.d.ts +66 -2
  16. package/dist/mockup.js +31 -14
  17. package/dist/opcode-runner.js +9 -0
  18. package/dist/program-signing.d.ts +4 -1
  19. package/dist/program-signing.js +4 -0
  20. package/dist/skill-packaging.d.ts +0 -16
  21. package/dist/skill-packaging.js +1 -51
  22. package/dist/transform-browser-url.d.ts +6 -0
  23. package/dist/transform-browser-url.js +28 -0
  24. package/dist/types.d.ts +11 -0
  25. package/dist/video-narration-schema.d.ts +4 -1
  26. package/dist/web-playwright-local.d.ts +1 -0
  27. package/dist/web-playwright-local.js +0 -0
  28. package/package.json +6 -6
  29. package/readme.md +15 -12
  30. package/assets/skill/OPCODE-REFERENCE.md +0 -625
  31. package/assets/skill/README.md +0 -38
  32. package/assets/skill/SKILL.md +0 -590
  33. package/assets/skill/references/STANDARDS.md +0 -236
  34. package/assets/skill/references/examples.md +0 -88
  35. package/assets/skill/references/mock-data.md +0 -178
  36. package/dist/auth-capture.d.ts +0 -17
  37. package/dist/auth-capture.js +0 -199
  38. package/dist/cli-utils.d.ts +0 -5
  39. package/dist/cli-utils.js +0 -14
  40. package/dist/version-check.d.ts +0 -4
  41. package/dist/version-check.js +0 -102
@@ -1072,9 +1072,9 @@ export declare const PreconditionSpecSchema: z.ZodObject<{
1072
1072
  httpOnly: z.ZodBoolean;
1073
1073
  secure: z.ZodBoolean;
1074
1074
  sameSite: z.ZodEnum<{
1075
- Strict: "Strict";
1076
1075
  Lax: "Lax";
1077
1076
  None: "None";
1077
+ Strict: "Strict";
1078
1078
  }>;
1079
1079
  }, z.core.$strict>>;
1080
1080
  origins: z.ZodArray<z.ZodObject<{
@@ -1221,9 +1221,9 @@ export declare const ExecutionProgramSchema: z.ZodObject<{
1221
1221
  httpOnly: z.ZodBoolean;
1222
1222
  secure: z.ZodBoolean;
1223
1223
  sameSite: z.ZodEnum<{
1224
- Strict: "Strict";
1225
1224
  Lax: "Lax";
1226
1225
  None: "None";
1226
+ Strict: "Strict";
1227
1227
  }>;
1228
1228
  }, z.core.$strict>>;
1229
1229
  origins: z.ZodArray<z.ZodObject<{
@@ -2226,6 +2226,9 @@ export declare const ExecutionProgramSchema: z.ZodObject<{
2226
2226
  defaultValues: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodString>>;
2227
2227
  replaceExisting: z.ZodOptional<z.ZodBoolean>;
2228
2228
  }, z.core.$strict>>>;
2229
+ deviceConfigs: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
2230
+ publicUrl: z.ZodOptional<z.ZodString>;
2231
+ environmentHttpHeaders: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
2229
2232
  }, z.core.$strict>;
2230
2233
  export declare const HealerPatchSchema: z.ZodObject<{
2231
2234
  opcodeIndex: z.ZodNumber;
@@ -4182,7 +4185,7 @@ export declare function safeParseProgramResult(data: unknown): z.ZodSafeParseRes
4182
4185
  expires: number;
4183
4186
  httpOnly: boolean;
4184
4187
  secure: boolean;
4185
- sameSite: "Strict" | "Lax" | "None";
4188
+ sameSite: "Lax" | "None" | "Strict";
4186
4189
  }[];
4187
4190
  origins: {
4188
4191
  origin: string;
@@ -4923,4 +4926,7 @@ export declare function safeParseProgramResult(data: unknown): z.ZodSafeParseRes
4923
4926
  defaultValues: Record<string, string>[];
4924
4927
  replaceExisting?: boolean | undefined;
4925
4928
  }[] | undefined;
4929
+ deviceConfigs?: Record<string, Record<string, unknown>> | undefined;
4930
+ publicUrl?: string | undefined;
4931
+ environmentHttpHeaders?: Record<string, string> | undefined;
4926
4932
  }>;
@@ -652,6 +652,18 @@ export const ExecutionProgramSchema = z.object({
652
652
  compiledAt: z.string().datetime(),
653
653
  compiledWith: z.string().optional(),
654
654
  mockDataGroups: z.array(MockDataGroupSchema).optional(),
655
+ // Server-embedded device frame configs. Shape is governed by the
656
+ // `device_mockups.config` JSON column; we accept anything the server stores
657
+ // and let mockup.ts validate structurally at use time.
658
+ deviceConfigs: z.record(z.string().min(1), z.record(z.string(), z.unknown())).optional(),
659
+ // Project-level public URL used to rewrite the captured origin when
660
+ // decorating browser mockups. Validated only as a non-empty string here;
661
+ // the runtime parses it with `new URL()` and falls back gracefully.
662
+ publicUrl: z.string().min(1).optional(),
663
+ // Auth headers attached to the resolved project environment. Key/value
664
+ // pairs that Playwright will inject as `extraHTTPHeaders` on the
665
+ // BrowserContext so protected staging/preview URLs load successfully.
666
+ environmentHttpHeaders: z.record(z.string().min(1), z.string().min(1)).optional(),
655
667
  }).strict().superRefine((value, ctx) => {
656
668
  if (value.mediaMode !== value.artifactPlan.mediaMode) {
657
669
  ctx.addIssue({
@@ -5,7 +5,8 @@
5
5
  * preset (natural language) -> ExecutionProgram (typed IR) -> deterministic runtime
6
6
  */
7
7
  import type { AKTree, BrowserStorageState, BrowserSessionStorageState, OutscaleConfig, VideoCursorTheme, VideoPageSignals } from './types.js';
8
- import type { MockupOptions } from './mockup.js';
8
+ import type { DeviceConfig, MockupOptions } from './mockup.js';
9
+ export type { DeviceConfig };
9
10
  /** Sentinel value that resolves to the current variant's locale or theme at runtime */
10
11
  export declare const VARIANT_PLACEHOLDER: "$variant";
11
12
  export declare const OPCODE_KINDS: readonly ["NAVIGATE", "DISMISS_OVERLAYS", "ASSERT_ROUTE", "ASSERT_SURFACE", "CLICK", "TYPE", "PRESS_KEY", "WAIT_FOR", "SLEEP", "SET_LOCALE", "SET_THEME", "SCROLL", "CAPTURE_SCREENSHOT", "BEGIN_CLIP", "END_CLIP", "HOVER", "SELECT_OPTION", "CHECK", "DOUBLE_CLICK", "DRAG", "CLONE_ELEMENT", "INJECT_MOCK_DATA", "REMOVE_ELEMENT", "SET_ATTRIBUTE"];
@@ -557,6 +558,28 @@ export interface ExecutionProgram {
557
558
  * Compiled from PresetConfig.mockDataInjection.groups by the server.
558
559
  */
559
560
  mockDataGroups?: MockDataGroup[];
561
+ /**
562
+ * Server-embedded device frame configs for every `deviceFrame` referenced by
563
+ * `variants[]`. The CLI seeds its mockup engine with these so it does not
564
+ * need direct Supabase access (end-users do not have the service role key).
565
+ * Keyed by the exact deviceFrame string used in the variants.
566
+ */
567
+ deviceConfigs?: Record<string, DeviceConfig>;
568
+ /**
569
+ * Project-level public URL used to decorate browser mockups. The CLI
570
+ * substitutes the captured origin (typically a local dev server) with this
571
+ * value via `transformBrowserUrl` before baking it into the browser bar.
572
+ * Server-resolved from `projects.public_url`.
573
+ */
574
+ publicUrl?: string;
575
+ /**
576
+ * Auth headers attached to the resolved project environment (Bearer token,
577
+ * Vercel protection bypass, x-api-key, etc.). Injected into the Playwright
578
+ * BrowserContext so requests to protected staging/preview URLs go through.
579
+ * Decrypted server-side from `project_environments.auth_headers_encrypted`
580
+ * and embedded in the signed program envelope.
581
+ */
582
+ environmentHttpHeaders?: Record<string, string>;
560
583
  }
561
584
  export interface CircuitBreakerConfig {
562
585
  /** Max recovery attempts per opcode. Default: 3 */
@@ -629,6 +652,8 @@ export interface ArtifactResult {
629
652
  altText?: string;
630
653
  /** Final URL at the time the artifact was produced */
631
654
  captureUrl?: string;
655
+ /** Document title at the time the artifact was produced (page.title()) */
656
+ pageTitle?: string;
632
657
  /** Step index that produced the artifact */
633
658
  stepIndex?: number;
634
659
  /** Human-readable label for the artifact */
@@ -855,6 +880,13 @@ export interface RuntimeAdapter {
855
880
  buffer: Buffer;
856
881
  mimeType: string;
857
882
  } | null>;
883
+ /**
884
+ * Document title of the current page (Playwright's `page.title()`). Captured
885
+ * at screenshot time and stored on the artifact metadata so browser mockups
886
+ * can render the actual page title instead of just the hostname. Optional —
887
+ * adapters that cannot resolve a title should leave this method off.
888
+ */
889
+ getPageTitle?(): Promise<string | null>;
858
890
  /**
859
891
  * Read the captured app's version from the live page (meta tag, window
860
892
  * global, or data attribute). Mirrors `extractAppVersionFromHtml` server-side
@@ -958,4 +990,3 @@ export interface RuntimeAdapter {
958
990
  selector: string;
959
991
  }): Promise<void>;
960
992
  }
961
- export {};
package/dist/mockup.d.ts CHANGED
@@ -11,7 +11,8 @@ export interface DeviceFrameDefinition {
11
11
  height: number;
12
12
  };
13
13
  }
14
- interface OrientationConfigData {
14
+ export type DeviceOrientation = 'portrait' | 'landscape';
15
+ export interface OrientationConfigData {
15
16
  screen: {
16
17
  logicalWidth: number;
17
18
  logicalHeight: number;
@@ -103,6 +104,69 @@ interface OrientationConfigData {
103
104
  right?: string;
104
105
  };
105
106
  }
107
+ export interface DeviceConfig {
108
+ id: string;
109
+ name: string;
110
+ category: DeviceCategory;
111
+ platform: string;
112
+ frameOrientation?: 'portrait' | 'landscape';
113
+ supportedOrientations?: ('portrait' | 'landscape')[];
114
+ orientations?: {
115
+ portrait?: OrientationConfigData;
116
+ landscape?: OrientationConfigData;
117
+ };
118
+ /** Row-level frame_url (shared fallback for all orientations) */
119
+ _rowFrameUrl?: string;
120
+ screen: {
121
+ logicalWidth: number;
122
+ logicalHeight: number;
123
+ scale: number;
124
+ cornerRadius: number;
125
+ };
126
+ viewport: {
127
+ width: number;
128
+ height: number;
129
+ };
130
+ safeArea?: {
131
+ top: number;
132
+ bottom: number;
133
+ left?: number;
134
+ right?: number;
135
+ };
136
+ statusBar?: {
137
+ asset: string;
138
+ height: number;
139
+ width: number;
140
+ type?: StatusBarDeviceType;
141
+ layout?: StatusBarLayout;
142
+ };
143
+ homeIndicator?: {
144
+ width: number;
145
+ height: number;
146
+ cornerRadius: number;
147
+ bottomOffset: number;
148
+ };
149
+ frame: {
150
+ type: 'png' | 'svg';
151
+ asset: string;
152
+ width: number;
153
+ height: number;
154
+ screenRect: {
155
+ x: number;
156
+ y: number;
157
+ width: number;
158
+ height: number;
159
+ };
160
+ };
161
+ frameRotation?: number;
162
+ frameBehindContent?: boolean;
163
+ windowBorder?: OrientationConfigData['windowBorder'];
164
+ frameDarkUrl?: string;
165
+ browserBarZones?: OrientationConfigData['browserBarZones'];
166
+ browserStyle?: OrientationConfigData['browserStyle'];
167
+ adminShowStatusBar?: OrientationConfigData['adminShowStatusBar'];
168
+ adminForcedSafeAreaColors?: OrientationConfigData['adminForcedSafeAreaColors'];
169
+ }
106
170
  export type MockupOrientation = 'portrait' | 'landscape';
107
171
  export interface MockupOptions {
108
172
  orientation?: MockupOrientation;
@@ -184,6 +248,7 @@ export interface ResolvedDeviceFrameDescriptor {
184
248
  disableOverlays: boolean;
185
249
  }
186
250
  export declare function invalidateDeviceConfigCache(): void;
251
+ export declare function seedDeviceConfigs(configs: Record<string, DeviceConfig> | null | undefined): void;
187
252
  export declare function resolveDeviceFrameDescriptor(id: DeviceFrameId, options?: {
188
253
  orientation?: MockupOrientation;
189
254
  }): Promise<ResolvedDeviceFrameDescriptor | null>;
@@ -191,4 +256,3 @@ export declare function rasterizeDeviceFrame(descriptor: ResolvedDeviceFrameDesc
191
256
  export declare function getDeviceFrames(): Promise<DeviceFrameDefinition[]>;
192
257
  export declare function getDeviceFrame(id: DeviceFrameId): Promise<DeviceFrameDefinition | undefined>;
193
258
  export declare function applyDeviceFrame(screenshot: Buffer, deviceId: DeviceFrameId, options?: MockupOptions): Promise<Buffer>;
194
- export {};
package/dist/mockup.js CHANGED
@@ -5,6 +5,7 @@ import { fileURLToPath } from 'url';
5
5
  import { renderStatusBarBuffer } from './status-bar-render.js';
6
6
  import { generateBrowserBarSvg } from './browser-bar.js';
7
7
  import { computeMockupLayout } from './mockup-html.js';
8
+ import { logger } from './logger.js';
8
9
  const __filename = fileURLToPath(import.meta.url);
9
10
  const __dirname = path.dirname(__filename);
10
11
  const DEVICES_DIR = path.join(__dirname, '..', 'assets', 'devices');
@@ -144,7 +145,23 @@ function resolveOrientationConfig(config, requestedOrientation) {
144
145
  adminForcedSafeAreaColors: config.adminForcedSafeAreaColors,
145
146
  };
146
147
  }
148
+ // Server-provided device configs embedded in signed execution programs. When
149
+ // set, these short-circuit `loadDeviceConfigs` — the CLI does not need direct
150
+ // Supabase access (and end-users do not have the service role key) because the
151
+ // server already vetted every device referenced by the program's variants and
152
+ // inlined the rows.
153
+ let seededDeviceConfigs = null;
154
+ export function seedDeviceConfigs(configs) {
155
+ if (!configs || Object.keys(configs).length === 0) {
156
+ seededDeviceConfigs = null;
157
+ return;
158
+ }
159
+ seededDeviceConfigs = new Map(Object.entries(configs));
160
+ }
147
161
  async function loadDeviceConfigs() {
162
+ if (seededDeviceConfigs && seededDeviceConfigs.size > 0) {
163
+ return seededDeviceConfigs;
164
+ }
148
165
  if (configCache && configCache.expiresAt > Date.now()) {
149
166
  return configCache.configs;
150
167
  }
@@ -478,12 +495,12 @@ export async function applyDeviceFrame(screenshot, deviceId, options) {
478
495
  const isBrowserDevice = config.category === 'browser';
479
496
  const os = Math.max(0.5, Math.min(4, opts.outputScale));
480
497
  const geometry = computeResolvedFrameGeometry(resolved);
481
- console.log(`[mockup] applyDeviceFrame: id=${deviceId}, category=${config.category}, orientation=${requestedOrientation}`);
482
- console.log(`[mockup] hasOrientationConfig=${!!config.orientations?.[requestedOrientation]}, orientations=${JSON.stringify(Object.keys(config.orientations ?? {}))}`);
483
- console.log(`[mockup] resolved.screen:`, JSON.stringify(resolved.screen));
484
- console.log(`[mockup] resolved.frame: w=${resolved.frame.width} h=${resolved.frame.height} asset="${resolved.frame.asset}" url=${resolved.frameUrl ? 'yes' : 'no'}`);
485
- console.log(`[mockup] resolved.safeArea:`, JSON.stringify(resolved.safeArea));
486
- console.log(`[mockup] scale=${scale}, outputScale=${os}`);
498
+ logger.debug(`[mockup] applyDeviceFrame: id=${deviceId}, category=${config.category}, orientation=${requestedOrientation}`);
499
+ logger.debug(`[mockup] hasOrientationConfig=${!!config.orientations?.[requestedOrientation]}, orientations=${JSON.stringify(Object.keys(config.orientations ?? {}))}`);
500
+ logger.debug(`[mockup] resolved.screen: ${JSON.stringify(resolved.screen)}`);
501
+ logger.debug(`[mockup] resolved.frame: w=${resolved.frame.width} h=${resolved.frame.height} asset="${resolved.frame.asset}" url=${resolved.frameUrl ? 'yes' : 'no'}`);
502
+ logger.debug(`[mockup] resolved.safeArea: ${JSON.stringify(resolved.safeArea)}`);
503
+ logger.debug(`[mockup] scale=${scale}, outputScale=${os}`);
487
504
  // Browser devices can work without a frame image
488
505
  const hasFrame = !!(resolved.frameUrl || resolved.frame.asset);
489
506
  let frameData = null;
@@ -509,7 +526,7 @@ export async function applyDeviceFrame(screenshot, deviceId, options) {
509
526
  geo.frameWidth = logicalW;
510
527
  geo.frameHeight = logicalH;
511
528
  geo.screenRect = { x: 0, y: 0, width: logicalW, height: logicalH };
512
- console.log(`[mockup] frameless browser: logicalW=${logicalW}, logicalH=${logicalH}, os=${os}, geo=${geo.frameWidth}x${geo.frameHeight}`);
529
+ logger.debug(`[mockup] frameless browser: logicalW=${logicalW}, logicalH=${logicalH}, os=${os}, geo=${geo.frameWidth}x${geo.frameHeight}`);
513
530
  }
514
531
  else if (!hasFrame && geo.frameWidth === 0 && geo.frameHeight === 0) {
515
532
  // Non-browser frameless fallback
@@ -551,21 +568,21 @@ export async function applyDeviceFrame(screenshot, deviceId, options) {
551
568
  });
552
569
  const contentW = layout.contentArea.width;
553
570
  const contentH = layout.contentArea.height;
554
- console.log(`[mockup] hasFrame=${hasFrame}, geo: fw=${geo.frameWidth} fh=${geo.frameHeight} sr=${JSON.stringify(geo.screenRect)}`);
555
- console.log(`[mockup] layout: container=${layout.containerWidth}x${layout.containerHeight} content=${contentW}x${contentH} contentArea=${JSON.stringify(layout.contentArea)}`);
571
+ logger.debug(`[mockup] hasFrame=${hasFrame}, geo: fw=${geo.frameWidth} fh=${geo.frameHeight} sr=${JSON.stringify(geo.screenRect)}`);
572
+ logger.debug(`[mockup] layout: container=${layout.containerWidth}x${layout.containerHeight} content=${contentW}x${contentH} contentArea=${JSON.stringify(layout.contentArea)}`);
556
573
  // Get incoming screenshot dimensions for logging
557
574
  const screenshotMeta = await sharp(screenshot).metadata();
558
- console.log(`[mockup] input screenshot: ${screenshotMeta.width}x${screenshotMeta.height}`);
575
+ logger.debug(`[mockup] input screenshot: ${screenshotMeta.width}x${screenshotMeta.height}`);
559
576
  const physicalContentW = Math.round(contentW * os);
560
577
  const physicalContentH = Math.round(contentH * os);
561
- console.log(`[mockup] resize target: ${physicalContentW}x${physicalContentH}`);
578
+ logger.debug(`[mockup] resize target: ${physicalContentW}x${physicalContentH}`);
562
579
  if (screenshotMeta.width
563
580
  && screenshotMeta.height
564
581
  && (Math.abs(screenshotMeta.width - physicalContentW) > 1
565
582
  || Math.abs(screenshotMeta.height - physicalContentH) > 1)) {
566
583
  const ratioX = physicalContentW / screenshotMeta.width;
567
584
  const ratioY = physicalContentH / screenshotMeta.height;
568
- console.warn(`[mockup] screenshot will be resampled: ` +
585
+ logger.debug(`[mockup] screenshot will be resampled: ` +
569
586
  `${screenshotMeta.width}x${screenshotMeta.height} -> ` +
570
587
  `${physicalContentW}x${physicalContentH} ` +
571
588
  `(x=${ratioX.toFixed(4)}, y=${ratioY.toFixed(4)}). ` +
@@ -597,7 +614,7 @@ export async function applyDeviceFrame(screenshot, deviceId, options) {
597
614
  colors.leftColor = sanitizeCssColor(opts.safeAreaLeftColor);
598
615
  if (opts.safeAreaRightColor)
599
616
  colors.rightColor = sanitizeCssColor(opts.safeAreaRightColor);
600
- console.log(`[mockup] sampled colors: top=${colors.topColor} bottom=${colors.bottomColor} left=${colors.leftColor} right=${colors.rightColor}`);
617
+ logger.debug(`[mockup] sampled colors: top=${colors.topColor} bottom=${colors.bottomColor} left=${colors.leftColor} right=${colors.rightColor}`);
601
618
  // Determine color scheme: use explicit override if provided, otherwise auto-detect from edge colors.
602
619
  // Laptops (MacBook) always use dark menu bar (white text on black background).
603
620
  const isLaptop = config.category === 'laptop';
@@ -664,7 +681,7 @@ export async function applyDeviceFrame(screenshot, deviceId, options) {
664
681
  const renderW = Math.round(geo.frameWidth);
665
682
  const renderH = Math.round(geo.frameHeight);
666
683
  const cornerRadius = (resolved.screen?.cornerRadius ?? 0) * scale;
667
- console.log(`[mockup] composeMockup: ${renderW}x${renderH} @${os}x, showBrowserBar=${isBrowserDevice}, windowBorderWidth=${wbw}`);
684
+ logger.debug(`[mockup] composeMockup: ${renderW}x${renderH} @${os}x, showBrowserBar=${isBrowserDevice}, windowBorderWidth=${wbw}`);
668
685
  // ── Sharp compositing — layer-by-layer mockup assembly ──
669
686
  // All dimensions in physical pixels (logical * os).
670
687
  const pw = Math.round(renderW * os);
@@ -708,12 +708,21 @@ async function executeOpcodeAction(opcode, opcodeIndex, adapter, artifacts, tele
708
708
  tabIconMimeType = favicon.mimeType;
709
709
  }
710
710
  }
711
+ // Capture document title so browser mockups can render the real tab
712
+ // title instead of falling back to the hostname.
713
+ let pageTitle;
714
+ if (adapter.getPageTitle) {
715
+ const resolved = await adapter.getPageTitle();
716
+ if (resolved)
717
+ pageTitle = resolved;
718
+ }
711
719
  artifacts.push({
712
720
  mediaMode: 'screenshot',
713
721
  buffer,
714
722
  mimeType: 'image/png',
715
723
  captureType: opcode.elementSelector ? 'element' : 'fullpage',
716
724
  captureUrl,
725
+ pageTitle,
717
726
  dimensions: currentVariant?.viewport,
718
727
  captureId: opcode.captureId,
719
728
  captureName: opcode.captureName ?? opcode.description,
@@ -121,9 +121,9 @@ export declare const SignedExecutionProgramEnvelopeSchema: z.ZodObject<{
121
121
  httpOnly: z.ZodBoolean;
122
122
  secure: z.ZodBoolean;
123
123
  sameSite: z.ZodEnum<{
124
- Strict: "Strict";
125
124
  Lax: "Lax";
126
125
  None: "None";
126
+ Strict: "Strict";
127
127
  }>;
128
128
  }, z.core.$strict>>;
129
129
  origins: z.ZodArray<z.ZodObject<{
@@ -1126,6 +1126,9 @@ export declare const SignedExecutionProgramEnvelopeSchema: z.ZodObject<{
1126
1126
  defaultValues: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodString>>;
1127
1127
  replaceExisting: z.ZodOptional<z.ZodBoolean>;
1128
1128
  }, z.core.$strict>>>;
1129
+ deviceConfigs: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
1130
+ publicUrl: z.ZodOptional<z.ZodString>;
1131
+ environmentHttpHeaders: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
1129
1132
  }, z.core.$strict>;
1130
1133
  signature: z.ZodString;
1131
1134
  meta: z.ZodOptional<z.ZodObject<{
@@ -102,6 +102,10 @@ export function signExecutionProgramEnvelope(params) {
102
102
  };
103
103
  }
104
104
  export function verifySignedExecutionProgramEnvelope(params) {
105
+ // The Zod schema describes `deviceConfigs` as a loose record (the server is
106
+ // the source of truth for the rich DeviceConfig shape). Cast to the strongly
107
+ // typed envelope at this trust boundary — the signature verification below
108
+ // proves the server produced this payload.
105
109
  const envelope = SignedExecutionProgramEnvelopeSchema.parse(params.envelope);
106
110
  const payload = {
107
111
  signedAt: envelope.signedAt,
@@ -1,28 +1,12 @@
1
1
  export type SkillType = 'preset';
2
- export type SkillAgent = 'claude' | 'codex' | 'cursor' | 'windsurf' | 'copilot';
3
2
  export interface SkillPlaceholderOptions {
4
3
  projectUrl?: string;
5
4
  projectId?: string;
6
5
  apiBaseUrl?: string;
7
6
  }
8
- export interface SkillExportResult {
9
- mode: 'single-file' | 'bundle';
10
- writtenPaths: string[];
11
- }
12
7
  export declare function resolveSkillAssetDir(): Promise<string>;
13
- export declare function shouldInstallSkillBundle(params: {
14
- agent?: string;
15
- outputPath?: string;
16
- type: SkillType;
17
- }): boolean;
18
8
  export declare function renderSkillSingleFile(params: {
19
9
  type: SkillType;
20
10
  agent?: string;
21
11
  placeholders: SkillPlaceholderOptions;
22
12
  }): Promise<string>;
23
- export declare function writeSkillExport(params: {
24
- type: SkillType;
25
- agent?: string;
26
- outputPath: string;
27
- placeholders: SkillPlaceholderOptions;
28
- }): Promise<SkillExportResult>;
@@ -17,6 +17,7 @@ const PRESET_SKILL_SOURCE = {
17
17
  coreFile: 'SKILL.md',
18
18
  references: [
19
19
  { relativePath: 'OPCODE-REFERENCE.md', title: 'Opcode Reference', anchor: 'reference-opcode-reference' },
20
+ { relativePath: 'references/STANDARDS.md', title: 'Prompt Charter & Quality Standards', anchor: 'reference-prompt-standards' },
20
21
  { relativePath: 'references/mock-data.md', title: 'Mock Data Injection', anchor: 'reference-mock-data-injection' },
21
22
  { relativePath: 'references/examples.md', title: 'Complete Examples', anchor: 'reference-complete-examples' },
22
23
  ],
@@ -57,23 +58,6 @@ export async function resolveSkillAssetDir() {
57
58
  }
58
59
  throw new Error('Could not find assets/skill. Make sure the autokap package is installed correctly.');
59
60
  }
60
- function inferBundleInstall(outputPath) {
61
- if (!outputPath)
62
- return false;
63
- const normalized = path.normalize(outputPath);
64
- const parts = normalized.split(path.sep).filter(Boolean);
65
- for (let index = 0; index < parts.length - 1; index += 1) {
66
- if (parts[index] === '.agents' && parts[index + 1] === 'skills') {
67
- return path.basename(normalized) === 'SKILL.md';
68
- }
69
- }
70
- return false;
71
- }
72
- export function shouldInstallSkillBundle(params) {
73
- if (params.agent?.toLowerCase() === 'codex')
74
- return true;
75
- return inferBundleInstall(params.outputPath);
76
- }
77
61
  async function readSkillFile(rootDir, relativePath, placeholders) {
78
62
  const absolutePath = path.join(rootDir, relativePath);
79
63
  const raw = await fs.readFile(absolutePath, 'utf-8');
@@ -122,38 +106,4 @@ export async function renderSkillSingleFile(params) {
122
106
  }
123
107
  return [core, ...bundledReferences].join('\n\n');
124
108
  }
125
- export async function writeSkillExport(params) {
126
- const rootDir = await resolveSkillAssetDir();
127
- const spec = getSkillSourceSpec(params.type);
128
- if (shouldInstallSkillBundle({
129
- agent: params.agent,
130
- outputPath: params.outputPath,
131
- type: params.type,
132
- })) {
133
- const destinationDir = path.dirname(params.outputPath);
134
- const writtenPaths = [];
135
- for (const relativePath of [spec.coreFile, ...spec.references.map((reference) => reference.relativePath)]) {
136
- const absolutePath = path.join(destinationDir, relativePath);
137
- const content = await readSkillFile(rootDir, relativePath, params.placeholders);
138
- await fs.mkdir(path.dirname(absolutePath), { recursive: true });
139
- await fs.writeFile(absolutePath, content, 'utf-8');
140
- writtenPaths.push(absolutePath);
141
- }
142
- return {
143
- mode: 'bundle',
144
- writtenPaths,
145
- };
146
- }
147
- const content = await renderSkillSingleFile({
148
- type: params.type,
149
- agent: params.agent,
150
- placeholders: params.placeholders,
151
- });
152
- await fs.mkdir(path.dirname(params.outputPath), { recursive: true });
153
- await fs.writeFile(params.outputPath, content, 'utf-8');
154
- return {
155
- mode: 'single-file',
156
- writtenPaths: [params.outputPath],
157
- };
158
- }
159
109
  //# sourceMappingURL=skill-packaging.js.map
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Transform a captured URL for display in the browser bar mockup.
3
+ * 1. Replace the origin with `publicUrl` (if provided).
4
+ * 2. Strip UUID v4 path segments.
5
+ */
6
+ export declare function transformBrowserUrl(capturedUrl: string, publicUrl?: string | null): string;
@@ -0,0 +1,28 @@
1
+ const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
2
+ /**
3
+ * Transform a captured URL for display in the browser bar mockup.
4
+ * 1. Replace the origin with `publicUrl` (if provided).
5
+ * 2. Strip UUID v4 path segments.
6
+ */
7
+ export function transformBrowserUrl(capturedUrl, publicUrl) {
8
+ let parsed;
9
+ try {
10
+ parsed = new URL(capturedUrl);
11
+ }
12
+ catch {
13
+ return capturedUrl;
14
+ }
15
+ if (publicUrl) {
16
+ try {
17
+ const pub = new URL(publicUrl);
18
+ parsed = new URL(`${pub.origin}${parsed.pathname}${parsed.search}${parsed.hash}`);
19
+ }
20
+ catch {
21
+ // invalid publicUrl — skip origin replacement
22
+ }
23
+ }
24
+ const segments = parsed.pathname.split("/").filter((s) => s && !UUID_RE.test(s));
25
+ parsed.pathname = "/" + segments.join("/");
26
+ return parsed.toString().replace(/\/$/, "") || parsed.origin;
27
+ }
28
+ //# sourceMappingURL=transform-browser-url.js.map
package/dist/types.d.ts CHANGED
@@ -246,6 +246,17 @@ export interface BrowserOptions {
246
246
  colorScheme?: 'light' | 'dark';
247
247
  /** Optional persisted cookies/localStorage captured during preparation. */
248
248
  storageState?: BrowserStorageState;
249
+ /**
250
+ * Extra HTTP headers injected on every navigation. Used to carry the
251
+ * environment-level auth (Bearer token, Vercel protection bypass, x-api-key,
252
+ * etc.) configured on the resolved `project_environments` row.
253
+ *
254
+ * These headers are sent to ALL requests the BrowserContext makes during
255
+ * capture, including cross-origin requests (third-party CDN, analytics).
256
+ * The dashboard surfaces a warning so users only put environment-scoped
257
+ * secrets here.
258
+ */
259
+ extraHttpHeaders?: Record<string, string>;
249
260
  }
250
261
  export interface OutscaleConfig {
251
262
  /** Uniform padding on all 4 sides (pixels). */
@@ -147,9 +147,9 @@ export declare const VideoIngestPayloadSchema: z.ZodObject<{
147
147
  httpOnly: z.ZodBoolean;
148
148
  secure: z.ZodBoolean;
149
149
  sameSite: z.ZodEnum<{
150
- Strict: "Strict";
151
150
  Lax: "Lax";
152
151
  None: "None";
152
+ Strict: "Strict";
153
153
  }>;
154
154
  }, z.core.$strict>>;
155
155
  origins: z.ZodArray<z.ZodObject<{
@@ -1152,6 +1152,9 @@ export declare const VideoIngestPayloadSchema: z.ZodObject<{
1152
1152
  defaultValues: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodString>>;
1153
1153
  replaceExisting: z.ZodOptional<z.ZodBoolean>;
1154
1154
  }, z.core.$strict>>>;
1155
+ deviceConfigs: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
1156
+ publicUrl: z.ZodOptional<z.ZodString>;
1157
+ environmentHttpHeaders: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
1155
1158
  }, z.core.$strict>;
1156
1159
  narration: z.ZodOptional<z.ZodObject<{
1157
1160
  voice: z.ZodString;
@@ -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
+ getPageTitle(): Promise<string | null>;
30
31
  detectAppVersion(): Promise<string | null>;
31
32
  getAKTree(): Promise<AKTree>;
32
33
  getPageSignals(): Promise<VideoPageSignals>;
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "autokap",
3
- "version": "1.6.1",
3
+ "version": "1.6.3",
4
4
  "description": "AI-powered CLI tool for capturing clean screenshots of websites",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -181,14 +181,15 @@
181
181
  "./program-signing": {
182
182
  "types": "./dist/program-signing.d.ts",
183
183
  "default": "./dist/program-signing.js"
184
+ },
185
+ "./transform-browser-url": {
186
+ "types": "./dist/transform-browser-url.d.ts",
187
+ "default": "./dist/transform-browser-url.js"
184
188
  }
185
189
  },
186
190
  "bin": {
187
191
  "autokap": "dist/cli.js"
188
192
  },
189
- "scripts": {
190
- "postinstall": "node -e \"try{require('child_process').execSync('npx playwright install chromium',{stdio:'inherit'})}catch(e){console.warn('AutoKap: Playwright Chromium install failed. Run npx playwright install chromium manually.',e.message||'')}\""
191
- },
192
193
  "engines": {
193
194
  "node": ">=20"
194
195
  },
@@ -203,8 +204,7 @@
203
204
  "assets/frames/MacBook*",
204
205
  "assets/frames/Status*",
205
206
  "assets/icons",
206
- "assets/cursors",
207
- "assets/skill"
207
+ "assets/cursors"
208
208
  ],
209
209
  "keywords": [
210
210
  "screenshot",
package/readme.md CHANGED
@@ -1,20 +1,23 @@
1
- # AutoKap
1
+ # AutoKap (backend package)
2
2
 
3
- AI-powered website screenshot capture across every device, language, and theme.
3
+ AI-powered website screenshot capture across every device, language, and
4
+ theme.
4
5
 
5
- ## Quick Start
6
+ This npm package (`autokap`) is the **backend** consumed by:
6
7
 
7
- ```bash
8
- # Install dependencies
9
- cd web && npm install
8
+ - the AutoKap Cloud Run service that handles
9
+ `npx autokap auto-recapture --cloud` jobs, and
10
+ - the official MCP server `@autokap/mcp`, which spawns the package as a
11
+ sub-process for local capture execution (`autokap_start_capture`).
10
12
 
11
- # Set up environment
12
- cp .env.example .env.local
13
- # Fill in NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_SUPABASE_ANON_KEY
13
+ **You probably do not need to install this package directly.** Wire your
14
+ IDE assistant to `@autokap/mcp` instead — see the per-IDE install guides
15
+ in the repository root README and the
16
+ [migration guide](https://github.com/autokap/autokap/blob/main/MIGRATION-v1-to-v2.md).
14
17
 
15
- # Run dev server
16
- npm run dev
17
- ```
18
+ The advanced commands `autokap login`, `autokap run`, `autokap doctor`,
19
+ and `autokap auto-recapture` remain available for users who need a
20
+ scriptable backend (CI/CD, custom orchestrators).
18
21
 
19
22
  ## Recapture Cloud in CI/CD
20
23