autokap 1.6.2 → 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.
- package/dist/cli-config.js +69 -25
- package/dist/cli-contract.d.ts +19 -5
- package/dist/cli-contract.js +55 -151
- package/dist/cli-doctor.d.ts +0 -1
- package/dist/cli-doctor.js +15 -143
- package/dist/cli-runner.d.ts +1 -1
- package/dist/cli-runner.js +2 -2
- package/dist/cli.js +24 -1163
- package/dist/execution-schema.d.ts +3 -3
- package/dist/program-signing.d.ts +1 -1
- package/dist/skill-packaging.d.ts +0 -16
- package/dist/skill-packaging.js +1 -51
- package/dist/video-narration-schema.d.ts +1 -1
- package/package.json +2 -6
- package/readme.md +15 -12
- package/assets/skill/OPCODE-REFERENCE.md +0 -625
- package/assets/skill/README.md +0 -38
- package/assets/skill/SKILL.md +0 -590
- package/assets/skill/references/STANDARDS.md +0 -236
- package/assets/skill/references/examples.md +0 -88
- package/assets/skill/references/mock-data.md +0 -178
- package/dist/auth-capture.d.ts +0 -17
- package/dist/auth-capture.js +0 -199
- package/dist/cli-utils.d.ts +0 -5
- package/dist/cli-utils.js +0 -14
- package/dist/version-check.d.ts +0 -4
- package/dist/version-check.js +0 -102
package/dist/cli-config.js
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
|
+
// CLI-local config helpers.
|
|
2
|
+
//
|
|
3
|
+
// NOTE: this file currently duplicates the canonical implementation in
|
|
4
|
+
// `@autokap/core/config`. The MCP server consumes the core package directly;
|
|
5
|
+
// the bundled CLI binary still ships a self-contained copy because
|
|
6
|
+
// `@autokap/core` is not yet published to npm. Once core@0.1.0 is on the
|
|
7
|
+
// registry and the published CLI bumps to declare it as a runtime dep, this
|
|
8
|
+
// file can become a thin re-export shim (just LOCAL_WS_URL + requireConfig).
|
|
9
|
+
//
|
|
10
|
+
// Hardening here must stay in lockstep with packages/core/src/config.ts:
|
|
11
|
+
// atomic writes, scheme allowlist on URL inputs, env-var precedence, and
|
|
12
|
+
// the "untrusted origin override" guard.
|
|
1
13
|
import path from 'node:path';
|
|
2
14
|
import os from 'node:os';
|
|
15
|
+
import crypto from 'node:crypto';
|
|
3
16
|
import fs from 'node:fs/promises';
|
|
4
17
|
import { logger } from './logger.js';
|
|
5
18
|
const DEFAULT_API_BASE_URL = 'https://autokap.app';
|
|
@@ -46,45 +59,76 @@ export async function readConfig() {
|
|
|
46
59
|
wsUrl,
|
|
47
60
|
};
|
|
48
61
|
}
|
|
62
|
+
// Only swallow IO / parse failures. Origin-allowlist / schema errors must
|
|
63
|
+
// bubble up so the user sees what's wrong instead of "not authenticated".
|
|
64
|
+
let raw;
|
|
49
65
|
try {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const wsUrl = envWsUrl
|
|
59
|
-
?? (envApiBaseUrl ? deriveWsUrl(apiBaseUrl) : normalizeUrl(parsed.wsUrl) ?? deriveWsUrl(apiBaseUrl));
|
|
60
|
-
assertAllowedApiOrigin(apiBaseUrl, storedApiBaseUrl, envApiBaseUrl ? API_BASE_URL_ENV_VAR : undefined);
|
|
61
|
-
if (envWsUrl) {
|
|
62
|
-
assertAllowedApiOrigin(wsUrl.replace(/^ws/, 'http'), deriveWsUrl(storedApiBaseUrl).replace(/^ws/, 'http'), WS_URL_ENV_VAR);
|
|
63
|
-
}
|
|
64
|
-
return {
|
|
65
|
-
apiKey: parsed.apiKey,
|
|
66
|
-
apiBaseUrl,
|
|
67
|
-
wsUrl,
|
|
68
|
-
};
|
|
66
|
+
raw = await fs.readFile(getConfigPath(), 'utf-8');
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
let parsed;
|
|
72
|
+
try {
|
|
73
|
+
parsed = JSON.parse(raw);
|
|
69
74
|
}
|
|
70
75
|
catch {
|
|
71
76
|
return null;
|
|
72
77
|
}
|
|
78
|
+
if (!parsed || typeof parsed !== 'object')
|
|
79
|
+
return null;
|
|
80
|
+
const record = parsed;
|
|
81
|
+
if (typeof record.apiKey !== 'string' || record.apiKey.length === 0)
|
|
82
|
+
return null;
|
|
83
|
+
const storedApiBaseUrlRaw = typeof record.apiBaseUrl === 'string' ? record.apiBaseUrl : null;
|
|
84
|
+
const storedWsUrlRaw = typeof record.wsUrl === 'string' ? record.wsUrl : null;
|
|
85
|
+
const envApiBaseUrl = normalizeUrl(process.env[API_BASE_URL_ENV_VAR]);
|
|
86
|
+
const envWsUrl = normalizeUrl(process.env[WS_URL_ENV_VAR]);
|
|
87
|
+
const storedApiBaseUrl = normalizeUrl(storedApiBaseUrlRaw) ?? DEFAULT_API_BASE_URL;
|
|
88
|
+
const apiBaseUrl = envApiBaseUrl ?? storedApiBaseUrl;
|
|
89
|
+
const wsUrl = envWsUrl ??
|
|
90
|
+
(envApiBaseUrl
|
|
91
|
+
? deriveWsUrl(apiBaseUrl)
|
|
92
|
+
: normalizeUrl(storedWsUrlRaw) ?? deriveWsUrl(apiBaseUrl));
|
|
93
|
+
assertAllowedApiOrigin(apiBaseUrl, storedApiBaseUrl, envApiBaseUrl ? API_BASE_URL_ENV_VAR : undefined);
|
|
94
|
+
if (envWsUrl) {
|
|
95
|
+
assertAllowedApiOrigin(wsUrl.replace(/^ws/, 'http'), deriveWsUrl(storedApiBaseUrl).replace(/^ws/, 'http'), WS_URL_ENV_VAR);
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
apiKey: record.apiKey,
|
|
99
|
+
apiBaseUrl,
|
|
100
|
+
wsUrl,
|
|
101
|
+
};
|
|
73
102
|
}
|
|
74
103
|
export async function writeConfig(config) {
|
|
75
104
|
const dir = getConfigDir();
|
|
76
105
|
await fs.mkdir(dir, { recursive: true });
|
|
106
|
+
if (process.platform !== 'win32') {
|
|
107
|
+
try {
|
|
108
|
+
await fs.chmod(dir, 0o700);
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
// best-effort — the file's 0600 below is the real safety
|
|
112
|
+
}
|
|
113
|
+
}
|
|
77
114
|
const configPath = getConfigPath();
|
|
78
115
|
const normalizedConfig = {
|
|
79
116
|
...config,
|
|
80
117
|
apiBaseUrl: normalizeUrl(config.apiBaseUrl) ?? DEFAULT_API_BASE_URL,
|
|
81
118
|
wsUrl: normalizeUrl(config.wsUrl) ?? deriveWsUrl(config.apiBaseUrl),
|
|
82
119
|
};
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
await fs.
|
|
87
|
-
|
|
120
|
+
// Atomic write — see packages/core/src/config.ts for the canonical impl.
|
|
121
|
+
const tmpPath = `${configPath}.${process.pid}.${crypto.randomBytes(4).toString('hex')}.tmp`;
|
|
122
|
+
try {
|
|
123
|
+
await fs.writeFile(tmpPath, JSON.stringify(normalizedConfig, null, 2), 'utf-8');
|
|
124
|
+
if (process.platform !== 'win32') {
|
|
125
|
+
await fs.chmod(tmpPath, 0o600);
|
|
126
|
+
}
|
|
127
|
+
await fs.rename(tmpPath, configPath);
|
|
128
|
+
}
|
|
129
|
+
catch (err) {
|
|
130
|
+
await fs.rm(tmpPath, { force: true }).catch(() => undefined);
|
|
131
|
+
throw err;
|
|
88
132
|
}
|
|
89
133
|
}
|
|
90
134
|
export async function deleteConfig() {
|
|
@@ -105,7 +149,7 @@ export async function requireConfig() {
|
|
|
105
149
|
process.exit(1);
|
|
106
150
|
}
|
|
107
151
|
if (!config) {
|
|
108
|
-
logger.error(`Not authenticated.
|
|
152
|
+
logger.error(`Not authenticated. Authenticate via your IDE's MCP integration (autokap_authenticate) or run \`autokap login <key>\` for CI / Cloud Run. CI may also set ${API_KEY_ENV_VAR} directly.`);
|
|
109
153
|
process.exit(1);
|
|
110
154
|
}
|
|
111
155
|
return config;
|
package/dist/cli-contract.d.ts
CHANGED
|
@@ -4,15 +4,29 @@ export interface CliPublicCommandDescriptor {
|
|
|
4
4
|
summary: string;
|
|
5
5
|
docsDescriptionKey?: string;
|
|
6
6
|
}
|
|
7
|
-
export declare const
|
|
7
|
+
export declare const API_KEY_TERM = "API key";
|
|
8
|
+
/**
|
|
9
|
+
* Back-compat alias for internal callers that still import `CLI_KEY_TERM`.
|
|
10
|
+
* Points at the same string as `API_KEY_TERM`.
|
|
11
|
+
*/
|
|
12
|
+
export declare const CLI_KEY_TERM = "API key";
|
|
13
|
+
/** HTTP header sent by the bundled CLI binary on every request. */
|
|
8
14
|
export declare const CLI_VERSION_HEADER = "x-autokap-cli-version";
|
|
9
15
|
export declare const MIN_CLI_VERSION_FOR_SIGNED_PROGRAMS = "1.0.9";
|
|
10
16
|
export declare const MIN_CLI_VERSION_FOR_DIRECT_ARTIFACT_UPLOADS = "1.5.0";
|
|
11
|
-
export declare const CLI_DEFAULT_INSTALL_COMMAND = "
|
|
12
|
-
export declare const CLI_DEFAULT_INSTALLED_SETUP_COMMAND = "autokap
|
|
13
|
-
export declare const CLI_DEFAULT_SETUP_COMMAND = "npx autokap@latest
|
|
14
|
-
export declare const CLI_ADVANCED_SKILL_COMMAND = "autokap skill --agent <agent>";
|
|
17
|
+
export declare const CLI_DEFAULT_INSTALL_COMMAND = "npx -y autokap@latest";
|
|
18
|
+
export declare const CLI_DEFAULT_INSTALLED_SETUP_COMMAND = "autokap login <your-api-key>";
|
|
19
|
+
export declare const CLI_DEFAULT_SETUP_COMMAND = "npx -y autokap@latest login <your-api-key>";
|
|
15
20
|
export declare const CLI_FALLBACK_PROGRAM_COMMAND = "autokap run <preset-id> --program <file>";
|
|
21
|
+
/** Bare MCP runner. Used by generic clients and as the npx command. */
|
|
22
|
+
export declare const MCP_INSTALL_COMMAND = "npx -y @autokap/mcp";
|
|
23
|
+
/** One-shot install command for Claude Code (CLI flag `claude mcp add`). */
|
|
24
|
+
export declare const MCP_CLAUDE_CODE_COMMAND = "claude mcp add autokap -- npx -y @autokap/mcp";
|
|
25
|
+
/**
|
|
26
|
+
* JSON snippet to merge into the IDE-specific MCP config file (Cursor, Codex,
|
|
27
|
+
* Windsurf, Cline...). Format follows the shared `mcpServers` schema.
|
|
28
|
+
*/
|
|
29
|
+
export declare const MCP_CONFIG_SNIPPET_JSON: string;
|
|
16
30
|
export declare function buildCliRunCommand(presetId: string, options?: {
|
|
17
31
|
local?: boolean;
|
|
18
32
|
env?: string;
|
package/dist/cli-contract.js
CHANGED
|
@@ -1,12 +1,54 @@
|
|
|
1
|
-
export const
|
|
1
|
+
export const API_KEY_TERM = "API key";
|
|
2
|
+
/**
|
|
3
|
+
* Back-compat alias for internal callers that still import `CLI_KEY_TERM`.
|
|
4
|
+
* Points at the same string as `API_KEY_TERM`.
|
|
5
|
+
*/
|
|
6
|
+
export const CLI_KEY_TERM = API_KEY_TERM;
|
|
7
|
+
/** HTTP header sent by the bundled CLI binary on every request. */
|
|
2
8
|
export const CLI_VERSION_HEADER = "x-autokap-cli-version";
|
|
3
9
|
export const MIN_CLI_VERSION_FOR_SIGNED_PROGRAMS = "1.0.9";
|
|
4
10
|
export const MIN_CLI_VERSION_FOR_DIRECT_ARTIFACT_UPLOADS = "1.5.0";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
11
|
+
// MCP-first install. The bundled CLI binary ships only for CI / Cloud Run;
|
|
12
|
+
// recommend `npx -y autokap@latest` over a global install to keep version
|
|
13
|
+
// drift contained and avoid the npm supply-chain anxiety the migration was
|
|
14
|
+
// designed to remove. The `CLI_DEFAULT_INSTALL_COMMAND` alias is retained for
|
|
15
|
+
// legacy doc surfaces that still want a single shell snippet.
|
|
16
|
+
export const CLI_DEFAULT_INSTALL_COMMAND = "npx -y autokap@latest";
|
|
17
|
+
// `autokap init` was removed in v2. The setup command now uses the surviving
|
|
18
|
+
// `autokap login` for the same purpose (store the API key in ~/.autokap/config.json).
|
|
19
|
+
// MCP-first workflows should use `autokap_authenticate` from the IDE instead.
|
|
20
|
+
export const CLI_DEFAULT_INSTALLED_SETUP_COMMAND = "autokap login <your-api-key>";
|
|
21
|
+
export const CLI_DEFAULT_SETUP_COMMAND = "npx -y autokap@latest login <your-api-key>";
|
|
22
|
+
// `autokap skill --agent` was removed in v2. The skill ships as the MCP resource
|
|
23
|
+
// `autokap://skill/preset-spec` (or `autokap_get_skill` tool fallback). The
|
|
24
|
+
// legacy `CLI_ADVANCED_SKILL_COMMAND` constant that pointed at the removed
|
|
25
|
+
// command was dropped entirely — it had no consumers and any future doc page
|
|
26
|
+
// surfacing it would mislead users into typing a non-existent command.
|
|
9
27
|
export const CLI_FALLBACK_PROGRAM_COMMAND = "autokap run <preset-id> --program <file>";
|
|
28
|
+
// ── MCP-first install contract ──────────────────────────────────────
|
|
29
|
+
//
|
|
30
|
+
// AutoKap is primarily exposed as a Model Context Protocol server via the
|
|
31
|
+
// `@autokap/mcp` package. Every supported IDE installs the server by adding a
|
|
32
|
+
// single stdio entry that runs `npx -y @autokap/mcp` — the constants below
|
|
33
|
+
// are the canonical shell forms used across onboarding surfaces (wizard,
|
|
34
|
+
// modal, banner, docs). For per-IDE payloads (config paths, snippets, notes),
|
|
35
|
+
// use `getMcpInstallEntries()` from `web/lib/site-discovery.ts`.
|
|
36
|
+
/** Bare MCP runner. Used by generic clients and as the npx command. */
|
|
37
|
+
export const MCP_INSTALL_COMMAND = "npx -y @autokap/mcp";
|
|
38
|
+
/** One-shot install command for Claude Code (CLI flag `claude mcp add`). */
|
|
39
|
+
export const MCP_CLAUDE_CODE_COMMAND = "claude mcp add autokap -- npx -y @autokap/mcp";
|
|
40
|
+
/**
|
|
41
|
+
* JSON snippet to merge into the IDE-specific MCP config file (Cursor, Codex,
|
|
42
|
+
* Windsurf, Cline...). Format follows the shared `mcpServers` schema.
|
|
43
|
+
*/
|
|
44
|
+
export const MCP_CONFIG_SNIPPET_JSON = JSON.stringify({
|
|
45
|
+
mcpServers: {
|
|
46
|
+
autokap: {
|
|
47
|
+
command: "npx",
|
|
48
|
+
args: ["-y", "@autokap/mcp"],
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
}, null, 2);
|
|
10
52
|
export function buildCliRunCommand(presetId, options = {}) {
|
|
11
53
|
const flags = [
|
|
12
54
|
options.local ? " --local" : "",
|
|
@@ -17,170 +59,32 @@ export function buildCliRunCommand(presetId, options = {}) {
|
|
|
17
59
|
return `autokap run${flags} ${presetId}`;
|
|
18
60
|
}
|
|
19
61
|
export function buildCliInstalledSetupCommand(cliKey) {
|
|
20
|
-
return `autokap
|
|
62
|
+
return `autokap login ${cliKey}`;
|
|
21
63
|
}
|
|
22
64
|
export const CLI_PUBLIC_COMMANDS = [
|
|
23
|
-
{
|
|
24
|
-
id: "init",
|
|
25
|
-
command: "autokap init",
|
|
26
|
-
summary: "Authenticate and install the AI skill in one step",
|
|
27
|
-
docsDescriptionKey: "cliCmdInit",
|
|
28
|
-
},
|
|
29
65
|
{
|
|
30
66
|
id: "login",
|
|
31
67
|
command: "autokap login <key>",
|
|
32
|
-
summary: "Authenticate with your
|
|
68
|
+
summary: "Authenticate with your API key (used by Cloud Run + CI; from the IDE, call autokap_authenticate via MCP instead)",
|
|
33
69
|
docsDescriptionKey: "cliCmdLogin",
|
|
34
70
|
},
|
|
35
|
-
{
|
|
36
|
-
id: "logout",
|
|
37
|
-
command: "autokap logout",
|
|
38
|
-
summary: "Remove stored credentials",
|
|
39
|
-
docsDescriptionKey: "cliCmdLogout",
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
id: "whoami",
|
|
43
|
-
command: "autokap whoami",
|
|
44
|
-
summary: "Show the account linked to the current CLI key",
|
|
45
|
-
docsDescriptionKey: "cliCmdWhoami",
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
id: "ping",
|
|
49
|
-
command: "autokap ping",
|
|
50
|
-
summary: "Verify CLI connection and confirm the stored key is still valid",
|
|
51
|
-
docsDescriptionKey: "cliCmdPing",
|
|
52
|
-
},
|
|
53
71
|
{
|
|
54
72
|
id: "run",
|
|
55
73
|
command: "autokap run <preset-id> --env local",
|
|
56
|
-
summary: "Run a preset capture using local Playwright",
|
|
74
|
+
summary: "Run a preset capture using local Playwright (also spawned by autokap_start_capture from the MCP server)",
|
|
57
75
|
docsDescriptionKey: "cliCmdRun",
|
|
58
76
|
},
|
|
59
77
|
{
|
|
60
78
|
id: "auto-recapture",
|
|
61
79
|
command: "autokap auto-recapture --project <project-id> --env local",
|
|
62
|
-
summary: "Run every preset enabled for Recapture Cloud in a project",
|
|
80
|
+
summary: "Run every preset enabled for Recapture Cloud in a project (the canonical surface for Cloud Run + CI; MCP exposes autokap_start_auto_recapture for end users)",
|
|
63
81
|
docsDescriptionKey: "cliCmdAutoRecapture",
|
|
64
82
|
},
|
|
65
83
|
{
|
|
66
|
-
id: "
|
|
67
|
-
command: "autokap
|
|
68
|
-
summary: "
|
|
69
|
-
docsDescriptionKey: "
|
|
70
|
-
},
|
|
71
|
-
{
|
|
72
|
-
id: "run-dry",
|
|
73
|
-
command: "autokap run <preset-id> --dry",
|
|
74
|
-
summary: "Dry run: execute the opcode program without capturing or uploading (0 credits charged)",
|
|
75
|
-
docsDescriptionKey: "cliCmdRunDry",
|
|
76
|
-
},
|
|
77
|
-
{
|
|
78
|
-
id: "project-list",
|
|
79
|
-
command: "autokap project list",
|
|
80
|
-
summary: "List accessible projects as JSON",
|
|
81
|
-
docsDescriptionKey: "cliCmdProjectList",
|
|
82
|
-
},
|
|
83
|
-
{
|
|
84
|
-
id: "project-get",
|
|
85
|
-
command: "autokap project get <project-id>",
|
|
86
|
-
summary: "Get a single project as JSON",
|
|
87
|
-
docsDescriptionKey: "cliCmdProjectGet",
|
|
88
|
-
},
|
|
89
|
-
{
|
|
90
|
-
id: "project-create",
|
|
91
|
-
command: "autokap project create --name <name> --url <url>",
|
|
92
|
-
summary: "Create a project and print its ID",
|
|
93
|
-
docsDescriptionKey: "cliCmdProjectCreate",
|
|
94
|
-
},
|
|
95
|
-
{
|
|
96
|
-
id: "preset-info",
|
|
97
|
-
command: "autokap preset info <preset-id>",
|
|
98
|
-
summary: "Get structured JSON with endpoints, variants, and URL parameters",
|
|
99
|
-
docsDescriptionKey: "cliCmdPresetInfo",
|
|
100
|
-
},
|
|
101
|
-
{
|
|
102
|
-
id: "capture-list",
|
|
103
|
-
command: "autokap capture list",
|
|
104
|
-
summary: "List captures as JSON",
|
|
105
|
-
docsDescriptionKey: "cliCmdCaptureList",
|
|
106
|
-
},
|
|
107
|
-
{
|
|
108
|
-
id: "usage",
|
|
109
|
-
command: "autokap usage",
|
|
110
|
-
summary: "Show the current billing-period usage as JSON",
|
|
111
|
-
docsDescriptionKey: "cliCmdUsage",
|
|
112
|
-
},
|
|
113
|
-
{
|
|
114
|
-
id: "endpoints-list",
|
|
115
|
-
command: "autokap endpoints list --preset <id>",
|
|
116
|
-
summary: "List dev link endpoints as JSON or table",
|
|
117
|
-
docsDescriptionKey: "cliCmdEndpointsList",
|
|
118
|
-
},
|
|
119
|
-
{
|
|
120
|
-
id: "endpoints-get",
|
|
121
|
-
command: "autokap endpoints get <endpoint-id>",
|
|
122
|
-
summary: "Get a single dev link endpoint as JSON",
|
|
123
|
-
docsDescriptionKey: "cliCmdEndpointsGet",
|
|
124
|
-
},
|
|
125
|
-
{
|
|
126
|
-
id: "endpoints-export",
|
|
127
|
-
command: "autokap endpoints export --preset <id>",
|
|
128
|
-
summary: "Export dev link endpoints as JSON or CSV",
|
|
129
|
-
docsDescriptionKey: "cliCmdEndpointsExport",
|
|
130
|
-
},
|
|
131
|
-
{
|
|
132
|
-
id: "auth-capture",
|
|
133
|
-
command: "autokap auth capture <project-id>",
|
|
134
|
-
summary: "Capture a browser session for a session account",
|
|
135
|
-
docsDescriptionKey: "cliCmdAuthCapture",
|
|
136
|
-
},
|
|
137
|
-
{
|
|
138
|
-
id: "auth-account-list",
|
|
139
|
-
command: "autokap auth account list <project-id>",
|
|
140
|
-
summary: "List project credential accounts as JSON",
|
|
141
|
-
docsDescriptionKey: "cliCmdAuthAccountList",
|
|
142
|
-
},
|
|
143
|
-
{
|
|
144
|
-
id: "auth-account-create",
|
|
145
|
-
command: "autokap auth account create <project-id> --name <name>",
|
|
146
|
-
summary: "Create a project credential account and print its ID",
|
|
147
|
-
docsDescriptionKey: "cliCmdAuthAccountCreate",
|
|
148
|
-
},
|
|
149
|
-
{
|
|
150
|
-
id: "auth-account-update",
|
|
151
|
-
command: "autokap auth account update <project-id> --account <name-or-id>",
|
|
152
|
-
summary: "Update a project credential account",
|
|
153
|
-
docsDescriptionKey: "cliCmdAuthAccountUpdate",
|
|
154
|
-
},
|
|
155
|
-
{
|
|
156
|
-
id: "auth-account-delete",
|
|
157
|
-
command: "autokap auth account delete <project-id> --account <name-or-id>",
|
|
158
|
-
summary: "Delete an unused project credential account",
|
|
159
|
-
docsDescriptionKey: "cliCmdAuthAccountDelete",
|
|
160
|
-
},
|
|
161
|
-
{
|
|
162
|
-
id: "auth-session-clear",
|
|
163
|
-
command: "autokap auth session clear <project-id> --account <name-or-id>",
|
|
164
|
-
summary: "Clear the stored browser session for a session account",
|
|
165
|
-
docsDescriptionKey: "cliCmdAuthSessionClear",
|
|
166
|
-
},
|
|
167
|
-
{
|
|
168
|
-
id: "proxy-register",
|
|
169
|
-
command: "autokap proxy register --project <id> --proxy-url <url>",
|
|
170
|
-
summary: "Register a user-side proxy for dev links",
|
|
171
|
-
docsDescriptionKey: "cliCmdProxyRegister",
|
|
172
|
-
},
|
|
173
|
-
{
|
|
174
|
-
id: "proxy-verify",
|
|
175
|
-
command: "autokap proxy verify --project <id>",
|
|
176
|
-
summary: "Verify the configured proxy for a project",
|
|
177
|
-
docsDescriptionKey: "cliCmdProxyVerify",
|
|
178
|
-
},
|
|
179
|
-
{
|
|
180
|
-
id: "skill",
|
|
181
|
-
command: "autokap skill",
|
|
182
|
-
summary: "Export the AI agent skill bundle",
|
|
183
|
-
docsDescriptionKey: "cliCmdSkill",
|
|
84
|
+
id: "doctor",
|
|
85
|
+
command: "autokap doctor",
|
|
86
|
+
summary: "Run environment diagnostics (Node version, config, API key validity, Chromium cache, ffmpeg)",
|
|
87
|
+
docsDescriptionKey: "cliCmdDoctor",
|
|
184
88
|
},
|
|
185
89
|
{
|
|
186
90
|
id: "version",
|
package/dist/cli-doctor.d.ts
CHANGED
package/dist/cli-doctor.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
117
|
+
fixCommand: 'autokap login <key>',
|
|
128
118
|
};
|
|
129
119
|
}
|
|
130
120
|
return {
|
|
@@ -133,78 +123,6 @@ 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
|
-
}
|
|
148
|
-
}
|
|
149
|
-
return null;
|
|
150
|
-
}
|
|
151
|
-
async function parseSkillVersion(skillPath) {
|
|
152
|
-
try {
|
|
153
|
-
const raw = await fs.readFile(skillPath, 'utf8');
|
|
154
|
-
const match = raw.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
155
|
-
if (!match)
|
|
156
|
-
return null;
|
|
157
|
-
const yamlText = match[1];
|
|
158
|
-
for (const line of yamlText.split(/\r?\n/)) {
|
|
159
|
-
const m = line.match(/^\s*version\s*:\s*(.+?)\s*$/);
|
|
160
|
-
if (m) {
|
|
161
|
-
return m[1].replace(/^["']|["']$/g, '').trim() || null;
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
return null;
|
|
165
|
-
}
|
|
166
|
-
catch {
|
|
167
|
-
return null;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
async function checkSkill(extras, agentOverride) {
|
|
171
|
-
const agent = agentOverride ?? (await detectAgent());
|
|
172
|
-
extras.agentDetected = agent;
|
|
173
|
-
if (!agent) {
|
|
174
|
-
return {
|
|
175
|
-
name: 'AI agent skill',
|
|
176
|
-
status: 'warn',
|
|
177
|
-
message: 'No agent directory detected (.claude, .agents, .cursor, .windsurf, .github/instructions). Skipping skill check.',
|
|
178
|
-
fixCommand: 'autokap init # or autokap skill --agent <name> to install',
|
|
179
|
-
};
|
|
180
|
-
}
|
|
181
|
-
const skillPath = path.join(process.cwd(), AGENT_PATHS[agent]);
|
|
182
|
-
if (await pathExists(skillPath)) {
|
|
183
|
-
extras.skillPath = AGENT_PATHS[agent];
|
|
184
|
-
extras.skillVersion = await parseSkillVersion(skillPath);
|
|
185
|
-
const versionSuffix = extras.skillVersion ? ` (v${extras.skillVersion})` : '';
|
|
186
|
-
return {
|
|
187
|
-
name: 'AI agent skill',
|
|
188
|
-
status: 'ok',
|
|
189
|
-
message: `${agent}: ${AGENT_PATHS[agent]}${versionSuffix}`,
|
|
190
|
-
};
|
|
191
|
-
}
|
|
192
|
-
extras.skillPath = AGENT_PATHS[agent];
|
|
193
|
-
return {
|
|
194
|
-
name: 'AI agent skill',
|
|
195
|
-
status: 'warn',
|
|
196
|
-
message: `${agent} agent detected but skill is missing at ${AGENT_PATHS[agent]}.`,
|
|
197
|
-
fixCommand: `autokap skill --agent ${agent}`,
|
|
198
|
-
fixFn: async () => {
|
|
199
|
-
await writeSkillExport({
|
|
200
|
-
type: 'preset',
|
|
201
|
-
agent,
|
|
202
|
-
outputPath: AGENT_PATHS[agent],
|
|
203
|
-
placeholders: {},
|
|
204
|
-
});
|
|
205
|
-
},
|
|
206
|
-
};
|
|
207
|
-
}
|
|
208
126
|
async function checkCliKeyValid() {
|
|
209
127
|
let config = null;
|
|
210
128
|
try {
|
|
@@ -215,68 +133,46 @@ async function checkCliKeyValid() {
|
|
|
215
133
|
}
|
|
216
134
|
if (!config || !config.apiKey) {
|
|
217
135
|
return {
|
|
218
|
-
name: '
|
|
136
|
+
name: 'API key valid',
|
|
219
137
|
status: 'warn',
|
|
220
|
-
message: 'No
|
|
221
|
-
fixCommand: 'autokap
|
|
138
|
+
message: 'No API key configured locally — skipping remote validation.',
|
|
139
|
+
fixCommand: 'autokap login <key> # or call autokap_authenticate via your MCP-enabled IDE',
|
|
222
140
|
};
|
|
223
141
|
}
|
|
224
142
|
try {
|
|
225
143
|
const res = await fetch(`${config.apiBaseUrl}/api/cli/validate`, {
|
|
226
144
|
headers: { Authorization: `Bearer ${config.apiKey}` },
|
|
145
|
+
signal: AbortSignal.timeout(15_000),
|
|
227
146
|
});
|
|
228
147
|
if (res.ok) {
|
|
229
148
|
return {
|
|
230
|
-
name: '
|
|
149
|
+
name: 'API key valid',
|
|
231
150
|
status: 'ok',
|
|
232
151
|
message: `Key validated against ${config.apiBaseUrl}.`,
|
|
233
152
|
};
|
|
234
153
|
}
|
|
235
154
|
if (res.status === 401 || res.status === 403) {
|
|
236
155
|
return {
|
|
237
|
-
name: '
|
|
156
|
+
name: 'API key valid',
|
|
238
157
|
status: 'fail',
|
|
239
|
-
message: `Server rejected the
|
|
240
|
-
fixCommand: 'autokap
|
|
158
|
+
message: `Server rejected the API key (HTTP ${res.status}). It was likely revoked or rotated.`,
|
|
159
|
+
fixCommand: 'autokap login <new-key>',
|
|
241
160
|
};
|
|
242
161
|
}
|
|
243
162
|
return {
|
|
244
|
-
name: '
|
|
163
|
+
name: 'API key valid',
|
|
245
164
|
status: 'warn',
|
|
246
165
|
message: `Validation endpoint returned HTTP ${res.status}.`,
|
|
247
166
|
};
|
|
248
167
|
}
|
|
249
168
|
catch (err) {
|
|
250
169
|
return {
|
|
251
|
-
name: '
|
|
170
|
+
name: 'API key valid',
|
|
252
171
|
status: 'warn',
|
|
253
172
|
message: `Cannot reach validation endpoint: ${err.message}`,
|
|
254
173
|
};
|
|
255
174
|
}
|
|
256
175
|
}
|
|
257
|
-
async function checkCliVersion(currentVersion) {
|
|
258
|
-
const latest = await getCachedOrFetchLatest();
|
|
259
|
-
if (!latest) {
|
|
260
|
-
return {
|
|
261
|
-
name: 'CLI version',
|
|
262
|
-
status: 'warn',
|
|
263
|
-
message: `${currentVersion} (latest version check unavailable — offline?)`,
|
|
264
|
-
};
|
|
265
|
-
}
|
|
266
|
-
if (isNewerVersion(latest, currentVersion)) {
|
|
267
|
-
return {
|
|
268
|
-
name: 'CLI version',
|
|
269
|
-
status: 'warn',
|
|
270
|
-
message: `${currentVersion} installed, ${latest} available.`,
|
|
271
|
-
fixCommand: 'npm install -g autokap@latest',
|
|
272
|
-
};
|
|
273
|
-
}
|
|
274
|
-
return {
|
|
275
|
-
name: 'CLI version',
|
|
276
|
-
status: 'ok',
|
|
277
|
-
message: `${currentVersion} (latest)`,
|
|
278
|
-
};
|
|
279
|
-
}
|
|
280
176
|
function runCommandStreaming(command, args) {
|
|
281
177
|
return new Promise((resolve, reject) => {
|
|
282
178
|
const child = spawn(command, args, {
|
|
@@ -320,15 +216,12 @@ function printCheck(result) {
|
|
|
320
216
|
}
|
|
321
217
|
console.log('');
|
|
322
218
|
}
|
|
323
|
-
function buildJsonReport(results,
|
|
219
|
+
function buildJsonReport(results, currentVersion) {
|
|
324
220
|
const ok = results.filter(r => r.status === 'ok').length;
|
|
325
221
|
const warn = results.filter(r => r.status === 'warn').length;
|
|
326
222
|
const fail = results.filter(r => r.status === 'fail').length;
|
|
327
223
|
return {
|
|
328
224
|
cli_version: currentVersion,
|
|
329
|
-
agent_detected: extras.agentDetected,
|
|
330
|
-
skill_path: extras.skillPath,
|
|
331
|
-
skill_version: extras.skillVersion,
|
|
332
225
|
checks: results.map(r => ({
|
|
333
226
|
name: r.name,
|
|
334
227
|
status: r.status,
|
|
@@ -340,42 +233,21 @@ function buildJsonReport(results, extras, currentVersion) {
|
|
|
340
233
|
};
|
|
341
234
|
}
|
|
342
235
|
export async function runDoctor(opts, currentVersion) {
|
|
343
|
-
const agentOverride = opts.agent
|
|
344
|
-
? opts.agent.toLowerCase()
|
|
345
|
-
: undefined;
|
|
346
236
|
const jsonMode = Boolean(opts.json);
|
|
347
|
-
if (agentOverride && !(agentOverride in AGENT_PATHS)) {
|
|
348
|
-
if (jsonMode) {
|
|
349
|
-
process.stdout.write(JSON.stringify({
|
|
350
|
-
error: `Unknown agent "${opts.agent}". Supported: ${Object.keys(AGENT_PATHS).join(', ')}`,
|
|
351
|
-
}) + '\n');
|
|
352
|
-
}
|
|
353
|
-
else {
|
|
354
|
-
logger.error(`Unknown agent "${opts.agent}". Supported: ${Object.keys(AGENT_PATHS).join(', ')}`);
|
|
355
|
-
}
|
|
356
|
-
process.exit(1);
|
|
357
|
-
}
|
|
358
237
|
if (!jsonMode) {
|
|
359
238
|
console.log(chalk.bold('\nautokap doctor'));
|
|
360
239
|
console.log(chalk.gray(`Checking environment for autokap ${currentVersion}...\n`));
|
|
361
240
|
}
|
|
362
|
-
const extras = {
|
|
363
|
-
agentDetected: null,
|
|
364
|
-
skillPath: null,
|
|
365
|
-
skillVersion: null,
|
|
366
|
-
};
|
|
367
241
|
const results = [];
|
|
368
242
|
results.push(checkNodeVersion());
|
|
369
243
|
results.push(await checkChromium());
|
|
370
244
|
results.push(await checkFfmpeg());
|
|
371
245
|
results.push(await checkConfig());
|
|
372
246
|
results.push(await checkCliKeyValid());
|
|
373
|
-
results.push(await checkSkill(extras, agentOverride));
|
|
374
|
-
results.push(await checkCliVersion(currentVersion));
|
|
375
247
|
const failures = results.filter(r => r.status === 'fail');
|
|
376
248
|
const warnings = results.filter(r => r.status === 'warn');
|
|
377
249
|
if (jsonMode) {
|
|
378
|
-
const report = buildJsonReport(results,
|
|
250
|
+
const report = buildJsonReport(results, currentVersion);
|
|
379
251
|
process.stdout.write(JSON.stringify(report, null, 2) + '\n');
|
|
380
252
|
process.exit(failures.length > 0 ? 1 : 0);
|
|
381
253
|
}
|