autokap 1.4.3 → 1.5.2

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.
@@ -7,6 +7,7 @@ export interface CliPublicCommandDescriptor {
7
7
  export declare const CLI_KEY_TERM = "CLI key";
8
8
  export declare const CLI_VERSION_HEADER = "x-autokap-cli-version";
9
9
  export declare const MIN_CLI_VERSION_FOR_SIGNED_PROGRAMS = "1.0.9";
10
+ export declare const MIN_CLI_VERSION_FOR_DIRECT_ARTIFACT_UPLOADS = "1.5.0";
10
11
  export declare const CLI_DEFAULT_INSTALL_COMMAND = "npm install -g autokap";
11
12
  export declare const CLI_DEFAULT_INSTALLED_SETUP_COMMAND = "autokap init --cli-key <your-cli-key>";
12
13
  export declare const CLI_DEFAULT_SETUP_COMMAND = "npx autokap@latest init --cli-key <your-cli-key>";
@@ -51,6 +52,12 @@ export interface VideoClipMetadata {
51
52
  width: number;
52
53
  height: number;
53
54
  } | null;
55
+ /**
56
+ * For TYPE opcodes captured in clipCursor mode: clip-relative ms timestamp
57
+ * of every keystroke produced by `humanType`. Drives per-keystroke keyboard
58
+ * SFX in the video compositor.
59
+ */
60
+ keystrokeOffsetsMs?: number[];
54
61
  }>;
55
62
  }
56
63
  export interface VideoAudioAsset {
@@ -79,4 +86,76 @@ export interface VideoCompletePayload {
79
86
  export interface VideoCompleteResponse {
80
87
  composeRunId: string;
81
88
  composeRunIds?: string[];
89
+ compositorTrigger?: {
90
+ triggered: boolean;
91
+ operationName?: string;
92
+ skippedReason?: string;
93
+ error?: string;
94
+ };
95
+ }
96
+ export type ArtifactUploadObjectId = "screenshotRaw" | "screenshot" | "clipGif" | "clipMp4" | "clipThumbnail" | "videoMp4" | "tabIcon";
97
+ export interface ArtifactUploadMetadata {
98
+ presetId: string;
99
+ runId: string;
100
+ variantId: string;
101
+ targetId?: string | null;
102
+ targetLabel?: string | null;
103
+ programVersion?: number | null;
104
+ compileFingerprint?: string | null;
105
+ mediaMode: "screenshot" | "clip" | "video";
106
+ mimeType: string;
107
+ captureType: "fullpage" | "element";
108
+ captureUrl: string;
109
+ lang: string;
110
+ theme: "light" | "dark";
111
+ deviceFrame?: string | null;
112
+ deviceScaleFactor?: number | null;
113
+ viewport?: {
114
+ width: number;
115
+ height: number;
116
+ } | null;
117
+ altText?: string | null;
118
+ elementSelector?: string | null;
119
+ captureId?: string | null;
120
+ captureName?: string | null;
121
+ clipId?: string | null;
122
+ clipName?: string | null;
123
+ stepDescription?: string | null;
124
+ stepIndex?: number | null;
125
+ durationMs?: number | null;
126
+ trimStartMs?: number | null;
127
+ artifactPlan?: Record<string, unknown> | null;
128
+ tabIconMimeType?: string | null;
129
+ tabIconSha256?: string | null;
130
+ }
131
+ export interface ArtifactUploadTarget {
132
+ id: ArtifactUploadObjectId;
133
+ bucket: "screenshots" | "videos";
134
+ path: string;
135
+ contentType: string;
136
+ method: "PUT";
137
+ signedUrl: string;
138
+ token?: string;
139
+ expiresAt: string;
140
+ }
141
+ export interface ArtifactUploadInitRequest {
142
+ metadata: ArtifactUploadMetadata;
143
+ }
144
+ export interface ArtifactUploadInitResponse {
145
+ uploadId: string;
146
+ uploads: ArtifactUploadTarget[];
147
+ expiresAt: string;
148
+ }
149
+ export interface ArtifactUploadedObject {
150
+ id: ArtifactUploadObjectId;
151
+ bucket: "screenshots" | "videos";
152
+ path: string;
153
+ contentType: string;
154
+ sizeBytes: number;
155
+ sha256: string;
156
+ }
157
+ export interface ArtifactUploadCompleteRequest {
158
+ uploadId: string;
159
+ metadata: ArtifactUploadMetadata;
160
+ objects: ArtifactUploadedObject[];
82
161
  }
@@ -1,6 +1,7 @@
1
1
  export const CLI_KEY_TERM = "CLI key";
2
2
  export const CLI_VERSION_HEADER = "x-autokap-cli-version";
3
3
  export const MIN_CLI_VERSION_FOR_SIGNED_PROGRAMS = "1.0.9";
4
+ export const MIN_CLI_VERSION_FOR_DIRECT_ARTIFACT_UPLOADS = "1.5.0";
4
5
  export const CLI_DEFAULT_INSTALL_COMMAND = "npm install -g autokap";
5
6
  export const CLI_DEFAULT_INSTALLED_SETUP_COMMAND = "autokap init --cli-key <your-cli-key>";
6
7
  export const CLI_DEFAULT_SETUP_COMMAND = "npx autokap@latest init --cli-key <your-cli-key>";
@@ -0,0 +1,4 @@
1
+ export declare function runDoctor(opts: {
2
+ fix: boolean;
3
+ agent?: string;
4
+ }, currentVersion: string): Promise<void>;
@@ -0,0 +1,302 @@
1
+ import { spawn } from 'node:child_process';
2
+ import fs from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import chalk from 'chalk';
5
+ import { chromium } from 'playwright';
6
+ import { logger } from './logger.js';
7
+ import { ensureFfmpegAvailable } from './clip-postprocess.js';
8
+ import { readConfig, getConfigPath } from './cli-config.js';
9
+ import { getCachedOrFetchLatest, isNewerVersion } from './version-check.js';
10
+ import { writeSkillExport } from './skill-packaging.js';
11
+ 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
+ async function pathExists(candidate) {
20
+ try {
21
+ await fs.access(candidate);
22
+ return true;
23
+ }
24
+ catch {
25
+ return false;
26
+ }
27
+ }
28
+ function checkNodeVersion() {
29
+ const major = Number.parseInt(process.version.slice(1).split('.')[0], 10);
30
+ if (Number.isFinite(major) && major >= REQUIRED_NODE_MAJOR) {
31
+ return {
32
+ name: 'Node.js',
33
+ status: 'ok',
34
+ message: `${process.version} (>= ${REQUIRED_NODE_MAJOR} required)`,
35
+ };
36
+ }
37
+ return {
38
+ name: 'Node.js',
39
+ status: 'fail',
40
+ message: `${process.version} is too old. Node ${REQUIRED_NODE_MAJOR}+ is required.`,
41
+ fixCommand: 'Install Node 20+ via nvm (`nvm install 20`), Homebrew (`brew install node`), or https://nodejs.org',
42
+ };
43
+ }
44
+ async function checkChromium() {
45
+ let executablePath = null;
46
+ try {
47
+ executablePath = chromium.executablePath();
48
+ }
49
+ catch {
50
+ executablePath = null;
51
+ }
52
+ if (executablePath && (await pathExists(executablePath))) {
53
+ return {
54
+ name: 'Chromium (Playwright)',
55
+ status: 'ok',
56
+ message: executablePath,
57
+ };
58
+ }
59
+ const installCommand = 'npx playwright install chromium';
60
+ return {
61
+ name: 'Chromium (Playwright)',
62
+ status: 'fail',
63
+ message: 'Chromium binary not found. Required for local capture (`autokap run`).',
64
+ fixCommand: installCommand,
65
+ fixFn: async () => {
66
+ await runCommandStreaming('npx', ['playwright', 'install', 'chromium']);
67
+ },
68
+ };
69
+ }
70
+ async function checkFfmpeg() {
71
+ try {
72
+ const resolved = await ensureFfmpegAvailable();
73
+ return {
74
+ name: 'ffmpeg',
75
+ status: 'ok',
76
+ message: resolved,
77
+ };
78
+ }
79
+ catch {
80
+ const installCommand = ffmpegInstallCommandForPlatform();
81
+ return {
82
+ name: 'ffmpeg',
83
+ status: 'warn',
84
+ message: 'ffmpeg not found. Required only for clip post-processing (gif/mp4/webm).',
85
+ fixCommand: installCommand,
86
+ };
87
+ }
88
+ }
89
+ function ffmpegInstallCommandForPlatform() {
90
+ switch (process.platform) {
91
+ case 'darwin':
92
+ return 'brew install ffmpeg';
93
+ case 'linux':
94
+ return 'sudo apt install ffmpeg # or your distro equivalent';
95
+ case 'win32':
96
+ return 'choco install ffmpeg # or download from https://ffmpeg.org';
97
+ default:
98
+ return 'Install ffmpeg from https://ffmpeg.org';
99
+ }
100
+ }
101
+ async function checkConfig() {
102
+ let config = null;
103
+ try {
104
+ config = await readConfig();
105
+ }
106
+ catch (err) {
107
+ return {
108
+ name: 'CLI configuration',
109
+ status: 'fail',
110
+ message: `Failed to read config: ${err.message}`,
111
+ fixCommand: 'autokap init --cli-key <key>',
112
+ };
113
+ }
114
+ if (!config) {
115
+ return {
116
+ name: 'CLI configuration',
117
+ status: 'fail',
118
+ message: `No API key found. Checked AUTOKAP_RUN_TOKEN, AUTOKAP_API_KEY, and ${getConfigPath()}.`,
119
+ fixCommand: 'autokap init --cli-key <key>',
120
+ };
121
+ }
122
+ if (!config.apiKey) {
123
+ return {
124
+ name: 'CLI configuration',
125
+ status: 'fail',
126
+ message: 'Config exists but apiKey is empty.',
127
+ fixCommand: 'autokap init --cli-key <key>',
128
+ };
129
+ }
130
+ return {
131
+ name: 'CLI configuration',
132
+ status: 'ok',
133
+ message: `apiBaseUrl=${config.apiBaseUrl}`,
134
+ };
135
+ }
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
+ }
148
+ }
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
+ };
160
+ }
161
+ const skillPath = path.join(process.cwd(), AGENT_PATHS[agent]);
162
+ if (await pathExists(skillPath)) {
163
+ return {
164
+ name: 'AI agent skill',
165
+ status: 'ok',
166
+ message: `${agent}: ${AGENT_PATHS[agent]}`,
167
+ };
168
+ }
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) {
187
+ return {
188
+ name: 'CLI version',
189
+ status: 'warn',
190
+ message: `${currentVersion} (latest version check unavailable — offline?)`,
191
+ };
192
+ }
193
+ if (isNewerVersion(latest, currentVersion)) {
194
+ return {
195
+ name: 'CLI version',
196
+ status: 'warn',
197
+ message: `${currentVersion} installed, ${latest} available.`,
198
+ fixCommand: 'npm install -g autokap@latest',
199
+ };
200
+ }
201
+ return {
202
+ name: 'CLI version',
203
+ status: 'ok',
204
+ message: `${currentVersion} (latest)`,
205
+ };
206
+ }
207
+ function runCommandStreaming(command, args) {
208
+ return new Promise((resolve, reject) => {
209
+ const child = spawn(command, args, {
210
+ stdio: 'inherit',
211
+ shell: process.platform === 'win32',
212
+ });
213
+ child.on('error', reject);
214
+ child.on('exit', code => {
215
+ if (code === 0)
216
+ resolve();
217
+ else
218
+ reject(new Error(`${command} ${args.join(' ')} exited with code ${code}`));
219
+ });
220
+ });
221
+ }
222
+ function statusBadge(status) {
223
+ switch (status) {
224
+ case 'ok':
225
+ return chalk.green('✓');
226
+ case 'warn':
227
+ return chalk.yellow('⚠');
228
+ case 'fail':
229
+ return chalk.red('✗');
230
+ }
231
+ }
232
+ function statusLabel(status) {
233
+ switch (status) {
234
+ case 'ok':
235
+ return chalk.green('OK');
236
+ case 'warn':
237
+ return chalk.yellow('WARN');
238
+ case 'fail':
239
+ return chalk.red('FAIL');
240
+ }
241
+ }
242
+ function printCheck(result) {
243
+ console.log(`${statusBadge(result.status)} ${chalk.bold(result.name)} ${chalk.gray('—')} ${statusLabel(result.status)}`);
244
+ console.log(` ${result.message}`);
245
+ if (result.fixCommand && result.status !== 'ok') {
246
+ console.log(` ${chalk.gray('Fix:')} ${chalk.cyan(result.fixCommand)}`);
247
+ }
248
+ console.log('');
249
+ }
250
+ 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);
257
+ }
258
+ console.log(chalk.bold('\nautokap doctor'));
259
+ console.log(chalk.gray(`Checking environment for autokap ${currentVersion}...\n`));
260
+ const results = [];
261
+ results.push(checkNodeVersion());
262
+ results.push(await checkChromium());
263
+ results.push(await checkFfmpeg());
264
+ results.push(await checkConfig());
265
+ results.push(await checkSkill(agentOverride));
266
+ results.push(await checkCliVersion(currentVersion));
267
+ for (const r of results) {
268
+ printCheck(r);
269
+ }
270
+ const failures = results.filter(r => r.status === 'fail');
271
+ const warnings = results.filter(r => r.status === 'warn');
272
+ if (opts.fix) {
273
+ const fixable = results.filter(r => r.status !== 'ok' && r.fixFn);
274
+ if (fixable.length === 0) {
275
+ if (failures.length === 0 && warnings.length === 0) {
276
+ logger.success('All checks passed. Nothing to fix.');
277
+ }
278
+ else {
279
+ logger.info('No auto-fixable issues. See suggested commands above.');
280
+ }
281
+ }
282
+ else {
283
+ console.log(chalk.bold(`Attempting to fix ${fixable.length} issue(s)...\n`));
284
+ for (const r of fixable) {
285
+ console.log(chalk.cyan(`→ ${r.name}`));
286
+ try {
287
+ await r.fixFn();
288
+ logger.success(`Fixed: ${r.name}`);
289
+ }
290
+ catch (err) {
291
+ logger.error(`Failed to fix ${r.name}: ${err.message}`);
292
+ }
293
+ console.log('');
294
+ }
295
+ logger.info('Re-run `autokap doctor` to verify the fixes.');
296
+ }
297
+ }
298
+ const summary = `${results.filter(r => r.status === 'ok').length} OK, ${warnings.length} warning(s), ${failures.length} failure(s)`;
299
+ console.log(chalk.bold(`Summary: ${summary}`));
300
+ process.exit(failures.length > 0 ? 1 : 0);
301
+ }
302
+ //# sourceMappingURL=cli-doctor.js.map