feat(stockbit): one browser sign-in in idx-core for the CLI and MCP

`stockbit::signin` is now the single sign-in flow. `idx stockbit login`
waits on it in the foreground; the `stockbit_login` MCP tool runs it on a
thread and `stockbit_status` reports `sign_in.state`, so an agent knows when
to stop polling. Auto uses a browser unless the shell is over SSH or has no
display, and falls back to the local page only when the browser cannot start.

The browser way opens Stockbit's sign-in page in a profile only idx uses and
reads the session cookie over DevTools. What a real sign-in showed:

- The cookie is host-only on stockbit.com, so deleting by the assumed
  .stockbit.com removed nothing and left live tokens in the profile. It is
  now deleted by its own domain and path, with every other tab closed first,
  and read back to confirm.
- A signed-out page writes the cookie without tokens; it no longer counts.
- A profile belongs to one browser, so it records the executable that made it.
- On macOS the config directory depends on XDG_CONFIG_HOME, so a shell and an
  app started from the Dock disagreed; both default locations are now checked.
- A thread does not outlive main, so idx-mcp closes a waiting browser when
  stdin closes or on SIGTERM or Ctrl-C.

Progress goes to stderr, which keeps `-o json` to one document on stdout.

Google refused its own sign-in in this window on 2026-10-31, most likely over
the debugging flag; password sign-in works, and the docs and roadmap say so.

Claude-Session: https://claude.ai/code/session_01RqPPVaoFxLToGAXXBEoCtH
This commit is contained in:
Rasyidan Akbar F. 2026-10-31 23:58:21 +07:00
commit ca4d3d09c2
21 changed files with 2799 additions and 212 deletions

View file

@ -80,44 +80,114 @@ idx --provider stockbit stocks history BBCA --period 1y # Stockbit's daily
idx stockbit logout
```
**Logging in.** `idx stockbit login` opens a small page on your own machine
(127.0.0.1, a fresh port each run). With stockbit.com open and logged in,
click the page's **Send my Stockbit session** bookmarklet: it reads
Stockbit's `credentialStorage` cookie in your browser and hands it to the
page, which checks it with one request and says "Connected". If bookmarklets
are blocked, click **Paste from clipboard** after copying the cookie, or
paste it into the box. Without a browser, `--no-browser` prints the page URL
instead, and `--port 7878` pins the port for `ssh -L 7878:127.0.0.1:7878`.
Piping the value in still works (`pbpaste | idx stockbit login`), as does a
refresh token on its own. On a server, set `IDX_STOCKBIT_REFRESH_TOKEN`.
**Logging in.** `idx stockbit login` opens Chrome (or Chromium, Edge, Brave,
Helium) on Stockbit's own sign-in page, in a browser profile only idx uses. Sign in
there as usual, including any one-time code: idx reads the
session cookie over the DevTools protocol, checks it with one request, and
closes the window. Your password never reaches idx. The profile sits beside
`config.toml`; idx takes the session cookie out of it once the session is
stored, so the profile keeps only Stockbit's device cookies. The profile
stays with the browser that made it: idx opens the same one next time, unless
`IDX_STOCKBIT_BROWSER` names another. Choose your browser that way on the
first sign-in if you have several.
Use your Stockbit password in that window. Google can refuse to sign in there
with "This browser or app may not be secure", most likely because idx starts
the browser with remote debugging on. If you only sign in with Google, use the
local page below: sign in from a private window, paste the cookie, then close
that window.
Run `login` again while the session is still good and there is nothing to
do: idx checks it with one request and exits without opening a browser. The
window it does open waits 15 minutes, and closes at once if you close it
yourself.
**How long a session lasts.** Stockbit's refresh token is good for about a
week, and idx spends it on every refresh, which extends the session. Use idx
at least once a week and you stay signed in. A longer gap, a `logout`, or
another tool spending the same token means one more sign-in.
Over SSH, on a machine with no display, without such a browser, or with
`--no-browser`, idx serves a small page on your own machine (127.0.0.1, port
7878 when it is free) instead. With
stockbit.com open and logged in, click the page's **Send my Stockbit
session** bookmarklet: it reads Stockbit's `credentialStorage` cookie in your
browser and hands it to the page, which checks it with one request and says
"Connected". If bookmarklets are blocked, click **Paste from clipboard**
after copying the cookie, or paste it into the box. `--port` picks another
port. Piping the value in still works
(`pbpaste | idx stockbit login`), as does a refresh token on its own. On a
server, set `IDX_STOCKBIT_REFRESH_TOKEN`. Set `IDX_STOCKBIT_BROWSER` to use a
browser idx does not find on its own.
The session lands in `~/.config/idx/stockbit-session.json`, readable only by
you. Tokens never appear in output, logs or errors. The page takes only the
you. On macOS without `XDG_CONFIG_HOME` that is
`~/Library/Application Support/idx/` instead; idx finds a session in either
one, so the CLI and an MCP client started from the Dock share it. Tokens never appear in output, logs or errors. The page takes only the
session value, keeps it in memory, and closes when it is done (or after five
minutes). A session Stockbit refuses leaves a previously stored one in place.
The page listens on this machine's loopback interface only, so the browser
must run on the same machine: from another host over SSH, or from a browser
outside a container or WSL distro, `127.0.0.1` is a different machine and the
page will not load. idx notices an SSH session (`SSH_CONNECTION`) and prints
the exact command to run on the machine with the browser, on port 7878:
## Remote and headless
The page listens on this machine's loopback interface only, and the browser
handoff needs a browser on this machine too. On a server, in a container, or
in a WSL distro, `127.0.0.1` is a different machine and neither will load.
Three ways around that, in order of how little they ask of you.
**Pipe the cookie over SSH.** No tunnel, one command. Copy the
`credentialStorage` value on the machine with the browser (DevTools,
Application, Cookies), then:
```bash
ssh -L 7878:127.0.0.1:7878 <this-host> # run where the browser is, keep it open
pbpaste | ssh <host> "idx stockbit login"
```
Elsewhere, pin the port yourself and forward it the same way:
idx reads stdin, verifies the session, and stores it on the server. The value
never reaches a shell history or a file.
**Forward the page.** Run the page on the server and forward its port from the
machine with the browser:
```bash
idx stockbit login --no-browser --port 7878
idx stockbit login # on the server: over SSH, it serves the page
ssh -L 7878:127.0.0.1:7878 <host> # where the browser is, keep it open
```
Then open the printed URL there and click the bookmarklet. Over SSH, idx
notices `SSH_CONNECTION`, skips the browser, and prints the exact forward
itself.
**Seed a token.** For anything unattended, keep the refresh token in a secret
store and pass it at run time:
```bash
op run --env-file=stockbit.env -- idx stocks orderbook BBCA
```
where the file holds `IDX_STOCKBIT_REFRESH_TOKEN=op://Private/stockbit/refresh`.
Only the refresh token is needed; idx mints its own access token.
**Agents.** Over MCP, an agent calls `stockbit_login`, which answers at once
and does the same as `idx stockbit login`. Where idx-mcp runs on your desktop,
a browser window opens on Stockbit's sign-in page (`"method": "browser"`) and
you sign in there. Over SSH or without a display, the answer carries the page
URL and its `ssh -L` forward (`"method": "page"`) for the agent to hand you.
The agent then polls `stockbit_status` until `sign_in.state` is `signed_in`
or `failed`. A session that already works answers `signed_in` without opening
anything, a second call while one is waiting returns it again, and
`{"method": "page"}` or `{"method": "browser"}` picks the way. If the server
stops while the window waits, the window closes with it.
One rule for all three: only one machine may own a session. Stockbit retires
the refresh token on every refresh, so a token copied to two machines signs the
other one out. Two machines need two sign-ins.
**One refresher per session.** Stockbit replaces the refresh token every time
it is used. Once idx refreshes your session, a browser holding the same
session has to log in again, and two tools sharing one session log each
other out. Log in through a separate browser profile (or a private window
you then close) for idx. idx processes on one machine take turns through a
lock, so they never spend the token twice.
other out. `idx stockbit login` keeps its own browser profile, so this only
affects a browser you use yourself. idx processes on one machine take turns
through a lock, so they never spend the token twice.
What the login adds:
@ -139,7 +209,8 @@ candles explicitly; windows it charts only weekly or hourly (`2y`, `5y`,
Stockbit's [terms](https://stockbit.com/terms) allow its content for personal,
non-commercial use. They prohibit data mining, robots and redistribution
without Stockbit's written consent. idx is not affiliated with or endorsed by
without Stockbit's written consent, which includes serving the data to other
people through `idx-mcp`. idx is not affiliated with or endorsed by
Stockbit. Use this for looking at a stock now and then, not for crawling the
market or republishing prices. With your login, automated use is tied to your
account: keep it human-paced.