thinkpool-pair 0.7.292 → 0.7.293
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 +16 -3
- package/bridge.mjs +6 -0
- package/package.json +2 -1
- package/privacy-report.mjs +108 -0
package/README.md
CHANGED
|
@@ -144,12 +144,25 @@ npx thinkpool-pair@latest <ROOM> -- claude # structured Claude, no TTY
|
|
|
144
144
|
- `pty-in` — keystrokes/prompts from the web → written to that terminal's PTY.
|
|
145
145
|
- `term-open` / `term-close` / `term-exit` — web-driven terminal lifecycle.
|
|
146
146
|
- `replay-request` / `pty-replay` — each terminal keeps a rolling ~120 KB
|
|
147
|
-
scrollback buffer
|
|
148
|
-
|
|
149
|
-
|
|
147
|
+
scrollback buffer for live recovery. Thinkpool also stores the cleaned reader
|
|
148
|
+
transcript and room events so members can reopen the room. It does not store
|
|
149
|
+
a copy of the repository or the raw PTY byte stream.
|
|
150
150
|
- `resize` — web viewport size → headless PTYs only (the attached terminal
|
|
151
151
|
follows your own TTY).
|
|
152
152
|
|
|
153
|
+
## Inspect the privacy boundary
|
|
154
|
+
|
|
155
|
+
Run a local report before pairing a repository:
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
npx thinkpool-pair@latest privacy-report
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
It lists the project directories configured for this bridge, the local records
|
|
162
|
+
present under `~/.thinkpool-pair`, known outbound service domains, what
|
|
163
|
+
Thinkpool stores remotely, and the limits of the report. It never prints a
|
|
164
|
+
provider key, refresh token, or bridge private key.
|
|
165
|
+
|
|
153
166
|
## Run any model
|
|
154
167
|
|
|
155
168
|
ThinkPool Code runs **any model you choose** — not just Anthropic. The agent
|
package/bridge.mjs
CHANGED
|
@@ -226,6 +226,12 @@ const pickAgent = (installed) => new Promise((resolve) => {
|
|
|
226
226
|
|
|
227
227
|
const argv = process.argv.slice(2)
|
|
228
228
|
|
|
229
|
+
if (argv[0] === 'privacy-report') {
|
|
230
|
+
const { runPrivacyReport } = await import('./privacy-report.mjs')
|
|
231
|
+
runPrivacyReport()
|
|
232
|
+
process.exit(0)
|
|
233
|
+
}
|
|
234
|
+
|
|
229
235
|
// Boot-persistent service install (cross-platform: launchd / systemd / Windows
|
|
230
236
|
// Startup). Subcommand form: `thinkpool-pair install-service <ROOM> [-- <cmd>]`.
|
|
231
237
|
if (argv[0] === 'install-service' || argv[0] === 'uninstall-service') {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "thinkpool-pair",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.293",
|
|
4
4
|
"description": "Share a local coding-agent CLI (Claude Code, Codex, Gemini, Aider, …) into a ThinkPool Code room, live.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
"sdk-admission.mjs",
|
|
14
14
|
"sdk-admission.mjs",
|
|
15
15
|
"launcher.mjs",
|
|
16
|
+
"privacy-report.mjs",
|
|
16
17
|
"byok-detect.mjs",
|
|
17
18
|
"context-windows.mjs",
|
|
18
19
|
"claude-session.mjs",
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import os from 'node:os'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
|
|
5
|
+
const cleanHost = (value) => {
|
|
6
|
+
try { return new URL(value).host } catch { return null }
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const readJson = (file, fallback) => {
|
|
10
|
+
try { return JSON.parse(fs.readFileSync(file, 'utf8')) } catch { return fallback }
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const displayPath = (value, home) => {
|
|
14
|
+
const resolved = path.resolve(String(value || ''))
|
|
15
|
+
return resolved === home ? '~' : resolved.startsWith(`${home}${path.sep}`) ? `~${resolved.slice(home.length)}` : resolved
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function buildPrivacyReport({
|
|
19
|
+
home = os.homedir(),
|
|
20
|
+
cwd = process.cwd(),
|
|
21
|
+
env = process.env,
|
|
22
|
+
exists = fs.existsSync,
|
|
23
|
+
read = readJson,
|
|
24
|
+
} = {}) {
|
|
25
|
+
const configDir = path.join(home, '.thinkpool-pair')
|
|
26
|
+
const dirs = read(path.join(configDir, 'dirs.json'), {}) || {}
|
|
27
|
+
const served = read(path.join(configDir, 'served.json'), {}) || {}
|
|
28
|
+
const providers = read(path.join(configDir, 'providers.json'), []) || []
|
|
29
|
+
const legacyProvider = read(path.join(configDir, 'provider.json'), null)
|
|
30
|
+
let defaultDir = ''
|
|
31
|
+
try { defaultDir = fs.readFileSync(path.join(configDir, 'default-dir'), 'utf8').trim() } catch { /* not configured */ }
|
|
32
|
+
|
|
33
|
+
const projectDirs = new Set([cwd, defaultDir, ...Object.values(dirs), ...Object.values(served)].filter(Boolean).map((p) => displayPath(p, home)))
|
|
34
|
+
const providerHosts = new Set([
|
|
35
|
+
...providers.map((provider) => cleanHost(provider?.baseUrl)),
|
|
36
|
+
cleanHost(legacyProvider?.baseUrl),
|
|
37
|
+
].filter(Boolean))
|
|
38
|
+
const supabaseHost = cleanHost(env.TP_SUPABASE_URL || 'https://daytvtakmlixpfbbqzjd.supabase.co')
|
|
39
|
+
const webHost = cleanHost(env.TP_WEB_BASE || 'https://thinkpool.io')
|
|
40
|
+
|
|
41
|
+
const localRecords = [
|
|
42
|
+
['Saved login', 'auth.json', exists(path.join(configDir, 'auth.json')), 'refresh token and account identity'],
|
|
43
|
+
['Provider registry', 'providers.json', exists(path.join(configDir, 'providers.json')), 'provider endpoint, model, and API key'],
|
|
44
|
+
['Bridge keypair', 'bridge-key.json', exists(path.join(configDir, 'bridge-key.json')), 'private key used to open browser-sealed provider keys'],
|
|
45
|
+
['Room directories', 'dirs.json / served.json', exists(path.join(configDir, 'dirs.json')) || exists(path.join(configDir, 'served.json')), 'room-to-project directory mappings'],
|
|
46
|
+
['Bridge logs', 'update.log and service logs', exists(path.join(configDir, 'update.log')), 'update and background-service diagnostics'],
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
generatedAt: new Date().toISOString(),
|
|
51
|
+
configDir: displayPath(configDir, home),
|
|
52
|
+
projectDirs: [...projectDirs].sort(),
|
|
53
|
+
localRecords,
|
|
54
|
+
outbound: [
|
|
55
|
+
[webHost, 'Thinkpool configuration, linking, version checks, and product APIs'],
|
|
56
|
+
[supabaseHost, 'authentication, room database, storage, and realtime transport'],
|
|
57
|
+
['registry.npmjs.org', 'package installation and updates initiated with npm or npx'],
|
|
58
|
+
...[...providerHosts].sort().map((host) => [host, 'AI provider selected on this bridge']),
|
|
59
|
+
].filter(([host]) => host),
|
|
60
|
+
remote: [
|
|
61
|
+
['Thinkpool cloud receives', 'account/profile data; room membership and metadata; room chat; cleaned terminal output; terminal and room events; attachments; operational request metadata'],
|
|
62
|
+
['Thinkpool cloud does not receive as a repository', 'a crawled copy of the project directory or repository file tree'],
|
|
63
|
+
['Readable provider key', 'stored locally by the bridge; Thinkpool relays only a browser-sealed envelope'],
|
|
64
|
+
['AI prompts and code context', 'sent by the local coding agent to the selected AI provider; content depends on that runtime and the task'],
|
|
65
|
+
],
|
|
66
|
+
limits: [
|
|
67
|
+
'Coding-agent runtimes and commands can contact domains beyond the bridge list above.',
|
|
68
|
+
'Thinkpool room transcripts are readable by Thinkpool infrastructure today; they are not end-to-end encrypted.',
|
|
69
|
+
'This report describes configured access. It is not a packet capture or a guarantee that a third-party agent obeys the same boundary.',
|
|
70
|
+
],
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function formatPrivacyReport(report) {
|
|
75
|
+
const yn = (value) => value ? 'present' : 'not present'
|
|
76
|
+
const lines = [
|
|
77
|
+
'',
|
|
78
|
+
' THINKPOOL PRIVACY REPORT',
|
|
79
|
+
` generated ${report.generatedAt}`,
|
|
80
|
+
'',
|
|
81
|
+
' LOCAL ACCESS',
|
|
82
|
+
` Bridge configuration: ${report.configDir}`,
|
|
83
|
+
...report.projectDirs.map((dir) => ` Project directory: ${dir}`),
|
|
84
|
+
'',
|
|
85
|
+
' LOCAL RECORDS',
|
|
86
|
+
...report.localRecords.map(([label, file, present, contains]) => ` ${label}: ${yn(present)} (${file})\n contains: ${contains}`),
|
|
87
|
+
'',
|
|
88
|
+
' OUTBOUND DOMAINS',
|
|
89
|
+
...report.outbound.map(([host, reason]) => ` ${host}\n ${reason}`),
|
|
90
|
+
'',
|
|
91
|
+
' REMOTE DATA',
|
|
92
|
+
...report.remote.map(([label, detail]) => ` ${label}:\n ${detail}`),
|
|
93
|
+
'',
|
|
94
|
+
' IMPORTANT LIMITS',
|
|
95
|
+
...report.limits.map((line) => ` - ${line}`),
|
|
96
|
+
'',
|
|
97
|
+
' Full data map: https://thinkpool.io/data',
|
|
98
|
+
'',
|
|
99
|
+
]
|
|
100
|
+
return lines.join('\n')
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function runPrivacyReport(options) {
|
|
104
|
+
const report = buildPrivacyReport(options)
|
|
105
|
+
process.stdout.write(formatPrivacyReport(report))
|
|
106
|
+
return report
|
|
107
|
+
}
|
|
108
|
+
|