mercury-agent 0.4.5

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.
Files changed (218) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +438 -0
  3. package/container/Dockerfile +127 -0
  4. package/container/Dockerfile.base +109 -0
  5. package/container/Dockerfile.power +17 -0
  6. package/container/agent-package.json +8 -0
  7. package/container/build.sh +54 -0
  8. package/docs/TODOS.md +147 -0
  9. package/docs/auth/dashboard.md +28 -0
  10. package/docs/auth/overview.md +109 -0
  11. package/docs/auth/whatsapp.md +173 -0
  12. package/docs/configuration.md +54 -0
  13. package/docs/container-lifecycle.md +349 -0
  14. package/docs/context-architecture.md +87 -0
  15. package/docs/deployment.md +199 -0
  16. package/docs/extensions.md +375 -0
  17. package/docs/graceful-shutdown.md +62 -0
  18. package/docs/kb-distillation.md +77 -0
  19. package/docs/media/overview.md +140 -0
  20. package/docs/media/whatsapp.md +171 -0
  21. package/docs/memory.md +137 -0
  22. package/docs/permissions.md +217 -0
  23. package/docs/pipeline.md +228 -0
  24. package/docs/prd-chat-memory.md +76 -0
  25. package/docs/prd-config-load.md +82 -0
  26. package/docs/rate-limiting.md +166 -0
  27. package/docs/scheduler.md +288 -0
  28. package/docs/setup-discord.md +100 -0
  29. package/docs/setup-slack.md +119 -0
  30. package/docs/setup-whatsapp.md +94 -0
  31. package/docs/subagents.md +166 -0
  32. package/docs/web-search.md +62 -0
  33. package/examples/extensions/README.md +12 -0
  34. package/examples/extensions/charts/index.ts +13 -0
  35. package/examples/extensions/charts/skill/SKILL.md +98 -0
  36. package/examples/extensions/gws/README.md +52 -0
  37. package/examples/extensions/gws/index.ts +106 -0
  38. package/examples/extensions/gws/skill/SKILL.md +57 -0
  39. package/examples/extensions/gws/skill/references/calendar.md +101 -0
  40. package/examples/extensions/gws/skill/references/docs.md +65 -0
  41. package/examples/extensions/gws/skill/references/drive.md +79 -0
  42. package/examples/extensions/gws/skill/references/gmail.md +85 -0
  43. package/examples/extensions/gws/skill/references/sheets.md +60 -0
  44. package/examples/extensions/napkin/index.ts +821 -0
  45. package/examples/extensions/napkin/prompts/consolidation-monthly.md +73 -0
  46. package/examples/extensions/napkin/prompts/consolidation-weekly.md +67 -0
  47. package/examples/extensions/napkin/prompts/kb-distillation.md +176 -0
  48. package/examples/extensions/napkin/skill/SKILL.md +728 -0
  49. package/examples/extensions/pdf/index.ts +23 -0
  50. package/examples/extensions/pdf/skill/LICENSE.txt +30 -0
  51. package/examples/extensions/pdf/skill/SKILL.md +314 -0
  52. package/examples/extensions/pdf/skill/forms.md +294 -0
  53. package/examples/extensions/pdf/skill/reference.md +612 -0
  54. package/examples/extensions/pdf/skill/scripts/check_bounding_boxes.py +65 -0
  55. package/examples/extensions/pdf/skill/scripts/check_fillable_fields.py +11 -0
  56. package/examples/extensions/pdf/skill/scripts/convert_pdf_to_images.py +33 -0
  57. package/examples/extensions/pdf/skill/scripts/create_validation_image.py +37 -0
  58. package/examples/extensions/pdf/skill/scripts/extract_form_field_info.py +122 -0
  59. package/examples/extensions/pdf/skill/scripts/extract_form_structure.py +115 -0
  60. package/examples/extensions/pdf/skill/scripts/fill_fillable_fields.py +98 -0
  61. package/examples/extensions/pdf/skill/scripts/fill_pdf_form_with_annotations.py +107 -0
  62. package/examples/extensions/permission-guard/index.ts +65 -0
  63. package/examples/extensions/pinchtab/index.ts +199 -0
  64. package/examples/extensions/pinchtab/lib/session-injector.ts +144 -0
  65. package/examples/extensions/pinchtab/skill/SKILL.md +224 -0
  66. package/examples/extensions/pinchtab/skill/TRUST.md +69 -0
  67. package/examples/extensions/pinchtab/skill/references/api.md +297 -0
  68. package/examples/extensions/pinchtab/skill/references/env.md +45 -0
  69. package/examples/extensions/pinchtab/skill/references/profiles.md +107 -0
  70. package/examples/extensions/tradestation/host/refresh.ts +102 -0
  71. package/examples/extensions/tradestation/index.ts +153 -0
  72. package/examples/extensions/tradestation/skill/SKILL.md +67 -0
  73. package/examples/extensions/tradestation/skill/scripts/ts-cli.ts +111 -0
  74. package/examples/extensions/voice-synth/index.ts +94 -0
  75. package/examples/extensions/voice-synth/skill/SKILL.md +38 -0
  76. package/examples/extensions/voice-transcribe/index.ts +381 -0
  77. package/examples/extensions/voice-transcribe/requirements.txt +8 -0
  78. package/examples/extensions/voice-transcribe/scripts/transcribe.py +179 -0
  79. package/examples/extensions/voice-transcribe/skill/SKILL.md +53 -0
  80. package/examples/extensions/web-search/index.ts +22 -0
  81. package/examples/extensions/web-search/skill/SKILL.md +114 -0
  82. package/examples/extensions/web-search/skill/references/apartments.md +178 -0
  83. package/examples/extensions/web-search/skill/references/car-purchase.md +132 -0
  84. package/examples/extensions/web-search/skill/references/car-rental.md +113 -0
  85. package/examples/extensions/web-search/skill/references/flights.md +133 -0
  86. package/examples/extensions/web-search/skill/references/hotels.md +148 -0
  87. package/examples/extensions/yahoo-mail/cli/bun.lock +66 -0
  88. package/examples/extensions/yahoo-mail/cli/package.json +13 -0
  89. package/examples/extensions/yahoo-mail/cli/ymail.mjs +353 -0
  90. package/examples/extensions/yahoo-mail/index.ts +57 -0
  91. package/examples/extensions/yahoo-mail/skill/SKILL.md +78 -0
  92. package/package.json +106 -0
  93. package/resources/agents/explore.md +50 -0
  94. package/resources/agents/worker.md +24 -0
  95. package/resources/builtin-extensions.txt +3 -0
  96. package/resources/connection-env-vars.json +25 -0
  97. package/resources/extensions/.gitkeep +0 -0
  98. package/resources/pi-extensions/subagent/agents.ts +126 -0
  99. package/resources/pi-extensions/subagent/index.ts +964 -0
  100. package/resources/profiles/coding/AGENTS.md +43 -0
  101. package/resources/profiles/coding/mercury-profile.yaml +15 -0
  102. package/resources/profiles/general/AGENTS.md +31 -0
  103. package/resources/profiles/general/mercury-profile.yaml +15 -0
  104. package/resources/profiles/research/AGENTS.md +40 -0
  105. package/resources/profiles/research/mercury-profile.yaml +15 -0
  106. package/resources/skills/config/SKILL.md +25 -0
  107. package/resources/skills/context/SKILL.md +33 -0
  108. package/resources/skills/conversation-recap/SKILL.md +19 -0
  109. package/resources/skills/media/SKILL.md +27 -0
  110. package/resources/skills/mutes/SKILL.md +31 -0
  111. package/resources/skills/permissions/SKILL.md +19 -0
  112. package/resources/skills/preferences/SKILL.md +31 -0
  113. package/resources/skills/recall/SKILL.md +24 -0
  114. package/resources/skills/roles/SKILL.md +18 -0
  115. package/resources/skills/spaces/SKILL.md +18 -0
  116. package/resources/skills/tasks/SKILL.md +45 -0
  117. package/resources/templates/AGENTS.md +157 -0
  118. package/resources/templates/env.template +34 -0
  119. package/resources/templates/mercury.example.yaml +75 -0
  120. package/src/adapters/discord-native.ts +534 -0
  121. package/src/adapters/discord.ts +38 -0
  122. package/src/adapters/setup.ts +89 -0
  123. package/src/adapters/slack.ts +9 -0
  124. package/src/adapters/whatsapp-media.ts +337 -0
  125. package/src/adapters/whatsapp.ts +629 -0
  126. package/src/agent/api-socket.ts +127 -0
  127. package/src/agent/container-entry.ts +967 -0
  128. package/src/agent/container-error.ts +49 -0
  129. package/src/agent/container-runner.ts +1272 -0
  130. package/src/agent/model-capabilities-core.ts +23 -0
  131. package/src/agent/model-capabilities.ts +231 -0
  132. package/src/agent/pi-failure-class.ts +83 -0
  133. package/src/agent/pi-jsonl-parser.ts +306 -0
  134. package/src/agent/preferences-prompt.ts +20 -0
  135. package/src/agent/user-error-messages.ts +78 -0
  136. package/src/bridges/discord.ts +171 -0
  137. package/src/bridges/slack.ts +177 -0
  138. package/src/bridges/teams.ts +160 -0
  139. package/src/bridges/telegram.ts +571 -0
  140. package/src/bridges/whatsapp.ts +290 -0
  141. package/src/chat-shim.ts +259 -0
  142. package/src/cli/mercury.ts +2508 -0
  143. package/src/cli/mrctl-http.ts +27 -0
  144. package/src/cli/mrctl.ts +611 -0
  145. package/src/cli/whatsapp-auth.ts +260 -0
  146. package/src/config-file.ts +397 -0
  147. package/src/config-model-chain.ts +30 -0
  148. package/src/config.ts +316 -0
  149. package/src/core/api-types.ts +58 -0
  150. package/src/core/api.ts +105 -0
  151. package/src/core/commands.ts +76 -0
  152. package/src/core/conversation.ts +47 -0
  153. package/src/core/handler.ts +206 -0
  154. package/src/core/media.ts +200 -0
  155. package/src/core/mute-duration.ts +22 -0
  156. package/src/core/outbox.ts +76 -0
  157. package/src/core/permissions.ts +192 -0
  158. package/src/core/profiles.ts +245 -0
  159. package/src/core/rate-limiter.ts +127 -0
  160. package/src/core/router.ts +191 -0
  161. package/src/core/routes/chat.ts +172 -0
  162. package/src/core/routes/config-builtin.ts +107 -0
  163. package/src/core/routes/config.ts +81 -0
  164. package/src/core/routes/connections.ts +190 -0
  165. package/src/core/routes/console.ts +668 -0
  166. package/src/core/routes/control.ts +46 -0
  167. package/src/core/routes/conversations.ts +66 -0
  168. package/src/core/routes/dashboard.ts +2491 -0
  169. package/src/core/routes/extensions.ts +37 -0
  170. package/src/core/routes/index.ts +14 -0
  171. package/src/core/routes/media.ts +72 -0
  172. package/src/core/routes/messages.ts +37 -0
  173. package/src/core/routes/mutes.ts +89 -0
  174. package/src/core/routes/prefs.ts +95 -0
  175. package/src/core/routes/roles.ts +125 -0
  176. package/src/core/routes/spaces.ts +60 -0
  177. package/src/core/routes/storage.ts +126 -0
  178. package/src/core/routes/tasks.ts +189 -0
  179. package/src/core/routes/tradestation.ts +268 -0
  180. package/src/core/routes/tts.ts +51 -0
  181. package/src/core/runtime.ts +1140 -0
  182. package/src/core/space-queue.ts +103 -0
  183. package/src/core/storage-cleanup.ts +140 -0
  184. package/src/core/storage-guard.ts +24 -0
  185. package/src/core/task-scheduler.ts +132 -0
  186. package/src/core/telegram-format.ts +178 -0
  187. package/src/core/trigger.ts +142 -0
  188. package/src/dashboard/index.html +729 -0
  189. package/src/dashboard/tokens.css +53 -0
  190. package/src/extensions/api.ts +252 -0
  191. package/src/extensions/catalog.ts +117 -0
  192. package/src/extensions/config-registry.ts +83 -0
  193. package/src/extensions/context.ts +36 -0
  194. package/src/extensions/hooks.ts +156 -0
  195. package/src/extensions/image-builder.ts +617 -0
  196. package/src/extensions/installer.ts +306 -0
  197. package/src/extensions/jobs.ts +122 -0
  198. package/src/extensions/loader.ts +271 -0
  199. package/src/extensions/permission-guard.ts +52 -0
  200. package/src/extensions/reserved.ts +28 -0
  201. package/src/extensions/skills.ts +123 -0
  202. package/src/extensions/types.ts +462 -0
  203. package/src/logger.ts +174 -0
  204. package/src/main.ts +586 -0
  205. package/src/server.ts +391 -0
  206. package/src/storage/db.ts +1624 -0
  207. package/src/storage/memory.ts +45 -0
  208. package/src/storage/pi-auth.ts +95 -0
  209. package/src/text/markdown.ts +117 -0
  210. package/src/text/rtl.ts +38 -0
  211. package/src/tradestation/host-api.ts +77 -0
  212. package/src/tradestation/pending-orders.ts +69 -0
  213. package/src/tts/azure.ts +52 -0
  214. package/src/tts/google.ts +128 -0
  215. package/src/tts/index.ts +8 -0
  216. package/src/tts/language.ts +20 -0
  217. package/src/tts/synthesize.ts +133 -0
  218. package/src/types.ts +295 -0
@@ -0,0 +1,297 @@
1
+ # Pinchtab API Reference
2
+
3
+ Base URL for all examples: `http://localhost:9867`
4
+
5
+ > **CLI alternative:** All endpoints have CLI equivalents. Use `pinchtab help` for the full list. Examples are shown as `# CLI:` comments below.
6
+
7
+ ## Navigate
8
+
9
+ ```bash
10
+ # CLI: pinchtab nav https://example.com [--new-tab] [--block-images]
11
+ curl -X POST /navigate \
12
+ -H 'Content-Type: application/json' \
13
+ -d '{"url": "https://example.com"}'
14
+
15
+ # With options: custom timeout, block images, open in new tab
16
+ curl -X POST /navigate \
17
+ -H 'Content-Type: application/json' \
18
+ -d '{"url": "https://example.com", "timeout": 60, "blockImages": true, "newTab": true}'
19
+ ```
20
+
21
+ ## Snapshot (accessibility tree)
22
+
23
+ ```bash
24
+ # CLI: pinchtab snap [-i] [-c] [-d] [-s main] [--max-tokens 2000]
25
+ # Full tree
26
+ curl /snapshot
27
+
28
+ # Interactive elements only (buttons, links, inputs) — much smaller
29
+ curl "/snapshot?filter=interactive"
30
+
31
+ # Limit depth
32
+ curl "/snapshot?depth=5"
33
+
34
+ # Smart diff — only changes since last snapshot (massive token savings)
35
+ curl "/snapshot?diff=true"
36
+
37
+ # Text format — indented tree, ~40-60% fewer tokens than JSON
38
+ curl "/snapshot?format=text"
39
+
40
+ # Compact format — one-line-per-node, 56-64% fewer tokens than JSON (recommended)
41
+ curl "/snapshot?format=compact"
42
+
43
+ # YAML format
44
+ curl "/snapshot?format=yaml"
45
+
46
+ # Scope to CSS selector (e.g. main content only)
47
+ curl "/snapshot?selector=main"
48
+
49
+ # Truncate to ~N tokens
50
+ curl "/snapshot?maxTokens=2000"
51
+
52
+ # Combine for maximum efficiency
53
+ curl "/snapshot?format=compact&selector=main&maxTokens=2000&filter=interactive"
54
+
55
+ # Disable animations before capture
56
+ curl "/snapshot?noAnimations=true"
57
+
58
+ # Write to file
59
+ curl "/snapshot?output=file&path=/tmp/snapshot.json"
60
+ ```
61
+
62
+ Returns flat JSON array of nodes with `ref`, `role`, `name`, `depth`, `value`, `nodeId`.
63
+
64
+ **Token optimization**: Use `?format=compact` for best token efficiency. Add `?filter=interactive` for action-oriented tasks (~75% fewer nodes). Use `?selector=main` to scope to relevant content. Use `?maxTokens=2000` to cap output. Use `?diff=true` on multi-step workflows to see only changes. Combine all params freely.
65
+
66
+ ## Act on elements
67
+
68
+ ```bash
69
+ # CLI: pinchtab click e5 / pinchtab type e12 hello / pinchtab press Enter
70
+ # Click by ref
71
+ curl -X POST /action -H 'Content-Type: application/json' \
72
+ -d '{"kind": "click", "ref": "e5"}'
73
+
74
+ # Type into focused element (click first, then type)
75
+ curl -X POST /action -H 'Content-Type: application/json' \
76
+ -d '{"kind": "click", "ref": "e12"}'
77
+ curl -X POST /action -H 'Content-Type: application/json' \
78
+ -d '{"kind": "type", "ref": "e12", "text": "hello world"}'
79
+
80
+ # Press a key
81
+ curl -X POST /action -H 'Content-Type: application/json' \
82
+ -d '{"kind": "press", "key": "Enter"}'
83
+
84
+ # Focus an element
85
+ curl -X POST /action -H 'Content-Type: application/json' \
86
+ -d '{"kind": "focus", "ref": "e3"}'
87
+
88
+ # Fill (set value directly, no keystrokes)
89
+ curl -X POST /action -H 'Content-Type: application/json' \
90
+ -d '{"kind": "fill", "selector": "#email", "text": "user@example.com"}'
91
+
92
+ # Hover (trigger dropdowns/tooltips)
93
+ curl -X POST /action -H 'Content-Type: application/json' \
94
+ -d '{"kind": "hover", "ref": "e8"}'
95
+
96
+ # Select dropdown option (by value or visible text)
97
+ curl -X POST /action -H 'Content-Type: application/json' \
98
+ -d '{"kind": "select", "ref": "e10", "value": "option2"}'
99
+
100
+ # Scroll to element
101
+ curl -X POST /action -H 'Content-Type: application/json' \
102
+ -d '{"kind": "scroll", "ref": "e20"}'
103
+
104
+ # Scroll by pixels (infinite scroll pages)
105
+ curl -X POST /action -H 'Content-Type: application/json' \
106
+ -d '{"kind": "scroll", "scrollY": 800}'
107
+
108
+ # Click and wait for navigation (link clicks)
109
+ curl -X POST /action -H 'Content-Type: application/json' \
110
+ -d '{"kind": "click", "ref": "e5", "waitNav": true}'
111
+ ```
112
+
113
+ ## Batch actions
114
+
115
+ ```bash
116
+ # Execute multiple actions in sequence
117
+ curl -X POST /actions -H 'Content-Type: application/json' \
118
+ -d '{"actions":[{"kind":"click","ref":"e3"},{"kind":"type","ref":"e3","text":"hello"},{"kind":"press","key":"Enter"}]}'
119
+
120
+ # Stop on first error (default: false)
121
+ curl -X POST /actions -H 'Content-Type: application/json' \
122
+ -d '{"tabId":"TARGET_ID","actions":[...],"stopOnError":true}'
123
+ ```
124
+
125
+ ## Extract text
126
+
127
+ ```bash
128
+ # CLI: pinchtab text [--raw]
129
+ # Readability mode (default) — strips nav/footer/ads
130
+ curl /text
131
+
132
+ # Raw innerText
133
+ curl "/text?mode=raw"
134
+ ```
135
+
136
+ Returns `{url, title, text}`. Cheapest option (~1K tokens for most pages).
137
+
138
+ ## PDF export
139
+
140
+ ```bash
141
+ # CLI: pinchtab pdf --tab TAB_ID [-o file.pdf] [--landscape] [--scale 0.8]
142
+ # Returns base64 JSON
143
+ curl "/tabs/TAB_ID/pdf"
144
+
145
+ # Raw PDF bytes
146
+ curl "/tabs/TAB_ID/pdf?raw=true" -o page.pdf
147
+
148
+ # Save to disk
149
+ curl "/tabs/TAB_ID/pdf?output=file&path=/tmp/page.pdf"
150
+
151
+ # Landscape with custom scale
152
+ curl "/tabs/TAB_ID/pdf?landscape=true&scale=0.8&raw=true" -o page.pdf
153
+
154
+ # Custom paper size (Letter: 8.5x11, A4: 8.27x11.69)
155
+ curl "/tabs/TAB_ID/pdf?paperWidth=8.5&paperHeight=11&marginTop=0.5&marginLeft=0.5&raw=true" -o custom.pdf
156
+
157
+ # Export specific pages
158
+ curl "/tabs/TAB_ID/pdf?pageRanges=1-5&raw=true" -o pages.pdf
159
+
160
+ # With header/footer
161
+ curl "/tabs/TAB_ID/pdf?displayHeaderFooter=true&headerTemplate=%3Cspan%20class=title%3E%3C/span%3E&raw=true" -o header.pdf
162
+
163
+ # Accessible PDF with document outline
164
+ curl "/tabs/TAB_ID/pdf?generateTaggedPDF=true&generateDocumentOutline=true&raw=true" -o accessible.pdf
165
+
166
+ # Honor CSS page size
167
+ curl "/tabs/TAB_ID/pdf?preferCSSPageSize=true&raw=true" -o css-sized.pdf
168
+ ```
169
+
170
+ **Query Parameters:**
171
+
172
+ | Param | Type | Default | Description |
173
+ |-------|------|---------|-------------|
174
+ | `paperWidth` | float | 8.5 | Paper width in inches |
175
+ | `paperHeight` | float | 11.0 | Paper height in inches |
176
+ | `landscape` | bool | false | Landscape orientation |
177
+ | `marginTop` | float | 0.4 | Top margin in inches |
178
+ | `marginBottom` | float | 0.4 | Bottom margin in inches |
179
+ | `marginLeft` | float | 0.4 | Left margin in inches |
180
+ | `marginRight` | float | 0.4 | Right margin in inches |
181
+ | `scale` | float | 1.0 | Print scale (0.1–2.0) |
182
+ | `pageRanges` | string | all | Pages to export (e.g., `1-3,5`) |
183
+ | `displayHeaderFooter` | bool | false | Show header and footer |
184
+ | `headerTemplate` | string | — | HTML template for header |
185
+ | `footerTemplate` | string | — | HTML template for footer |
186
+ | `preferCSSPageSize` | bool | false | Honor CSS `@page` size |
187
+ | `generateTaggedPDF` | bool | false | Generate accessible/tagged PDF |
188
+ | `generateDocumentOutline` | bool | false | Embed document outline |
189
+ | `output` | string | JSON | `file` to save to disk, default returns base64 |
190
+ | `path` | string | auto | Custom file path (with `output=file`) |
191
+ | `raw` | bool | false | Return raw PDF bytes instead of JSON |
192
+
193
+ Wraps `Page.printToPDF`. Prints background graphics by default.
194
+
195
+ ## Download files
196
+
197
+ ```bash
198
+ # Returns base64 JSON by default (uses browser session/cookies/stealth)
199
+ curl "/download?url=https://site.com/report.pdf"
200
+
201
+ # Raw bytes (pipe to file)
202
+ curl "/download?url=https://site.com/image.jpg&raw=true" -o image.jpg
203
+
204
+ # Save directly to disk
205
+ curl "/download?url=https://site.com/export.csv&output=file&path=/tmp/export.csv"
206
+ ```
207
+
208
+ ## Upload files
209
+
210
+ ```bash
211
+ # Upload a local file to a file input
212
+ curl -X POST "/upload?tabId=TAB_ID" -H "Content-Type: application/json" \
213
+ -d '{"selector": "input[type=file]", "paths": ["/tmp/photo.jpg"]}'
214
+
215
+ # Upload base64-encoded data
216
+ curl -X POST /upload -H "Content-Type: application/json" \
217
+ -d '{"selector": "#avatar-input", "files": ["data:image/png;base64,iVBOR..."]}'
218
+ ```
219
+
220
+ Sets files on `<input type=file>` elements via CDP. Fires `change` events. Selector defaults to `input[type=file]` if omitted.
221
+
222
+ ## Screenshot
223
+
224
+ ```bash
225
+ # CLI: pinchtab ss [-o file.jpg] [-q 80]
226
+ curl "/screenshot?raw=true" -o screenshot.jpg
227
+ curl "/screenshot?raw=true&quality=50" -o screenshot.jpg
228
+ ```
229
+
230
+ ## Evaluate JavaScript
231
+
232
+ ```bash
233
+ # CLI: pinchtab eval "document.title"
234
+ curl -X POST /evaluate -H 'Content-Type: application/json' \
235
+ -d '{"expression": "document.title"}'
236
+ ```
237
+
238
+ ## Tab management
239
+
240
+ ```bash
241
+ # CLI: pinchtab tabs / pinchtab tabs new <url> / pinchtab tabs close <id>
242
+ # List tabs
243
+ curl /tabs
244
+
245
+ # Open new tab
246
+ curl -X POST /tab -H 'Content-Type: application/json' \
247
+ -d '{"action": "new", "url": "https://example.com"}'
248
+
249
+ # Close tab
250
+ curl -X POST /tab -H 'Content-Type: application/json' \
251
+ -d '{"action": "close", "tabId": "TARGET_ID"}'
252
+ ```
253
+
254
+ Multi-tab: pass `?tabId=TARGET_ID` to snapshot/screenshot/text, or `"tabId"` in POST body.
255
+
256
+ ## Tab locking (multi-agent)
257
+
258
+ ```bash
259
+ # Lock a tab (default 30s timeout, max 5min)
260
+ curl -X POST /tab/lock -H 'Content-Type: application/json' \
261
+ -d '{"tabId": "TARGET_ID", "owner": "agent-1", "timeoutSec": 60}'
262
+
263
+ # Unlock
264
+ curl -X POST /tab/unlock -H 'Content-Type: application/json' \
265
+ -d '{"tabId": "TARGET_ID", "owner": "agent-1"}'
266
+ ```
267
+
268
+ Locked tabs show `owner` and `lockedUntil` in `/tabs`. Returns 409 on conflict.
269
+
270
+ ## Cookies
271
+
272
+ ```bash
273
+ # Get cookies for current page
274
+ curl /cookies
275
+
276
+ # Set cookies
277
+ curl -X POST /cookies -H 'Content-Type: application/json' \
278
+ -d '{"url":"https://example.com","cookies":[{"name":"session","value":"abc123"}]}'
279
+ ```
280
+
281
+ ## Stealth
282
+
283
+ ```bash
284
+ # Check stealth status and score
285
+ curl /stealth/status
286
+
287
+ # Rotate browser fingerprint
288
+ curl -X POST /fingerprint/rotate -H 'Content-Type: application/json' \
289
+ -d '{"os":"windows"}'
290
+ # os: "windows", "mac", or omit for random
291
+ ```
292
+
293
+ ## Health check
294
+
295
+ ```bash
296
+ curl /health
297
+ ```
@@ -0,0 +1,45 @@
1
+ # Pinchtab Environment Variables
2
+
3
+ ## Core runtime
4
+
5
+ | Var | Default | Description |
6
+ |---|---|---|
7
+ | `BRIDGE_BIND` | `127.0.0.1` | Bind address. Set `0.0.0.0` for network access |
8
+ | `BRIDGE_PORT` | `9867` | HTTP port |
9
+ | `BRIDGE_HEADLESS` | `true` | Run Chrome headless |
10
+ | `BRIDGE_TOKEN` | (none) | Bearer auth token (recommended with `0.0.0.0`) |
11
+ | `BRIDGE_PROFILE` | `~/.pinchtab/chrome-profile` | Chrome profile dir |
12
+ | `BRIDGE_STATE_DIR` | `~/.pinchtab` | State/session storage |
13
+ | `BRIDGE_NO_RESTORE` | `false` | Skip tab restore on startup |
14
+ | `BRIDGE_STEALTH` | `light` | Stealth level: `light` or `full` |
15
+ | `BRIDGE_MAX_TABS` | `20` | Max open tabs (0 = unlimited) |
16
+ | `BRIDGE_BLOCK_IMAGES` | `false` | Block image loading |
17
+ | `BRIDGE_BLOCK_MEDIA` | `false` | Block all media (images + fonts + CSS + video) |
18
+ | `BRIDGE_NO_ANIMATIONS` | `false` | Disable CSS animations/transitions |
19
+ | `BRIDGE_TIMEZONE` | (none) | Force browser timezone (IANA tz) |
20
+ | `BRIDGE_CHROME_VERSION` | `144.0.7559.133` | Chrome version for fingerprint rotation |
21
+ | `BRIDGE_USER_AGENT` | (none) | Custom User-Agent string; also overrides Sec-Ch-Ua client hints via CDP |
22
+ | `CHROME_BINARY` | (auto) | Path to Chrome/Chromium binary |
23
+ | `CHROME_FLAGS` | (none) | Extra Chrome flags (space-separated) |
24
+ | `BRIDGE_CONFIG` | `~/.pinchtab/config.json` | Path to config JSON file |
25
+ | `BRIDGE_TIMEOUT` | `15` | Action timeout (seconds) |
26
+ | `BRIDGE_NAV_TIMEOUT` | `30` | Navigation timeout (seconds) |
27
+ | `CDP_URL` | (none) | Connect to existing Chrome DevTools |
28
+ | `BRIDGE_NO_DASHBOARD` | `false` | Disable dashboard endpoints on instance processes |
29
+
30
+ ## CLI client
31
+
32
+ | Var | Default | Description |
33
+ |---|---|---|
34
+ | `PINCHTAB_URL` | `http://localhost:9867` | Pinchtab server URL for CLI commands |
35
+ | `PINCHTAB_TOKEN` | (none) | Auth token for CLI (sent as `Authorization: Bearer`) |
36
+
37
+ ## Dashboard (`pinchtab`)
38
+
39
+ | Var | Default | Description |
40
+ |---|---|---|
41
+ | `PINCHTAB_AUTO_LAUNCH` | `false` | Auto-launch default profile at startup |
42
+ | `PINCHTAB_DEFAULT_PROFILE` | `default` | Profile name for auto-launch |
43
+ | `PINCHTAB_DEFAULT_PORT` | `9867` | Port for auto-launched profile |
44
+ | `PINCHTAB_HEADED` | (unset) | If set, auto-launched profile is headed |
45
+ | `PINCHTAB_DASHBOARD_URL` | `http://localhost:$BRIDGE_PORT` | Base URL for `pinchtab connect` |
@@ -0,0 +1,107 @@
1
+ # Profile Management
2
+
3
+ When running `pinchtab`, profiles are managed via the dashboard API on port 9867.
4
+
5
+ ## List profiles
6
+
7
+ ```bash
8
+ curl http://localhost:9867/profiles
9
+ ```
10
+
11
+ Returns array of profiles with `id`, `name`, `accountEmail`, `useWhen`, etc.
12
+
13
+ ## Start a profile
14
+
15
+ ```bash
16
+ # Auto-allocate port (recommended)
17
+ curl -X POST http://localhost:9867/profiles/<ID>/start
18
+
19
+ # With specific port and headless mode
20
+ curl -X POST http://localhost:9867/profiles/<ID>/start \
21
+ -H 'Content-Type: application/json' \
22
+ -d '{"port": "9868", "headless": true}'
23
+
24
+ # Short alias
25
+ curl -X POST http://localhost:9867/start/<ID>
26
+ ```
27
+
28
+ Returns instance info including allocated `port`. Use that port for all subsequent API calls.
29
+
30
+ ## Stop a profile
31
+
32
+ ```bash
33
+ curl -X POST http://localhost:9867/profiles/<ID>/stop
34
+
35
+ # Short alias
36
+ curl -X POST http://localhost:9867/stop/<ID>
37
+ ```
38
+
39
+ ## Check instance status
40
+
41
+ ```bash
42
+ # By profile ID (recommended)
43
+ curl http://localhost:9867/profiles/<ID>/instance
44
+
45
+ # By profile name
46
+ curl http://localhost:9867/profiles/My%20Profile/instance
47
+ ```
48
+
49
+ ## Launch by name
50
+
51
+ ```bash
52
+ curl -X POST http://localhost:9867/instances/launch \
53
+ -H 'Content-Type: application/json' \
54
+ -d '{"name": "work", "port": "9868"}'
55
+ ```
56
+
57
+ ## CLI usage with profiles
58
+
59
+ The CLI doesn't have profile subcommands yet — use `curl` for profile management.
60
+ Once a profile instance is running, point the CLI at it:
61
+
62
+ ```bash
63
+ # Get the instance port, then use CLI
64
+ PINCHTAB_URL=http://localhost:9868 pinchtab snap -i
65
+ ```
66
+
67
+ ## Typical agent flow
68
+
69
+ ```bash
70
+ # 1. List profiles
71
+ PROFILES=$(curl -s http://localhost:9867/profiles)
72
+
73
+ # 2. Start profile (auto-allocates port)
74
+ INSTANCE=$(curl -s -X POST http://localhost:9867/profiles/$PROFILE_ID/start)
75
+ PORT=$(echo $INSTANCE | jq -r .port)
76
+
77
+ # 3. Use the instance
78
+ curl -X POST http://localhost:$PORT/navigate -H 'Content-Type: application/json' \
79
+ -d '{"url": "https://mail.google.com"}'
80
+ curl http://localhost:$PORT/snapshot?maxTokens=4000
81
+
82
+ # 4. Stop when done
83
+ curl -s -X POST http://localhost:9867/profiles/$PROFILE_ID/stop
84
+ ```
85
+
86
+ ## Profile IDs
87
+
88
+ Each profile gets a stable 12-char hex ID (SHA-256 of name, truncated) stored in `profile.json`. IDs are URL-safe and never change — use them instead of names in automation.
89
+
90
+ ## Headed mode
91
+
92
+ Headed mode = real visible Chrome window managed by Pinchtab.
93
+
94
+ - Human can log in, pass 2FA/captcha, validate state
95
+ - Agent calls HTTP APIs against the same running instance
96
+ - Session state persists in profile directory (cookies/storage carry over)
97
+
98
+ Recommended human + agent flow:
99
+
100
+ ```bash
101
+ # Human starts dashboard and sets up profile
102
+ pinchtab
103
+
104
+ # Agent resolves the profile endpoint
105
+ PINCHTAB_BASE_URL="$(pinchtab connect <profile-name>)"
106
+ curl "$PINCHTAB_BASE_URL/health"
107
+ ```
@@ -0,0 +1,102 @@
1
+ export const TRADESTATION_EXT = "tradestation" as const;
2
+
3
+ type Db = {
4
+ getExtState(ext: string, key: string): string | null;
5
+ setExtState(ext: string, key: string, value: string): void;
6
+ deleteExtState(ext: string, key: string): boolean;
7
+ };
8
+
9
+ const TRADESTATION_TOKEN_URL = "https://signin.tradestation.com/oauth/token";
10
+
11
+ export async function runTradeStationTokenRefresh(
12
+ db: Db,
13
+ log: { info(msg: string): void; warn(msg: string): void; error(msg: string): void },
14
+ fetchImpl: typeof fetch = fetch,
15
+ ): Promise<void> {
16
+ const clientId =
17
+ process.env.MERCURY_TS_CLIENT_ID?.trim() ||
18
+ process.env.TS_CLIENT_ID?.trim() ||
19
+ null;
20
+ if (!clientId) {
21
+ log.warn("[ts-refresh] no client_id — skipping");
22
+ return;
23
+ }
24
+
25
+ const clientSecret = process.env.MERCURY_TS_CLIENT_SECRET?.trim() || null;
26
+
27
+ let refreshToken =
28
+ db.getExtState(TRADESTATION_EXT, "refresh_token") ??
29
+ process.env.MERCURY_TRADESTATION_REFRESH_TOKEN?.trim() ??
30
+ process.env.TS_REFRESH_TOKEN?.trim() ??
31
+ null;
32
+
33
+ if (!refreshToken) {
34
+ log.warn("[ts-refresh] no refresh_token — skipping");
35
+ return;
36
+ }
37
+
38
+ const accessToken = db.getExtState(TRADESTATION_EXT, "access_token");
39
+ const expiryRaw = db.getExtState(TRADESTATION_EXT, "token_expiry_ms");
40
+ if (accessToken && expiryRaw) {
41
+ const expiry = Number(expiryRaw);
42
+ if (expiry > Date.now() + 60_000) {
43
+ return;
44
+ }
45
+ }
46
+
47
+ const bodyParams: Record<string, string> = {
48
+ grant_type: "refresh_token",
49
+ client_id: clientId,
50
+ refresh_token: refreshToken,
51
+ };
52
+ if (clientSecret) bodyParams.client_secret = clientSecret;
53
+ const body = new URLSearchParams(bodyParams);
54
+
55
+ let res: Response;
56
+ try {
57
+ res = await fetchImpl(TRADESTATION_TOKEN_URL, {
58
+ method: "POST",
59
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
60
+ body: body.toString(),
61
+ });
62
+ } catch (err) {
63
+ const msg = err instanceof Error ? err.message : String(err);
64
+ log.error(`[ts-refresh] fetch error: ${msg}`);
65
+ db.setExtState(TRADESTATION_EXT, "auth_error", "refresh_failed:network");
66
+ return;
67
+ }
68
+
69
+ if (!res.ok) {
70
+ log.warn(`[ts-refresh] token refresh failed: ${res.status}`);
71
+ db.setExtState(TRADESTATION_EXT, "auth_error", `refresh_failed:${res.status}`);
72
+ return;
73
+ }
74
+
75
+ let data: Record<string, unknown>;
76
+ try {
77
+ data = (await res.json()) as Record<string, unknown>;
78
+ } catch {
79
+ db.setExtState(TRADESTATION_EXT, "auth_error", "refresh_failed:invalid_json");
80
+ return;
81
+ }
82
+
83
+ const newAccess = typeof data.access_token === "string" ? data.access_token : null;
84
+ if (!newAccess) {
85
+ db.setExtState(TRADESTATION_EXT, "auth_error", "refresh_failed:no_access_token");
86
+ return;
87
+ }
88
+
89
+ db.setExtState(TRADESTATION_EXT, "access_token", newAccess);
90
+ if (typeof data.refresh_token === "string" && data.refresh_token) {
91
+ db.setExtState(TRADESTATION_EXT, "refresh_token", data.refresh_token);
92
+ }
93
+ if (typeof data.expires_in === "number") {
94
+ db.setExtState(
95
+ TRADESTATION_EXT,
96
+ "token_expiry_ms",
97
+ String(Date.now() + data.expires_in * 1000),
98
+ );
99
+ }
100
+ db.deleteExtState(TRADESTATION_EXT, "auth_error");
101
+ log.info("[ts-refresh] tokens refreshed");
102
+ }