bridgerator 1.0.5 → 1.0.6
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 +109 -107
- package/install.ps1 +8 -5
- package/package.json +1 -1
- package/src/cli.mjs +13 -7
- package/src/notice.mjs +1 -1
- package/src/pair.mjs +43 -0
package/README.md
CHANGED
|
@@ -1,107 +1,109 @@
|
|
|
1
|
-
# Bridgerator
|
|
2
|
-
|
|
3
|
-
Connect your Windows PC to ChatGPT. The assistant can read and edit files, search folders and run
|
|
4
|
-
PowerShell commands on **your** computer — through a bridge that only makes an **outbound**
|
|
5
|
-
connection. No inbound ports, no remote-desktop software.
|
|
6
|
-
|
|
7
|
-
```
|
|
8
|
-
ChatGPT ──OAuth 2.1 + MCP (HTTPS)──► relay (mcp.bridgerator.recondeck.com) ──WebSocket──► bridgerator on your PC ──stdio──► bundled MCP engine
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
This repository is the part that runs on your PC. It is small on purpose so you (or an AI agent
|
|
12
|
-
installing it for you) can read all of it: `src/bridge.mjs` (forwarder), `src/policy.mjs` (access
|
|
13
|
-
rules), `src/dc-isolate.mjs` (separate engine config), `src/cli.mjs` (setup/autostart), `install.ps1` (installer).
|
|
14
|
-
|
|
15
|
-
## What people use it for
|
|
16
|
-
|
|
17
|
-
- "Sort my Downloads folder by type and month" — ChatGPT lists, plans and moves the files on your PC.
|
|
18
|
-
- "Find every invoice PDF in Documents and rename it by date" — search and rename in plain words.
|
|
19
|
-
- "Why is drive C almost full?" — disk usage, big folders, what is safe to delete.
|
|
20
|
-
- "Check the event log for errors since yesterday" / "Read the end of this log" — diagnostics.
|
|
21
|
-
- Work on code: "explain how D:\\Projects\\app is built and run its tests".
|
|
22
|
-
|
|
23
|
-
Everything runs as your Windows user and only inside the folders you allow.
|
|
24
|
-
|
|
25
|
-
## Plans
|
|
26
|
-
|
|
27
|
-
Free:
|
|
28
|
-
Details: https://bridgerator.recondeck.com/pricing
|
|
29
|
-
|
|
30
|
-
## Install (Windows)
|
|
31
|
-
|
|
32
|
-
1.
|
|
33
|
-
2.
|
|
34
|
-
```powershell
|
|
35
|
-
& ([scriptblock]::Create((irm https://bridgerator.recondeck.com/install.ps1)))
|
|
36
|
-
```
|
|
37
|
-
It downloads portable Node.js LTS if missing (checksum-verified, no admin), installs this package and runs `bridgerator setup`.
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
Manual install (PowerShell): `$env:PUPPETEER_SKIP_DOWNLOAD=1; npm install -g bridgerator`, then `bridgerator setup --token <key
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
|
52
|
-
|
|
53
|
-
| `
|
|
54
|
-
| `
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
bridgerator
|
|
80
|
-
bridgerator
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
1
|
+
# Bridgerator
|
|
2
|
+
|
|
3
|
+
Connect your Windows PC to ChatGPT. The assistant can read and edit files, search folders and run
|
|
4
|
+
PowerShell commands on **your** computer — through a bridge that only makes an **outbound**
|
|
5
|
+
connection. No inbound ports, no remote-desktop software.
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
ChatGPT ──OAuth 2.1 + MCP (HTTPS)──► relay (mcp.bridgerator.recondeck.com) ──WebSocket──► bridgerator on your PC ──stdio──► bundled MCP engine
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
This repository is the part that runs on your PC. It is small on purpose so you (or an AI agent
|
|
12
|
+
installing it for you) can read all of it: `src/bridge.mjs` (forwarder), `src/policy.mjs` (access
|
|
13
|
+
rules), `src/dc-isolate.mjs` (separate engine config), `src/cli.mjs` (setup/autostart), `src/pair.mjs` (browser pairing), `install.ps1` (installer).
|
|
14
|
+
|
|
15
|
+
## What people use it for
|
|
16
|
+
|
|
17
|
+
- "Sort my Downloads folder by type and month" — ChatGPT lists, plans and moves the files on your PC.
|
|
18
|
+
- "Find every invoice PDF in Documents and rename it by date" — search and rename in plain words.
|
|
19
|
+
- "Why is drive C almost full?" — disk usage, big folders, what is safe to delete.
|
|
20
|
+
- "Check the event log for errors since yesterday" / "Read the end of this log" — diagnostics.
|
|
21
|
+
- Work on code: "explain how D:\\Projects\\app is built and run its tests".
|
|
22
|
+
|
|
23
|
+
Everything runs as your Windows user and only inside the folders you allow.
|
|
24
|
+
|
|
25
|
+
## Plans
|
|
26
|
+
|
|
27
|
+
Free: 10,000 tool calls per month per account, any number of PCs. Pro: $10/month, unlimited.
|
|
28
|
+
Details: https://bridgerator.recondeck.com/pricing
|
|
29
|
+
|
|
30
|
+
## Install (Windows)
|
|
31
|
+
|
|
32
|
+
1. In ChatGPT, add the Bridgerator app and sign in with Google (an account is created for you), or sign up at https://bridgerator.recondeck.com.
|
|
33
|
+
2. On the PC, in PowerShell (no admin rights needed):
|
|
34
|
+
```powershell
|
|
35
|
+
& ([scriptblock]::Create((irm https://bridgerator.recondeck.com/install.ps1)))
|
|
36
|
+
```
|
|
37
|
+
It downloads portable Node.js LTS if missing (checksum-verified, no admin), installs this package and runs `bridgerator setup`.
|
|
38
|
+
3. A browser tab opens with a code: check that it matches the one in PowerShell and click **Connect this computer**. Done — no key to copy.
|
|
39
|
+
|
|
40
|
+
Update later with the same line (the key and folder settings are kept). Prefer to read first? Download `install.ps1`, read it, then run it.
|
|
41
|
+
Servers or PCs without a browser: create a device key in your account (Add a computer → Advanced) and add `-Token <device key>` to the line.
|
|
42
|
+
|
|
43
|
+
Manual install (PowerShell): `$env:PUPPETEER_SKIP_DOWNLOAD=1; npm install -g bridgerator`, then `bridgerator setup` (or `bridgerator setup --token <key>`). The variable skips a ~700 MB Chrome download that only PDF export needs; PDF export fetches a browser itself on first use.
|
|
44
|
+
|
|
45
|
+
Other MCP clients: server URL `https://mcp.bridgerator.recondeck.com/mcp`, OAuth sign-in.
|
|
46
|
+
|
|
47
|
+
## Access rules — what the assistant can and cannot touch
|
|
48
|
+
|
|
49
|
+
Config: `%APPDATA%\Bridgerator\config.json` (written by `setup`, edit and restart to change).
|
|
50
|
+
|
|
51
|
+
| Key | Meaning | Default |
|
|
52
|
+
|---|---|---|
|
|
53
|
+
| `allowedDirectories` | File tools work only inside these folders (enforced by the engine). Empty = whole disk. | your user folder |
|
|
54
|
+
| `deniedDirectories` | Folders excluded **inside** the allowed ones (enforced by Bridgerator, see below). | `~\.ssh`, `~\AppData` |
|
|
55
|
+
| `blockedCommands` | Commands the engine refuses to run. Leave it out to keep the built-in list. | built-in list (`format`, `diskpart`, `reg`, `net`, `sc`, `runas`, `netsh`, `takeown`, …) |
|
|
56
|
+
| `disabledTools` | Tools never shown to the assistant, e.g. `["start_process","interact_with_process"]` to turn the terminal off. | none |
|
|
57
|
+
|
|
58
|
+
Full file, search and terminal toolset, including ChatGPT widgets (file preview,
|
|
59
|
+
config view). Hidden: `set_config_value` (would let the assistant change its own limits), feedback and
|
|
60
|
+
usage-stats tools.
|
|
61
|
+
|
|
62
|
+
How exclusions work (`src/policy.mjs`): every path argument is resolved (symlinks and junctions
|
|
63
|
+
included) and refused if it lies inside an excluded folder; result lines that mention an excluded
|
|
64
|
+
path (search hits, recursive listings) are removed; `set_config_value` is never exposed, so the
|
|
65
|
+
assistant cannot change these limits.
|
|
66
|
+
|
|
67
|
+
⚠ **The terminal is not a folder sandbox.** A shell command runs with your Windows user's rights
|
|
68
|
+
and can read anything you can. Bridgerator refuses commands that literally name an excluded folder,
|
|
69
|
+
but that is a courtesy, not a boundary. If folder limits must be strict, add the terminal tools to
|
|
70
|
+
`disabledTools`.
|
|
71
|
+
|
|
72
|
+
The bridge runs as **your** Windows user (hidden scheduled task at logon), never as SYSTEM.
|
|
73
|
+
|
|
74
|
+
Bridgerator gives its bundled engine its **own** config folder (`%APPDATA%\Bridgerator\dc`, see `src/dc-isolate.mjs`): if you already run the same open-source MCP server for Claude or another app, its settings and folder limits are not touched. The engine keeps a local log of recent tool calls in that folder; it never leaves your PC.
|
|
75
|
+
|
|
76
|
+
## Commands
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
bridgerator setup [--token <key> | --pair] [--allow <dir>]... [--deny <dir>]... # pair in the browser (no key yet / --pair) + config + autostart + start
|
|
80
|
+
bridgerator run # foreground (what autostart runs)
|
|
81
|
+
bridgerator status # config (token masked) + log tail
|
|
82
|
+
bridgerator uninstall # stop + remove autostart
|
|
83
|
+
```
|
|
84
|
+
Log: `%APPDATA%\Bridgerator\bridge.log`. Stopping the bridge cuts access immediately.
|
|
85
|
+
|
|
86
|
+
## Privacy
|
|
87
|
+
|
|
88
|
+
File contents and command output go to ChatGPT (OpenAI) as tool results; the relay forwards them
|
|
89
|
+
in memory and does not store them. Engine telemetry, remote feature flags and any browser pop-ups are turned off by the bridge.
|
|
90
|
+
Policy: https://bridgerator.recondeck.com/privacy
|
|
91
|
+
|
|
92
|
+
## Development
|
|
93
|
+
|
|
94
|
+
`npm test` — policy unit tests plus an end-to-end run against the real bundled engine
|
|
95
|
+
(throw-away folders; asserts that an existing config of the same engine stays byte-identical).
|
|
96
|
+
|
|
97
|
+
## License
|
|
98
|
+
|
|
99
|
+
MIT. The bundled open-source MCP engine is MIT-licensed as well; its copyright notice is kept in
|
|
100
|
+
`LICENSE` and the dependency is listed in `package.json`.
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
### По-русски коротко
|
|
105
|
+
Bridgerator подключает ваш ПК к ChatGPT: файлы, поиск, терминал. Мост только исходящий, входящих
|
|
106
|
+
портов нет. Установка — одна строка PowerShell, затем подтверждение компьютера во вкладке браузера (см. выше).
|
|
107
|
+
Разрешённые папки — `allowedDirectories`, исключения внутри них — `deniedDirectories`
|
|
108
|
+
(исключения обеспечивает сам Bridgerator). Терминал папками не
|
|
109
|
+
ограничивается — для строгих границ выключите его через `disabledTools`.
|
package/install.ps1
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
# Bridgerator installer for Windows (PowerShell 5.1+). Source: npm package "bridgerator" (https://www.npmjs.com/package/bridgerator)
|
|
2
2
|
# Usage (one line, from your Bridgerator account page):
|
|
3
|
-
# & ([scriptblock]::Create((irm https://bridgerator.recondeck.com/install.ps1)))
|
|
4
|
-
#
|
|
3
|
+
# & ([scriptblock]::Create((irm https://bridgerator.recondeck.com/install.ps1)))
|
|
4
|
+
# A browser tab opens: sign in and confirm this computer — no key to paste. Run again to update
|
|
5
|
+
# (keeps the key and folders). Servers / headless PCs: add -Token <device key> from the account page.
|
|
5
6
|
# What it does: installs Node.js LTS if missing (portable zip, no admin), installs the bridgerator npm package,
|
|
6
|
-
# then runs `bridgerator setup` (
|
|
7
|
+
# then runs `bridgerator setup` (pairing in the browser if there is no key yet, config + hidden autostart
|
|
8
|
+
# at logon for THIS user, no admin rights),
|
|
7
9
|
# then opens the welcome page in the default browser (skip with -NoBrowser).
|
|
8
10
|
param(
|
|
9
11
|
[string]$Token,
|
|
@@ -13,7 +15,7 @@ param(
|
|
|
13
15
|
[switch]$NoBrowser
|
|
14
16
|
)
|
|
15
17
|
$ErrorActionPreference = 'Stop'
|
|
16
|
-
|
|
18
|
+
$paired = $Token -or (Test-Path (Join-Path $env:APPDATA 'Bridgerator\config.json'))
|
|
17
19
|
|
|
18
20
|
function Refresh-Path {
|
|
19
21
|
$env:Path = [Environment]::GetEnvironmentVariable('Path', 'Machine') + ';' + [Environment]::GetEnvironmentVariable('Path', 'User')
|
|
@@ -67,9 +69,10 @@ $setupArgs = @('setup')
|
|
|
67
69
|
if ($Token) { $setupArgs += @('--token', $Token) }
|
|
68
70
|
foreach ($a in $Allow) { $setupArgs += @('--allow', $a) }
|
|
69
71
|
foreach ($d in $Deny) { $setupArgs += @('--deny', $d) }
|
|
72
|
+
if (-not $paired) { Write-Host 'Next: a browser tab opens - sign in to Bridgerator and confirm this computer.' }
|
|
70
73
|
& bridgerator @setupArgs
|
|
71
74
|
if ($LASTEXITCODE -ne 0) { throw 'bridgerator setup failed' }
|
|
72
|
-
Write-Host 'Done. Check with: bridgerator status'
|
|
75
|
+
Write-Host 'Done. This computer is connected. Check with: bridgerator status'
|
|
73
76
|
|
|
74
77
|
# Next steps (connect ChatGPT, example prompts) on the welcome page; skipped for -NoBrowser and
|
|
75
78
|
# non-interactive sessions (scheduled tasks, remote shells). Never fails the install.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bridgerator",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "Connect ChatGPT to your Windows PC: files, search and terminal through an outbound-only bridge with folder limits. No inbound ports, no admin rights.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
package/src/cli.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// bridgerator setup --token <t> [--relay <wss url>] [--allow <dir>]... [--deny <dir>]... → config + autostart + start
|
|
2
|
+
// bridgerator setup [--token <t> | --pair] [--relay <wss url>] [--allow <dir>]... [--deny <dir>]... → config + autostart + start
|
|
3
|
+
// (no key yet: pairs this PC through the browser — the signed-in owner confirms a code)
|
|
3
4
|
// bridgerator run | status | uninstall
|
|
4
5
|
import fs from 'node:fs';
|
|
5
6
|
import os from 'node:os';
|
|
@@ -7,6 +8,7 @@ import path from 'node:path';
|
|
|
7
8
|
import { spawn, execFileSync } from 'node:child_process';
|
|
8
9
|
import { fileURLToPath } from 'node:url';
|
|
9
10
|
import { startDesktopCommander, connect } from './bridge.mjs';
|
|
11
|
+
import { pair, siteOf } from './pair.mjs';
|
|
10
12
|
|
|
11
13
|
const DEFAULT_RELAY = 'wss://mcp.bridgerator.recondeck.com/link';
|
|
12
14
|
const TASK = 'Bridgerator';
|
|
@@ -82,10 +84,14 @@ async function main() {
|
|
|
82
84
|
const cmd = o._[0];
|
|
83
85
|
if (cmd === 'setup') {
|
|
84
86
|
const prev = readConfig() || {};
|
|
85
|
-
|
|
86
|
-
|
|
87
|
+
if (o.token === true) throw new Error('--token needs the device key from your Bridgerator account');
|
|
88
|
+
const relay = o.relay || prev.relay || DEFAULT_RELAY;
|
|
89
|
+
// --pair: get a new key through the browser even if one is configured (computer removed in the account).
|
|
90
|
+
const paired = !o.token && (o.pair || !prev.token);
|
|
91
|
+
const token = o.token || (paired ? (await pair(siteOf(relay), os.hostname())).token : prev.token);
|
|
92
|
+
if (paired) console.log('Confirmed — this computer is linked to your account.');
|
|
87
93
|
const cfg = {
|
|
88
|
-
relay
|
|
94
|
+
relay,
|
|
89
95
|
token,
|
|
90
96
|
allowedDirectories: o.allow || prev.allowedDirectories || [home],
|
|
91
97
|
deniedDirectories: o.deny || prev.deniedDirectories || [path.join(home, '.ssh'), path.join(home, 'AppData')],
|
|
@@ -102,12 +108,12 @@ async function main() {
|
|
|
102
108
|
stopRunning();
|
|
103
109
|
startHidden();
|
|
104
110
|
console.log(`started in background — log: ${LOG}`);
|
|
105
|
-
console.log('Next: in ChatGPT add the
|
|
111
|
+
console.log('Next: in ChatGPT add the Bridgerator app and sign in with the same account.');
|
|
106
112
|
return;
|
|
107
113
|
}
|
|
108
114
|
if (cmd === 'run') {
|
|
109
115
|
const cfg = readConfig();
|
|
110
|
-
if (!cfg) throw new Error(`no config at ${CONFIG} — run: bridgerator setup
|
|
116
|
+
if (!cfg) throw new Error(`no config at ${CONFIG} — run: bridgerator setup`);
|
|
111
117
|
const dc = await startDesktopCommander({ ...cfg, dcConfigDir: path.join(DIR, 'dc') }, log);
|
|
112
118
|
connect(cfg, dc, log);
|
|
113
119
|
return;
|
|
@@ -125,7 +131,7 @@ async function main() {
|
|
|
125
131
|
console.log(`stopped and autostart removed. Config kept at ${CONFIG}. Remove the package: npm rm -g bridgerator`);
|
|
126
132
|
return;
|
|
127
133
|
}
|
|
128
|
-
console.log('usage: bridgerator setup --token <key> [--allow <dir>]... [--deny <dir>]... | run | status | uninstall');
|
|
134
|
+
console.log('usage: bridgerator setup [--token <key> | --pair] [--allow <dir>]... [--deny <dir>]... | run | status | uninstall');
|
|
129
135
|
}
|
|
130
136
|
|
|
131
137
|
main().catch((e) => { console.error(`[bridgerator] ${e?.message || e}`); process.exit(1); });
|
package/src/notice.mjs
CHANGED
|
@@ -11,7 +11,7 @@ const xml = (s) => String(s).replace(/[<>&"']/g, (c) => ({ '<': '<', '>': '&g
|
|
|
11
11
|
export function noticeText(n) {
|
|
12
12
|
const t = {
|
|
13
13
|
limit: ['Bridgerator: monthly limit reached', `${n.calls} of ${n.limit} tool calls used. ChatGPT can't use this PC until the 1st. Turn on Pro in your account.`],
|
|
14
|
-
expired: ['Bridgerator Pro has ended', "You're on Free now (
|
|
14
|
+
expired: ['Bridgerator Pro has ended', "You're on Free now (10,000 tool calls a month). Renew Pro in your account."],
|
|
15
15
|
expiring: [`Bridgerator Pro ends on ${day(n.until)}`, 'Pay for 30 more days in your account to keep unlimited calls.'],
|
|
16
16
|
}[n.kind];
|
|
17
17
|
return t && { title: t[0], body: t[1], button: 'Open account' };
|
package/src/pair.mjs
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// Key-less setup: ask the relay for a pairing code, open the confirmation page in the browser, wait
|
|
2
|
+
// until the signed-in owner confirms this computer there, and get this PC's device key back.
|
|
3
|
+
import { spawn } from 'node:child_process';
|
|
4
|
+
|
|
5
|
+
// wss://mcp.bridgerator.recondeck.com/link → https://bridgerator.recondeck.com (the site host, where the account pages live).
|
|
6
|
+
export const siteOf = (relay) => { const u = new URL(relay); return `${u.protocol === 'ws:' ? 'http' : 'https'}://${u.host.replace(/^mcp\./, '')}`; };
|
|
7
|
+
|
|
8
|
+
// Opens the URL in the default browser. Only a URL we checked ourselves reaches cmd's `start`.
|
|
9
|
+
export function openBrowser(url) {
|
|
10
|
+
if (!/^https?:\/\/[A-Za-z0-9.:-]+\/pair\?code=[A-Z0-9]{4}-[A-Z0-9]{4}$/.test(url)) return;
|
|
11
|
+
try {
|
|
12
|
+
const p = process.platform === 'win32'
|
|
13
|
+
? spawn('cmd', ['/c', `start "" "${url}"`], { windowsHide: true, windowsVerbatimArguments: true, stdio: 'ignore', detached: true })
|
|
14
|
+
: spawn(process.platform === 'darwin' ? 'open' : 'xdg-open', [url], { stdio: 'ignore', detached: true });
|
|
15
|
+
p.on('error', () => {});
|
|
16
|
+
p.unref();
|
|
17
|
+
} catch { /* no browser: the URL is printed */ }
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** → { token, name } once the owner confirmed; throws when the code expires. */
|
|
21
|
+
export async function pair(site, name, { fetch: f = globalThis.fetch, open = openBrowser, log = console.log, sleep = (ms) => new Promise((r) => setTimeout(r, ms)), everyMs = 3000 } = {}) {
|
|
22
|
+
const post = (path, body) => f(`${site}${path}`, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(body), signal: AbortSignal.timeout(20000) });
|
|
23
|
+
const r = await post('/pair/start', { name });
|
|
24
|
+
if (!r.ok) throw new Error(`could not start pairing (HTTP ${r.status}) — try again in a minute`);
|
|
25
|
+
const s = await r.json();
|
|
26
|
+
const url = `${site}/pair?code=${s.code}`;
|
|
27
|
+
if (!/^[A-Z0-9]{4}-[A-Z0-9]{4}$/.test(String(s.code)) || typeof s.poll !== 'string') throw new Error('unexpected answer from the relay');
|
|
28
|
+
log('');
|
|
29
|
+
log(` Confirm this computer in your browser: ${url}`);
|
|
30
|
+
log(` Code: ${s.code} (check that the page shows the same code)`);
|
|
31
|
+
log('');
|
|
32
|
+
open(url);
|
|
33
|
+
log('Waiting for the confirmation (up to 15 minutes)...');
|
|
34
|
+
const until = Date.now() + (Number(s.expiresIn) || 900) * 1000;
|
|
35
|
+
while (Date.now() < until) {
|
|
36
|
+
await sleep(everyMs);
|
|
37
|
+
let p;
|
|
38
|
+
try { p = await post('/pair/poll', { poll: s.poll }); } catch { continue; } // network blip: try again
|
|
39
|
+
if (p.status === 200) { const j = await p.json(); if (j.token) return { token: j.token, name: j.name || name }; }
|
|
40
|
+
if (p.status === 410) break;
|
|
41
|
+
}
|
|
42
|
+
throw new Error('the code expired before it was confirmed — run the command again');
|
|
43
|
+
}
|