Use saved 1Password credentials in command-line programs and background browser tabs. Separate CLI invocations share one local SDK process, so they can reuse its desktop authorization.
Open Pass uses Bun, TypeScript, Effect v4, and the official 1Password SDK.
Install Bun and the 1Password desktop app. In 1Password's Developer settings, enable Integrate with 1Password SDKs.
bun install --frozen-lockfile
make install-localmake install-local checks, tests, and builds the CLI and private consumer module, then links ~/.cargo/bin/open-pass to this checkout's dist/cli.js. Add ~/.cargo/bin to your PATH. Run the command again after source changes to rebuild the installed CLI.
open-pass --help lists the commands. Keep this checkout and its dependencies in place while using the link. The SDK and Effect versions are pinned in package.json and bun.lock.
Create the config with the account name shown in the 1Password desktop app:
open-pass config init --account 'My 1Password'Edit ~/.config/open-pass/config.json. Replace the example with references to your existing items:
{
"version": 1,
"account": "My 1Password",
"profiles": {
"service": {
"env": {
"API_KEY": "op://vault-id/item-id/credential"
}
},
"login": {
"env": {
"EMAIL": "op://vault-id/login-item-id/username",
"PASSWORD": "op://vault-id/login-item-id/password",
"OTP": "op://vault-id/login-item-id/one-time password?attribute=otp"
}
}
}
}Use IDs when item or vault names are ambiguous. Config values are references, never literal passwords or API keys.
open-pass config check
open-pass config list
open-pass daemon start
open-pass check serviceconfig check checks syntax without accessing 1Password. check service resolves the profile and prints field names only. The first credential request can prompt for desktop approval.
open-pass run service -- bun -e 'process.stdout.write(JSON.stringify({configured: Boolean(process.env.API_KEY)}) + "\n")'The program receives API_KEY in its environment. Open Pass preserves the working directory, arguments, and exit code. Put -- before the child command so its flags are passed through.
Stdout and stderr mask exact credential values, including values split across output chunks. To suppress child output entirely:
open-pass run --quiet service -- bun ./your-script.tsThe selected program is trusted code. Exact masking does not catch encoded or transformed credentials and does not restrict the program's file or network access. Interactive programs that require a real output TTY are outside this pipe-based command mode.
Trusted Node or Bun code can import consumeSecret from the built dist/consumer.js module. Supply the reference-only config path, the existing daemon's runtime directory, a profile, a binding, and an async use callback:
import { consumeSecret } from "./dist/consumer.js";
const result = await consumeSecret({
configPath,
runtimeDir,
profile: "login",
binding: "PASSWORD",
use: async (value) => { await input.fill(value); },
});The callback receives the credential in local memory. The promise returns only { consumed: true, binding: "PASSWORD" }. Callback failures become CONSUMER_FAILED without exposing their original messages or payloads. The callback is trusted code and must not log, return to the agent, or store the credential.
This API resolves one binding per call through the existing daemon. It does not create another SDK client or start a daemon. The caller owns destination checks and input readiness. Request OTP only when its input is ready, then reuse the authenticated destination session.
The module was verified in Node, Bun, and the chrome-control runtime. Chrome runtime access requires an explicit allowance for the daemon's Unix socket. The local launcher forwards NODE_REPL_SANDBOX_ALLOWED_UNIX_SOCKETS, a platform-path-delimited list, into the runtime's scoped sandbox configuration. A real-runtime test verifies that unlisted sockets remain blocked. See verification evidence for the real Chrome login and authorization limits.
Use the CDP endpoint of a Chromium browser you control. Open the target page with your existing browser automation, then run:
open-pass fill login EMAIL \
--cdp http://127.0.0.1:9222 \
--page https://example.com/login \
--selector 'input[name=email]'
open-pass fill login PASSWORD \
--cdp http://127.0.0.1:9222 \
--page https://example.com/login \
--selector 'input[name=password]'Continue the login with your browser automation. When the OTP input is ready, fill OTP the same way. A current code is requested from 1Password at that point. A profile resolved earlier by run contains a snapshot of its OTP, so use a separate just-in-time operation for a delayed second factor.
Open Pass requires exactly one page at the supplied URL and a unique visible input. It waits for the input before fetching the credential. fill confirms field entry, not successful login. Some forms submit automatically when filled, so inspect the resulting page before another action.
The browser remains open when Open Pass disconnects. Neither clipboard access nor window activation is used. This command requires CDP; it does not attach to an ordinary Chrome window solely by its visible title.
open-pass daemon status
open-pass daemon stopStatus reports the process, account, instance, and request counters without probing credentials. A running daemon is not proof that 1Password authorization is still valid.
Desktop authorization expires after ten minutes of inactivity or when 1Password locks. The next credential operation can require another approval. Open Pass does not read secrets periodically to keep authorization alive or cache passwords between operations.
If an SDK call throws, Open Pass returns a safe error and discards the cached client. Your next credential operation creates a replacement client in the same daemon. Open Pass does not automatically retry the failed operation. Missing fields and other completed batch errors keep the existing client.
Use --config PATH and --runtime-dir DIR before the subcommand to choose another configuration or daemon. The runtime directory is private. An occupied, stale, or unsafe socket path produces a recovery error rather than automatic deletion.
After a crash, first confirm no Open Pass daemon is running. Move the stale runtime directory aside, then start with a new private directory. You can also use a fresh --runtime-dir directly. Do not remove a socket belonging to a live daemon.
For a service supervisor, open-pass daemon serve --account 'My 1Password' runs the daemon in the current process.
bun run check
bun test
bun run buildThe tests use synthetic credentials and real local IPC. Browser tests launch an owned headless Chrome process. Set CHROME_PATH if Chrome is installed elsewhere; those tests skip when no executable is available.
See the design and verification evidence for the ownership decisions and tested limits.