thinkpool-pair 0.7.3 → 0.7.4
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/account.mjs +27 -19
- package/package.json +1 -1
package/account.mjs
CHANGED
|
@@ -19,27 +19,35 @@ const VERSION = (() => { try { return JSON.parse(fs.readFileSync(new URL('./pack
|
|
|
19
19
|
|
|
20
20
|
const BRIDGE = fileURLToPath(new URL('./bridge.mjs', import.meta.url))
|
|
21
21
|
|
|
22
|
-
// ── login:
|
|
23
|
-
// The bridge
|
|
24
|
-
//
|
|
25
|
-
//
|
|
22
|
+
// ── login: OAuth-style DEVICE-CODE flow (hardened) ──────────────────────────
|
|
23
|
+
// The bridge asks Postgres (start_device_code) for a secret device_code + a short
|
|
24
|
+
// human user_code, prints the user_code + URL, then POLLS claim_device_code with
|
|
25
|
+
// the secret until the signed-in web has approved (approve_device_code stores the
|
|
26
|
+
// browser's session). The session is delivered only over authenticated TLS RPCs
|
|
27
|
+
// + a single-use secret — never broadcast on a public channel.
|
|
26
28
|
export async function runLogin(SUPABASE_URL, SUPABASE_ANON, WEB_BASE) {
|
|
27
29
|
const sb = createClient(SUPABASE_URL, SUPABASE_ANON, { auth: { persistSession: false } })
|
|
28
|
-
const
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
30
|
+
const { data, error } = await sb.rpc('start_device_code')
|
|
31
|
+
const row = Array.isArray(data) ? data[0] : data
|
|
32
|
+
if (error || !row?.device_code) { process.stderr.write(`\n ✗ couldn't start login: ${error?.message || 'no response'}\n`); process.exit(1) }
|
|
33
|
+
const { device_code, user_code } = row
|
|
34
|
+
const url = `${WEB_BASE}/code/link?c=${encodeURIComponent(user_code)}`
|
|
35
|
+
process.stderr.write(`\n ◆ Link this device to your ThinkPool account.\n Open (signed in) and approve:\n\n ${url}\n\n code: ${user_code}\n\n Waiting…\n`)
|
|
36
|
+
const started = Date.now()
|
|
37
|
+
const poll = async () => {
|
|
38
|
+
if (Date.now() - started > 5 * 60 * 1000) { process.stderr.write('\n ◇ link timed out — run `npx thinkpool-pair login` again.\n'); process.exit(1) }
|
|
39
|
+
try {
|
|
40
|
+
const { data: r } = await sb.rpc('claim_device_code', { p_device_code: device_code })
|
|
41
|
+
if (r?.status === 'approved' && r.session?.refresh_token) {
|
|
42
|
+
saveAuth(r.session)
|
|
43
|
+
process.stderr.write(`\n ◆ linked as ${r.session.email || 'your account'}. You can close the browser tab.\n Now run: npx thinkpool-pair\n`)
|
|
44
|
+
process.exit(0)
|
|
45
|
+
}
|
|
46
|
+
if (r?.status === 'expired' || r?.status === 'not_found') { process.stderr.write('\n ◇ link expired — run `npx thinkpool-pair login` again.\n'); process.exit(1) }
|
|
47
|
+
} catch { /* transient — keep polling */ }
|
|
48
|
+
setTimeout(poll, 3000)
|
|
49
|
+
}
|
|
50
|
+
poll()
|
|
43
51
|
await new Promise(() => {})
|
|
44
52
|
}
|
|
45
53
|
|