gologin-web-access 0.3.1 → 0.3.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/CHANGELOG.md +6 -0
- package/README.md +37 -1
- package/dist/cli.js +9 -1
- package/dist/commands/batchScrape.js +75 -10
- package/dist/commands/gologinApi.js +324 -0
- package/dist/commands/read.js +18 -8
- package/dist/commands/scrapeMarkdown.js +18 -8
- package/dist/commands/scrapeText.js +18 -8
- package/dist/config.js +1 -1
- package/dist/lib/cloudApi.js +61 -0
- package/dist/lib/extractRunner.js +4 -0
- package/dist/lib/output.js +1 -1
- package/dist/lib/pageOutcome.js +192 -0
- package/dist/lib/readSource.js +39 -1
- package/dist/lib/structuredScrape.js +45 -56
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,12 @@
|
|
|
5
5
|
- browser automation is now embedded directly in `gologin-web-access`, so one repo and one install contains both Web Unlocker and Cloud Browser flows
|
|
6
6
|
- doctor now reports the embedded browser runtime source and version
|
|
7
7
|
|
|
8
|
+
## 0.3.2 - 2026-04-03
|
|
9
|
+
|
|
10
|
+
- added unified page outcome classification across `read`, `scrape-json`, and `batch-scrape`
|
|
11
|
+
- structured and readable paths now distinguish `ok`, `empty`, `incomplete`, `authwall`, `challenge`, `blocked`, and `cookie_wall`
|
|
12
|
+
- batch and extract-oriented flows now propagate next-step hints and fallback metadata more consistently for agents
|
|
13
|
+
|
|
8
14
|
## 0.1.0 - 2026-03-10
|
|
9
15
|
|
|
10
16
|
Initial public release of Gologin Web Access.
|
package/README.md
CHANGED
|
@@ -7,6 +7,7 @@ This is a unified web access layer, not just a scraping tool and not just a brow
|
|
|
7
7
|
- Read the web through stateless extraction APIs
|
|
8
8
|
- Interact with the web through stateful cloud browser sessions
|
|
9
9
|
- Carry Gologin’s browser-side strengths into those workflows: profiles, identity-aware browser sessions, cloud browser infrastructure, and Gologin’s profile/proxy stack when you run against a configured profile
|
|
10
|
+
- Manage common GoLogin profile/proxy API operations without leaving the CLI: cloud usage, cloud profile start/stop, profile cookies, fingerprint refresh, managed proxies, and user-agent updates
|
|
10
11
|
|
|
11
12
|
Package name and binary are the same:
|
|
12
13
|
|
|
@@ -108,6 +109,24 @@ These commands use Gologin Cloud Browser through the local daemon-backed agent l
|
|
|
108
109
|
|
|
109
110
|
Use these when you need state, interaction, or multi-step browser flows.
|
|
110
111
|
|
|
112
|
+
### GoLogin API Helpers
|
|
113
|
+
|
|
114
|
+
These commands use the GoLogin REST API directly through `GOLOGIN_TOKEN`. They do not require Web Unlocker and do not start the browser daemon:
|
|
115
|
+
|
|
116
|
+
- `gologin-web-access cloud-usage --profile <profileId> | --workspace <workspaceId> [--days <1-30>] [--json]`
|
|
117
|
+
- `gologin-web-access profile-cloud start <profileId> [--json]`
|
|
118
|
+
- `gologin-web-access profile-cloud stop <profileId> [--json]`
|
|
119
|
+
- `gologin-web-access profile-cookies export <profileId> [--output <path>] [--json]`
|
|
120
|
+
- `gologin-web-access profile-cookies import <profileId> <cookies.json> [--clean] [--json]`
|
|
121
|
+
- `gologin-web-access profile-fingerprint refresh <profileId...> [--json]`
|
|
122
|
+
- `gologin-web-access profile-proxy list [--page <n>] [--json]`
|
|
123
|
+
- `gologin-web-access profile-proxy traffic`
|
|
124
|
+
- `gologin-web-access profile-proxy add-gologin <profileId> --country <cc> [--city <city>] [--type residential|mobile|dc] [--json]`
|
|
125
|
+
- `gologin-web-access profile-ua latest [--os lin|mac|win|android|android-cloud] [--json]`
|
|
126
|
+
- `gologin-web-access profile-ua update <profileId...> [--all-profiles] [--workspace <id>] [--json]`
|
|
127
|
+
|
|
128
|
+
Use these when an agent needs GoLogin account/profile operations and would otherwise drop into raw REST calls or SDK code.
|
|
129
|
+
|
|
111
130
|
## When To Use `scrape` vs `browser`
|
|
112
131
|
|
|
113
132
|
- Use `scrape` commands when you need page content, extracted text, markdown, or simple structured output.
|
|
@@ -124,6 +143,7 @@ Use these when you need state, interaction, or multi-step browser flows.
|
|
|
124
143
|
- Use `batch-change-track` when you want to monitor a watchlist of pages in one pass.
|
|
125
144
|
- Use `parse-document` when the source is a PDF, DOCX, XLSX, HTML, or local document path instead of a normal HTML page.
|
|
126
145
|
- Use browser commands when you need clicks, forms, navigation, screenshots, sessions, or logged-in/profile-backed flows.
|
|
146
|
+
- Use GoLogin API helper commands when you need to attach managed proxy traffic, export/import profile cookies, refresh fingerprints, update user agents, inspect usage, or start/stop a cloud profile.
|
|
127
147
|
- Use browser commands when you need ref-based interaction, uploads, PDFs, semantic find flows, keyboard control, or a browser-visible search journey.
|
|
128
148
|
- Use `run` and `batch` when you want reusable workflows or multi-target execution on top of the CLI surface.
|
|
129
149
|
- Use `scrape` when stateless speed matters more than interaction.
|
|
@@ -168,7 +188,7 @@ This CLI uses two different GoLogin credentials on purpose, because the underlyi
|
|
|
168
188
|
- `GOLOGIN_WEB_UNLOCKER_API_KEY`
|
|
169
189
|
Required for Scraping / Read commands.
|
|
170
190
|
- `GOLOGIN_TOKEN`
|
|
171
|
-
Required for `gologin-web-access open
|
|
191
|
+
Required for `gologin-web-access open`, GoLogin API helper commands, and profile validation in `gologin-web-access doctor`.
|
|
172
192
|
- `GOLOGIN_DEFAULT_PROFILE_ID`
|
|
173
193
|
Optional default profile for browser flows.
|
|
174
194
|
- `GOLOGIN_DAEMON_PORT`
|
|
@@ -287,6 +307,20 @@ gologin-web-access current
|
|
|
287
307
|
gologin-web-access close
|
|
288
308
|
```
|
|
289
309
|
|
|
310
|
+
### Manage Profiles And Proxies
|
|
311
|
+
|
|
312
|
+
```bash
|
|
313
|
+
export GOLOGIN_TOKEN="gl_..."
|
|
314
|
+
|
|
315
|
+
gologin-web-access cloud-usage --profile profile_123
|
|
316
|
+
gologin-web-access profile-proxy add-gologin profile_123 --country us --type residential
|
|
317
|
+
gologin-web-access profile-proxy traffic
|
|
318
|
+
gologin-web-access profile-cookies export profile_123 --output ./cookies.json
|
|
319
|
+
gologin-web-access profile-fingerprint refresh profile_123
|
|
320
|
+
gologin-web-access profile-ua latest --os mac
|
|
321
|
+
gologin-web-access profile-ua update profile_123
|
|
322
|
+
```
|
|
323
|
+
|
|
290
324
|
### Search In A Real Browser
|
|
291
325
|
|
|
292
326
|
```bash
|
|
@@ -305,6 +339,7 @@ gologin-web-access snapshot -i
|
|
|
305
339
|
- `batch-extract` reuses the same extraction path across many URLs and returns one structured result per URL, including request and fallback metadata. Add `--output <path>` to save the full array directly.
|
|
306
340
|
- `scrape-json` now returns both a flat `headings` array and `headingsByLevel` buckets for `h1` through `h6`.
|
|
307
341
|
- `scrape-json --fallback browser` is available for JS-heavy pages where stateless extraction returns weak heading data.
|
|
342
|
+
- `scrape-json` now also classifies the page outcome as `ok`, `empty`, `incomplete`, `authwall`, `challenge`, `blocked`, or `cookie_wall`, and includes `nextActionHint` when the result is weak or gated.
|
|
308
343
|
- `scrape`, `scrape-markdown`, `scrape-text`, `scrape-json`, and `batch-scrape` accept `--retry`, `--backoff-ms`, and `--timeout-ms`.
|
|
309
344
|
- `batch-scrape --only-main-content` lets markdown, text, and html batch runs use the same readable-content isolation path as `read`.
|
|
310
345
|
- `crawl --only-main-content` uses the same readable-fragment extraction strategy for html, markdown, and text crawl output, but stays on the stateless unlocker path.
|
|
@@ -312,6 +347,7 @@ gologin-web-access snapshot -i
|
|
|
312
347
|
- `batch-scrape` now returns exit code `0` on partial success by default and only fails the command when every URL failed. Add `--strict` if any single failed URL should make the whole batch exit non-zero.
|
|
313
348
|
- `batch-scrape --output <path>` writes the full JSON to disk so shells and agent consoles cannot truncate a large payload silently.
|
|
314
349
|
- `batch-scrape --format json` now returns the same structured scrape envelope as `scrape-json`, including `renderSource`, `fallbackAttempted`, `fallbackUsed`, and `request.attemptCount/retryCount/attempts`.
|
|
350
|
+
- `batch-scrape --only-main-content` now propagates `outcome`, `outcomeReason`, `nextActionHint`, and fallback metadata per URL so agents can tell "weak page" from "gated page" without scraping log text.
|
|
315
351
|
- `scrape-json` now surfaces explicit `BLOCKED_PAGE` failures when structured output clearly matches a challenge or block page, instead of silently looking like a valid empty result.
|
|
316
352
|
- `search` now returns `requestedLimit`, `returnedCount`, `warnings`, `cacheTtlMs`, and per-result `position`.
|
|
317
353
|
- `search` may return fewer results than the requested `--limit` when the upstream SERP contains fewer valid results; inspect `returnedCount`, `warnings`, and `attempts`.
|
package/dist/cli.js
CHANGED
|
@@ -30,6 +30,7 @@ const find_1 = require("./commands/find");
|
|
|
30
30
|
const focus_1 = require("./commands/focus");
|
|
31
31
|
const forward_1 = require("./commands/forward");
|
|
32
32
|
const get_1 = require("./commands/get");
|
|
33
|
+
const gologinApi_1 = require("./commands/gologinApi");
|
|
33
34
|
const hover_1 = require("./commands/hover");
|
|
34
35
|
const jobs_1 = require("./commands/jobs");
|
|
35
36
|
const map_1 = require("./commands/map");
|
|
@@ -67,7 +68,7 @@ const wait_1 = require("./commands/wait");
|
|
|
67
68
|
const doctor_1 = require("./doctor");
|
|
68
69
|
const errors_1 = require("./lib/errors");
|
|
69
70
|
const output_1 = require("./lib/output");
|
|
70
|
-
const CLI_VERSION = "0.3.
|
|
71
|
+
const CLI_VERSION = "0.3.3";
|
|
71
72
|
async function main() {
|
|
72
73
|
const program = new commander_1.Command();
|
|
73
74
|
program
|
|
@@ -137,6 +138,12 @@ async function main() {
|
|
|
137
138
|
program.addCommand((0, close_1.buildCloseCommand)());
|
|
138
139
|
program.addCommand((0, sessions_1.buildSessionsCommand)());
|
|
139
140
|
program.addCommand((0, current_1.buildCurrentCommand)());
|
|
141
|
+
program.addCommand((0, gologinApi_1.buildCloudUsageCommand)());
|
|
142
|
+
program.addCommand((0, gologinApi_1.buildProfileCloudCommand)());
|
|
143
|
+
program.addCommand((0, gologinApi_1.buildProfileCookiesCommand)());
|
|
144
|
+
program.addCommand((0, gologinApi_1.buildProfileFingerprintCommand)());
|
|
145
|
+
program.addCommand((0, gologinApi_1.buildProfileProxyCommand)());
|
|
146
|
+
program.addCommand((0, gologinApi_1.buildProfileUaCommand)());
|
|
140
147
|
program
|
|
141
148
|
.command("doctor")
|
|
142
149
|
.description("Inspect both recommended keys, profile configuration, and local daemon health.")
|
|
@@ -163,6 +170,7 @@ Quick picks:
|
|
|
163
170
|
Command groups:
|
|
164
171
|
Scraping: gologin-web-access scrape|read|scrape-markdown|scrape-text|scrape-json|batch-scrape|batch-extract|search|map|crawl|crawl-start|crawl-status|crawl-result|crawl-errors|extract|change-track|batch-change-track|parse-document
|
|
165
172
|
Browser: gologin-web-access open|search-browser|scrape-screenshot|tabs|tabopen|tabfocus|tabclose|snapshot|click|dblclick|focus|type|fill|hover|select|check|uncheck|press|scroll|scrollintoview|wait|get|back|forward|reload|find|cookies|cookies-import|cookies-clear|storage-export|storage-import|storage-clear|eval|upload|pdf|screenshot|close|sessions|current
|
|
173
|
+
GoLogin API: gologin-web-access cloud-usage|profile-cloud|profile-cookies|profile-fingerprint|profile-proxy|profile-ua
|
|
166
174
|
Agent: gologin-web-access run|batch|jobs|job
|
|
167
175
|
|
|
168
176
|
Key model:
|
|
@@ -47,7 +47,16 @@ function buildBatchScrapeCommand() {
|
|
|
47
47
|
url,
|
|
48
48
|
ok: true,
|
|
49
49
|
format,
|
|
50
|
-
output,
|
|
50
|
+
output: output.output,
|
|
51
|
+
outcome: output.outcome,
|
|
52
|
+
outcomeReason: output.outcomeReason,
|
|
53
|
+
nextActionHint: output.nextActionHint,
|
|
54
|
+
renderSource: output.renderSource,
|
|
55
|
+
fallbackAttempted: output.fallbackAttempted,
|
|
56
|
+
fallbackUsed: output.fallbackUsed,
|
|
57
|
+
fallbackReason: output.fallbackReason,
|
|
58
|
+
warning: output.warning,
|
|
59
|
+
request: output.request,
|
|
51
60
|
};
|
|
52
61
|
}
|
|
53
62
|
catch (error) {
|
|
@@ -59,6 +68,8 @@ function buildBatchScrapeCommand() {
|
|
|
59
68
|
error: error instanceof Error ? error.message : "Unknown error",
|
|
60
69
|
code: extractErrorCode(error),
|
|
61
70
|
status: extractStatusCode(error),
|
|
71
|
+
outcome: extractOutcome(error),
|
|
72
|
+
nextActionHint: extractNextActionHint(error),
|
|
62
73
|
request,
|
|
63
74
|
};
|
|
64
75
|
}
|
|
@@ -97,31 +108,67 @@ async function formatOutput(url, config, apiKey, format, requestOptions, fallbac
|
|
|
97
108
|
};
|
|
98
109
|
switch (format) {
|
|
99
110
|
case "html":
|
|
100
|
-
return (await (0, readSource_1.readHtmlContent)(url, config, apiKey, readOptions))
|
|
111
|
+
return mapReadableBatchResult(await (0, readSource_1.readHtmlContent)(url, config, apiKey, readOptions));
|
|
101
112
|
case "markdown":
|
|
102
|
-
return (await (0, readSource_1.readMarkdownContent)(url, config, apiKey, readOptions))
|
|
113
|
+
return mapReadableBatchResult(await (0, readSource_1.readMarkdownContent)(url, config, apiKey, readOptions));
|
|
103
114
|
case "text":
|
|
104
|
-
return (await (0, readSource_1.readTextContent)(url, config, apiKey, readOptions))
|
|
115
|
+
return mapReadableBatchResult(await (0, readSource_1.readTextContent)(url, config, apiKey, readOptions));
|
|
105
116
|
default:
|
|
106
117
|
break;
|
|
107
118
|
}
|
|
108
119
|
}
|
|
109
120
|
switch (format) {
|
|
110
121
|
case "html":
|
|
111
|
-
return
|
|
122
|
+
return {
|
|
123
|
+
output: (await (0, unlocker_1.scrapeRenderedHtml)(url, apiKey, requestOptions)).content,
|
|
124
|
+
};
|
|
112
125
|
case "markdown":
|
|
113
|
-
return
|
|
126
|
+
return {
|
|
127
|
+
output: (await (0, unlocker_1.scrapeMarkdown)(url, apiKey, requestOptions)).markdown,
|
|
128
|
+
};
|
|
114
129
|
case "text":
|
|
115
|
-
return
|
|
130
|
+
return {
|
|
131
|
+
output: (await (0, unlocker_1.scrapeText)(url, apiKey, requestOptions)).text,
|
|
132
|
+
};
|
|
116
133
|
case "json":
|
|
117
|
-
return await (0, structuredScrape_1.scrapeStructuredJson)(url, config, apiKey, {
|
|
134
|
+
return mapStructuredBatchResult(await (0, structuredScrape_1.scrapeStructuredJson)(url, config, apiKey, {
|
|
118
135
|
fallback,
|
|
119
136
|
request: requestOptions,
|
|
120
|
-
});
|
|
137
|
+
}));
|
|
121
138
|
default:
|
|
122
|
-
return
|
|
139
|
+
return {
|
|
140
|
+
output: (await (0, unlocker_1.scrapeRenderedHtml)(url, apiKey, requestOptions)).content,
|
|
141
|
+
};
|
|
123
142
|
}
|
|
124
143
|
}
|
|
144
|
+
function mapReadableBatchResult(result) {
|
|
145
|
+
return {
|
|
146
|
+
output: result.content,
|
|
147
|
+
outcome: result.outcome,
|
|
148
|
+
outcomeReason: result.outcomeReason,
|
|
149
|
+
nextActionHint: result.nextActionHint,
|
|
150
|
+
renderSource: result.renderSource,
|
|
151
|
+
fallbackAttempted: result.fallbackAttempted,
|
|
152
|
+
fallbackUsed: result.fallbackUsed,
|
|
153
|
+
fallbackReason: result.fallbackReason,
|
|
154
|
+
warning: result.warning,
|
|
155
|
+
request: result.request,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
function mapStructuredBatchResult(result) {
|
|
159
|
+
return {
|
|
160
|
+
output: result,
|
|
161
|
+
outcome: result.outcome,
|
|
162
|
+
outcomeReason: result.outcomeReason,
|
|
163
|
+
nextActionHint: result.nextActionHint,
|
|
164
|
+
renderSource: result.renderSource,
|
|
165
|
+
fallbackAttempted: result.fallbackAttempted,
|
|
166
|
+
fallbackUsed: result.fallbackUsed,
|
|
167
|
+
fallbackReason: result.fallbackReason,
|
|
168
|
+
warning: result.warning,
|
|
169
|
+
request: result.request,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
125
172
|
async function mapWithConcurrency(items, concurrency, mapper) {
|
|
126
173
|
const results = new Array(items.length);
|
|
127
174
|
let nextIndex = 0;
|
|
@@ -180,3 +227,21 @@ function extractErrorCode(error) {
|
|
|
180
227
|
}
|
|
181
228
|
return undefined;
|
|
182
229
|
}
|
|
230
|
+
function extractOutcome(error) {
|
|
231
|
+
if (typeof error === "object" &&
|
|
232
|
+
error !== null &&
|
|
233
|
+
"outcome" in error &&
|
|
234
|
+
typeof error.outcome === "string") {
|
|
235
|
+
return error.outcome;
|
|
236
|
+
}
|
|
237
|
+
return undefined;
|
|
238
|
+
}
|
|
239
|
+
function extractNextActionHint(error) {
|
|
240
|
+
if (typeof error === "object" &&
|
|
241
|
+
error !== null &&
|
|
242
|
+
"nextActionHint" in error &&
|
|
243
|
+
typeof error.nextActionHint === "string") {
|
|
244
|
+
return error.nextActionHint;
|
|
245
|
+
}
|
|
246
|
+
return undefined;
|
|
247
|
+
}
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.buildCloudUsageCommand = buildCloudUsageCommand;
|
|
7
|
+
exports.buildProfileCloudCommand = buildProfileCloudCommand;
|
|
8
|
+
exports.buildProfileCookiesCommand = buildProfileCookiesCommand;
|
|
9
|
+
exports.buildProfileFingerprintCommand = buildProfileFingerprintCommand;
|
|
10
|
+
exports.buildProfileProxyCommand = buildProfileProxyCommand;
|
|
11
|
+
exports.buildProfileUaCommand = buildProfileUaCommand;
|
|
12
|
+
const fs_1 = require("fs");
|
|
13
|
+
const path_1 = __importDefault(require("path"));
|
|
14
|
+
const commander_1 = require("commander");
|
|
15
|
+
const config_1 = require("../config");
|
|
16
|
+
const cloudApi_1 = require("../lib/cloudApi");
|
|
17
|
+
const errors_1 = require("../lib/errors");
|
|
18
|
+
const output_1 = require("../lib/output");
|
|
19
|
+
async function getCloudToken() {
|
|
20
|
+
const config = await (0, config_1.loadConfig)();
|
|
21
|
+
return (0, config_1.requireCloudToken)(config);
|
|
22
|
+
}
|
|
23
|
+
function parseDays(value) {
|
|
24
|
+
const days = value ? Number(value) : 7;
|
|
25
|
+
if (!Number.isInteger(days) || days < 1 || days > 30) {
|
|
26
|
+
throw new errors_1.CliError("--days must be an integer from 1 to 30.");
|
|
27
|
+
}
|
|
28
|
+
return days;
|
|
29
|
+
}
|
|
30
|
+
function parsePage(value) {
|
|
31
|
+
if (!value) {
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
const page = Number(value);
|
|
35
|
+
if (!Number.isInteger(page) || page < 1) {
|
|
36
|
+
throw new errors_1.CliError("--page must be a positive integer.");
|
|
37
|
+
}
|
|
38
|
+
return page;
|
|
39
|
+
}
|
|
40
|
+
function normalizeCountryCode(value) {
|
|
41
|
+
const countryCode = value.toLowerCase();
|
|
42
|
+
if (!/^[a-z]{2}$/.test(countryCode)) {
|
|
43
|
+
throw new errors_1.CliError("--country must be a 2-letter country code, for example us.");
|
|
44
|
+
}
|
|
45
|
+
return countryCode;
|
|
46
|
+
}
|
|
47
|
+
function normalizeProxyType(value) {
|
|
48
|
+
const type = value ?? "residential";
|
|
49
|
+
if (type === "mobile") {
|
|
50
|
+
return { isMobile: true, label: "mobile" };
|
|
51
|
+
}
|
|
52
|
+
if (type === "dc" || type === "datacenter") {
|
|
53
|
+
return { isDC: true, label: "datacenter" };
|
|
54
|
+
}
|
|
55
|
+
if (type === "residential") {
|
|
56
|
+
return { label: "residential" };
|
|
57
|
+
}
|
|
58
|
+
throw new errors_1.CliError("--type must be one of residential, mobile, or dc.");
|
|
59
|
+
}
|
|
60
|
+
function detectHostOs() {
|
|
61
|
+
if (process.platform === "darwin") {
|
|
62
|
+
return "mac";
|
|
63
|
+
}
|
|
64
|
+
if (process.platform === "win32") {
|
|
65
|
+
return "win";
|
|
66
|
+
}
|
|
67
|
+
return "lin";
|
|
68
|
+
}
|
|
69
|
+
function normalizeOs(value) {
|
|
70
|
+
const os = value ?? detectHostOs();
|
|
71
|
+
if (!["lin", "mac", "win", "android", "android-cloud"].includes(os)) {
|
|
72
|
+
throw new errors_1.CliError("--os must be one of lin, mac, win, android, or android-cloud.");
|
|
73
|
+
}
|
|
74
|
+
return os;
|
|
75
|
+
}
|
|
76
|
+
async function readJsonFile(targetPath) {
|
|
77
|
+
const absolutePath = path_1.default.resolve(targetPath);
|
|
78
|
+
try {
|
|
79
|
+
return JSON.parse(await fs_1.promises.readFile(absolutePath, "utf8"));
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
throw new errors_1.CliError(`Failed to read JSON file ${absolutePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
async function writeJsonFile(targetPath, payload) {
|
|
86
|
+
const absolutePath = path_1.default.resolve(targetPath);
|
|
87
|
+
await fs_1.promises.mkdir(path_1.default.dirname(absolutePath), { recursive: true });
|
|
88
|
+
await fs_1.promises.writeFile(absolutePath, `${JSON.stringify(payload, null, 2)}\n`, "utf8");
|
|
89
|
+
return absolutePath;
|
|
90
|
+
}
|
|
91
|
+
function buildCloudUsageCommand() {
|
|
92
|
+
return new commander_1.Command("cloud-usage")
|
|
93
|
+
.description("Read GoLogin Cloud Browser usage statistics.")
|
|
94
|
+
.option("--profile <profileId>", "Profile ID to inspect")
|
|
95
|
+
.option("--workspace <workspaceId>", "Workspace ID to inspect")
|
|
96
|
+
.option("--days <days>", "Workspace stats range from 1 to 30 days", "7")
|
|
97
|
+
.option("--json", "Print JSON output")
|
|
98
|
+
.action(async (options) => {
|
|
99
|
+
if (options.profile && options.workspace) {
|
|
100
|
+
throw new errors_1.CliError("Use either --profile or --workspace, not both.");
|
|
101
|
+
}
|
|
102
|
+
const token = await getCloudToken();
|
|
103
|
+
if (options.profile) {
|
|
104
|
+
const payload = await (0, cloudApi_1.gologinApiRequest)(token, "GET", `/cloud-usage/profile/${options.profile}/stats`);
|
|
105
|
+
if (options.json) {
|
|
106
|
+
(0, output_1.printJson)(payload);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
(0, output_1.printText)(`profile=${options.profile} usage=${JSON.stringify(payload)}`);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
if (!options.workspace) {
|
|
113
|
+
throw new errors_1.CliError("Usage: gologin-web-access cloud-usage --profile <profileId> | --workspace <workspaceId> [--days <1-30>] [--json]");
|
|
114
|
+
}
|
|
115
|
+
const days = parseDays(options.days);
|
|
116
|
+
const payload = await (0, cloudApi_1.gologinApiRequest)(token, "GET", "/cloud-usage/stats", {
|
|
117
|
+
query: { workspaceId: options.workspace, days },
|
|
118
|
+
});
|
|
119
|
+
if (options.json) {
|
|
120
|
+
(0, output_1.printJson)(payload);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
(0, output_1.printText)(`workspace=${options.workspace} days=${days} usage=${JSON.stringify(payload)}`);
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
function buildProfileCloudCommand() {
|
|
127
|
+
const command = new commander_1.Command("profile-cloud").description("Start or stop a GoLogin profile in Cloud Browser.");
|
|
128
|
+
command
|
|
129
|
+
.command("start")
|
|
130
|
+
.argument("<profileId>", "Profile ID")
|
|
131
|
+
.option("--json", "Print JSON output")
|
|
132
|
+
.action(async (profileId, options) => {
|
|
133
|
+
const token = await getCloudToken();
|
|
134
|
+
const payload = await (0, cloudApi_1.gologinApiRequest)(token, "POST", `/browser/${profileId}/web`, { body: {} });
|
|
135
|
+
if (options.json) {
|
|
136
|
+
(0, output_1.printJson)(payload ?? { profileId, started: true });
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
const value = (0, cloudApi_1.asObjectPayload)(payload);
|
|
140
|
+
const remoteUrl = typeof value.remoteOrbitaUrl === "string" ? ` remote=${value.remoteOrbitaUrl}` : "";
|
|
141
|
+
(0, output_1.printText)(`profile=${profileId} cloud=started${remoteUrl}`);
|
|
142
|
+
});
|
|
143
|
+
command
|
|
144
|
+
.command("stop")
|
|
145
|
+
.argument("<profileId>", "Profile ID")
|
|
146
|
+
.option("--json", "Print JSON output")
|
|
147
|
+
.action(async (profileId, options) => {
|
|
148
|
+
const token = await getCloudToken();
|
|
149
|
+
await (0, cloudApi_1.gologinApiRequest)(token, "DELETE", `/browser/${profileId}/web`);
|
|
150
|
+
if (options.json) {
|
|
151
|
+
(0, output_1.printJson)({ profileId, stopped: true });
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
(0, output_1.printText)(`profile=${profileId} cloud=stopped`);
|
|
155
|
+
});
|
|
156
|
+
return command;
|
|
157
|
+
}
|
|
158
|
+
function buildProfileCookiesCommand() {
|
|
159
|
+
const command = new commander_1.Command("profile-cookies").description("Export or import cookies from the GoLogin profile database.");
|
|
160
|
+
command
|
|
161
|
+
.command("export")
|
|
162
|
+
.argument("<profileId>", "Profile ID")
|
|
163
|
+
.option("--output <path>", "Write cookies JSON to a file")
|
|
164
|
+
.option("--json", "Print JSON output with profile metadata")
|
|
165
|
+
.action(async (profileId, options) => {
|
|
166
|
+
const token = await getCloudToken();
|
|
167
|
+
const cookies = await (0, cloudApi_1.gologinApiRequest)(token, "GET", `/browser/${profileId}/cookies`);
|
|
168
|
+
if (options.output) {
|
|
169
|
+
(0, output_1.printText)(await writeJsonFile(options.output, cookies));
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
(0, output_1.printJson)(options.json ? { profileId, cookies } : cookies);
|
|
173
|
+
});
|
|
174
|
+
command
|
|
175
|
+
.command("import")
|
|
176
|
+
.argument("<profileId>", "Profile ID")
|
|
177
|
+
.argument("<cookiesJson>", "Cookie JSON file")
|
|
178
|
+
.option("--clean", "Clear existing cookies before importing")
|
|
179
|
+
.option("--json", "Print JSON output")
|
|
180
|
+
.action(async (profileId, cookiesJson, options) => {
|
|
181
|
+
const cookies = await readJsonFile(cookiesJson);
|
|
182
|
+
if (!Array.isArray(cookies)) {
|
|
183
|
+
throw new errors_1.CliError("Cookie import file must contain a JSON array.");
|
|
184
|
+
}
|
|
185
|
+
const token = await getCloudToken();
|
|
186
|
+
await (0, cloudApi_1.gologinApiRequest)(token, "POST", `/browser/${profileId}/cookies`, {
|
|
187
|
+
query: { fromUser: true, cleanCookies: options.clean || undefined },
|
|
188
|
+
body: cookies,
|
|
189
|
+
});
|
|
190
|
+
if (options.json) {
|
|
191
|
+
(0, output_1.printJson)({ profileId, imported: cookies.length, cleanCookies: options.clean === true });
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
(0, output_1.printText)(`profile=${profileId} importedCookies=${cookies.length}${options.clean ? " clean=true" : ""}`);
|
|
195
|
+
});
|
|
196
|
+
return command;
|
|
197
|
+
}
|
|
198
|
+
function buildProfileFingerprintCommand() {
|
|
199
|
+
const command = new commander_1.Command("profile-fingerprint").description("Refresh fingerprints for one or more GoLogin profiles.");
|
|
200
|
+
command
|
|
201
|
+
.command("refresh")
|
|
202
|
+
.argument("<profileIds...>", "Profile IDs")
|
|
203
|
+
.option("--json", "Print JSON output")
|
|
204
|
+
.action(async (profileIds, options) => {
|
|
205
|
+
const token = await getCloudToken();
|
|
206
|
+
const payload = await (0, cloudApi_1.gologinApiRequest)(token, "PATCH", "/browser/fingerprints", {
|
|
207
|
+
body: { browsersIds: profileIds },
|
|
208
|
+
});
|
|
209
|
+
if (options.json) {
|
|
210
|
+
(0, output_1.printJson)(payload);
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
(0, output_1.printText)(`refreshedFingerprints=${profileIds.length} profiles=${profileIds.join(",")}`);
|
|
214
|
+
});
|
|
215
|
+
return command;
|
|
216
|
+
}
|
|
217
|
+
function buildProfileProxyCommand() {
|
|
218
|
+
const command = new commander_1.Command("profile-proxy").description("Manage GoLogin proxies through the REST API.");
|
|
219
|
+
command
|
|
220
|
+
.command("list")
|
|
221
|
+
.option("--page <page>", "Page number", "1")
|
|
222
|
+
.option("--json", "Print JSON output")
|
|
223
|
+
.action(async (options) => {
|
|
224
|
+
const token = await getCloudToken();
|
|
225
|
+
const payload = await (0, cloudApi_1.gologinApiRequest)(token, "GET", "/proxy/v2", {
|
|
226
|
+
query: { page: parsePage(options.page) },
|
|
227
|
+
});
|
|
228
|
+
if (options.json) {
|
|
229
|
+
(0, output_1.printJson)(payload);
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
const value = (0, cloudApi_1.asObjectPayload)(payload);
|
|
233
|
+
const proxies = Array.isArray(value.proxies) ? value.proxies : [];
|
|
234
|
+
(0, output_1.printText)(`proxies=${proxies.length} hasMore=${value.hasMore === true}`);
|
|
235
|
+
});
|
|
236
|
+
command
|
|
237
|
+
.command("traffic")
|
|
238
|
+
.description("Read GoLogin managed proxy traffic balance/usage.")
|
|
239
|
+
.action(async () => {
|
|
240
|
+
const token = await getCloudToken();
|
|
241
|
+
(0, output_1.printJson)(await (0, cloudApi_1.gologinApiRequest)(token, "GET", "/users-proxies/geolocation/traffic"));
|
|
242
|
+
});
|
|
243
|
+
command
|
|
244
|
+
.command("add-gologin")
|
|
245
|
+
.argument("<profileId>", "Profile ID to link the managed proxy to")
|
|
246
|
+
.requiredOption("--country <cc>", "2-letter country code, for example us")
|
|
247
|
+
.option("--city <city>", "Optional city name")
|
|
248
|
+
.option("--type <type>", "residential, mobile, or dc", "residential")
|
|
249
|
+
.option("--name <name>", "Custom proxy name")
|
|
250
|
+
.option("--json", "Print JSON output")
|
|
251
|
+
.action(async (profileId, options) => {
|
|
252
|
+
const token = await getCloudToken();
|
|
253
|
+
const countryCode = normalizeCountryCode(options.country);
|
|
254
|
+
const proxyType = normalizeProxyType(options.type);
|
|
255
|
+
const body = {
|
|
256
|
+
countryCode,
|
|
257
|
+
profileIdToLink: profileId,
|
|
258
|
+
customName: options.name ?? `gologin-${countryCode}-${profileId.slice(0, 6)}`,
|
|
259
|
+
};
|
|
260
|
+
if (options.city) {
|
|
261
|
+
body.city = options.city;
|
|
262
|
+
}
|
|
263
|
+
if (proxyType.isMobile !== undefined) {
|
|
264
|
+
body.isMobile = proxyType.isMobile;
|
|
265
|
+
}
|
|
266
|
+
if (proxyType.isDC !== undefined) {
|
|
267
|
+
body.isDC = proxyType.isDC;
|
|
268
|
+
}
|
|
269
|
+
const payload = await (0, cloudApi_1.gologinApiRequest)(token, "POST", "/users-proxies/mobile-proxy", { body });
|
|
270
|
+
if (options.json) {
|
|
271
|
+
(0, output_1.printJson)(payload ?? { profileId, countryCode, type: proxyType.label });
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
(0, output_1.printText)(`profile=${profileId} proxy=gologin:${countryCode} type=${proxyType.label}`);
|
|
275
|
+
});
|
|
276
|
+
return command;
|
|
277
|
+
}
|
|
278
|
+
function buildProfileUaCommand() {
|
|
279
|
+
const command = new commander_1.Command("profile-ua").description("Read latest GoLogin user agent or update profile UA.");
|
|
280
|
+
command
|
|
281
|
+
.command("latest")
|
|
282
|
+
.option("--os <os>", "lin, mac, win, android, or android-cloud")
|
|
283
|
+
.option("--json", "Print JSON output")
|
|
284
|
+
.action(async (options) => {
|
|
285
|
+
const os = normalizeOs(options.os);
|
|
286
|
+
const token = await getCloudToken();
|
|
287
|
+
const payload = await (0, cloudApi_1.gologinApiRequest)(token, "GET", "/browser/latest-useragent", {
|
|
288
|
+
query: { os },
|
|
289
|
+
});
|
|
290
|
+
if (options.json) {
|
|
291
|
+
(0, output_1.printJson)(payload);
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
(0, output_1.printText)(`os=${os} latestUserAgent=${typeof payload === "string" ? payload : JSON.stringify(payload)}`);
|
|
295
|
+
});
|
|
296
|
+
command
|
|
297
|
+
.command("update")
|
|
298
|
+
.argument("[profileIds...]", "Profile IDs")
|
|
299
|
+
.option("--all-profiles", "Update all profiles in the current workspace")
|
|
300
|
+
.option("--workspace <id>", "Current workspace ID")
|
|
301
|
+
.option("--json", "Print JSON output")
|
|
302
|
+
.action(async (profileIds, options) => {
|
|
303
|
+
if (profileIds.length === 0 && !options.allProfiles) {
|
|
304
|
+
throw new errors_1.CliError("Usage: gologin-web-access profile-ua update <profileId...> [--all-profiles] [--workspace <id>] [--json]");
|
|
305
|
+
}
|
|
306
|
+
const token = await getCloudToken();
|
|
307
|
+
const payload = await (0, cloudApi_1.gologinApiRequest)(token, "PATCH", "/browser/update_ua_to_new_browser_v", {
|
|
308
|
+
query: { currentWorkspace: options.workspace },
|
|
309
|
+
body: {
|
|
310
|
+
browserIds: profileIds,
|
|
311
|
+
updateUaToNewBrowserV: true,
|
|
312
|
+
updateAllProfiles: options.allProfiles === true,
|
|
313
|
+
},
|
|
314
|
+
});
|
|
315
|
+
if (options.json) {
|
|
316
|
+
(0, output_1.printJson)(payload ?? { updated: true, profileIds, allProfiles: options.allProfiles === true });
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
(0, output_1.printText)(options.allProfiles
|
|
320
|
+
? "updatedUserAgent=allProfiles"
|
|
321
|
+
: `updatedUserAgentProfiles=${profileIds.length} profiles=${profileIds.join(",")}`);
|
|
322
|
+
});
|
|
323
|
+
return command;
|
|
324
|
+
}
|
package/dist/commands/read.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.buildReadCommand = buildReadCommand;
|
|
4
4
|
const commander_1 = require("commander");
|
|
5
5
|
const config_1 = require("../config");
|
|
6
|
+
const pageOutcome_1 = require("../lib/pageOutcome");
|
|
6
7
|
const readSource_1 = require("../lib/readSource");
|
|
7
8
|
const output_1 = require("../lib/output");
|
|
8
9
|
const shared_1 = require("./shared");
|
|
@@ -27,7 +28,7 @@ function buildReadCommand() {
|
|
|
27
28
|
: format === "markdown"
|
|
28
29
|
? await (0, readSource_1.readMarkdownContent)(url, config, apiKey, readOptions)
|
|
29
30
|
: await (0, readSource_1.readTextContent)(url, config, apiKey, readOptions);
|
|
30
|
-
emitReadNotice(result
|
|
31
|
+
emitReadNotice(result);
|
|
31
32
|
(0, output_1.printText)(result.content);
|
|
32
33
|
})));
|
|
33
34
|
}
|
|
@@ -37,15 +38,24 @@ function normalizeReadFormat(value) {
|
|
|
37
38
|
}
|
|
38
39
|
throw new Error(`Unsupported read format: ${value}`);
|
|
39
40
|
}
|
|
40
|
-
function emitReadNotice(
|
|
41
|
-
if (
|
|
42
|
-
|
|
41
|
+
function emitReadNotice(result) {
|
|
42
|
+
if (result.fallbackAttempted) {
|
|
43
|
+
if (result.fallbackUsed) {
|
|
44
|
+
process.stderr.write(`JS-rendered page detected, retrying with browser. ${result.fallbackReason ?? ""}\n`);
|
|
45
|
+
}
|
|
46
|
+
else if (result.fallbackReason) {
|
|
47
|
+
process.stderr.write(`${result.fallbackReason}\n`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (result.outcome !== "ok") {
|
|
51
|
+
process.stderr.write(`Outcome: ${result.outcome}\n`);
|
|
43
52
|
}
|
|
44
|
-
if (
|
|
45
|
-
process.stderr.write(
|
|
53
|
+
if (result.warning) {
|
|
54
|
+
process.stderr.write(`${result.warning}\n`);
|
|
46
55
|
return;
|
|
47
56
|
}
|
|
48
|
-
|
|
49
|
-
|
|
57
|
+
const hint = (0, pageOutcome_1.describeNextActionHint)(result.nextActionHint);
|
|
58
|
+
if (hint && result.outcome !== "ok") {
|
|
59
|
+
process.stderr.write(`${hint}\n`);
|
|
50
60
|
}
|
|
51
61
|
}
|