@keywaysh/cli 0.0.2 → 0.0.4

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 (3) hide show
  1. package/README.md +138 -164
  2. package/dist/cli.js +82 -50
  3. package/package.json +15 -11
package/README.md CHANGED
@@ -1,60 +1,82 @@
1
- # Keyway CLI
2
-
3
- > GitHub-native secrets manager for dev teams
1
+ <div align="center">
2
+ <h1>Keyway CLI</h1>
3
+ <strong>The simplest way to sync your project's environment variables.</strong><br/>
4
+ Stop sending <code>.env</code> files on Slack. One command and you're in sync.
5
+ <br/><br/>
6
+ <a href="https://keyway.sh">keyway.sh</a> ·
7
+ <a href="https://github.com/keywaysh/cli">GitHub</a> ·
8
+ <a href="https://www.npmjs.com/package/@keywaysh/cli">NPM</a>
9
+ <br/><br/>
4
10
 
5
11
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
12
  [![npm version](https://badge.fury.io/js/%40keywaysh%2Fcli.svg)](https://www.npmjs.com/package/@keywaysh/cli)
7
13
 
8
- ## Installation
14
+ </div>
9
15
 
10
- ```bash
11
- pnpm add -g @keywaysh/cli
12
- ```
16
+ ## Why Keyway?
17
+
18
+ Most devs store secrets in... chaotic places:
19
+
20
+ - Slack
21
+ - Notion
22
+ - Discord
23
+ - Google Docs
24
+ - Lost `.env` files
25
+ - Messages you can't find anymore
26
+ - Machine of the dev who left the project
13
27
 
14
- Or use without installing:
28
+ **Keyway fixes that.**
29
+
30
+ If you have GitHub access to a repo → you have access to its secrets.
31
+ No invites. No dashboards. No complex config.
32
+ Just one command that works.
33
+
34
+ ## Install
15
35
 
16
36
  ```bash
17
- npx @keywaysh/cli init
37
+ npm install -g @keywaysh/cli
18
38
  ```
19
39
 
20
40
  ## Quick Start
21
41
 
42
+ Inside any project connected to GitHub:
43
+
22
44
  ```bash
23
- # 0. Authenticate once (browser/device flow)
24
45
  keyway login
25
-
26
- # 1. Initialize a vault for your repository
27
46
  keyway init
47
+ ```
48
+
49
+ What happens:
28
50
 
29
- # 2. Prepare your env file with secrets (e.g., .env or .env.staging)
51
+ 1. Keyway authenticates via GitHub OAuth
52
+ 2. Detects your GitHub repo
53
+ 3. Creates a vault for this repo
54
+ 4. Asks if you want to sync your `.env`
30
55
 
31
- # 3. Push secrets to the vault
32
- keyway push --file .env
56
+ Then any teammate can simply run:
33
57
 
34
- # 4. On another machine, pull secrets
35
- keyway pull --file .env
58
+ ```bash
59
+ keyway pull
36
60
  ```
37
61
 
62
+ And boom: your `.env` is recreated locally.
63
+
38
64
  ## Commands
39
65
 
40
66
  ### `keyway login`
41
67
 
42
- Authenticate with GitHub through the Keyway OAuth/device flow and cache a session locally.
68
+ Authenticate with GitHub through the Keyway OAuth/device flow.
43
69
 
44
70
  ```bash
45
71
  keyway login
46
72
  ```
47
73
 
48
- If you forget to log in, `init`, `push`, and `pull` will prompt you to authenticate (skip with `--no-login-prompt` in CI).
49
-
50
- Fine-grained PAT alternative:
74
+ If you prefer using a fine-grained PAT:
51
75
 
52
76
  ```bash
53
77
  keyway login --token
54
78
  ```
55
79
 
56
- This opens GitHub to create a repo-scoped fine-grained PAT (metadata: read-only, no account permissions). Paste the `github_pat_...` token when prompted; the CLI validates and stores it.
57
-
58
80
  ### `keyway init`
59
81
 
60
82
  Initialize a vault for the current repository.
@@ -63,18 +85,15 @@ Initialize a vault for the current repository.
63
85
  keyway init
64
86
  ```
65
87
 
66
- **Requirements:**
67
- - Must be in a git repository
68
- - Repository must have a GitHub remote
69
- - Authenticated via `keyway login` (or provide `GITHUB_TOKEN`)
88
+ Creates a vault, pushes your `.env`, and sets everything up.
70
89
 
71
90
  ### `keyway push`
72
91
 
73
- Upload secrets from a local env file to the vault.
92
+ Push your local `.env` to the vault.
74
93
 
75
94
  ```bash
76
- # Push env file to development environment (default)
77
- keyway push --file .env
95
+ # Push to development (default)
96
+ keyway push
78
97
 
79
98
  # Push to a specific environment
80
99
  keyway push --env production
@@ -83,35 +102,36 @@ keyway push --env production
83
102
  keyway push --file .env.staging --env staging
84
103
  ```
85
104
 
86
- **Options:**
87
- - `-e, --env <environment>` - Environment name (default: "development")
88
- - `-f, --file <file>` - File to push (default file used if not provided)
105
+ Useful when:
106
+ - you added a new variable
107
+ - you rotated a key
108
+ - you fixed a staging/production mismatch
89
109
 
90
110
  ### `keyway pull`
91
111
 
92
- Download secrets from the vault to a local env file.
112
+ Pull secrets from the vault and write them to `.env`.
93
113
 
94
114
  ```bash
95
- # Pull development environment to your env file (default path if omitted)
96
- keyway pull --file .env
115
+ # Pull development environment (default)
116
+ keyway pull
97
117
 
98
118
  # Pull from a specific environment
99
119
  keyway pull --env production
100
120
 
101
121
  # Pull to a different file
102
- keyway pull --file .env.local --env development
122
+ keyway pull --file .env.local
103
123
  ```
104
124
 
105
- **Options:**
106
- - `-e, --env <environment>` - Environment name (default: "development")
107
- - `-f, --file <file>` - File to write to (default file used if not provided)
125
+ Perfect for:
126
+ - onboarding a new dev
127
+ - syncing your local environment
128
+ - switching between machines
108
129
 
109
130
  ### `keyway doctor`
110
131
 
111
- Run comprehensive environment diagnostics.
132
+ Diagnostic command to check your setup.
112
133
 
113
134
  ```bash
114
- # Run all checks
115
135
  keyway doctor
116
136
 
117
137
  # Output as JSON (for CI/CD)
@@ -122,132 +142,106 @@ keyway doctor --strict
122
142
  ```
123
143
 
124
144
  **Checks performed:**
125
- - Node.js version (≥18.0.0 required)
126
- - Git installation and repository status
127
- - Network connectivity to API
128
- - File system write permissions
129
- - ✅ .gitignore configuration for environment files
130
-
131
- ## Configuration
145
+ - Node.js version (≥18.0.0 required)
146
+ - Git installation and repository status
147
+ - API connectivity
148
+ - File system write permissions
149
+ - `.gitignore` configuration
150
+ - System clock synchronization
132
151
 
133
- ### GitHub Token
152
+ ### `keyway logout`
134
153
 
135
- Keyway prefers the OAuth/device flow:
154
+ Clear stored Keyway credentials.
136
155
 
137
156
  ```bash
138
- keyway login
157
+ keyway logout
139
158
  ```
140
159
 
141
- This opens a browser (or gives you a code/URL) and stores a Keyway token in `~/.config/keyway/config.json`.
160
+ ## Security
142
161
 
143
- If you cannot use the login flow, set a GitHub token manually:
162
+ Keyway is designed to be **simple and secure** a major upgrade from Slack or Notion, without the complexity of Hashicorp Vault or AWS Secrets Manager.
144
163
 
145
- **Option 1: Environment Variable**
164
+ **What we do:**
165
+ - AES-256-GCM encryption server-side
166
+ - TLS everywhere
167
+ - GitHub read-only permissions
168
+ - No access to your code
169
+ - Secrets stored encrypted at rest
170
+ - No analytics on secret values (only metadata)
146
171
 
147
- ```bash
148
- export GITHUB_TOKEN=your_github_personal_access_token
149
- ```
172
+ **What we don't do:**
173
+ - No zero-trust enterprise model
174
+ - No access to your cloud infrastructure
175
+ - No access to your production deployment keys
150
176
 
151
- **Option 2: Git Config**
177
+ More details: [keyway.sh/security](https://keyway.sh/security)
152
178
 
153
- ```bash
154
- git config --global github.token your_github_personal_access_token
155
- ```
179
+ ## Who is this for?
156
180
 
157
- **Creating a GitHub Token:**
181
+ Keyway is perfect for:
182
+ - Solo developers
183
+ - Small teams
184
+ - Side-projects
185
+ - Early SaaS
186
+ - Agencies managing many repos
187
+ - Rapid prototyping
158
188
 
159
- 1. Go to GitHub Settings → Developer settings → Personal access tokens → Tokens (classic)
160
- 2. Click "Generate new token"
161
- 3. Select scopes: `repo` (Full control of private repositories)
162
- 4. Generate and copy the token
189
+ **Not designed for:**
190
+ - Banks
191
+ - Governments
192
+ - Enterprise zero-trust teams
193
+ *(you're looking for Vault, Doppler, or AWS Secrets Manager)*
163
194
 
164
- ### API URL
165
-
166
- By default, Keyway uses the production API at `https://keyway-backend-production.up.railway.app`. To point to another API:
195
+ ## Example Workflow
167
196
 
168
197
  ```bash
169
- export KEYWAY_API_URL=http://localhost:3000
198
+ git clone git@github.com:acme/backend.git
199
+ cd backend
200
+ keyway pull
201
+ # ✓ secrets pulled
202
+ npm run dev
170
203
  ```
171
204
 
172
- ### Analytics
173
-
174
- Keyway uses PostHog for privacy-first analytics. To configure:
205
+ Add a new secret:
175
206
 
176
207
  ```bash
177
- export KEYWAY_POSTHOG_KEY=your_posthog_key
178
- export KEYWAY_POSTHOG_HOST=https://app.posthog.com
208
+ echo "STRIPE_KEY=sk_live_xxx" >> .env
209
+ keyway push
210
+ # ✓ 1 secret updated
179
211
  ```
180
212
 
181
- Disable telemetry:
182
-
183
- ```bash
184
- export KEYWAY_DISABLE_TELEMETRY=1
185
- ```
186
-
187
- The CLI ships with built-in analytics defaults; use the env vars above to override for development.
188
-
189
- **Privacy:** No secret names or values are ever sent to analytics.
190
-
191
- ## How It Works
213
+ ## Configuration
192
214
 
193
- 1. **Authentication**: Uses your GitHub token to verify identity
194
- 2. **Authorization**: Checks if you're a collaborator/admin on the repository
195
- 3. **Encryption**: All secrets are encrypted server-side with AES-256-GCM
196
- 4. **Storage**: Encrypted secrets stored in PostgreSQL
197
- 5. **Retrieval**: Secrets are decrypted and returned only to authorized users
215
+ ### GitHub Token (alternative to login)
198
216
 
199
- ## Development
217
+ If you cannot use the login flow, set a GitHub token manually:
200
218
 
201
219
  ```bash
202
- # Install dependencies
203
- npm install
204
-
205
- # Run in dev mode
206
- npm run dev
220
+ # Environment variable
221
+ export GITHUB_TOKEN=your_github_personal_access_token
207
222
 
208
- # Build
209
- npm run build
223
+ # Or via git config
224
+ git config --global github.token your_github_personal_access_token
225
+ ```
210
226
 
211
- # Watch mode
212
- npm run build:watch
227
+ ### API URL
213
228
 
214
- # Run tests
215
- npm test
229
+ By default, Keyway uses the production API. To point to another API:
216
230
 
217
- # Test locally
218
- npm link
219
- keyway --version
231
+ ```bash
232
+ export KEYWAY_API_URL=http://localhost:3000
220
233
  ```
221
234
 
222
- ## Architecture
235
+ ### Disable Telemetry
223
236
 
237
+ ```bash
238
+ export KEYWAY_DISABLE_TELEMETRY=1
224
239
  ```
225
- src/
226
- ├── cli.tsx # Main CLI entry point with commander
227
- ├── types.ts # TypeScript types and interfaces
228
- ├── ui/ # Ink React components
229
- │ ├── Banner.tsx # Startup banner with gradient
230
- │ └── Spinner.tsx # Loading spinner component
231
- ├── cmds/ # Command implementations
232
- │ ├── init.ts # Initialize vault
233
- │ ├── push.ts # Push secrets
234
- │ ├── pull.ts # Pull secrets
235
- │ └── doctor.tsx # Environment diagnostics
236
- ├── utils/ # Utility functions
237
- │ ├── analytics.ts # PostHog integration
238
- │ ├── api.ts # API client
239
- │ └── git.ts # Git helpers
240
- └── core/ # Core business logic
241
- └── doctor.ts # Doctor checks implementations
242
- ```
243
-
244
- ## Privacy & Security
245
240
 
246
- ### Analytics Safety
241
+ ## Privacy & Analytics
247
242
 
248
243
  **NEVER tracked:**
249
- - Secret names (e.g., `API_KEY`, `DATABASE_URL`)
250
- - Secret values
244
+ - Secret names or values
251
245
  - Environment variable content
252
246
  - Access tokens
253
247
  - File contents
@@ -255,21 +249,13 @@ src/
255
249
  **Only tracked:**
256
250
  - Command usage (init, push, pull)
257
251
  - Repository names (public info)
258
- - Environment names (e.g., "production")
259
- - Number of variables (count only)
260
252
  - Error messages (sanitized)
261
- - Machine-specific anonymous ID
262
-
263
- ### Distinct ID
264
-
265
- Each machine has a unique, anonymous identifier stored in `~/.config/keyway/id.json`. This ID is randomly generated and contains no personally identifiable information.
266
253
 
267
254
  ## Troubleshooting
268
255
 
269
256
  ### "Not in a git repository"
270
257
 
271
258
  ```bash
272
- # Initialize git and add a remote
273
259
  git init
274
260
  git remote add origin git@github.com:your-org/your-repo.git
275
261
  ```
@@ -277,14 +263,14 @@ git remote add origin git@github.com:your-org/your-repo.git
277
263
  ### "GitHub token not found"
278
264
 
279
265
  ```bash
280
- # Set your GitHub token
266
+ keyway login
267
+ # or
281
268
  export GITHUB_TOKEN=your_token
282
269
  ```
283
270
 
284
271
  ### "Vault not found"
285
272
 
286
273
  ```bash
287
- # Initialize the vault first
288
274
  keyway init
289
275
  ```
290
276
 
@@ -292,36 +278,24 @@ keyway init
292
278
 
293
279
  Make sure you're a collaborator or admin on the GitHub repository.
294
280
 
295
- ### Disabling the Banner
281
+ ## TL;DR
296
282
 
297
283
  ```bash
298
- # Via command line flag
299
- keyway --no-banner doctor
300
-
301
- # Via environment variable
302
- export KEYWAY_NO_BANNER=1
303
- keyway doctor
284
+ npm i -g @keywaysh/cli
285
+ keyway login
286
+ keyway init
287
+ keyway pull
304
288
  ```
305
289
 
306
- ## Publishing to npm
307
-
308
- ```bash
309
- # Update version
310
- npm version patch # or minor, or major
290
+ No more Slack. No more outdated `.env`.
291
+ Your team stays perfectly in sync.
311
292
 
312
- # Build
313
- npm run build
293
+ ## Support
314
294
 
315
- # Publish
316
- npm publish
317
- ```
295
+ - **Issues**: [github.com/keywaysh/cli/issues](https://github.com/keywaysh/cli/issues)
296
+ - **Email**: unlock@keyway.sh
297
+ - **Website**: [keyway.sh](https://keyway.sh)
318
298
 
319
299
  ## License
320
300
 
321
301
  MIT © Nicolas Ritouet
322
-
323
- ## Support
324
-
325
- - **Issues**: https://github.com/keywaysh/cli/issues
326
- - **Email**: unlock@keyway.sh
327
- - **Website**: https://keyway.sh
package/dist/cli.js CHANGED
@@ -106,47 +106,64 @@ async function initVault(repoFullName, accessToken) {
106
106
  if (accessToken) {
107
107
  headers.Authorization = `Bearer ${accessToken}`;
108
108
  }
109
- const response = await fetch(`${API_BASE_URL}/vaults/init`, {
109
+ const response = await fetch(`${API_BASE_URL}/v1/vaults`, {
110
110
  method: "POST",
111
111
  headers,
112
112
  body: JSON.stringify(body)
113
113
  });
114
- return handleResponse(response);
114
+ const result = await handleResponse(response);
115
+ return result.data;
116
+ }
117
+ function parseEnvContent(content) {
118
+ const result = {};
119
+ const lines = content.split("\n");
120
+ for (const line of lines) {
121
+ const trimmed = line.trim();
122
+ if (!trimmed || trimmed.startsWith("#")) continue;
123
+ const eqIndex = trimmed.indexOf("=");
124
+ if (eqIndex === -1) continue;
125
+ const key = trimmed.substring(0, eqIndex).trim();
126
+ let value = trimmed.substring(eqIndex + 1);
127
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
128
+ value = value.slice(1, -1);
129
+ }
130
+ if (key) result[key] = value;
131
+ }
132
+ return result;
115
133
  }
116
134
  async function pushSecrets(repoFullName, environment, content, accessToken) {
117
- const body = { content };
135
+ const secrets = parseEnvContent(content);
136
+ const body = { repoFullName, environment, secrets };
118
137
  const headers = { "Content-Type": "application/json" };
119
138
  if (accessToken) {
120
139
  headers.Authorization = `Bearer ${accessToken}`;
121
140
  }
122
- const encodedRepo = encodeURIComponent(repoFullName);
123
- const response = await fetch(
124
- `${API_BASE_URL}/vaults/${encodedRepo}/${environment}/push`,
125
- {
126
- method: "POST",
127
- headers,
128
- body: JSON.stringify(body)
129
- }
130
- );
131
- return handleResponse(response);
141
+ const response = await fetch(`${API_BASE_URL}/v1/secrets/push`, {
142
+ method: "POST",
143
+ headers,
144
+ body: JSON.stringify(body)
145
+ });
146
+ const result = await handleResponse(response);
147
+ return result.data;
132
148
  }
133
149
  async function pullSecrets(repoFullName, environment, accessToken) {
134
- const encodedRepo = encodeURIComponent(repoFullName);
135
150
  const headers = { "Content-Type": "application/json" };
136
151
  if (accessToken) {
137
152
  headers.Authorization = `Bearer ${accessToken}`;
138
153
  }
139
- const response = await fetch(
140
- `${API_BASE_URL}/vaults/${encodedRepo}/${environment}/pull`,
141
- {
142
- method: "GET",
143
- headers
144
- }
145
- );
146
- return handleResponse(response);
154
+ const params = new URLSearchParams({
155
+ repo: repoFullName,
156
+ environment
157
+ });
158
+ const response = await fetch(`${API_BASE_URL}/v1/secrets/pull?${params}`, {
159
+ method: "GET",
160
+ headers
161
+ });
162
+ const result = await handleResponse(response);
163
+ return { content: result.data.content };
147
164
  }
148
165
  async function startDeviceLogin(repository) {
149
- const response = await fetch(`${API_BASE_URL}/auth/device/start`, {
166
+ const response = await fetch(`${API_BASE_URL}/v1/auth/device/start`, {
150
167
  method: "POST",
151
168
  headers: { "Content-Type": "application/json" },
152
169
  body: JSON.stringify(repository ? { repository } : {})
@@ -154,7 +171,7 @@ async function startDeviceLogin(repository) {
154
171
  return handleResponse(response);
155
172
  }
156
173
  async function pollDeviceLogin(deviceCode) {
157
- const response = await fetch(`${API_BASE_URL}/auth/device/poll`, {
174
+ const response = await fetch(`${API_BASE_URL}/v1/auth/device/poll`, {
158
175
  method: "POST",
159
176
  headers: { "Content-Type": "application/json" },
160
177
  body: JSON.stringify({ deviceCode })
@@ -162,7 +179,7 @@ async function pollDeviceLogin(deviceCode) {
162
179
  return handleResponse(response);
163
180
  }
164
181
  async function validateToken(token) {
165
- const response = await fetch(`${API_BASE_URL}/auth/token/validate`, {
182
+ const response = await fetch(`${API_BASE_URL}/v1/auth/token/validate`, {
166
183
  method: "POST",
167
184
  headers: {
168
185
  "Content-Type": "application/json",
@@ -183,7 +200,7 @@ import fs from "fs";
183
200
  // package.json
184
201
  var package_default = {
185
202
  name: "@keywaysh/cli",
186
- version: "0.0.2",
203
+ version: "0.0.4",
187
204
  description: "One link to all your secrets",
188
205
  type: "module",
189
206
  bin: {
@@ -199,7 +216,10 @@ var package_default = {
199
216
  "build:watch": "pnpm exec tsup --watch",
200
217
  prepublishOnly: "pnpm run build",
201
218
  test: "pnpm exec vitest run",
202
- "test:watch": "pnpm exec vitest"
219
+ "test:watch": "pnpm exec vitest",
220
+ release: "npm version patch && git push && git push --tags",
221
+ "release:minor": "npm version minor && git push && git push --tags",
222
+ "release:major": "npm version major && git push && git push --tags"
203
223
  },
204
224
  keywords: [
205
225
  "secrets",
@@ -228,8 +248,7 @@ var package_default = {
228
248
  conf: "^15.0.2",
229
249
  open: "^11.0.0",
230
250
  "posthog-node": "^3.5.0",
231
- prompts: "^2.4.2",
232
- undici: "^7.13.0"
251
+ prompts: "^2.4.2"
233
252
  },
234
253
  devDependencies: {
235
254
  "@types/node": "^24.2.0",
@@ -536,7 +555,7 @@ import path2 from "path";
536
555
  import prompts2 from "prompts";
537
556
  import chalk2 from "chalk";
538
557
  function generateBadge(repo) {
539
- return `[![Keyway Secrets](https://keyway.sh/badge.svg?repo=${repo})](https://keyway.sh/repo/${repo})`;
558
+ return `[![Keyway Secrets](https://www.keyway.sh/badge.svg?repo=${repo})](https://www.keyway.sh/dashboard/vaults/${repo})`;
540
559
  }
541
560
  function insertBadgeIntoReadme(readmeContent, badge) {
542
561
  if (readmeContent.includes("keyway.sh/badge.svg")) {
@@ -547,6 +566,9 @@ function insertBadgeIntoReadme(readmeContent, badge) {
547
566
  if (titleIndex !== -1) {
548
567
  const before = lines.slice(0, titleIndex + 1);
549
568
  const after = lines.slice(titleIndex + 1);
569
+ while (after.length > 0 && after[0].trim() === "") {
570
+ after.shift();
571
+ }
550
572
  const newLines = [...before, "", badge, "", ...after];
551
573
  return newLines.join("\n");
552
574
  }
@@ -829,6 +851,16 @@ async function pushCommand(options) {
829
851
  console.log("\nUploading secrets...");
830
852
  const response = await pushSecrets(repoFullName, environment, content, accessToken);
831
853
  console.log(chalk4.green("\n\u2713 " + response.message));
854
+ if (response.stats) {
855
+ const { created, updated, deleted } = response.stats;
856
+ const parts = [];
857
+ if (created > 0) parts.push(chalk4.green(`+${created} created`));
858
+ if (updated > 0) parts.push(chalk4.yellow(`~${updated} updated`));
859
+ if (deleted > 0) parts.push(chalk4.red(`-${deleted} deleted`));
860
+ if (parts.length > 0) {
861
+ console.log(`Stats: ${parts.join(", ")}`);
862
+ }
863
+ }
832
864
  console.log(`
833
865
  Your secrets are now encrypted and stored securely.`);
834
866
  console.log(`To retrieve them, run: ${chalk4.cyan(`keyway pull --env ${environment}`)}`);
@@ -926,8 +958,7 @@ import { execSync as execSync2 } from "child_process";
926
958
  import { writeFileSync, unlinkSync, readFileSync, existsSync } from "fs";
927
959
  import { tmpdir } from "os";
928
960
  import { join } from "path";
929
- import { fetch as fetch2 } from "undici";
930
- var API_HEALTH_URL = `${process.env.KEYWAY_API_URL || INTERNAL_API_URL}/`;
961
+ var API_HEALTH_URL = `${process.env.KEYWAY_API_URL || INTERNAL_API_URL}/v1/health`;
931
962
  async function checkNode() {
932
963
  const nodeVersion = process.versions.node;
933
964
  const [major] = nodeVersion.split(".").map(Number);
@@ -978,10 +1009,19 @@ async function checkGit() {
978
1009
  }
979
1010
  }
980
1011
  async function checkNetwork() {
1012
+ const fetchFn = globalThis.fetch;
1013
+ if (!fetchFn) {
1014
+ return {
1015
+ id: "network",
1016
+ name: "API connectivity",
1017
+ status: "warn",
1018
+ detail: "Fetch API not available in this Node.js runtime"
1019
+ };
1020
+ }
981
1021
  try {
982
1022
  const controller = new AbortController();
983
1023
  const timeout = setTimeout(() => controller.abort(), 2e3);
984
- const response = await fetch2(API_HEALTH_URL, {
1024
+ const response = await fetchFn(API_HEALTH_URL, {
985
1025
  method: "HEAD",
986
1026
  signal: controller.signal
987
1027
  });
@@ -1093,7 +1133,7 @@ async function checkSystemClock() {
1093
1133
  try {
1094
1134
  const controller = new AbortController();
1095
1135
  const timeout = setTimeout(() => controller.abort(), 2e3);
1096
- const response = await fetch2("https://api.keyway.sh/", {
1136
+ const response = await fetch("https://api.keyway.sh/v1/health", {
1097
1137
  method: "HEAD",
1098
1138
  signal: controller.signal
1099
1139
  });
@@ -1227,7 +1267,7 @@ Summary: ${formatSummary(results)}`);
1227
1267
  // package.json with { type: 'json' }
1228
1268
  var package_default2 = {
1229
1269
  name: "@keywaysh/cli",
1230
- version: "0.0.2",
1270
+ version: "0.0.4",
1231
1271
  description: "One link to all your secrets",
1232
1272
  type: "module",
1233
1273
  bin: {
@@ -1243,7 +1283,10 @@ var package_default2 = {
1243
1283
  "build:watch": "pnpm exec tsup --watch",
1244
1284
  prepublishOnly: "pnpm run build",
1245
1285
  test: "pnpm exec vitest run",
1246
- "test:watch": "pnpm exec vitest"
1286
+ "test:watch": "pnpm exec vitest",
1287
+ release: "npm version patch && git push && git push --tags",
1288
+ "release:minor": "npm version minor && git push && git push --tags",
1289
+ "release:major": "npm version major && git push && git push --tags"
1247
1290
  },
1248
1291
  keywords: [
1249
1292
  "secrets",
@@ -1272,8 +1315,7 @@ var package_default2 = {
1272
1315
  conf: "^15.0.2",
1273
1316
  open: "^11.0.0",
1274
1317
  "posthog-node": "^3.5.0",
1275
- prompts: "^2.4.2",
1276
- undici: "^7.13.0"
1318
+ prompts: "^2.4.2"
1277
1319
  },
1278
1320
  devDependencies: {
1279
1321
  "@types/node": "^24.2.0",
@@ -1287,11 +1329,6 @@ var package_default2 = {
1287
1329
 
1288
1330
  // src/cli.ts
1289
1331
  var program = new Command();
1290
- var shouldShowBanner = () => {
1291
- if (process.env.KEYWAY_NO_BANNER === "1") return false;
1292
- const argv = process.argv.slice(2);
1293
- return !argv.includes("--no-banner") && argv.length > 0;
1294
- };
1295
1332
  var showBanner = () => {
1296
1333
  const text = chalk7.cyan.bold("Keyway CLI");
1297
1334
  const subtitle = chalk7.gray("GitHub-native secrets manager for dev teams");
@@ -1300,10 +1337,8 @@ ${text}
1300
1337
  ${subtitle}
1301
1338
  `);
1302
1339
  };
1303
- if (shouldShowBanner()) {
1304
- showBanner();
1305
- }
1306
- program.name("keyway").description("GitHub-native secrets manager for dev teams").version(package_default2.version).option("--no-banner", "Disable the startup banner");
1340
+ showBanner();
1341
+ program.name("keyway").description("GitHub-native secrets manager for dev teams").version(package_default2.version);
1307
1342
  program.command("init").description("Initialize a vault for the current repository").option("--no-login-prompt", "Fail instead of prompting to login if unauthenticated").action(async (options) => {
1308
1343
  await initCommand(options);
1309
1344
  });
@@ -1322,9 +1357,6 @@ program.command("logout").description("Clear stored Keyway credentials").action(
1322
1357
  program.command("doctor").description("Run environment checks to ensure Keyway runs smoothly").option("--json", "Output results as JSON for machine processing", false).option("--strict", "Treat warnings as failures", false).action(async (options) => {
1323
1358
  await doctorCommand(options);
1324
1359
  });
1325
- program.command("readme").description("README utilities").command("add-badge").description("Insert the Keyway badge into README").action(async () => {
1326
- await addBadgeToReadme();
1327
- });
1328
1360
  program.parseAsync().catch((error) => {
1329
1361
  console.error(chalk7.red("Error:"), error.message || error);
1330
1362
  process.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@keywaysh/cli",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "description": "One link to all your secrets",
5
5
  "type": "module",
6
6
  "bin": {
@@ -10,6 +10,17 @@
10
10
  "files": [
11
11
  "dist"
12
12
  ],
13
+ "scripts": {
14
+ "dev": "pnpm exec tsx src/cli.ts",
15
+ "build": "pnpm exec tsup",
16
+ "build:watch": "pnpm exec tsup --watch",
17
+ "prepublishOnly": "pnpm run build",
18
+ "test": "pnpm exec vitest run",
19
+ "test:watch": "pnpm exec vitest",
20
+ "release": "npm version patch && git push && git push --tags",
21
+ "release:minor": "npm version minor && git push && git push --tags",
22
+ "release:major": "npm version major && git push && git push --tags"
23
+ },
13
24
  "keywords": [
14
25
  "secrets",
15
26
  "env",
@@ -27,6 +38,7 @@
27
38
  "bugs": {
28
39
  "url": "https://github.com/keywaysh/cli/issues"
29
40
  },
41
+ "packageManager": "pnpm@10.6.1",
30
42
  "engines": {
31
43
  "node": ">=18.0.0"
32
44
  },
@@ -36,8 +48,7 @@
36
48
  "conf": "^15.0.2",
37
49
  "open": "^11.0.0",
38
50
  "posthog-node": "^3.5.0",
39
- "prompts": "^2.4.2",
40
- "undici": "^7.13.0"
51
+ "prompts": "^2.4.2"
41
52
  },
42
53
  "devDependencies": {
43
54
  "@types/node": "^24.2.0",
@@ -46,12 +57,5 @@
46
57
  "tsx": "^4.20.3",
47
58
  "typescript": "^5.9.2",
48
59
  "vitest": "^3.2.4"
49
- },
50
- "scripts": {
51
- "dev": "pnpm exec tsx src/cli.ts",
52
- "build": "pnpm exec tsup",
53
- "build:watch": "pnpm exec tsup --watch",
54
- "test": "pnpm exec vitest run",
55
- "test:watch": "pnpm exec vitest"
56
60
  }
57
- }
61
+ }