Skip to content

Commit c6e0910

Browse files
committed
feat: delegate keyring operations to CLI instead of native binaries
Replace @napi-rs/keyring (4 native .node binaries) with CLI subprocess calls. The extension now runs `coder login --use-token-as-session` to store tokens and `coder login token --url` to read them, keeping the credential format in sync with the CLI automatically. - Add CliCredentialManager that shells out to the Coder CLI - Update CliManager.configure() to accept binPath and delegate to CLI - Update LoginCoordinator to fetch CLI binary for keyring reads - Remove clearCredentials keyring cleanup (stale entries are harmless) - Remove @napi-rs/keyring dep, vendor script, supportedArchitectures - Delete KeyringStore and its tests
1 parent 5fc228c commit c6e0910

18 files changed

+404
-720
lines changed

esbuild.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ const buildOptions = {
3232
// undefined when bundled to CJS, causing runtime errors.
3333
openpgp: "./node_modules/openpgp/dist/node/openpgp.min.cjs",
3434
},
35-
external: ["vscode", "@napi-rs/keyring"],
35+
external: ["vscode"],
3636
sourcemap: production ? "external" : true,
3737
minify: production,
3838
plugins: watch ? [logRebuildPlugin] : [],

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
"test:integration": "tsc -p test --outDir out --noCheck && node esbuild.mjs && vscode-test",
3333
"test:webview": "vitest --project webview",
3434
"typecheck": "concurrently -g \"tsc --noEmit\" \"tsc --noEmit -p test\"",
35-
"vscode:prepublish": "pnpm build:production && node scripts/vendor-keyring.mjs",
35+
"vscode:prepublish": "pnpm build:production",
3636
"watch": "concurrently -n extension,webviews \"pnpm watch:extension\" \"pnpm watch:webviews\"",
3737
"watch:extension": "node esbuild.mjs --watch",
3838
"watch:webviews": "pnpm -r --filter \"./packages/*\" --parallel dev"
@@ -468,7 +468,6 @@
468468
"word-wrap": "1.2.5"
469469
},
470470
"dependencies": {
471-
"@napi-rs/keyring": "^1.2.0",
472471
"@peculiar/x509": "^1.14.3",
473472
"@repo/shared": "workspace:*",
474473
"axios": "1.13.6",

pnpm-lock.yaml

Lines changed: 0 additions & 135 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pnpm-workspace.yaml

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,3 @@ onlyBuiltDependencies:
2727
- keytar
2828
- unrs-resolver
2929
- utf-8-validate
30-
31-
# Install @napi-rs/keyring native binaries for macOS and Windows so they're
32-
# available when building the universal VSIX (even on Linux CI).
33-
# Only macOS and Windows use the keyring; Linux falls back to file storage.
34-
supportedArchitectures:
35-
os:
36-
- current
37-
- darwin
38-
- win32
39-
cpu:
40-
- current
41-
- x64
42-
- arm64

scripts/vendor-keyring.mjs

Lines changed: 0 additions & 61 deletions
This file was deleted.

src/cliConfig.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1+
import { isKeyringSupported } from "./core/cliCredentialManager";
12
import { getHeaderArgs } from "./headers";
2-
import { isKeyringSupported } from "./keyringStore";
33
import { escapeCommandArg } from "./util";
44

55
import type { WorkspaceConfiguration } from "vscode";

src/core/cliCredentialManager.ts

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { execFile } from "node:child_process";
2+
import { promisify } from "node:util";
3+
4+
import { getHeaderArgs } from "../headers";
5+
6+
import type { WorkspaceConfiguration } from "vscode";
7+
8+
import type { Logger } from "../logging/logger";
9+
10+
const execFileAsync = promisify(execFile);
11+
12+
/**
13+
* Returns true on platforms where the OS keyring is supported (macOS, Windows).
14+
*/
15+
export function isKeyringSupported(): boolean {
16+
return process.platform === "darwin" || process.platform === "win32";
17+
}
18+
19+
/**
20+
* Delegates credential storage to the Coder CLI to keep the credentials in sync.
21+
*/
22+
export class CliCredentialManager {
23+
constructor(private readonly logger: Logger) {}
24+
25+
/**
26+
* Store a token by running:
27+
* CODER_SESSION_TOKEN=<token> <bin> login --use-token-as-session <url>
28+
*
29+
* The token is passed via environment variable so it never appears in
30+
* process argument lists.
31+
*/
32+
async storeToken(
33+
binPath: string,
34+
url: string,
35+
token: string,
36+
configs: Pick<WorkspaceConfiguration, "get">,
37+
): Promise<void> {
38+
const args = [
39+
...getHeaderArgs(configs),
40+
"login",
41+
"--use-token-as-session",
42+
url,
43+
];
44+
try {
45+
await execFileAsync(binPath, args, {
46+
env: { ...process.env, CODER_SESSION_TOKEN: token },
47+
});
48+
this.logger.info("Stored token via CLI for", url);
49+
} catch (error) {
50+
this.logger.error("Failed to store token via CLI:", error);
51+
throw error;
52+
}
53+
}
54+
55+
/**
56+
* Read a token by running:
57+
* <bin> login token --url <url>
58+
*
59+
* Returns trimmed stdout, or undefined on any failure.
60+
*/
61+
async readToken(
62+
binPath: string,
63+
url: string,
64+
configs: Pick<WorkspaceConfiguration, "get">,
65+
): Promise<string | undefined> {
66+
const args = [...getHeaderArgs(configs), "login", "token", "--url", url];
67+
try {
68+
const { stdout } = await execFileAsync(binPath, args);
69+
const token = stdout.trim();
70+
return token || undefined;
71+
} catch (error) {
72+
this.logger.warn("Failed to read token via CLI:", error);
73+
return undefined;
74+
}
75+
}
76+
}

0 commit comments

Comments
 (0)