Repository navigation
feat: let direct_url take a secret from a command - #102
Conversation
A $(command) in direct_url is replaced by what it prints, so a password can come from a password manager or a cloud's token command instead of a file. Output in the user or password is percent-encoded; a refused login runs the command again, so an expired token is replaced.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Important
The refresh-on-refused-login retry isn't single-flight. When several calls are refused at once, which is what happens when a token expires under a busy session, the command runs once per call, connections leak, and the session can still end up offline for a minute.
Reviewed changes
Reviewed the whole PR: $(command) support in direct_url, and reconnecting with the command's fresh output when the server refuses the login.
$(command)parsing and execution:urlPartssplitsdirect_urlinto text and commands.commandOutputruns each command once per process throughspawnSyncwith a 2-minute limit. Output that lands in the userinfo is percent-encoded.- Variable scoping:
${VAR}is still substituted in text. Inside a command, it is left to the shell, whose environment holds the.envfiles overlaid by the process environment, so a value never runs as shell code. - Refresh on refused login:
Session.#connectionretries the first open, and wrapsdescribe/queryinagain, which reopens on the command's fresh output whenrefusedLoginmatches Postgres class 28 or MySQL 1045. - Docs and tests: a paragraph under
direct_urlin Configuration, and tests for parsing, encoding, shell-injection safety, error messages and token rotation against a stand-in MySQL server.
claude-opus-5-5 | 𝕏
There was a problem hiding this comment.
ℹ️ Both earlier findings are fixed. One minor suggestion inline: after a session recovers, the editor can keep using the wrong dialect.
Reviewed changes
Reviewed the two commits since the last Pullfrog review: a single-flight reconnect, and a kept session that recovers once a failed direct_url command succeeds.
- Made the refused-login reopen single-flight:
againkeepscurrentas a promise. The first call refused on a connection replaces it with one reopen, and every other call retries on that same reopen, so the command and the new login each run once. - Closed dead connections in
#drop: onUnreachable/NotMysqlthe session now closes the connection the failing call ran on, and only if it is still the current one. A late failure never ends a connection opened since. - Re-resolved the project in a kept session:
#rereadrunsopenProjectbefore eachanalyze()while the project's error is a command failure, and adopts the new project, engine and URL. It clears the schema and the engine's answers. - Added tests: two schema reads refused together after a token rotation run the command once and leave one open connection. A session opened while the command fails recovers once its failure has stood a minute.
claude-opus-5-5 | 𝕏
There was a problem hiding this comment.
✅ No new issues found. The stale-dialect finding from the last review is fixed.
Reviewed changes
Reviewed the one commit since the last Pullfrog review: the type check now names the dialect of the project a kept session recovers to.
- Dropped the dialect captured at
openProject:Projectinsrc/mapper-server.tsno longer storesdialect.transformawaitssession.analyze(sql)first, so a#rereadhas already run, and then readssession.project.dialectforQueryError<…>,Query<…>,moduleTypesandleadingComment. - Moved lexing after the analysis:
session.lexnow runs afteranalyze(), so the transform that recovers lexes with the adopted engine. The settings that come from the project file (strictParams,sqlcommenter) are still captured at open, which is right because the URL does not change them. - Added a test through
mapperHandlers(): a project with no engine installed and a failingmysql://command emitsQueryError<"postgres">with code 11. Once the command succeeds after the minute, it emitsQueryError<"mysql">and"many", "mysql">.
claude-opus-5-5 | 𝕏

A
$(command)indirect_urlis replaced by what the command prints, so the type check's database password can come from a password manager or a cloud's token command instead of a.envfile:reylo generate), in the project's directory, through the system shell, with a 2-minute limit. A failure, an empty output or an unclosed$(is an error that names the command; a failure is retried after a minute.://and the@is percent-encoded, which both drivers decode, so a password with@,/or?works. A command that is the whole value prints a URL, taken as printed.${VAR}outside a command is substituted as before. Inside a command it is the shell's, from the environment and then the project's.envfiles, so a variable's value is never run..reylo/cache key still name the host and user only.Docs: a paragraph and example under
direct_urlin Configuration.Verified:
nub run typecheck, andnub run testagainst throwaway Postgres 18 and MySQL 8.4: 248 passed, none skipped.tests/project.test.tsandtests/analyze.test.ts, including a stand-in MySQL server that rotates its accepted token.@,/and?typed a query, and after the role's password was changed and the command's output with it, the session re-ran the command and typed the next query, with exactly one refused login in the server log.nub run buildat the root and insite/.