Connector: make the local agent a durable, installable machine (`chopin` CLI, persisted pairing, service, reconnect)
Los mantenedores suelen responder en 1 día
@lavaman131 ya está trabajando en esto.
Desde el 12/10/2026.
Evaluación
Este issue todavía no se ha evaluado.
Descripción
Problem
The connector in apps/connector works well while someone keeps a terminal open, but it isn't yet something a team can leave running. We (@lavaman131) plan to implement the steps below and want to agree on the shape first.
The current gaps:
- It needs Bun and curl. Setup downloads a script with
curl … /connector.jsand runs it withbun1.4.2 (docs/local-launcher.md,docs/local-experiments.md). After a deploy, the fix is to "rerun the whole command", because that downloads the connector again. Users get no installed executable, no version check, and nothing a service can point at. - Pairing doesn't survive a restart or sign-out. "The connector credential is held in memory and tied to your browser login … Reconnect after a server restart or logout." The cause is in the server code.
Connectionskeeps pending pairings, connections and tokens in in-memoryMaps (apps/server/src/experiments/connections.ts:50-59). EachConnectionstores the browsersessionId(connections.ts:11).revokeSessiondrops a connection when that browser session ends (connections.ts:228-230), androutes.ts:343resolves the browser session again on every connector call. As a result, every deploy needs a person back at the terminal to pair again. The Let Chopin build and prototype automatically on this laptop choice (autoRun) is also lost and asked again. - The connector doesn't reconnect. In
main.ts, thewait_for_workloop rethrows any error that isn't an abort (main.ts:135-136), so a network blip or server restart ends the process. During a run, a failed heartbeat aborts the agent (run.ts:80:.catch(() => abort.abort())). The server forgets a connection after 90 seconds of silence (CONNECTION_MS,connections.ts:38) and "fails its builds and rebuilds and interrupts its investigations immediately" when the connector disconnects. - Nothing installs or starts it. The docs say so: "Keep the connector running. Each browser Build action starts an agent process; automatically installing or starting the connector itself remains future work." (
docs/local-launcher.md:35-36, repeated at line 98-100 as "automatic connector startup … remain future work"). - One process serves one checkout. "One connector process serves one checkout" (
docs/local-experiments.md, Trouble connecting). Each checkout has its own lock, created inlockWorkspace. - Users can't choose the machine. The server picks "your idle connection for the repository, preferring the one that last ran work for this document" (
docs/local-launcher.md:51-53). The UI doesn't let anyone see or choose which machine a document or repository uses. - Planning and building need separate local processes. Under
HARNESS=remote, the Planner runs in a client attached over/planner-linkwith a GitHub bearer (docs/planner-link.md). Builds and investigations run through the connector over/connector/mcp. The same laptop needs two separate long-lived processes with two different credentials. Attaching also needs "the same account … signed in to Chopin in a browser, on this server, since the server last started" (sign-in-required), so a server restart breaks the Planner side too.
#521 makes a missing agent visible ("Start your local agent"). This issue is about keeping the agent from going missing.
Plan
Each step is meant to be one reviewable PR. Steps run in order unless a step says otherwise. The interactive connect flow and GET /connector.js keep working throughout.
-
Ship the connector as an installable
chopinCLI that needs no Bun or curl.- Compile
apps/connector/src/main.tswithbun build --compilefor linux-x64, linux-arm64, darwin-x64, darwin-arm64 and windows-x64. - Publish it on npm as one package per platform behind a small launcher package that picks the right one through
optionalDependencies(the esbuild pattern). That waynpm install -g <package>works with plain Node and no Bun. - Attach the same binaries to GitHub releases, with
install.shandinstall.ps1that install to a stable per-user path. - Make the stdio bridge work from a compiled binary. Today it is spawned as
process.execPath [resolve(process.argv[1]), "bridge"](run.ts:151-161,main.ts:128). In a compiled binary it has to be[process.execPath, "bridge"]. - Have the CLI send its version when it pairs or connects. On an incompatible version, the server refuses with a message naming the upgrade command, replacing "rerun the whole command".
- Keep
/connector.jsserved and documented as a fallback during the migration. - The service step depends on this step, because a service unit must point at a stable installed executable rather than
bun ~/.chopin/connector.js.
- Compile
-
Persist a revocable machine credential.
chopin connect [checkout] -- <ACP command>pairs once through the existing link and confirmation code. It stores a machine credential in the state directory (stateDirectory(), mode 0600) and stores the ACP command and checkout in a machine config.- On the server, keep the machine (owner, label, repositories,
autoRun, credential fingerprint) in storage instead of the in-memoryConnectionsmaps, and stop tying it to the browsersessionId. Reconnecting after a server restart, a deploy or a browser sign-out then needs no new pairing. - Add revocation in two places: a Disconnect machine action in the UI and
chopin disconnecton the machine. A revoked credential gets a distinct error code, and the CLI stops instead of retrying. - The per-call push-access recheck stays, but it must work without a live browser session (see Open questions).
-
Reconnect with backoff.
- Wrap the
wait_for_workloop in a reconnect loop. Retry transient failures (network errors, 5xx,connection-unavailableafter a restart) after 1, 2, 5, 10, 20 and 30 seconds, then every minute. - Stop only on revocation, lost repository access or an incompatible version.
- A failed heartbeat during a run no longer aborts the agent right away. Reporting is held and retried until the server's grace period for the run expires.
- Wrap the
-
Server-side liveness and drop handling.
- Measure liveness on the server side, as
/planner-linknow does with pings every 15 seconds and a 120-second timeout. Either keep the long poll and track last-heard per machine, or move the machine channel to a socket with pings. Then a connector killed without warning is noticed promptly, and a connector that is still alive isn't dropped. - When a machine drops, its runs move to a "machine offline" state within a grace period instead of failing at once. When the same machine reconnects, it reports which runs it still holds.
- A reconnect with the same machine credential replaces that machine's own stale connection immediately. It must not count toward the 8-connection limit (
connections.ts:95) or wait outCONNECTION_MS.
- Measure liveness on the server side, as
-
Several checkouts per machine, and
chopin status.- One
chopinprocess serves every checkout in its config, keeping onelockWorkspacelock per checkout.chopin connectin another checkout adds that checkout to the running machine instead of starting a second process. chopin statusprints:- the installed version and the version the running process uses;
- the server and the paired account;
- whether the credential is stored (never the credential itself);
- each checkout and repository, with its connection state and last contact;
- the current runs with their worktree paths;
- the log path.
- One
-
chopin service install|status|uninstall.- Linux: a systemd user unit with
Restart=always, enabled withsystemctl --user enable --now, plusloginctl enable-lingerso the machine runs at boot and after logout. When enabling linger needs an administrator, print the onesudocommand and install nothing. - macOS: a LaunchAgent with
RunAtLoadandKeepAlive, loaded withlaunchctl bootstrap gui/<uid>. - Windows: a per-user Task Scheduler task that restarts after failure. On Windows, stopping must ask the running process to stop through a file or pipe, because Task Scheduler kills the process with no signal.
- The unit runs the absolute path of the installed
chopinexecutable, and output is appended to a log under the state directory. service statusreportsrunning,stopped,not-installed, or "restart pending" when the installed version differs from the running one.uninstallstops the service gracefully and leaves the credential, config and worktrees in place.
- Linux: a systemd user unit with
-
Choose a machine per repository or per document in the UI.
- List the owner's machines: label, last seen, version, repositories and the
autoRunchoice. - Allow pinning a machine to a repository, with an optional override per document. Build, Run, rebuilds and spikes go to the pinned machine. If none is pinned, the current preference applies (the machine that last ran work for this document, then the owner's most recently active idle one).
- Another person's machine is never eligible, as today.
- List the owner's machines: label, last seen, version, repositories and the
-
Launch a Planner backend for
HARNESS=remotedocuments from the same machine.- The machine config can name a Planner backend command next to the ACP command.
- For each document assigned to the machine, the machine creates a separate worktree, starts that command there and attaches it over
/planner-link. When the link drops, it attaches again with the same backoff. - One paired machine then serves both planning and builds.
- A stale link left by this machine must not block its own reattach. Today a dead link holds the document as
planner-attached, refusing the same account, until the 120-second timeout and then the 2-minute grace period. The server should let the same machine replace its own dead link. At minimum, the client must recognise itself as the holder and retry quickly rather than wait minutes. - This step depends on step 2's credential decision for
sign-in-required.
Requirements carried into each step
-
Outbound only. The machine never listens on a public interface. The loopback bridge in
local-bridge.tsstays loopback. -
A drop pauses work and never answers. When a link or connection drops, the machine pauses its runs and keeps their worktrees. Open questions, Decisions and ACP permission requests are withdrawn and are never answered on anyone's behalf. Runs resume only through an explicit resume or a person's action after reattach. A service has no TTY, so permission requests stay declined as today (
run.ts:181) and are never auto-approved by the machine. -
Pairing survives restarts. A credential tied to a browser sign-in breaks on every deploy, so the machine credential must outlive server restarts, deploys and browser sign-out. It must still be revocable from both the UI and the CLI. It is stored at 0600 and never printed, logged or written into a unit file or plist.
-
The service PATH survives a reboot. The unit records the absolute
chopinpath and a PATH where the agent command andgitstill resolve after a reboot:- the directory of the resolved agent executable comes first;
- entries under
$XDG_RUNTIME_DIRor/run/user/<uid>are dropped; - per-shell version-manager directories (for example fnm's
fnm_multishells) are dropped.
service installchecks that the ACP command resolves under that PATH before it writes anything. On Windows, the task inherits the user's environment variables. -
A stale link from the same machine must not block reattach. This applies to connector connections and
/planner-linkalike, and to the local.lockfile.staleLockalready replaces a lock whose process is gone. -
Worktrees stay isolated. Each run keeps its own worktree under
runs/<id>(prepareWorkspace), and each Planner document gets its own worktree. Local edits are preserved. Spikes keepnoPushEnvironment().stopProcessesInstill runs when a job ends. -
No regressions. Every step keeps
bun run e2e e2e/implementation-launcher.e2e.ts e2e/connector.e2e.tspassing and adds tests for the behaviour it introduces: a restart without re-pairing, reconnect after a drop, revocation, and reattach past a stale link.
Open questions
-
Planner link vs ACP. Should the machine keep two channels (
/connector/mcpfor builds and investigations,/planner-linkfor the Planner), or should Planner turns move onto the machine's connector channel under one credential? With two channels, does/planner-linkaccept the machine credential in place of a GitHub bearer? -
Repository credential for a durable machine. Repository tools and push-access rechecks currently use the browser login's GitHub App token (
routes.ts:343, andsign-in-requiredon/planner-link). The options we see:- store a GitHub App user refresh token server-side for each machine;
- run a device-flow sign-in on the machine;
- persist browser sessions across restarts.
Which does the team prefer?
-
Packaging. Is
bun build --compileacceptable given the binary size? What should the npm package name and scope be? Should the binaries be code-signed and notarized for macOS and Windows? Should the CLI update itself, or only tell the user when the server needs a newer version? -
Running builds during a drop. Should a build keep running locally through a short drop and report on reconnect, or pause at once?
-
Permission prompts under a service. Should they stay declined, rely on the agent's own auto-approval flag after
--, or be routed to the owner in Chopin? -
Ownership. Who owns the CLI, its release pipeline and the service units after they land? Should they live in
apps/connectoror in a newapps/cli?
- Lenguaje dominante
- TypeScript
- Estrellas
- 392
- Forks
- 22
- Merge medio
- 15 h 9 min
- PR fusionados (30 d)
- 338
Preparar el entorno
- Incluye un Dockerfile o un archivo de Docker Compose
- Sin plantilla de pull request
- Sin guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de githubnext/chopin
-
documentation triage/human-response
Dificultad 2/5 1-3 horas Aptitud para principiantes 74/100
githubnext/chopin#149 · 3 comentarios ·
Los mantenedores suelen responder en 1 día
-
enhancement triage/human-response
Dificultad 4/5 3-5 días Aptitud para principiantes 55/100
githubnext/chopin#203 · 2 comentarios ·
Los mantenedores suelen responder en 1 día
-
[Design] Style Mermaid diagramsAbiertoenhancement triage/human-response
Dificultad 5/5 Más de una semana Aptitud para principiantes 35/100
githubnext/chopin#165 ·
Los mantenedores suelen responder en 1 día
-
enhancement triage/human-response
Dificultad 5/5 Más de una semana Aptitud para principiantes 35/100
githubnext/chopin#164 ·
Los mantenedores suelen responder en 1 día
-
triage/human-response
Dificultad 5/5 Más de una semana Aptitud para principiantes 38/100
githubnext/chopin#163 ·
Los mantenedores suelen responder en 1 día
Todos los issues de githubnext/chopin
Issues similares
-
Contrib indemnité de départ à la retraite : faire le pont avec le simulateur sur la page génériqueAbiertoP1
Dificultad 2/5 1-3 horas Aptitud para principiantes 62/100
SocialGouv/code-du-travail-numerique#7561 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 85/100
kubernetes-sigs/headlamp#7950 ·
Los mantenedores suelen responder en 3 días
-
README for cliPosiblemente ocupada @Janvi-kapoor la tomó hoy. AbiertoOSCI'26
Dificultad 2/5 1-3 horas Aptitud para principiantes 70/100
GauravKarakoti/SecureFlow#1308 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 74/100
Los mantenedores suelen responder en 1 día
-
documentation
Dificultad 2/5 1-3 horas Aptitud para principiantes 62/100
vitest-dev/vitest#11577 ·
Los mantenedores suelen responder en 1 día