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
@@ -1,21 +1,11 @@
1
1
  import { spawn } from 'node:child_process';
2
2
  import fs from 'node:fs/promises';
3
- import path from 'node:path';
4
3
  import chalk from 'chalk';
5
4
  import { chromium } from 'playwright';
6
5
  import { logger } from './logger.js';
7
6
  import { ensureFfmpegAvailable } from './clip-postprocess.js';
8
7
  import { readConfig, getConfigPath } from './cli-config.js';
9
- import { getCachedOrFetchLatest, isNewerVersion } from './version-check.js';
10
- import { writeSkillExport } from './skill-packaging.js';
11
8
  const REQUIRED_NODE_MAJOR = 20;
12
- const AGENT_PATHS = {
13
- claude: '.claude/commands/autokap-preset.md',
14
- codex: '.agents/skills/autokap-preset/SKILL.md',
15
- cursor: '.cursor/rules/autokap-preset.md',
16
- windsurf: '.windsurf/rules/autokap-preset.md',
17
- copilot: '.github/instructions/autokap-preset.instructions.md',
18
- };
19
9
  async function pathExists(candidate) {
20
10
  try {
21
11
  await fs.access(candidate);
@@ -108,7 +98,7 @@ async function checkConfig() {
108
98
  name: 'CLI configuration',
109
99
  status: 'fail',
110
100
  message: `Failed to read config: ${err.message}`,
111
- fixCommand: 'autokap init --cli-key <key>',
101
+ fixCommand: 'autokap login <key> # or call autokap_authenticate via your MCP-enabled IDE',
112
102
  };
113
103
  }
114
104
  if (!config) {
@@ -116,7 +106,7 @@ async function checkConfig() {
116
106
  name: 'CLI configuration',
117
107
  status: 'fail',
118
108
  message: `No API key found. Checked AUTOKAP_RUN_TOKEN, AUTOKAP_API_KEY, and ${getConfigPath()}.`,
119
- fixCommand: 'autokap init --cli-key <key>',
109
+ fixCommand: 'autokap login <key> # or call autokap_authenticate via your MCP-enabled IDE',
120
110
  };
121
111
  }
122
112
  if (!config.apiKey) {
@@ -124,7 +114,7 @@ async function checkConfig() {
124
114
  name: 'CLI configuration',
125
115
  status: 'fail',
126
116
  message: 'Config exists but apiKey is empty.',
127
- fixCommand: 'autokap init --cli-key <key>',
117
+ fixCommand: 'autokap login <key>',
128
118
  };
129
119
  }
130
120
  return {
@@ -133,76 +123,55 @@ async function checkConfig() {
133
123
  message: `apiBaseUrl=${config.apiBaseUrl}`,
134
124
  };
135
125
  }
136
- async function detectAgent() {
137
- const detectors = [
138
- ['.claude', 'claude'],
139
- ['.agents', 'codex'],
140
- ['.cursor', 'cursor'],
141
- ['.windsurf', 'windsurf'],
142
- ['.github/instructions', 'copilot'],
143
- ];
144
- for (const [dir, key] of detectors) {
145
- if (await pathExists(path.join(process.cwd(), dir))) {
146
- return key;
147
- }
126
+ async function checkCliKeyValid() {
127
+ let config = null;
128
+ try {
129
+ config = await readConfig();
148
130
  }
149
- return null;
150
- }
151
- async function checkSkill(agentOverride) {
152
- const agent = agentOverride ?? (await detectAgent());
153
- if (!agent) {
154
- return {
155
- name: 'AI agent skill',
156
- status: 'warn',
157
- message: 'No agent directory detected (.claude, .agents, .cursor, .windsurf, .github/instructions). Skipping skill check.',
158
- fixCommand: 'autokap init # or autokap skill --agent <name> to install',
159
- };
131
+ catch {
132
+ config = null;
160
133
  }
161
- const skillPath = path.join(process.cwd(), AGENT_PATHS[agent]);
162
- if (await pathExists(skillPath)) {
134
+ if (!config || !config.apiKey) {
163
135
  return {
164
- name: 'AI agent skill',
165
- status: 'ok',
166
- message: `${agent}: ${AGENT_PATHS[agent]}`,
136
+ name: 'API key valid',
137
+ status: 'warn',
138
+ message: 'No API key configured locally — skipping remote validation.',
139
+ fixCommand: 'autokap login <key> # or call autokap_authenticate via your MCP-enabled IDE',
167
140
  };
168
141
  }
169
- return {
170
- name: 'AI agent skill',
171
- status: 'warn',
172
- message: `${agent} agent detected but skill is missing at ${AGENT_PATHS[agent]}.`,
173
- fixCommand: `autokap skill --agent ${agent}`,
174
- fixFn: async () => {
175
- await writeSkillExport({
176
- type: 'preset',
177
- agent,
178
- outputPath: AGENT_PATHS[agent],
179
- placeholders: {},
180
- });
181
- },
182
- };
183
- }
184
- async function checkCliVersion(currentVersion) {
185
- const latest = await getCachedOrFetchLatest();
186
- if (!latest) {
142
+ try {
143
+ const res = await fetch(`${config.apiBaseUrl}/api/cli/validate`, {
144
+ headers: { Authorization: `Bearer ${config.apiKey}` },
145
+ signal: AbortSignal.timeout(15_000),
146
+ });
147
+ if (res.ok) {
148
+ return {
149
+ name: 'API key valid',
150
+ status: 'ok',
151
+ message: `Key validated against ${config.apiBaseUrl}.`,
152
+ };
153
+ }
154
+ if (res.status === 401 || res.status === 403) {
155
+ return {
156
+ name: 'API key valid',
157
+ status: 'fail',
158
+ message: `Server rejected the API key (HTTP ${res.status}). It was likely revoked or rotated.`,
159
+ fixCommand: 'autokap login <new-key>',
160
+ };
161
+ }
187
162
  return {
188
- name: 'CLI version',
163
+ name: 'API key valid',
189
164
  status: 'warn',
190
- message: `${currentVersion} (latest version check unavailable — offline?)`,
165
+ message: `Validation endpoint returned HTTP ${res.status}.`,
191
166
  };
192
167
  }
193
- if (isNewerVersion(latest, currentVersion)) {
168
+ catch (err) {
194
169
  return {
195
- name: 'CLI version',
170
+ name: 'API key valid',
196
171
  status: 'warn',
197
- message: `${currentVersion} installed, ${latest} available.`,
198
- fixCommand: 'npm install -g autokap@latest',
172
+ message: `Cannot reach validation endpoint: ${err.message}`,
199
173
  };
200
174
  }
201
- return {
202
- name: 'CLI version',
203
- status: 'ok',
204
- message: `${currentVersion} (latest)`,
205
- };
206
175
  }
207
176
  function runCommandStreaming(command, args) {
208
177
  return new Promise((resolve, reject) => {
@@ -247,28 +216,44 @@ function printCheck(result) {
247
216
  }
248
217
  console.log('');
249
218
  }
219
+ function buildJsonReport(results, currentVersion) {
220
+ const ok = results.filter(r => r.status === 'ok').length;
221
+ const warn = results.filter(r => r.status === 'warn').length;
222
+ const fail = results.filter(r => r.status === 'fail').length;
223
+ return {
224
+ cli_version: currentVersion,
225
+ checks: results.map(r => ({
226
+ name: r.name,
227
+ status: r.status,
228
+ message: r.message,
229
+ fixable: Boolean(r.fixFn),
230
+ fix_command: r.fixCommand ?? null,
231
+ })),
232
+ summary: { ok, warn, fail, all_ok: fail === 0 && warn === 0 },
233
+ };
234
+ }
250
235
  export async function runDoctor(opts, currentVersion) {
251
- const agentOverride = opts.agent
252
- ? opts.agent.toLowerCase()
253
- : undefined;
254
- if (agentOverride && !(agentOverride in AGENT_PATHS)) {
255
- logger.error(`Unknown agent "${opts.agent}". Supported: ${Object.keys(AGENT_PATHS).join(', ')}`);
256
- process.exit(1);
236
+ const jsonMode = Boolean(opts.json);
237
+ if (!jsonMode) {
238
+ console.log(chalk.bold('\nautokap doctor'));
239
+ console.log(chalk.gray(`Checking environment for autokap ${currentVersion}...\n`));
257
240
  }
258
- console.log(chalk.bold('\nautokap doctor'));
259
- console.log(chalk.gray(`Checking environment for autokap ${currentVersion}...\n`));
260
241
  const results = [];
261
242
  results.push(checkNodeVersion());
262
243
  results.push(await checkChromium());
263
244
  results.push(await checkFfmpeg());
264
245
  results.push(await checkConfig());
265
- results.push(await checkSkill(agentOverride));
266
- results.push(await checkCliVersion(currentVersion));
246
+ results.push(await checkCliKeyValid());
247
+ const failures = results.filter(r => r.status === 'fail');
248
+ const warnings = results.filter(r => r.status === 'warn');
249
+ if (jsonMode) {
250
+ const report = buildJsonReport(results, currentVersion);
251
+ process.stdout.write(JSON.stringify(report, null, 2) + '\n');
252
+ process.exit(failures.length > 0 ? 1 : 0);
253
+ }
267
254
  for (const r of results) {
268
255
  printCheck(r);
269
256
  }
270
- const failures = results.filter(r => r.status === 'fail');
271
- const warnings = results.filter(r => r.status === 'warn');
272
257
  if (opts.fix) {
273
258
  const fixable = results.filter(r => r.status !== 'ok' && r.fixFn);
274
259
  if (fixable.length === 0) {
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Entry point for deterministic execution from the CLI.
5
5
  * Flow:
6
- * 1. Authenticate via stored CLI key
6
+ * 1. Authenticate via stored API key
7
7
  * 2. Fetch compiled program from server
8
8
  * 3. Validate program against Zod schema
9
9
  * 4. Launch local Playwright browser per variant
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Entry point for deterministic execution from the CLI.
5
5
  * Flow:
6
- * 1. Authenticate via stored CLI key
6
+ * 1. Authenticate via stored API key
7
7
  * 2. Fetch compiled program from server
8
8
  * 3. Validate program against Zod schema
9
9
  * 4. Launch local Playwright browser per variant
@@ -25,7 +25,8 @@ 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 } from './mockup.js';
28
+ import { applyDeviceFrame, seedDeviceConfigs } from './mockup.js';
29
+ import { transformBrowserUrl } from './transform-browser-url.js';
29
30
  import { localizeStatusBar } from './status-bar-l10n.js';
30
31
  import { logger } from './logger.js';
31
32
  import { callLLM } from './llm-provider.js';
@@ -166,6 +167,11 @@ export async function runCapture(options) {
166
167
  presetId: options.presetId,
167
168
  };
168
169
  }
170
+ // Custom device frames live in Supabase (table `device_mockups`) and the
171
+ // end-user CLI does not have the service role key. The server pre-fetches
172
+ // every device referenced by `variants[].deviceFrame` and embeds the rows
173
+ // here; we seed mockup.ts so the export pipeline can apply them locally.
174
+ seedDeviceConfigs(program.deviceConfigs ?? null);
169
175
  const runId = randomUUID();
170
176
  let videoAudioAssets;
171
177
  let videoAudioAssetsByLocale;
@@ -248,6 +254,7 @@ export async function runCapture(options) {
248
254
  lang: variant.locale,
249
255
  colorScheme: variant.theme,
250
256
  storageState: program.preconditions.storageState,
257
+ extraHttpHeaders: program.environmentHttpHeaders,
251
258
  };
252
259
  let recordingDir;
253
260
  let browser;
@@ -884,6 +891,9 @@ async function uploadArtifactMultipart(config, program, runId, job, filename) {
884
891
  formData.append('mimeType', artifact.mimeType);
885
892
  formData.append('captureType', artifact.captureType ?? 'fullpage');
886
893
  formData.append('captureUrl', artifact.captureUrl ?? program.baseUrl);
894
+ if (artifact.pageTitle) {
895
+ formData.append('pageTitle', artifact.pageTitle);
896
+ }
887
897
  formData.append('lang', variantSpec?.locale ?? 'en');
888
898
  formData.append('theme', variantSpec?.theme ?? 'light');
889
899
  if (variantSpec?.deviceFrame) {
@@ -961,6 +971,7 @@ async function prepareDirectArtifactUpload(params) {
961
971
  mimeType: artifact.mimeType,
962
972
  captureType: artifact.captureType ?? 'fullpage',
963
973
  captureUrl: artifact.captureUrl ?? program.baseUrl,
974
+ pageTitle: artifact.pageTitle ?? null,
964
975
  lang: variantSpec?.locale ?? 'en',
965
976
  theme: variantSpec?.theme ?? 'light',
966
977
  deviceFrame: variantSpec?.deviceFrame ?? null,
@@ -1039,7 +1050,7 @@ async function prepareScreenshotBufferForDirectUpload(input, metadata, program,
1039
1050
  ?? 2,
1040
1051
  showStatusBar: artifactPlan.applyStatusBar ?? false,
1041
1052
  statusBar: localizeStatusBar({}, metadata.lang),
1042
- browserBar: buildCliBrowserBar(metadata.captureUrl, metadata.theme, tabIcon),
1053
+ browserBar: buildCliBrowserBar(metadata.captureUrl, metadata.theme, tabIcon, { publicUrl: program.publicUrl, pageTitle: metadata.pageTitle ?? null }),
1043
1054
  });
1044
1055
  }
1045
1056
  if (artifactPlan?.format?.screenshotFormat === 'jpeg') {
@@ -1122,14 +1133,16 @@ function normalizeCliDeviceScaleFactor(value) {
1122
1133
  return null;
1123
1134
  return Math.max(0.5, Math.min(4, Number(value)));
1124
1135
  }
1125
- function buildCliBrowserBar(captureUrl, colorScheme, tabIcon) {
1136
+ function buildCliBrowserBar(captureUrl, colorScheme, tabIcon, options = {}) {
1126
1137
  if (!captureUrl)
1127
1138
  return undefined;
1139
+ const displayUrl = transformBrowserUrl(captureUrl, options.publicUrl);
1140
+ const explicitTitle = options.pageTitle?.trim() || null;
1128
1141
  try {
1129
- const parsed = new URL(captureUrl);
1142
+ const parsed = new URL(displayUrl);
1130
1143
  return {
1131
- url: captureUrl,
1132
- pageTitle: parsed.hostname,
1144
+ url: displayUrl,
1145
+ pageTitle: explicitTitle ?? parsed.hostname,
1133
1146
  colorScheme,
1134
1147
  tabIconUrl: tabIcon
1135
1148
  ? `data:${tabIcon.mimeType};base64,${tabIcon.buffer.toString('base64')}`
@@ -1138,8 +1151,8 @@ function buildCliBrowserBar(captureUrl, colorScheme, tabIcon) {
1138
1151
  }
1139
1152
  catch {
1140
1153
  return {
1141
- url: captureUrl,
1142
- pageTitle: captureUrl,
1154
+ url: displayUrl,
1155
+ pageTitle: explicitTitle ?? displayUrl,
1143
1156
  colorScheme,
1144
1157
  ...(tabIcon
1145
1158
  ? { tabIconUrl: `data:${tabIcon.mimeType};base64,${tabIcon.buffer.toString('base64')}` }
@@ -1230,7 +1243,7 @@ async function formatServerError(response, requestedUrl) {
1230
1243
  `server returned 404 for ${requestedUrl}.`,
1231
1244
  `The configured AutoKap server (${safeOrigin(requestedUrl)}) does not expose this CLI route yet.`,
1232
1245
  'This usually means the web app deployment is older than the CLI.',
1233
- `Point the CLI at your local dev server with \`autokap run --local <preset-id>\`, \`${API_BASE_URL_ENV_VAR}=http://localhost:3000 autokap run <preset-id>\`, or re-initialize against a local server with \`autokap init --cli-key <key> --api-base-url http://localhost:3000\`.`,
1246
+ `Point the CLI at your local dev server with \`autokap run --local <preset-id>\`, \`${API_BASE_URL_ENV_VAR}=http://localhost:3000 autokap run <preset-id>\`, or re-authenticate via your IDE's MCP integration (autokap_authenticate). From CI, set \`${API_BASE_URL_ENV_VAR}=http://localhost:3000\` and run \`autokap login <key>\`.`,
1234
1247
  ].join(' ');
1235
1248
  }
1236
1249
  if (isHtml) {