@seliseblocks/cli-os 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/AI_USAGE_GUIDE.md +285 -305
  2. package/LICENSE +21 -21
  3. package/README.md +136 -159
  4. package/bin/run.js +2 -2
  5. package/dist/commands/auth/status.js +16 -51
  6. package/dist/commands/doctor.js +11 -7
  7. package/dist/commands/init.js +4 -2
  8. package/dist/commands/login.d.ts +0 -1
  9. package/dist/commands/login.js +15 -55
  10. package/dist/commands/new/web.js +1 -1
  11. package/dist/index.js +129 -175
  12. package/dist/lib/auth.d.ts +4 -11
  13. package/dist/lib/auth.js +43 -18
  14. package/dist/lib/config.d.ts +2 -2
  15. package/dist/lib/config.js +45 -13
  16. package/dist/lib/open-browser.js +26 -3
  17. package/dist/lib/secret-store.d.ts +0 -1
  18. package/dist/lib/secret-store.js +1 -4
  19. package/dist/lib/token-store.d.ts +0 -1
  20. package/dist/lib/token-store.js +1 -5
  21. package/dist/lib/token.d.ts +3 -1
  22. package/dist/lib/token.js +34 -7
  23. package/package.json +47 -47
  24. package/dist/commands/auth/add.d.ts +0 -1
  25. package/dist/commands/auth/add.js +0 -51
  26. package/dist/commands/auth/list.d.ts +0 -1
  27. package/dist/commands/auth/list.js +0 -33
  28. package/dist/commands/auth/repair.d.ts +0 -1
  29. package/dist/commands/auth/repair.js +0 -39
  30. package/dist/commands/auth/show.d.ts +0 -1
  31. package/dist/commands/auth/show.js +0 -23
  32. package/dist/commands/auth/use.d.ts +0 -1
  33. package/dist/commands/auth/use.js +0 -15
  34. package/dist/commands/login-device.d.ts +0 -1
  35. package/dist/commands/login-device.js +0 -6
  36. package/dist/lib/login-server.d.ts +0 -1
  37. package/dist/lib/login-server.js +0 -43
  38. package/dist/lib/pkce.d.ts +0 -4
  39. package/dist/lib/pkce.js +0 -9
  40. package/dist/lib/prompt.d.ts +0 -2
  41. package/dist/lib/prompt.js +0 -36
@@ -17,7 +17,6 @@ export declare function writeSecretStore(store: BlocksSecretStore): Promise<void
17
17
  export declare function setClientSecret(account: string, clientSecret: string): Promise<void>;
18
18
  export declare function getClientSecret(account: string): Promise<string | undefined>;
19
19
  export declare function removeAccountSecrets(account: string): Promise<void>;
20
- export declare function clearSecretStore(): Promise<void>;
21
20
  export declare function setSecretValue(key: string, value: string): Promise<void>;
22
21
  export declare function getSecretValue(key: string): Promise<string | undefined>;
23
22
  export declare function removeSecretValue(key: string): Promise<void>;
@@ -1,5 +1,5 @@
1
1
  import { execFile, spawn } from "node:child_process";
2
- import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
2
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
3
3
  import { platform } from "node:os";
4
4
  import { dirname, join } from "node:path";
5
5
  import { promisify } from "node:util";
@@ -57,9 +57,6 @@ export async function removeAccountSecrets(account) {
57
57
  await removeSecretValue(`client-secret:${account}`);
58
58
  await removeFallbackSecret(account);
59
59
  }
60
- export async function clearSecretStore() {
61
- await rm(secretPath(), { force: true });
62
- }
63
60
  export async function setSecretValue(key, value) {
64
61
  const backend = await resolveBackend();
65
62
  if (backend === "macos-keychain") {
@@ -15,4 +15,3 @@ export declare function tokenStoreInfo(): Promise<{
15
15
  export declare function readTokenStore(): Promise<BlocksTokenStore>;
16
16
  export declare function writeTokenStore(store: BlocksTokenStore): Promise<void>;
17
17
  export declare function removeAccountTokens(account: string): Promise<void>;
18
- export declare function clearTokenStore(): Promise<void>;
@@ -1,7 +1,7 @@
1
1
  import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
2
2
  import { dirname, join } from "node:path";
3
3
  import { configDir, configPath } from "./config.js";
4
- import { getSecretValue, removeSecretValue, secretStoreInfo, setSecretValue } from "./secret-store.js";
4
+ import { getSecretValue, secretStoreInfo, setSecretValue } from "./secret-store.js";
5
5
  const TOKEN_SECRET_KEY = "oauth-token-store";
6
6
  export function tokenPath() {
7
7
  return join(configDir(), "tokens.json");
@@ -56,10 +56,6 @@ export async function removeAccountTokens(account) {
56
56
  const { [account]: _, ...accounts } = store.accounts;
57
57
  await writeTokenStore({ accounts });
58
58
  }
59
- export async function clearTokenStore() {
60
- await removeSecretValue(TOKEN_SECRET_KEY);
61
- await rm(tokenPath(), { force: true });
62
- }
63
59
  function normalizeTokenStore(store) {
64
60
  return {
65
61
  accounts: store?.accounts ?? {}
@@ -8,12 +8,14 @@ export type TokenResponse = {
8
8
  expires_in?: number;
9
9
  id_token?: string;
10
10
  refresh_token?: string;
11
+ refresh_expires_in?: number;
12
+ refresh_token_expires_in?: number;
11
13
  scope?: string;
12
14
  token_type?: string;
13
15
  };
14
- export declare function randomState(): string;
15
16
  export declare function expiresAtFromToken(response: TokenResponse, accessToken: string): string | undefined;
16
17
  export declare function expiresAtFromJwtFirst(response: TokenResponse, accessToken: string): string | undefined;
18
+ export declare function refreshExpiresAtFromToken(response: TokenResponse, refreshToken?: string): string | undefined;
17
19
  export declare function isExpiring(expiresAt?: string): boolean;
18
20
  export declare function applyAccountToken(config: BlocksCliConfig, store: BlocksTokenStore, account: string, clientId: string, response: TokenResponse): {
19
21
  config: BlocksCliConfig;
package/dist/lib/token.js CHANGED
@@ -1,9 +1,6 @@
1
- import { randomBytes } from "node:crypto";
2
1
  import { decodeJwtPayload, tenantFromToken } from "./jwt.js";
3
2
  const EXPIRY_SKEW_MS = 60_000;
4
- export function randomState() {
5
- return randomBytes(24).toString("base64url");
6
- }
3
+ const DEFAULT_REFRESH_TOKEN_LIFETIME_SECONDS = 30 * 60;
7
4
  export function expiresAtFromToken(response, accessToken) {
8
5
  if (typeof response.expires_in === "number" && response.expires_in > 0) {
9
6
  return new Date(Date.now() + response.expires_in * 1000).toISOString();
@@ -21,6 +18,26 @@ export function expiresAtFromJwtFirst(response, accessToken) {
21
18
  }
22
19
  return expiresAtFromToken(response, accessToken);
23
20
  }
21
+ export function refreshExpiresAtFromToken(response, refreshToken) {
22
+ if (refreshToken) {
23
+ try {
24
+ const payload = decodeJwtPayload(refreshToken);
25
+ if (typeof payload.exp === "number") {
26
+ return new Date(payload.exp * 1000).toISOString();
27
+ }
28
+ }
29
+ catch {
30
+ // Opaque refresh tokens have no JWT payload; use server metadata or the configured lifetime.
31
+ }
32
+ }
33
+ const expiresIn = response.refresh_expires_in ?? response.refresh_token_expires_in;
34
+ if (typeof expiresIn === "number" && expiresIn > 0) {
35
+ return new Date(Date.now() + expiresIn * 1000).toISOString();
36
+ }
37
+ return refreshToken
38
+ ? new Date(Date.now() + DEFAULT_REFRESH_TOKEN_LIFETIME_SECONDS * 1000).toISOString()
39
+ : undefined;
40
+ }
24
41
  export function isExpiring(expiresAt) {
25
42
  if (!expiresAt)
26
43
  return true;
@@ -34,12 +51,17 @@ export function applyAccountToken(config, store, account, clientId, response) {
34
51
  // rotate it (still valid, just not reissued) -- fall back to the previous
35
52
  // one instead of overwriting a working refresh token with undefined.
36
53
  const previousRefreshToken = store.accounts[account]?.account?.refreshToken;
54
+ const previousRefreshTokenExpiresAt = store.accounts[account]?.account?.refreshTokenExpiresAt;
55
+ const refreshToken = response.refresh_token ?? previousRefreshToken;
37
56
  const tokenSet = {
38
57
  accessToken: response.access_token,
39
58
  accountTenant: tenantFromToken(response.access_token),
40
- expiresAt: expiresAtFromToken(response, response.access_token),
59
+ expiresAt: expiresAtFromJwtFirst(response, response.access_token),
41
60
  idToken: response.id_token,
42
- refreshToken: response.refresh_token ?? previousRefreshToken,
61
+ refreshToken,
62
+ refreshTokenExpiresAt: response.refresh_token
63
+ ? refreshExpiresAtFromToken(response, response.refresh_token)
64
+ : previousRefreshTokenExpiresAt,
43
65
  scope: response.scope,
44
66
  tokenType: response.token_type ?? "Bearer"
45
67
  };
@@ -72,10 +94,15 @@ export function applyProjectToken(config, store, account, tenantId, response) {
72
94
  throw new Error(response.error_description ?? response.error ?? "Project token response did not include an access token");
73
95
  }
74
96
  const previousRefreshToken = store.accounts[account]?.projects?.[tenantId]?.refreshToken;
97
+ const previousRefreshTokenExpiresAt = store.accounts[account]?.projects?.[tenantId]?.refreshTokenExpiresAt;
98
+ const refreshToken = response.refresh_token ?? previousRefreshToken;
75
99
  const tokenSet = {
76
100
  accessToken: response.access_token,
77
101
  expiresAt: expiresAtFromJwtFirst(response, response.access_token),
78
- refreshToken: response.refresh_token ?? previousRefreshToken,
102
+ refreshToken,
103
+ refreshTokenExpiresAt: response.refresh_token
104
+ ? refreshExpiresAtFromToken(response, response.refresh_token)
105
+ : previousRefreshTokenExpiresAt,
79
106
  scope: response.scope,
80
107
  tokenType: response.token_type ?? "Bearer"
81
108
  };
package/package.json CHANGED
@@ -1,47 +1,47 @@
1
- {
2
- "name": "@seliseblocks/cli-os",
3
- "version": "0.1.1",
4
- "description": "CLI for SELISE Blocks OS project setup and configuration.",
5
- "license": "MIT",
6
- "type": "module",
7
- "bin": {
8
- "blocks-os": "./bin/run.js"
9
- },
10
- "main": "./dist/index.js",
11
- "types": "./dist/index.d.ts",
12
- "exports": {
13
- ".": {
14
- "types": "./dist/index.d.ts",
15
- "import": "./dist/index.js"
16
- },
17
- "./package.json": "./package.json"
18
- },
19
- "publishConfig": {
20
- "access": "public"
21
- },
22
- "sideEffects": false,
23
- "files": [
24
- "bin",
25
- "dist",
26
- "README.md",
27
- "AI_USAGE_GUIDE.md",
28
- "LICENSE"
29
- ],
30
- "scripts": {
31
- "clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"",
32
- "build": "npm run clean && tsc -p tsconfig.json",
33
- "dev": "tsx src/index.ts",
34
- "lint": "tsc -p tsconfig.json --noEmit",
35
- "test": "npm run build && node --test test/*.test.mjs",
36
- "prepack": "npm run build",
37
- "prepublishOnly": "npm test"
38
- },
39
- "devDependencies": {
40
- "@types/node": "^22.0.0",
41
- "tsx": "^4.16.0",
42
- "typescript": "^5.5.0"
43
- },
44
- "engines": {
45
- "node": ">=20"
46
- }
47
- }
1
+ {
2
+ "name": "@seliseblocks/cli-os",
3
+ "version": "0.1.3",
4
+ "description": "CLI for SELISE Blocks OS project setup and configuration.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "bin": {
8
+ "blocks-os": "bin/run.js"
9
+ },
10
+ "main": "./dist/index.js",
11
+ "types": "./dist/index.d.ts",
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/index.d.ts",
15
+ "import": "./dist/index.js"
16
+ },
17
+ "./package.json": "./package.json"
18
+ },
19
+ "publishConfig": {
20
+ "access": "public"
21
+ },
22
+ "sideEffects": false,
23
+ "files": [
24
+ "bin",
25
+ "dist",
26
+ "README.md",
27
+ "AI_USAGE_GUIDE.md",
28
+ "LICENSE"
29
+ ],
30
+ "scripts": {
31
+ "clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"",
32
+ "build": "npm run clean && tsc -p tsconfig.json",
33
+ "dev": "tsx src/index.ts",
34
+ "lint": "tsc -p tsconfig.json --noEmit",
35
+ "test": "npm run build && node --test test/*.test.mjs",
36
+ "prepack": "npm run build",
37
+ "prepublishOnly": "npm test"
38
+ },
39
+ "devDependencies": {
40
+ "@types/node": "^22.0.0",
41
+ "tsx": "^4.16.0",
42
+ "typescript": "^5.5.0"
43
+ },
44
+ "engines": {
45
+ "node": ">=20"
46
+ }
47
+ }
@@ -1 +0,0 @@
1
- export declare function authAdd(argv: string[]): Promise<void>;
@@ -1,51 +0,0 @@
1
- import { parseFlags, stringFlag } from "../../lib/args.js";
2
- import { defaults, normalizeAccountName, readConfig, writeConfig } from "../../lib/config.js";
3
- import { promptSecret, promptValue } from "../../lib/prompt.js";
4
- import { getClientSecret, setClientSecret } from "../../lib/secret-store.js";
5
- export async function authAdd(argv) {
6
- const { args, flags } = parseFlags(argv);
7
- const account = normalizeAccountName(args[0]);
8
- const current = await readConfig();
9
- const previous = current.accounts[account];
10
- const now = new Date().toISOString();
11
- const apiUrl = stringFlag(flags, "api-url") || previous?.apiUrl || defaults().apiUrl;
12
- const oidcUrl = stringFlag(flags, "oidc-url") || previous?.oidcUrl || defaults().oidcUrl;
13
- const osUrl = stringFlag(flags, "os-url") || defaults().osUrl;
14
- const clientId = await value(flags, "client-id", "OIDC client id", previous?.clientId);
15
- const existingSecret = await getClientSecret(account);
16
- const clientSecret = await value(flags, "client-secret", "OIDC client secret", existingSecret, true);
17
- const scope = stringFlag(flags, "scope") || previous?.scope || defaults().scope;
18
- const redirectUri = stringFlag(flags, "redirect-uri") || previous?.redirectUri || defaults().redirectUri;
19
- const rootTenantId = optionalStringFlag(flags, "root-tenant") ?? previous?.rootTenantId ?? defaults().rootTenantId;
20
- await writeConfig({
21
- ...current,
22
- activeAccount: current.activeAccount ?? account,
23
- accounts: {
24
- ...current.accounts,
25
- [account]: {
26
- apiUrl,
27
- clientId,
28
- createdAt: previous?.createdAt ?? now,
29
- oidcUrl,
30
- osUrl,
31
- redirectUri,
32
- rootTenantId,
33
- scope,
34
- updatedAt: now
35
- }
36
- }
37
- });
38
- await setClientSecret(account, clientSecret);
39
- console.log(`Saved OIDC account '${account}'.`);
40
- }
41
- async function value(flags, name, label, defaultValue, secret = false) {
42
- const flag = stringFlag(flags, name);
43
- const resolved = flag || await (secret ? promptSecret(label, defaultValue) : promptValue(label, defaultValue));
44
- if (!resolved)
45
- throw new Error(`Missing --${name}`);
46
- return resolved;
47
- }
48
- function optionalStringFlag(flags, name) {
49
- const value = flags[name];
50
- return typeof value === "string" && value ? value : undefined;
51
- }
@@ -1 +0,0 @@
1
- export declare function authList(argv?: string[]): Promise<void>;
@@ -1,33 +0,0 @@
1
- import { parseFlags } from "../../lib/args.js";
2
- import { readConfig } from "../../lib/config.js";
3
- import { writeOutput } from "../../lib/output.js";
4
- export async function authList(argv = []) {
5
- const { flags } = parseFlags(argv);
6
- const config = await readConfig();
7
- const names = Object.keys(config.accounts);
8
- if (names.length === 0) {
9
- if (flags.json) {
10
- writeOutput({ activeAccount: config.activeAccount ?? null, accounts: [] }, flags);
11
- return;
12
- }
13
- console.log("No OIDC accounts configured.");
14
- return;
15
- }
16
- if (flags.json) {
17
- writeOutput({
18
- activeAccount: config.activeAccount ?? null,
19
- accounts: names.map((name) => ({
20
- name,
21
- active: config.activeAccount === name,
22
- apiUrl: config.accounts[name].apiUrl,
23
- oidcUrl: config.accounts[name].oidcUrl
24
- }))
25
- }, flags);
26
- return;
27
- }
28
- for (const name of names) {
29
- const profile = config.accounts[name];
30
- const active = config.activeAccount === name ? "*" : " ";
31
- console.log(`${active} ${name} ${profile.apiUrl} ${profile.oidcUrl}`);
32
- }
33
- }
@@ -1 +0,0 @@
1
- export declare function authRepair(argv: string[]): Promise<void>;
@@ -1,39 +0,0 @@
1
- import { booleanFlag, parseFlags } from "../../lib/args.js";
2
- import { confirmMutation } from "../../lib/confirm.js";
3
- import { getAccountProfile, normalizeAccountName, readConfig } from "../../lib/config.js";
4
- import { writeOutput } from "../../lib/output.js";
5
- import { removeAccountSecrets } from "../../lib/secret-store.js";
6
- import { clearTokenStore } from "../../lib/token-store.js";
7
- export async function authRepair(argv) {
8
- const { args, flags } = parseFlags(argv);
9
- const config = await readConfig();
10
- const account = args[0] ? normalizeAccountName(args[0]) : getAccountProfile(config).name;
11
- if (!config.accounts[account]) {
12
- throw new Error(`OIDC account '${account}' is not configured.`);
13
- }
14
- const result = {
15
- account,
16
- clearedClientSecret: true,
17
- clearedTokenCache: "all accounts",
18
- nextSteps: [
19
- ["blocks-os auth:add", account === "default" ? "" : account, "--client-id <id>", "--client-secret <secret>"].filter(Boolean).join(" "),
20
- `blocks-os login${account === "default" ? "" : ` --account ${account}`}`
21
- ]
22
- };
23
- if (booleanFlag(flags, "dry-run")) {
24
- writeOutput({ dryRun: true, ...result }, flags);
25
- return;
26
- }
27
- await confirmMutation(flags, `Repair local auth state for '${account}'. This clears the account client secret and all cached OAuth tokens, but keeps account profile config.`);
28
- await removeAccountSecrets(account);
29
- await clearTokenStore();
30
- if (flags.json) {
31
- writeOutput(result, flags);
32
- return;
33
- }
34
- console.log(`Repaired local auth state for '${account}'.`);
35
- console.log("Client secret and cached OAuth tokens were cleared.");
36
- console.log("Next:");
37
- for (const step of result.nextSteps)
38
- console.log(` ${step}`);
39
- }
@@ -1 +0,0 @@
1
- export declare function authShow(argv: string[]): Promise<void>;
@@ -1,23 +0,0 @@
1
- import { parseFlags, stringFlag } from "../../lib/args.js";
2
- import { getAccountProfile, readConfig } from "../../lib/config.js";
3
- import { getClientSecret, secretStoreInfo } from "../../lib/secret-store.js";
4
- export async function authShow(argv) {
5
- const { args, flags } = parseFlags(argv);
6
- const config = await readConfig();
7
- const account = args[0] || stringFlag(flags, "account") || config.activeAccount;
8
- const { name, profile } = getAccountProfile(config, account);
9
- const secretInfo = await secretStoreInfo();
10
- console.log(JSON.stringify({
11
- account: name,
12
- active: config.activeAccount === name,
13
- apiUrl: profile.apiUrl,
14
- clientId: profile.clientId,
15
- clientSecret: await getClientSecret(name) ? "configured" : "missing",
16
- secretStorage: secretInfo,
17
- oidcUrl: profile.oidcUrl,
18
- osUrl: profile.osUrl,
19
- redirectUri: profile.redirectUri,
20
- rootTenantId: profile.rootTenantId,
21
- scope: profile.scope
22
- }, null, 2));
23
- }
@@ -1 +0,0 @@
1
- export declare function authUse(argv: string[]): Promise<void>;
@@ -1,15 +0,0 @@
1
- import { parseFlags } from "../../lib/args.js";
2
- import { normalizeAccountName, readConfig, writeConfig } from "../../lib/config.js";
3
- export async function authUse(argv) {
4
- const { args } = parseFlags(argv);
5
- const account = normalizeAccountName(args[0]);
6
- const config = await readConfig();
7
- if (!config.accounts[account]) {
8
- throw new Error("OIDC account is not configured. Run 'blocks-os auth:add --client-id <id> --client-secret <secret>' first.");
9
- }
10
- await writeConfig({
11
- ...config,
12
- activeAccount: account
13
- });
14
- console.log(`Active account is now '${account}'.`);
15
- }
@@ -1 +0,0 @@
1
- export declare function loginDevice(argv: string[]): Promise<void>;
@@ -1,6 +0,0 @@
1
- import { parseFlags, stringFlag } from "../lib/args.js";
2
- import { loginWithDeviceProfile } from "./login.js";
3
- export async function loginDevice(argv) {
4
- const { flags } = parseFlags(argv);
5
- await loginWithDeviceProfile(stringFlag(flags, "account"));
6
- }
@@ -1 +0,0 @@
1
- export declare function waitForAuthorizationCode(port: number, expectedState?: string): Promise<string>;
@@ -1,43 +0,0 @@
1
- import { createServer } from "node:http";
2
- export async function waitForAuthorizationCode(port, expectedState) {
3
- return new Promise((resolve, reject) => {
4
- const server = createServer((request, response) => {
5
- const url = new URL(request.url ?? "/", `http://127.0.0.1:${port}`);
6
- if (url.pathname !== "/callback") {
7
- response.writeHead(404);
8
- response.end("Not found");
9
- return;
10
- }
11
- const error = url.searchParams.get("error");
12
- const code = url.searchParams.get("code");
13
- const state = url.searchParams.get("state");
14
- if (error) {
15
- response.writeHead(400, { "Content-Type": "text/plain" });
16
- response.end(`Blocks OS login failed: ${error}`);
17
- server.close();
18
- reject(new Error(error));
19
- return;
20
- }
21
- if (expectedState && state !== expectedState) {
22
- response.writeHead(400, { "Content-Type": "text/plain" });
23
- response.end("Blocks OS login failed: invalid state");
24
- server.close();
25
- reject(new Error("Invalid authorization state"));
26
- return;
27
- }
28
- if (!code) {
29
- response.writeHead(400, { "Content-Type": "text/plain" });
30
- response.end("Blocks OS login failed: missing code");
31
- server.close();
32
- reject(new Error("Missing authorization code"));
33
- return;
34
- }
35
- response.writeHead(200, { "Content-Type": "text/plain" });
36
- response.end("Blocks OS login complete. You can close this tab.");
37
- server.close();
38
- resolve(code);
39
- });
40
- server.on("error", reject);
41
- server.listen(port, "127.0.0.1");
42
- });
43
- }
@@ -1,4 +0,0 @@
1
- export declare function createPkcePair(): {
2
- challenge: string;
3
- verifier: string;
4
- };
package/dist/lib/pkce.js DELETED
@@ -1,9 +0,0 @@
1
- import { createHash, randomBytes } from "node:crypto";
2
- function base64Url(input) {
3
- return input.toString("base64").replaceAll("+", "-").replaceAll("/", "_").replaceAll("=", "");
4
- }
5
- export function createPkcePair() {
6
- const verifier = base64Url(randomBytes(32));
7
- const challenge = base64Url(createHash("sha256").update(verifier).digest());
8
- return { challenge, verifier };
9
- }
@@ -1,2 +0,0 @@
1
- export declare function promptValue(label: string, defaultValue?: string): Promise<string>;
2
- export declare function promptSecret(label: string, defaultValue?: string): Promise<string>;
@@ -1,36 +0,0 @@
1
- import { createInterface } from "node:readline";
2
- import { createInterface as createPromiseInterface } from "node:readline/promises";
3
- import { stdin as input, stdout as output } from "node:process";
4
- export async function promptValue(label, defaultValue) {
5
- const rl = createPromiseInterface({ input, output });
6
- try {
7
- const suffix = defaultValue ? ` (${defaultValue})` : "";
8
- const answer = await rl.question(`${label}${suffix}: `);
9
- return answer.trim() || defaultValue || "";
10
- }
11
- finally {
12
- rl.close();
13
- }
14
- }
15
- export async function promptSecret(label, defaultValue) {
16
- if (!input.isTTY || !output.isTTY)
17
- return promptValue(label, defaultValue);
18
- return new Promise((resolve) => {
19
- const rl = createInterface({ input, output, terminal: true });
20
- const muted = rl;
21
- muted._writeToOutput = (value) => {
22
- if (value === "\r\n" || value === "\n" || value === "\r") {
23
- output.write(value);
24
- return;
25
- }
26
- output.write("*".repeat(value.length));
27
- };
28
- const suffix = defaultValue ? " (configured, press Enter to keep)" : "";
29
- output.write(`${label}${suffix}: `);
30
- rl.question("", (answer) => {
31
- rl.close();
32
- output.write("\n");
33
- resolve(answer.trim() || defaultValue || "");
34
- });
35
- });
36
- }