@vibetechnologies/chrome-sync 0.4.0
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/README.md +78 -0
- package/dist/api.d.ts +46 -0
- package/dist/api.js +152 -0
- package/dist/api.js.map +1 -0
- package/dist/cli.d.ts +15 -0
- package/dist/cli.js +133 -0
- package/dist/cli.js.map +1 -0
- package/dist/extract.d.ts +40 -0
- package/dist/extract.js +228 -0
- package/dist/extract.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +22 -0
- package/dist/server.js +87 -0
- package/dist/server.js.map +1 -0
- package/package.json +42 -0
- package/src/api.ts +189 -0
- package/src/cli.ts +162 -0
- package/src/extract.ts +321 -0
- package/src/index.ts +3 -0
- package/src/server.ts +116 -0
- package/tsconfig.json +15 -0
package/README.md
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# @vibetechnologies/browser-sync
|
|
2
|
+
|
|
3
|
+
Sync browser cookies and sessions from your local Chrome to your OpenClaw cloud browser.
|
|
4
|
+
|
|
5
|
+
## Why
|
|
6
|
+
|
|
7
|
+
Cloud browsers can't log into Google, GitHub, or other sites that detect automation. Instead of fighting detection, bring your existing authenticated session to the cloud.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install -g @vibetechnologies/browser-sync
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
### 1. Authenticate
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
browser-sync login --token <your-api-token>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### 2. Push cookies to cloud browser
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# Sync Google cookies
|
|
27
|
+
browser-sync push --domains google.com,gmail.com
|
|
28
|
+
|
|
29
|
+
# Sync multiple services
|
|
30
|
+
browser-sync push --domains google.com,github.com,linkedin.com
|
|
31
|
+
|
|
32
|
+
# Use a specific Chrome profile
|
|
33
|
+
browser-sync push --domains google.com --profile "Profile 1"
|
|
34
|
+
|
|
35
|
+
# Dry run — see what would be synced
|
|
36
|
+
browser-sync push --domains google.com --dry-run
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### 3. List Chrome profiles
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
browser-sync profiles
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## How it works
|
|
46
|
+
|
|
47
|
+
1. Reads and decrypts cookies from your local Chrome cookie database
|
|
48
|
+
2. Sends them (encrypted in transit) to the OpenClaw API
|
|
49
|
+
3. Server injects cookies into your tenant's cloud Chrome via CDP `Network.setCookies`
|
|
50
|
+
4. Your cloud browser now has your authenticated sessions
|
|
51
|
+
|
|
52
|
+
## Supported platforms
|
|
53
|
+
|
|
54
|
+
| OS | Cookie decryption |
|
|
55
|
+
|----|-------------------|
|
|
56
|
+
| macOS | Keychain → AES-128-CBC |
|
|
57
|
+
| Linux | libsecret / fallback → AES-128-CBC |
|
|
58
|
+
| Windows | DPAPI → AES-256-GCM (requires `win-dpapi`) |
|
|
59
|
+
|
|
60
|
+
## Security
|
|
61
|
+
|
|
62
|
+
- Cookies are only sent to your authenticated tenant
|
|
63
|
+
- Transport uses HTTPS
|
|
64
|
+
- Local auth token stored with 0600 permissions at `~/.config/browser-sync/auth.json`
|
|
65
|
+
- No cookies are stored server-side — they're injected directly into the browser process
|
|
66
|
+
|
|
67
|
+
## Programmatic API
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
import { extractStorageState, pushCookies, login } from "@vibetechnologies/browser-sync";
|
|
71
|
+
|
|
72
|
+
// Extract Google cookies from local Chrome
|
|
73
|
+
const state = await extractStorageState([".google.com", ".gmail.com"]);
|
|
74
|
+
|
|
75
|
+
// Push to cloud
|
|
76
|
+
await login("https://console.openclaw.vibebrowser.app", "your-token");
|
|
77
|
+
await pushCookies(state);
|
|
78
|
+
```
|
package/dist/api.d.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API client for syncing cookies to OpenClaw tenant cloud browser.
|
|
3
|
+
*
|
|
4
|
+
* Flow:
|
|
5
|
+
* 1. CLI starts localhost callback server, opens browser to /auth/cli
|
|
6
|
+
* 2. User authenticates on the web (Telegram OAuth or session)
|
|
7
|
+
* 3. Server redirects to localhost callback with token + username
|
|
8
|
+
* 4. CLI saves credentials, user runs `chrome-sync push`
|
|
9
|
+
*/
|
|
10
|
+
import type { StorageState } from "./extract.js";
|
|
11
|
+
interface AuthConfig {
|
|
12
|
+
apiUrl: string;
|
|
13
|
+
token: string;
|
|
14
|
+
username: string;
|
|
15
|
+
subdomain: string;
|
|
16
|
+
}
|
|
17
|
+
/** Store auth credentials */
|
|
18
|
+
export declare function saveAuth(config: AuthConfig): void;
|
|
19
|
+
/** Load saved auth credentials */
|
|
20
|
+
export declare function loadAuth(): AuthConfig | null;
|
|
21
|
+
/** Clear saved auth */
|
|
22
|
+
export declare function clearAuth(): void;
|
|
23
|
+
/**
|
|
24
|
+
* Login via browser OAuth callback flow.
|
|
25
|
+
*
|
|
26
|
+
* 1. Starts a localhost HTTP server on a random port
|
|
27
|
+
* 2. Opens browser to apiUrl/auth/cli?callback_port=<port>
|
|
28
|
+
* 3. User authenticates on the web
|
|
29
|
+
* 4. Server redirects to http://localhost:<port>/callback?token=...&username=...&subdomain=...
|
|
30
|
+
* 5. CLI saves credentials and closes
|
|
31
|
+
*/
|
|
32
|
+
export declare function loginViaBrowser(apiUrl?: string): Promise<AuthConfig>;
|
|
33
|
+
/**
|
|
34
|
+
* Login with a direct token (for CI/automation).
|
|
35
|
+
*/
|
|
36
|
+
export declare function loginWithToken(apiUrl: string, token: string): Promise<AuthConfig>;
|
|
37
|
+
/**
|
|
38
|
+
* Push cookies to the tenant's cloud browser.
|
|
39
|
+
*/
|
|
40
|
+
export declare function pushCookies(storageState: StorageState, options?: {
|
|
41
|
+
subdomain?: string;
|
|
42
|
+
}): Promise<{
|
|
43
|
+
injected: number;
|
|
44
|
+
errors: string[];
|
|
45
|
+
}>;
|
|
46
|
+
export {};
|
package/dist/api.js
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API client for syncing cookies to OpenClaw tenant cloud browser.
|
|
3
|
+
*
|
|
4
|
+
* Flow:
|
|
5
|
+
* 1. CLI starts localhost callback server, opens browser to /auth/cli
|
|
6
|
+
* 2. User authenticates on the web (Telegram OAuth or session)
|
|
7
|
+
* 3. Server redirects to localhost callback with token + username
|
|
8
|
+
* 4. CLI saves credentials, user runs `chrome-sync push`
|
|
9
|
+
*/
|
|
10
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync } from "node:fs";
|
|
11
|
+
import { createServer } from "node:http";
|
|
12
|
+
import { homedir } from "node:os";
|
|
13
|
+
import { join } from "node:path";
|
|
14
|
+
const CONFIG_DIR = join(homedir(), ".config", "chrome-sync");
|
|
15
|
+
const TOKEN_FILE = join(CONFIG_DIR, "auth.json");
|
|
16
|
+
/** Store auth credentials */
|
|
17
|
+
export function saveAuth(config) {
|
|
18
|
+
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
19
|
+
writeFileSync(TOKEN_FILE, JSON.stringify(config, null, 2), { mode: 0o600 });
|
|
20
|
+
}
|
|
21
|
+
/** Load saved auth credentials */
|
|
22
|
+
export function loadAuth() {
|
|
23
|
+
if (!existsSync(TOKEN_FILE))
|
|
24
|
+
return null;
|
|
25
|
+
try {
|
|
26
|
+
return JSON.parse(readFileSync(TOKEN_FILE, "utf-8"));
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/** Clear saved auth */
|
|
33
|
+
export function clearAuth() {
|
|
34
|
+
if (existsSync(TOKEN_FILE)) {
|
|
35
|
+
writeFileSync(TOKEN_FILE, "", { mode: 0o600 });
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Login via browser OAuth callback flow.
|
|
40
|
+
*
|
|
41
|
+
* 1. Starts a localhost HTTP server on a random port
|
|
42
|
+
* 2. Opens browser to apiUrl/auth/cli?callback_port=<port>
|
|
43
|
+
* 3. User authenticates on the web
|
|
44
|
+
* 4. Server redirects to http://localhost:<port>/callback?token=...&username=...&subdomain=...
|
|
45
|
+
* 5. CLI saves credentials and closes
|
|
46
|
+
*/
|
|
47
|
+
export async function loginViaBrowser(apiUrl = "https://console.openclaw.vibebrowser.app") {
|
|
48
|
+
return new Promise((resolve, reject) => {
|
|
49
|
+
const server = createServer((req, res) => {
|
|
50
|
+
const url = new URL(req.url ?? "/", `http://localhost`);
|
|
51
|
+
if (url.pathname === "/callback") {
|
|
52
|
+
const token = url.searchParams.get("token");
|
|
53
|
+
const username = url.searchParams.get("username");
|
|
54
|
+
const subdomain = url.searchParams.get("subdomain");
|
|
55
|
+
if (!token) {
|
|
56
|
+
res.writeHead(400, { "Content-Type": "text/html" });
|
|
57
|
+
res.end("<html><body><h2>Authentication failed</h2><p>No token received. Close this window and try again.</p></body></html>");
|
|
58
|
+
server.close();
|
|
59
|
+
reject(new Error("No token received from server"));
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const config = {
|
|
63
|
+
apiUrl,
|
|
64
|
+
token,
|
|
65
|
+
username: username || "user",
|
|
66
|
+
subdomain: subdomain || "",
|
|
67
|
+
};
|
|
68
|
+
saveAuth(config);
|
|
69
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
70
|
+
res.end(`<html><body><h2>✓ Authenticated as ${config.username}</h2><p>You can close this window and return to the terminal.</p></body></html>`);
|
|
71
|
+
server.close();
|
|
72
|
+
resolve(config);
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
res.writeHead(404);
|
|
76
|
+
res.end();
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
server.listen(0, "127.0.0.1", () => {
|
|
80
|
+
const addr = server.address();
|
|
81
|
+
if (!addr || typeof addr === "string") {
|
|
82
|
+
server.close();
|
|
83
|
+
reject(new Error("Failed to start callback server"));
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const port = addr.port;
|
|
87
|
+
const authUrl = `${apiUrl}/auth/cli?callback_port=${port}`;
|
|
88
|
+
// Open browser
|
|
89
|
+
import("node:child_process").then(({ exec }) => {
|
|
90
|
+
const openCmd = process.platform === "darwin"
|
|
91
|
+
? `open "${authUrl}"`
|
|
92
|
+
: process.platform === "win32"
|
|
93
|
+
? `start "" "${authUrl}"`
|
|
94
|
+
: `xdg-open "${authUrl}"`;
|
|
95
|
+
exec(openCmd);
|
|
96
|
+
});
|
|
97
|
+
console.log(` Opening browser for authentication...`);
|
|
98
|
+
console.log(` If it doesn't open, visit: ${authUrl}`);
|
|
99
|
+
});
|
|
100
|
+
// Timeout after 2 minutes
|
|
101
|
+
setTimeout(() => {
|
|
102
|
+
server.close();
|
|
103
|
+
reject(new Error("Authentication timed out (2 minutes). Try again."));
|
|
104
|
+
}, 120_000);
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Login with a direct token (for CI/automation).
|
|
109
|
+
*/
|
|
110
|
+
export async function loginWithToken(apiUrl, token) {
|
|
111
|
+
const res = await fetch(`${apiUrl}/admin/api/users`, {
|
|
112
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
113
|
+
});
|
|
114
|
+
if (!res.ok) {
|
|
115
|
+
throw new Error(`Authentication failed: ${res.status} ${res.statusText}`);
|
|
116
|
+
}
|
|
117
|
+
const config = {
|
|
118
|
+
apiUrl,
|
|
119
|
+
token,
|
|
120
|
+
username: "admin",
|
|
121
|
+
subdomain: "",
|
|
122
|
+
};
|
|
123
|
+
saveAuth(config);
|
|
124
|
+
return config;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Push cookies to the tenant's cloud browser.
|
|
128
|
+
*/
|
|
129
|
+
export async function pushCookies(storageState, options) {
|
|
130
|
+
const auth = loadAuth();
|
|
131
|
+
if (!auth) {
|
|
132
|
+
throw new Error("Not authenticated. Run `chrome-sync login` first.");
|
|
133
|
+
}
|
|
134
|
+
const subdomain = options?.subdomain || auth.subdomain;
|
|
135
|
+
const res = await fetch(`${auth.apiUrl}/admin/api/browser-sync/cookies`, {
|
|
136
|
+
method: "POST",
|
|
137
|
+
headers: {
|
|
138
|
+
Authorization: `Bearer ${auth.token}`,
|
|
139
|
+
"Content-Type": "application/json",
|
|
140
|
+
},
|
|
141
|
+
body: JSON.stringify({
|
|
142
|
+
storageState,
|
|
143
|
+
subdomain: subdomain || undefined,
|
|
144
|
+
}),
|
|
145
|
+
});
|
|
146
|
+
if (!res.ok) {
|
|
147
|
+
const body = await res.text();
|
|
148
|
+
throw new Error(`Failed to push cookies: ${res.status} — ${body}`);
|
|
149
|
+
}
|
|
150
|
+
return res.json();
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=api.js.map
|
package/dist/api.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* @vibetechnologies/chrome-sync CLI
|
|
4
|
+
*
|
|
5
|
+
* Sync browser cookies from local Chrome to OpenClaw cloud browser.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* chrome-sync login
|
|
9
|
+
* chrome-sync login --token <ADMIN_SECRET>
|
|
10
|
+
* chrome-sync push --domains google.com,github.com
|
|
11
|
+
* chrome-sync push --profile "Profile 1"
|
|
12
|
+
* chrome-sync profiles
|
|
13
|
+
* chrome-sync logout
|
|
14
|
+
*/
|
|
15
|
+
export {};
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* @vibetechnologies/chrome-sync CLI
|
|
4
|
+
*
|
|
5
|
+
* Sync browser cookies from local Chrome to OpenClaw cloud browser.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* chrome-sync login
|
|
9
|
+
* chrome-sync login --token <ADMIN_SECRET>
|
|
10
|
+
* chrome-sync push --domains google.com,github.com
|
|
11
|
+
* chrome-sync push --profile "Profile 1"
|
|
12
|
+
* chrome-sync profiles
|
|
13
|
+
* chrome-sync logout
|
|
14
|
+
*/
|
|
15
|
+
import { Command } from "commander";
|
|
16
|
+
import { extractStorageState } from "./extract.js";
|
|
17
|
+
import { loginViaBrowser, loginWithToken, pushCookies, loadAuth, clearAuth } from "./api.js";
|
|
18
|
+
const program = new Command();
|
|
19
|
+
program
|
|
20
|
+
.name("chrome-sync")
|
|
21
|
+
.description("Sync browser cookies and sessions from local Chrome to OpenClaw cloud browser")
|
|
22
|
+
.version("0.4.0");
|
|
23
|
+
program
|
|
24
|
+
.command("login")
|
|
25
|
+
.description("Authenticate with OpenClaw (opens browser)")
|
|
26
|
+
.option("--token <token>", "Use a direct API token instead of browser auth")
|
|
27
|
+
.option("--api-url <url>", "OpenClaw API URL", "https://console.openclaw.vibebrowser.app")
|
|
28
|
+
.action(async (opts) => {
|
|
29
|
+
try {
|
|
30
|
+
let config;
|
|
31
|
+
if (opts.token) {
|
|
32
|
+
config = await loginWithToken(opts.apiUrl, opts.token);
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
config = await loginViaBrowser(opts.apiUrl);
|
|
36
|
+
}
|
|
37
|
+
console.log(`✓ Authenticated as ${config.username}`);
|
|
38
|
+
if (config.subdomain) {
|
|
39
|
+
console.log(` Tenant: ${config.subdomain}`);
|
|
40
|
+
}
|
|
41
|
+
console.log(` API: ${config.apiUrl}`);
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
console.error(`✗ Login failed: ${err.message}`);
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
program
|
|
49
|
+
.command("push")
|
|
50
|
+
.description("Extract and push cookies to cloud browser")
|
|
51
|
+
.option("--domains <domains>", "Comma-separated domains to sync (omit for all cookies)")
|
|
52
|
+
.option("--profile <profile>", "Chrome profile name", "Default")
|
|
53
|
+
.option("--subdomain <subdomain>", "Target tenant subdomain")
|
|
54
|
+
.option("--dry-run", "Extract cookies but don't push", false)
|
|
55
|
+
.action(async (opts) => {
|
|
56
|
+
try {
|
|
57
|
+
// Check auth first
|
|
58
|
+
let auth = loadAuth();
|
|
59
|
+
if (!auth) {
|
|
60
|
+
console.log("Not authenticated. Starting login...\n");
|
|
61
|
+
auth = await loginViaBrowser();
|
|
62
|
+
console.log(`✓ Authenticated as ${auth.username}\n`);
|
|
63
|
+
}
|
|
64
|
+
const domains = opts.domains
|
|
65
|
+
? opts.domains
|
|
66
|
+
.split(",")
|
|
67
|
+
.map((d) => d.trim())
|
|
68
|
+
.map((d) => (d.startsWith(".") ? d : `.${d}`))
|
|
69
|
+
: [];
|
|
70
|
+
const label = domains.length > 0 ? domains.join(", ") : "ALL";
|
|
71
|
+
console.log(`Extracting cookies for: ${label} (profile: ${opts.profile})`);
|
|
72
|
+
const storageState = await extractStorageState(domains, opts.profile);
|
|
73
|
+
console.log(` Found ${storageState.cookies.length} cookies`);
|
|
74
|
+
if (storageState.cookies.length === 0) {
|
|
75
|
+
console.log(" No cookies found. Make sure you're logged into these sites in Chrome.");
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
if (opts.dryRun) {
|
|
79
|
+
console.log("\n Dry run — cookies extracted but not pushed.");
|
|
80
|
+
console.log(" Domains covered:");
|
|
81
|
+
const domainSet = new Set(storageState.cookies.map((c) => c.domain));
|
|
82
|
+
for (const d of domainSet) {
|
|
83
|
+
const count = storageState.cookies.filter((c) => c.domain === d).length;
|
|
84
|
+
console.log(` ${d}: ${count} cookies`);
|
|
85
|
+
}
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
console.log(" Pushing to cloud browser...");
|
|
89
|
+
const result = await pushCookies(storageState, {
|
|
90
|
+
subdomain: opts.subdomain,
|
|
91
|
+
});
|
|
92
|
+
console.log(`✓ Injected ${result.injected} cookies into cloud browser`);
|
|
93
|
+
if (result.errors.length > 0) {
|
|
94
|
+
console.warn(` Warnings: ${result.errors.join(", ")}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
catch (err) {
|
|
98
|
+
console.error(`✗ Failed: ${err.message}`);
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
program
|
|
103
|
+
.command("profiles")
|
|
104
|
+
.description("List available Chrome profiles")
|
|
105
|
+
.action(() => {
|
|
106
|
+
const { getChromeProfileDir } = require("./extract.js");
|
|
107
|
+
const { readdirSync } = require("node:fs");
|
|
108
|
+
const { dirname } = require("node:path");
|
|
109
|
+
const { existsSync } = require("node:fs");
|
|
110
|
+
const defaultDir = getChromeProfileDir("Default");
|
|
111
|
+
const chromeDir = dirname(defaultDir);
|
|
112
|
+
if (!existsSync(chromeDir)) {
|
|
113
|
+
console.log("Chrome profile directory not found.");
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
const profiles = readdirSync(chromeDir, { withFileTypes: true })
|
|
117
|
+
.filter((d) => d.isDirectory() &&
|
|
118
|
+
(d.name === "Default" || d.name.startsWith("Profile ")))
|
|
119
|
+
.map((d) => d.name);
|
|
120
|
+
console.log("Available Chrome profiles:");
|
|
121
|
+
for (const p of profiles) {
|
|
122
|
+
console.log(` ${p}`);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
program
|
|
126
|
+
.command("logout")
|
|
127
|
+
.description("Clear saved authentication")
|
|
128
|
+
.action(() => {
|
|
129
|
+
clearAuth();
|
|
130
|
+
console.log("✓ Logged out");
|
|
131
|
+
});
|
|
132
|
+
program.parse();
|
|
133
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,WAAW,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAE7F,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,aAAa,CAAC;KACnB,WAAW,CACV,+EAA+E,CAChF;KACA,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,OAAO;KACJ,OAAO,CAAC,OAAO,CAAC;KAChB,WAAW,CAAC,4CAA4C,CAAC;KACzD,MAAM,CAAC,iBAAiB,EAAE,gDAAgD,CAAC;KAC3E,MAAM,CACL,iBAAiB,EACjB,kBAAkB,EAClB,0CAA0C,CAC3C;KACA,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;IACrB,IAAI,CAAC;QACH,IAAI,MAAM,CAAC;QACX,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,MAAM,GAAG,MAAM,cAAc,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QACzD,CAAC;aAAM,CAAC;YACN,MAAM,GAAG,MAAM,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC9C,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,sBAAsB,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC;QACrD,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;YACrB,OAAO,CAAC,GAAG,CAAC,aAAa,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC;QAC/C,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,UAAU,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;IACzC,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,OAAO,CAAC,KAAK,CAAC,mBAAmB,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QAChD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,2CAA2C,CAAC;KACxD,MAAM,CACL,qBAAqB,EACrB,wDAAwD,CACzD;KACA,MAAM,CAAC,qBAAqB,EAAE,qBAAqB,EAAE,SAAS,CAAC;KAC/D,MAAM,CAAC,yBAAyB,EAAE,yBAAyB,CAAC;KAC5D,MAAM,CAAC,WAAW,EAAE,gCAAgC,EAAE,KAAK,CAAC;KAC5D,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;IACrB,IAAI,CAAC;QACH,mBAAmB;QACnB,IAAI,IAAI,GAAG,QAAQ,EAAE,CAAC;QACtB,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,OAAO,CAAC,GAAG,CAAC,wCAAwC,CAAC,CAAC;YACtD,IAAI,GAAG,MAAM,eAAe,EAAE,CAAC;YAC/B,OAAO,CAAC,GAAG,CAAC,sBAAsB,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;QACvD,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO;YAC1B,CAAC,CAAC,IAAI,CAAC,OAAO;iBACT,KAAK,CAAC,GAAG,CAAC;iBACV,GAAG,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;iBAC5B,GAAG,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAC1D,CAAC,CAAC,EAAE,CAAC;QAEP,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;QAC9D,OAAO,CAAC,GAAG,CACT,2BAA2B,KAAK,cAAc,IAAI,CAAC,OAAO,GAAG,CAC9D,CAAC;QAEF,MAAM,YAAY,GAAG,MAAM,mBAAmB,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QACtE,OAAO,CAAC,GAAG,CAAC,WAAW,YAAY,CAAC,OAAO,CAAC,MAAM,UAAU,CAAC,CAAC;QAE9D,IAAI,YAAY,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtC,OAAO,CAAC,GAAG,CAAC,yEAAyE,CAAC,CAAC;YACvF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,OAAO,CAAC,GAAG,CAAC,iDAAiD,CAAC,CAAC;YAC/D,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;YAClC,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,YAAY,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;YACrE,KAAK,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;gBAC1B,MAAM,KAAK,GAAG,YAAY,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;gBACxE,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,KAAK,UAAU,CAAC,CAAC;YAC5C,CAAC;YACD,OAAO;QACT,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,+BAA+B,CAAC,CAAC;QAC7C,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,YAAY,EAAE;YAC7C,SAAS,EAAE,IAAI,CAAC,SAAS;SAC1B,CAAC,CAAC;QAEH,OAAO,CAAC,GAAG,CAAC,cAAc,MAAM,CAAC,QAAQ,6BAA6B,CAAC,CAAC;QACxE,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC7B,OAAO,CAAC,IAAI,CAAC,eAAe,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC1D,CAAC;IACH,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,OAAO,CAAC,KAAK,CAAC,aAAa,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QAC1C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,UAAU,CAAC;KACnB,WAAW,CAAC,gCAAgC,CAAC;KAC7C,MAAM,CAAC,GAAG,EAAE;IACX,MAAM,EAAE,mBAAmB,EAAE,GAAG,OAAO,CAAC,cAAc,CAAkC,CAAC;IACzF,MAAM,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IAC3C,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IACzC,MAAM,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IAE1C,MAAM,UAAU,GAAG,mBAAmB,CAAC,SAAS,CAAC,CAAC;IAClD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAEtC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC3B,OAAO,CAAC,GAAG,CAAC,qCAAqC,CAAC,CAAC;QACnD,OAAO;IACT,CAAC;IAED,MAAM,QAAQ,GAAG,WAAW,CAAC,SAAS,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC;SAC7D,MAAM,CACL,CAAC,CAAM,EAAE,EAAE,CACT,CAAC,CAAC,WAAW,EAAE;QACf,CAAC,CAAC,CAAC,IAAI,KAAK,SAAS,IAAI,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,CAC1D;SACA,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAE3B,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;IAC1C,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACxB,CAAC;AACH,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,QAAQ,CAAC;KACjB,WAAW,CAAC,4BAA4B,CAAC;KACzC,MAAM,CAAC,GAAG,EAAE;IACX,SAAS,EAAE,CAAC;IACZ,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;AAC9B,CAAC,CAAC,CAAC;AAEL,OAAO,CAAC,KAAK,EAAE,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chrome cookie extraction — reads and decrypts cookies from local Chrome profile.
|
|
3
|
+
*
|
|
4
|
+
* Supports: macOS (Keychain), Linux (libsecret / fallback "peanuts"), Windows (DPAPI via AES-GCM).
|
|
5
|
+
*/
|
|
6
|
+
export interface Cookie {
|
|
7
|
+
name: string;
|
|
8
|
+
value: string;
|
|
9
|
+
domain: string;
|
|
10
|
+
path: string;
|
|
11
|
+
expires: number;
|
|
12
|
+
httpOnly: boolean;
|
|
13
|
+
secure: boolean;
|
|
14
|
+
sameSite: "Strict" | "Lax" | "None";
|
|
15
|
+
}
|
|
16
|
+
export interface StorageState {
|
|
17
|
+
cookies: Cookie[];
|
|
18
|
+
origins: {
|
|
19
|
+
origin: string;
|
|
20
|
+
localStorage: {
|
|
21
|
+
name: string;
|
|
22
|
+
value: string;
|
|
23
|
+
}[];
|
|
24
|
+
}[];
|
|
25
|
+
}
|
|
26
|
+
/** Get Chrome profile directory for current OS */
|
|
27
|
+
export declare function getChromeProfileDir(profile?: string): string;
|
|
28
|
+
/** Get Chrome cookie DB path */
|
|
29
|
+
export declare function getCookieDbPath(profile?: string): string;
|
|
30
|
+
/**
|
|
31
|
+
* Extract cookies from local Chrome for specific domains (or all cookies if domains is empty).
|
|
32
|
+
*
|
|
33
|
+
* @param domains - Array of domains to extract (e.g., [".google.com"]). Empty = all cookies.
|
|
34
|
+
* @param profile - Chrome profile name (default: "Default")
|
|
35
|
+
*/
|
|
36
|
+
export declare function extractCookies(domains: string[], profile?: string): Promise<Cookie[]>;
|
|
37
|
+
/**
|
|
38
|
+
* Extract cookies and format as Playwright-compatible StorageState
|
|
39
|
+
*/
|
|
40
|
+
export declare function extractStorageState(domains: string[], profile?: string): Promise<StorageState>;
|