imprint-mcp 0.3.0 → 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 +42 -2
- package/examples/google-flights/get_flight_calendar_prices/index.ts +2 -2
- package/examples/google-flights/get_flight_calendar_prices/workflow.json +4 -2
- package/examples/southwest/README.md +3 -2
- package/examples/southwest/search_southwest_flights/index.ts +18 -1
- package/examples/southwest/search_southwest_flights/workflow.json +18 -1
- package/package.json +1 -1
- package/src/cli.ts +152 -1
- package/src/imprint/cdp-browser-fetch.ts +4 -1
- package/src/imprint/chromium.ts +279 -8
- package/src/imprint/compile-tools.ts +11 -3
- package/src/imprint/cron.ts +3 -0
- package/src/imprint/doctor.ts +9 -3
- package/src/imprint/export-archive.ts +355 -0
- package/src/imprint/install.ts +79 -4
- package/src/imprint/integrations.ts +1 -1
- package/src/imprint/mcp-maintenance.ts +15 -13
- package/src/imprint/mcp-server.ts +1 -1
- package/src/imprint/stealth-chromium.ts +6 -8
- package/src/imprint/teach-state.ts +37 -0
- package/src/imprint/teach.ts +62 -29
package/README.md
CHANGED
|
@@ -29,6 +29,24 @@ imprint teach southwest --url https://www.southwest.com
|
|
|
29
29
|
|
|
30
30
|
A browser opens. You drive the workflow and narrate what you're doing. Imprint records every request and interaction, then compiles a deterministic **MCP tool** your agent can call forever.
|
|
31
31
|
|
|
32
|
+
Want to try a finished MCP before recording anything?
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
imprint install google-flights --source examples --platform claude-desktop
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
That registers the checked-in Google Flights example as an MCP server in your client. Swap `claude-desktop` for `claude-code`, `codex`, `openclaw`, or `hermes`, or add `--print` to see the config without changing anything.
|
|
39
|
+
|
|
40
|
+
On a Hermes agent or Docker host, register the examples directly into Hermes:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
for site in google-flights google-hotels southwest discoverandgo echo; do
|
|
44
|
+
imprint install "$site" --source examples --platform hermes --no-interactive
|
|
45
|
+
done
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
When `HERMES_HOME` is set, Imprint writes Hermes MCP entries to `$HERMES_HOME/config.yaml`; outside Hermes it uses `~/.hermes/config.yaml`. For browser-backed MCPs, `imprint install` also installs Playwright Chromium into `$HERMES_HOME/.cache/ms-playwright` and writes `PLAYWRIGHT_BROWSERS_PATH` into the MCP config so Hermes can find it. Use `--skip-browser-install` only for offline builds where you preinstall the browser yourself. In a fresh Linux image that is missing browser system libraries, install those during image build with `bunx playwright install --with-deps chromium`.
|
|
49
|
+
|
|
32
50
|
---
|
|
33
51
|
|
|
34
52
|
## See It in Action
|
|
@@ -118,7 +136,7 @@ bun install && bun link
|
|
|
118
136
|
|
|
119
137
|
<br>
|
|
120
138
|
|
|
121
|
-
**Browser commands** (`teach`, `record`, `login`, `playbook`)
|
|
139
|
+
**Browser commands** (`teach`, `record`, `login`, `playbook`) and browser-backed `imprint install` targets auto-install Playwright Chromium when it is missing. For offline CI or prebuilt Linux images where you pass `--skip-browser-install`, preinstall it ahead of time:
|
|
122
140
|
|
|
123
141
|
```bash
|
|
124
142
|
bunx playwright install chromium
|
|
@@ -207,6 +225,21 @@ Install any example into your MCP client:
|
|
|
207
225
|
imprint install google-flights --source examples --platform claude-desktop
|
|
208
226
|
```
|
|
209
227
|
|
|
228
|
+
Examples are real generated MCPs, not handwritten SDK samples. `imprint install <site> --source examples` points the MCP server at this repo's `examples/` directory with `IMPRINT_HOME`, ensures Playwright Chromium for browser-backed tools, and lets your client list and call the checked-in tools immediately:
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
imprint install google-hotels --source examples --platform codex
|
|
232
|
+
imprint install southwest --source examples --platform claude-code
|
|
233
|
+
imprint install echo --source examples --platform claude-desktop --print
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
For your own generated tools, leave off `--source examples`:
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
imprint install mysite --platform claude-code
|
|
240
|
+
imprint install mysite --platform codex
|
|
241
|
+
```
|
|
242
|
+
|
|
210
243
|
---
|
|
211
244
|
|
|
212
245
|
## CLI Reference
|
|
@@ -227,7 +260,14 @@ imprint <command> --help # per-command options
|
|
|
227
260
|
|
|
228
261
|
## Sharing Skills
|
|
229
262
|
|
|
230
|
-
Teach on your laptop, ship to a remote agent.
|
|
263
|
+
Teach on your laptop, ship to a remote agent. Generated MCP folders contain the portable tool artifacts: `workflow.json`, `playbook.yaml`, `index.ts`, optional shared modules, and cron/backend metadata. Copy `~/.imprint/<site>` into the receiver's `~/.imprint/<site>` or commit it to a private repo, install Imprint there, then register it:
|
|
264
|
+
|
|
265
|
+
```bash
|
|
266
|
+
bun install -g imprint-mcp
|
|
267
|
+
imprint install mysite --platform claude-code
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
Credentials stay separate. Skill folders contain **zero plaintext credentials** — only `${credential.NAME}` placeholders and a manifest listing what the receiver must provision.
|
|
231
271
|
|
|
232
272
|
```bash
|
|
233
273
|
# Export (encrypted with libsodium + argon2id)
|
|
@@ -75,7 +75,7 @@ const WORKFLOW: Workflow = {
|
|
|
75
75
|
{
|
|
76
76
|
"name": "f_sid",
|
|
77
77
|
"required": false,
|
|
78
|
-
"capability": "
|
|
78
|
+
"capability": "browser_bootstrap",
|
|
79
79
|
"source": "html_regex",
|
|
80
80
|
"pattern": "\"FdrFJe\":\"([^\"]+)\"",
|
|
81
81
|
"group": 1
|
|
@@ -83,7 +83,7 @@ const WORKFLOW: Workflow = {
|
|
|
83
83
|
{
|
|
84
84
|
"name": "bl",
|
|
85
85
|
"required": false,
|
|
86
|
-
"capability": "
|
|
86
|
+
"capability": "browser_bootstrap",
|
|
87
87
|
"source": "html_regex",
|
|
88
88
|
"pattern": "\"cfb2h\":\"([^\"]+)\"",
|
|
89
89
|
"group": 1
|
|
@@ -44,14 +44,16 @@
|
|
|
44
44
|
"name": "f_sid",
|
|
45
45
|
"pattern": "\"FdrFJe\":\"([^\"]+)\"",
|
|
46
46
|
"group": 1,
|
|
47
|
-
"required": false
|
|
47
|
+
"required": false,
|
|
48
|
+
"capability": "browser_bootstrap"
|
|
48
49
|
},
|
|
49
50
|
{
|
|
50
51
|
"source": "html_regex",
|
|
51
52
|
"name": "bl",
|
|
52
53
|
"pattern": "\"cfb2h\":\"([^\"]+)\"",
|
|
53
54
|
"group": 1,
|
|
54
|
-
"required": false
|
|
55
|
+
"required": false,
|
|
56
|
+
"capability": "browser_bootstrap"
|
|
55
57
|
}
|
|
56
58
|
]
|
|
57
59
|
},
|
|
@@ -8,13 +8,14 @@
|
|
|
8
8
|
- **`probe-backends` skipping the futile rung.** The cached `backends.json` orders the ladder `stealth-fetch → playbook` so cron doesn't burn 200ms on a fetch attempt every tick.
|
|
9
9
|
- **`notifyWhen: price_below`** pushing only on real drops, with the `pricePath` extracting from real Southwest response shape.
|
|
10
10
|
- **The fresh-UUID header trick** — Southwest rejects stale `X-User-Experience-ID`; stealth-fetch regenerates per call.
|
|
11
|
+
- **Public bootstrap config capture.** The workflow fetches Southwest's public bootstrap `data.js` and captures the current `api-keys.prod` value, so installed examples do not require a `SOUTHWEST_API_KEY` environment variable.
|
|
11
12
|
- **Multi-path `pricePath`** — `cron.json`'s notifyWhen lists both the raw API shape (when stealth-fetch wins) and the playbook's reshaped output, so the push fires regardless of which backend produced the result.
|
|
12
13
|
|
|
13
14
|
## Run it
|
|
14
15
|
|
|
15
16
|
```bash
|
|
16
|
-
# One-time setup
|
|
17
|
-
|
|
17
|
+
# One-time setup: registers the example MCP and installs Playwright Chromium if missing
|
|
18
|
+
imprint install southwest --source examples --platform claude-desktop
|
|
18
19
|
|
|
19
20
|
# Run a single tick (verifies everything still works)
|
|
20
21
|
imprint cron southwest --once
|
|
@@ -55,13 +55,30 @@ const WORKFLOW: Workflow = {
|
|
|
55
55
|
}
|
|
56
56
|
],
|
|
57
57
|
"requests": [
|
|
58
|
+
{
|
|
59
|
+
"method": "GET",
|
|
60
|
+
"url": "https://www.southwest.com/swa-ui/bootstrap/landing-home-page-v2/1/data.js",
|
|
61
|
+
"headers": {
|
|
62
|
+
"Accept": "application/javascript, text/javascript, */*; q=0.01"
|
|
63
|
+
},
|
|
64
|
+
"captures": [
|
|
65
|
+
{
|
|
66
|
+
"name": "southwest_api_key",
|
|
67
|
+
"source": "text_regex",
|
|
68
|
+
"pattern": "\"swa-bootstrap-landing-home-page-v2/api-keys\":\\[function\\(require,module,exports\\)\\{\\s*module\\.exports = \\{[^}]*\"prod\":\"([^\"]+)\"",
|
|
69
|
+
"group": 1,
|
|
70
|
+
"required": true,
|
|
71
|
+
"capability": "ordinary_http"
|
|
72
|
+
}
|
|
73
|
+
]
|
|
74
|
+
},
|
|
58
75
|
{
|
|
59
76
|
"method": "POST",
|
|
60
77
|
"url": "https://www.southwest.com/api/air-booking/v1/air-booking/page/air/booking/shopping",
|
|
61
78
|
"headers": {
|
|
62
79
|
"Content-Type": "application/json",
|
|
63
80
|
"Accept": "application/json, text/javascript, */*; q=0.01",
|
|
64
|
-
"X-API-Key": "${
|
|
81
|
+
"X-API-Key": "${state.southwest_api_key}",
|
|
65
82
|
"X-App-ID": "air-booking",
|
|
66
83
|
"X-Channel-ID": "southwest"
|
|
67
84
|
},
|
|
@@ -37,13 +37,30 @@
|
|
|
37
37
|
}
|
|
38
38
|
],
|
|
39
39
|
"requests": [
|
|
40
|
+
{
|
|
41
|
+
"method": "GET",
|
|
42
|
+
"url": "https://www.southwest.com/swa-ui/bootstrap/landing-home-page-v2/1/data.js",
|
|
43
|
+
"headers": {
|
|
44
|
+
"Accept": "application/javascript, text/javascript, */*; q=0.01"
|
|
45
|
+
},
|
|
46
|
+
"captures": [
|
|
47
|
+
{
|
|
48
|
+
"name": "southwest_api_key",
|
|
49
|
+
"source": "text_regex",
|
|
50
|
+
"pattern": "\"swa-bootstrap-landing-home-page-v2/api-keys\":\\[function\\(require,module,exports\\)\\{\\s*module\\.exports = \\{[^}]*\"prod\":\"([^\"]+)\"",
|
|
51
|
+
"group": 1,
|
|
52
|
+
"required": true,
|
|
53
|
+
"capability": "ordinary_http"
|
|
54
|
+
}
|
|
55
|
+
]
|
|
56
|
+
},
|
|
40
57
|
{
|
|
41
58
|
"method": "POST",
|
|
42
59
|
"url": "https://www.southwest.com/api/air-booking/v1/air-booking/page/air/booking/shopping",
|
|
43
60
|
"headers": {
|
|
44
61
|
"Content-Type": "application/json",
|
|
45
62
|
"Accept": "application/json, text/javascript, */*; q=0.01",
|
|
46
|
-
"X-API-Key": "${
|
|
63
|
+
"X-API-Key": "${state.southwest_api_key}",
|
|
47
64
|
"X-App-ID": "air-booking",
|
|
48
65
|
"X-Channel-ID": "southwest"
|
|
49
66
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "imprint-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Teach an AI agent how to use any website. Once. Records a real browser session + narration; generates a deterministic MCP tool plus a DOM-replay playbook fallback.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
package/src/cli.ts
CHANGED
|
@@ -62,6 +62,10 @@ INSTALL
|
|
|
62
62
|
install [<site>] Install an emitted MCP server into an AI platform.
|
|
63
63
|
uninstall [<site>] Remove an installed Imprint MCP server from an AI platform.
|
|
64
64
|
|
|
65
|
+
SHARE
|
|
66
|
+
export <site> [<site2>] Bundle site tools into a portable .tar.gz archive.
|
|
67
|
+
import <archive.tar.gz> Unpack an archive into ~/.imprint and set up tools.
|
|
68
|
+
|
|
65
69
|
RUN
|
|
66
70
|
mcp-server <site> Serve one site's tools as MCP (stdio default).
|
|
67
71
|
cron <site> Polling daemon for ~/.imprint/<site>/<toolName>/cron.json.
|
|
@@ -238,7 +242,7 @@ export const VERB_HELP: Record<string, VerbHelp> = {
|
|
|
238
242
|
summary:
|
|
239
243
|
'Install an already-emitted MCP server into Claude Code, Codex, Claude Desktop, OpenClaw, or Hermes.',
|
|
240
244
|
usage: [
|
|
241
|
-
'imprint install [<site>] [--platform <name>] [--source local|examples] [--print] [--no-interactive]',
|
|
245
|
+
'imprint install [<site>] [--platform <name>] [--source local|examples] [--print] [--no-interactive] [--skip-browser-install]',
|
|
242
246
|
],
|
|
243
247
|
flags: [
|
|
244
248
|
{
|
|
@@ -257,6 +261,10 @@ export const VERB_HELP: Record<string, VerbHelp> = {
|
|
|
257
261
|
name: '--no-interactive',
|
|
258
262
|
description: 'Do not prompt; requires <site> and --platform.',
|
|
259
263
|
},
|
|
264
|
+
{
|
|
265
|
+
name: '--skip-browser-install',
|
|
266
|
+
description: 'Do not auto-install Playwright Chromium for browser-backed tools.',
|
|
267
|
+
},
|
|
260
268
|
],
|
|
261
269
|
example: 'imprint install google-flights --source examples --platform claude-desktop',
|
|
262
270
|
},
|
|
@@ -280,6 +288,39 @@ export const VERB_HELP: Record<string, VerbHelp> = {
|
|
|
280
288
|
],
|
|
281
289
|
example: 'imprint uninstall google-flights --platform claude-desktop',
|
|
282
290
|
},
|
|
291
|
+
export: {
|
|
292
|
+
summary:
|
|
293
|
+
'Bundle one or more site tool sets into a portable .tar.gz archive for sharing across machines.',
|
|
294
|
+
usage: ['imprint export <site> [<site2> ...] [--out <path>] [--include-credentials]'],
|
|
295
|
+
flags: [
|
|
296
|
+
{
|
|
297
|
+
name: '--out <path>',
|
|
298
|
+
description:
|
|
299
|
+
'Output path. Defaults to ./imprint-export-<site>.tar.gz (single) or ./imprint-export-<timestamp>.tar.gz (multi).',
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
name: '--include-credentials',
|
|
303
|
+
description: 'Embed encrypted credential bundles (prompts for a passphrase per site).',
|
|
304
|
+
},
|
|
305
|
+
],
|
|
306
|
+
example: 'imprint export avis southwest marriott --out tools.tar.gz --include-credentials',
|
|
307
|
+
},
|
|
308
|
+
import: {
|
|
309
|
+
summary: 'Unpack an imprint export archive into ~/.imprint and set up tools for use.',
|
|
310
|
+
usage: ['imprint import <archive.tar.gz> [--force] [--platform <name>]'],
|
|
311
|
+
flags: [
|
|
312
|
+
{
|
|
313
|
+
name: '--force',
|
|
314
|
+
description: 'Overwrite existing sites instead of skipping them.',
|
|
315
|
+
},
|
|
316
|
+
{
|
|
317
|
+
name: '--platform <name>',
|
|
318
|
+
description:
|
|
319
|
+
'Auto-install MCP servers after import: claude-code, codex, claude-desktop, openclaw, hermes.',
|
|
320
|
+
},
|
|
321
|
+
],
|
|
322
|
+
example: 'imprint import tools.tar.gz --force --platform claude-code',
|
|
323
|
+
},
|
|
283
324
|
login: {
|
|
284
325
|
summary: 'Persist auth cookies for <site> from a captured session.',
|
|
285
326
|
usage: ['imprint login <site> --from-session <session.json>'],
|
|
@@ -822,6 +863,7 @@ async function main(argv: string[]): Promise<number> {
|
|
|
822
863
|
source: { type: 'string' },
|
|
823
864
|
print: { type: 'boolean' },
|
|
824
865
|
'no-interactive': { type: 'boolean' },
|
|
866
|
+
'skip-browser-install': { type: 'boolean' },
|
|
825
867
|
},
|
|
826
868
|
allowPositionals: false,
|
|
827
869
|
});
|
|
@@ -850,6 +892,7 @@ async function main(argv: string[]): Promise<number> {
|
|
|
850
892
|
source: values.source as (typeof sources)[number] | undefined,
|
|
851
893
|
print: values.print,
|
|
852
894
|
noInteractive: values['no-interactive'],
|
|
895
|
+
skipBrowserInstall: values['skip-browser-install'],
|
|
853
896
|
});
|
|
854
897
|
console.log(`[imprint] ${result.message}`);
|
|
855
898
|
if ('source' in result)
|
|
@@ -890,6 +933,114 @@ async function main(argv: string[]): Promise<number> {
|
|
|
890
933
|
return 0;
|
|
891
934
|
}
|
|
892
935
|
|
|
936
|
+
case 'export': {
|
|
937
|
+
const sites: string[] = [];
|
|
938
|
+
let i = 1;
|
|
939
|
+
for (; i < argv.length; i++) {
|
|
940
|
+
const arg = argv[i];
|
|
941
|
+
if (!arg || arg.startsWith('-')) break;
|
|
942
|
+
sites.push(arg);
|
|
943
|
+
}
|
|
944
|
+
if (sites.length === 0) {
|
|
945
|
+
console.error('error: `imprint export` requires at least one <site> argument.');
|
|
946
|
+
return 2;
|
|
947
|
+
}
|
|
948
|
+
const { values } = parseArgs({
|
|
949
|
+
args: argv.slice(i),
|
|
950
|
+
options: {
|
|
951
|
+
out: { type: 'string' },
|
|
952
|
+
'include-credentials': { type: 'boolean' },
|
|
953
|
+
},
|
|
954
|
+
allowPositionals: false,
|
|
955
|
+
});
|
|
956
|
+
const defaultOut =
|
|
957
|
+
sites.length === 1
|
|
958
|
+
? `imprint-export-${sites[0]}.tar.gz`
|
|
959
|
+
: `imprint-export-${new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19)}.tar.gz`;
|
|
960
|
+
const out = values.out ?? defaultOut;
|
|
961
|
+
const { exportArchive } = await import('./imprint/export-archive.ts');
|
|
962
|
+
const result = await exportArchive({
|
|
963
|
+
sites,
|
|
964
|
+
out,
|
|
965
|
+
includeCredentials: values['include-credentials'],
|
|
966
|
+
});
|
|
967
|
+
console.log(`[imprint] exported → ${result.archivePath}`);
|
|
968
|
+
for (const s of result.sites) {
|
|
969
|
+
console.log(
|
|
970
|
+
`[imprint] ${s.name}: ${s.tools.length} tool${s.tools.length === 1 ? '' : 's'} (${s.tools.join(', ')})`,
|
|
971
|
+
);
|
|
972
|
+
}
|
|
973
|
+
const kb = (result.byteSize / 1024).toFixed(1);
|
|
974
|
+
console.log(`[imprint] archive size: ${kb} KB`);
|
|
975
|
+
console.log('');
|
|
976
|
+
console.log('next step:');
|
|
977
|
+
console.log(` imprint import ${out} # on the target machine`);
|
|
978
|
+
return 0;
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
case 'import': {
|
|
982
|
+
const archivePath = requirePositional(argv, 'import', 'an <archive.tar.gz> argument');
|
|
983
|
+
if (archivePath === null) return 2;
|
|
984
|
+
const { values } = parseArgs({
|
|
985
|
+
args: argv.slice(2),
|
|
986
|
+
options: {
|
|
987
|
+
force: { type: 'boolean' },
|
|
988
|
+
platform: { type: 'string' },
|
|
989
|
+
},
|
|
990
|
+
allowPositionals: false,
|
|
991
|
+
});
|
|
992
|
+
|
|
993
|
+
if (values.platform) {
|
|
994
|
+
const { PLATFORMS } = await import('./imprint/integrations.ts');
|
|
995
|
+
if (!PLATFORMS.includes(values.platform as (typeof PLATFORMS)[number])) {
|
|
996
|
+
console.error(
|
|
997
|
+
`error: unknown platform '${values.platform}' — valid: ${PLATFORMS.join(', ')}`,
|
|
998
|
+
);
|
|
999
|
+
return 2;
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
const { importArchive } = await import('./imprint/export-archive.ts');
|
|
1004
|
+
const result = await importArchive({
|
|
1005
|
+
archivePath,
|
|
1006
|
+
force: values.force,
|
|
1007
|
+
});
|
|
1008
|
+
|
|
1009
|
+
for (const s of result.sites) {
|
|
1010
|
+
if (s.skipped) {
|
|
1011
|
+
console.log(`[imprint] ${s.name}: skipped (already exists)`);
|
|
1012
|
+
} else {
|
|
1013
|
+
console.log(
|
|
1014
|
+
`[imprint] ${s.name}: imported ${s.tools.length} tool${s.tools.length === 1 ? '' : 's'} (${s.tools.join(', ')})${s.credentialsImported ? ' + credentials' : ''}`,
|
|
1015
|
+
);
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
const imported = result.sites.filter((s) => !s.skipped);
|
|
1020
|
+
if (imported.length > 0 && !values.platform) {
|
|
1021
|
+
console.log('');
|
|
1022
|
+
console.log('next steps:');
|
|
1023
|
+
for (const s of imported) {
|
|
1024
|
+
console.log(` imprint install ${s.name} # register MCP server`);
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
if (values.platform) {
|
|
1029
|
+
const { install } = await import('./imprint/install.ts');
|
|
1030
|
+
const { PLATFORMS } = await import('./imprint/integrations.ts');
|
|
1031
|
+
for (const s of imported) {
|
|
1032
|
+
const installResult = await install({
|
|
1033
|
+
site: s.name,
|
|
1034
|
+
platform: values.platform as (typeof PLATFORMS)[number],
|
|
1035
|
+
noInteractive: true,
|
|
1036
|
+
});
|
|
1037
|
+
console.log(`[imprint] ${installResult.message}`);
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
return 0;
|
|
1042
|
+
}
|
|
1043
|
+
|
|
893
1044
|
case 'login': {
|
|
894
1045
|
const site = requirePositional(argv, 'login', 'a <site> argument');
|
|
895
1046
|
if (site === null) return 2;
|
|
@@ -283,7 +283,10 @@ export function createCdpBrowserFetch(opts: CdpBrowserFetchOptions): CdpBrowserF
|
|
|
283
283
|
throw new Error('navigate timeout');
|
|
284
284
|
}),
|
|
285
285
|
]);
|
|
286
|
-
await
|
|
286
|
+
await Promise.race([
|
|
287
|
+
Page.loadEventFired(),
|
|
288
|
+
sleep(Math.min(abckWaitMs, 5000)).then(() => undefined),
|
|
289
|
+
]).catch(() => {});
|
|
287
290
|
} catch (err) {
|
|
288
291
|
log(`navigation issue (continuing): ${err instanceof Error ? err.message : String(err)}`);
|
|
289
292
|
}
|