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.
@@ -1,199 +0,0 @@
1
- import { chromium } from 'playwright';
2
- import { logger } from './logger.js';
3
- import { ensureChromiumInstalled } from './playwright-installer.js';
4
- /**
5
- * Opens a headed Chromium window, lets the user log in, and uploads the
6
- * resulting storageState (cookies + localStorage, HttpOnly included) to the
7
- * given project credentials account.
8
- *
9
- * The session is captured continuously via polling so we don't depend on the
10
- * user explicitly clicking a button — closing the window after login is
11
- * enough. A "Save & close" button is also rendered for explicit confirmation.
12
- */
13
- export async function captureAuthSession(options) {
14
- const { apiBaseUrl, apiKey, projectId, accountId, startUrl } = options;
15
- await ensureChromiumInstalled();
16
- logger.info('[auth] Launching Chromium…');
17
- const browser = await chromium.launch({ headless: false });
18
- let context = null;
19
- let lastGoodState = null;
20
- let pollHandle = null;
21
- try {
22
- context = await browser.newContext();
23
- const page = await context.newPage();
24
- // Bridge to Node: clicking the banner button triggers this function.
25
- let userConfirmed = false;
26
- await context.exposeFunction('__autokapSaveSession', async () => {
27
- userConfirmed = true;
28
- });
29
- // Visible banner with a "Save & close" button on every page.
30
- await context.addInitScript(() => {
31
- const mountBanner = () => {
32
- if (document.getElementById('__autokap_auth_banner__'))
33
- return;
34
- const banner = document.createElement('div');
35
- banner.id = '__autokap_auth_banner__';
36
- Object.assign(banner.style, {
37
- position: 'fixed',
38
- top: '0',
39
- left: '0',
40
- right: '0',
41
- zIndex: '2147483647',
42
- background: '#111827',
43
- color: '#f9fafb',
44
- font: '500 13px/1.4 system-ui, sans-serif',
45
- padding: '8px 14px',
46
- display: 'flex',
47
- alignItems: 'center',
48
- justifyContent: 'space-between',
49
- gap: '12px',
50
- boxShadow: '0 1px 4px rgba(0,0,0,0.4)',
51
- });
52
- const label = document.createElement('span');
53
- label.textContent =
54
- '🔐 AutoKap — Log in, then click "Save & close" (or just close this window).';
55
- banner.appendChild(label);
56
- const btn = document.createElement('button');
57
- btn.textContent = 'Save & close';
58
- Object.assign(btn.style, {
59
- background: '#10b981',
60
- color: '#ffffff',
61
- border: '0',
62
- borderRadius: '6px',
63
- padding: '4px 12px',
64
- font: '600 12px/1 system-ui, sans-serif',
65
- cursor: 'pointer',
66
- });
67
- btn.addEventListener('click', async () => {
68
- btn.disabled = true;
69
- btn.textContent = 'Saving…';
70
- const win = window;
71
- if (typeof win.__autokapSaveSession === 'function') {
72
- await win.__autokapSaveSession();
73
- }
74
- });
75
- banner.appendChild(btn);
76
- document.documentElement.appendChild(banner);
77
- };
78
- if (document.readyState === 'loading') {
79
- document.addEventListener('DOMContentLoaded', mountBanner);
80
- }
81
- else {
82
- mountBanner();
83
- }
84
- });
85
- logger.info(`[auth] Opening ${startUrl} — log in when ready.`);
86
- try {
87
- await page.goto(startUrl, { waitUntil: 'domcontentloaded' });
88
- }
89
- catch (err) {
90
- logger.error(`[auth] Failed to navigate: ${err.message}`);
91
- await browser.close();
92
- process.exit(1);
93
- }
94
- // Poll the storage state every 2s so we always have a fresh snapshot
95
- // cached in memory, independent of how the user exits.
96
- pollHandle = setInterval(async () => {
97
- if (!context || !browser.isConnected())
98
- return;
99
- try {
100
- const snapshot = sanitizeStorageStateForStartUrl((await context.storageState()), startUrl);
101
- const hasAny = (snapshot.cookies?.length ?? 0) > 0 || (snapshot.origins?.length ?? 0) > 0;
102
- if (hasAny)
103
- lastGoodState = snapshot;
104
- }
105
- catch {
106
- // Context may be closing; swallow — we still have the last good state.
107
- }
108
- }, 2000);
109
- // Wait for either an explicit confirmation or window close.
110
- await new Promise((resolve) => {
111
- context.once('close', () => resolve());
112
- browser.once('disconnected', () => resolve());
113
- const tick = setInterval(() => {
114
- if (userConfirmed) {
115
- clearInterval(tick);
116
- resolve();
117
- }
118
- }, 200);
119
- });
120
- // If the user confirmed while the context is still alive, take one final
121
- // snapshot (most up-to-date) before tearing down.
122
- if (userConfirmed && browser.isConnected()) {
123
- try {
124
- lastGoodState = sanitizeStorageStateForStartUrl((await context.storageState()), startUrl);
125
- }
126
- catch {
127
- // fall back to whatever the poll captured
128
- }
129
- }
130
- if (pollHandle)
131
- clearInterval(pollHandle);
132
- if (!lastGoodState) {
133
- logger.error('[auth] No session data captured — the browser had no cookies or localStorage. Did you finish logging in before closing?');
134
- process.exit(1);
135
- }
136
- const cookieCount = lastGoodState.cookies?.length ?? 0;
137
- const originCount = lastGoodState.origins?.length ?? 0;
138
- logger.info(`[auth] Captured ${cookieCount} cookie(s), ${originCount} origin(s). Uploading…`);
139
- const res = await fetch(`${apiBaseUrl}/api/cli/projects/${projectId}/credentials/${accountId}/session`, {
140
- method: 'PUT',
141
- headers: {
142
- Authorization: `Bearer ${apiKey}`,
143
- 'Content-Type': 'application/json',
144
- },
145
- body: JSON.stringify(lastGoodState),
146
- });
147
- if (!res.ok) {
148
- const body = (await res.json().catch(() => ({ error: res.statusText })));
149
- logger.error(`[auth] Upload failed: ${body.error || res.statusText}`);
150
- process.exit(1);
151
- }
152
- logger.info('[auth] ✓ Session saved to the project credentials account.');
153
- }
154
- finally {
155
- if (pollHandle)
156
- clearInterval(pollHandle);
157
- try {
158
- if (browser.isConnected())
159
- await browser.close();
160
- }
161
- catch {
162
- // already closed
163
- }
164
- }
165
- }
166
- function sanitizeStorageStateForStartUrl(state, startUrl) {
167
- try {
168
- const parsed = new URL(startUrl);
169
- const allowedSuffix = buildAllowedCookieSuffix(parsed.hostname);
170
- const cookies = (state.cookies ?? []).filter((cookie) => {
171
- const domain = String(cookie.domain ?? '').trim().replace(/^\./, '').toLowerCase();
172
- return domain === parsed.hostname || domain.endsWith(`.${allowedSuffix}`) || domain === allowedSuffix;
173
- });
174
- const origins = (state.origins ?? []).filter((originEntry) => {
175
- try {
176
- const origin = new URL(String(originEntry.origin ?? ''));
177
- return origin.hostname === parsed.hostname
178
- || origin.hostname === allowedSuffix
179
- || origin.hostname.endsWith(`.${allowedSuffix}`);
180
- }
181
- catch {
182
- return false;
183
- }
184
- });
185
- return { cookies, origins };
186
- }
187
- catch {
188
- return state;
189
- }
190
- }
191
- function buildAllowedCookieSuffix(hostname) {
192
- const normalized = hostname.trim().toLowerCase().replace(/^\.+/, '');
193
- if (!normalized || normalized === 'localhost' || /^\d{1,3}(?:\.\d{1,3}){3}$/.test(normalized)) {
194
- return normalized;
195
- }
196
- const parts = normalized.split('.');
197
- return parts.length >= 2 ? parts.slice(-2).join('.') : normalized;
198
- }
199
- //# sourceMappingURL=auth-capture.js.map
@@ -1,5 +0,0 @@
1
- export declare function replaceSkillPlaceholders(content: string, opts: {
2
- projectUrl?: string;
3
- projectId?: string;
4
- apiBaseUrl?: string;
5
- }): string;
package/dist/cli-utils.js DELETED
@@ -1,14 +0,0 @@
1
- export function replaceSkillPlaceholders(content, opts) {
2
- let result = content;
3
- if (opts.projectUrl) {
4
- result = result.replace(/\[AUTOKAP_PROJECT_URL\]/g, opts.projectUrl);
5
- }
6
- if (opts.projectId) {
7
- result = result.replace(/\[AUTOKAP_PROJECT_ID\]/g, opts.projectId);
8
- }
9
- if (opts.apiBaseUrl) {
10
- result = result.replace(/https:\/\/autokap\.app/g, opts.apiBaseUrl);
11
- }
12
- return result;
13
- }
14
- //# sourceMappingURL=cli-utils.js.map
@@ -1,4 +0,0 @@
1
- export declare function fetchLatestVersionFromRegistry(): Promise<string | null>;
2
- export declare function isNewerVersion(latest: string, current: string): boolean;
3
- export declare function getCachedOrFetchLatest(): Promise<string | null>;
4
- export declare function displayNewVersionNoticeIfAvailable(currentVersion: string): Promise<void>;
@@ -1,102 +0,0 @@
1
- import fs from 'node:fs/promises';
2
- import path from 'node:path';
3
- import { logger } from './logger.js';
4
- import { getConfigDir } from './cli-config.js';
5
- const NPM_REGISTRY_URL = 'https://registry.npmjs.org/autokap';
6
- const CACHE_TTL_MS = 24 * 60 * 60 * 1000;
7
- const FETCH_TIMEOUT_MS = 2500;
8
- const TOTAL_BUDGET_MS = 1000;
9
- function getCachePath() {
10
- return path.join(getConfigDir(), 'version-check.json');
11
- }
12
- async function readCache() {
13
- try {
14
- const raw = await fs.readFile(getCachePath(), 'utf-8');
15
- const parsed = JSON.parse(raw);
16
- if (typeof parsed.latestVersion !== 'string' || typeof parsed.checkedAt !== 'number') {
17
- return null;
18
- }
19
- return { latestVersion: parsed.latestVersion, checkedAt: parsed.checkedAt };
20
- }
21
- catch {
22
- return null;
23
- }
24
- }
25
- async function writeCache(entry) {
26
- try {
27
- await fs.mkdir(getConfigDir(), { recursive: true });
28
- await fs.writeFile(getCachePath(), JSON.stringify(entry, null, 2), 'utf-8');
29
- }
30
- catch {
31
- // Cache write failure is non-fatal; we just won't have a cache next time
32
- }
33
- }
34
- export async function fetchLatestVersionFromRegistry() {
35
- const controller = new AbortController();
36
- const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
37
- try {
38
- const res = await fetch(NPM_REGISTRY_URL, { signal: controller.signal });
39
- if (!res.ok)
40
- return null;
41
- const data = (await res.json());
42
- return data['dist-tags']?.latest ?? null;
43
- }
44
- catch {
45
- return null;
46
- }
47
- finally {
48
- clearTimeout(timeout);
49
- }
50
- }
51
- function parseSemver(version) {
52
- const stripped = version.split('-')[0];
53
- const parts = stripped.split('.').map(Number);
54
- if (parts.length !== 3 || parts.some(n => !Number.isFinite(n)))
55
- return null;
56
- return [parts[0], parts[1], parts[2]];
57
- }
58
- export function isNewerVersion(latest, current) {
59
- const a = parseSemver(latest);
60
- const b = parseSemver(current);
61
- if (!a || !b)
62
- return false;
63
- if (a[0] !== b[0])
64
- return a[0] > b[0];
65
- if (a[1] !== b[1])
66
- return a[1] > b[1];
67
- return a[2] > b[2];
68
- }
69
- function isPreRelease(version) {
70
- return version.includes('-');
71
- }
72
- export async function getCachedOrFetchLatest() {
73
- const cache = await readCache();
74
- if (cache && Date.now() - cache.checkedAt < CACHE_TTL_MS) {
75
- return cache.latestVersion;
76
- }
77
- const latest = await fetchLatestVersionFromRegistry();
78
- if (latest) {
79
- await writeCache({ latestVersion: latest, checkedAt: Date.now() });
80
- return latest;
81
- }
82
- return cache?.latestVersion ?? null;
83
- }
84
- export async function displayNewVersionNoticeIfAvailable(currentVersion) {
85
- if (isPreRelease(currentVersion))
86
- return;
87
- try {
88
- const latest = await Promise.race([
89
- getCachedOrFetchLatest(),
90
- new Promise(resolve => setTimeout(() => resolve(null), TOTAL_BUDGET_MS)),
91
- ]);
92
- if (!latest)
93
- return;
94
- if (!isNewerVersion(latest, currentVersion))
95
- return;
96
- logger.info(`A new version of autokap (${latest}) is available, run npm install -g autokap@latest to update`);
97
- }
98
- catch {
99
- // Silent failure — the version check must never block or break the CLI
100
- }
101
- }
102
- //# sourceMappingURL=version-check.js.map