@keywaysh/cli 0.0.3 → 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.
- package/README.md +140 -156
- package/dist/cli.js +70 -44
- package/package.json +14 -9
package/README.md
CHANGED
|
@@ -1,50 +1,82 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
>
|
|
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
|
[](https://opensource.org/licenses/MIT)
|
|
6
12
|
[](https://www.npmjs.com/package/@keywaysh/cli)
|
|
7
13
|
|
|
8
|
-
|
|
14
|
+
</div>
|
|
15
|
+
|
|
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
|
|
27
|
+
|
|
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
|
|
9
35
|
|
|
10
36
|
```bash
|
|
11
|
-
npm install @keywaysh/cli
|
|
37
|
+
npm install -g @keywaysh/cli
|
|
12
38
|
```
|
|
13
39
|
|
|
14
40
|
## Quick Start
|
|
15
41
|
|
|
42
|
+
Inside any project connected to GitHub:
|
|
43
|
+
|
|
16
44
|
```bash
|
|
17
|
-
# Authenticate and create your vault
|
|
18
45
|
keyway login
|
|
19
46
|
keyway init
|
|
47
|
+
```
|
|
20
48
|
|
|
21
|
-
|
|
22
|
-
keyway push
|
|
49
|
+
What happens:
|
|
23
50
|
|
|
24
|
-
|
|
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`
|
|
55
|
+
|
|
56
|
+
Then any teammate can simply run:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
25
59
|
keyway pull
|
|
26
60
|
```
|
|
27
61
|
|
|
62
|
+
And boom: your `.env` is recreated locally.
|
|
63
|
+
|
|
28
64
|
## Commands
|
|
29
65
|
|
|
30
66
|
### `keyway login`
|
|
31
67
|
|
|
32
|
-
Authenticate with GitHub through the Keyway OAuth/device flow
|
|
68
|
+
Authenticate with GitHub through the Keyway OAuth/device flow.
|
|
33
69
|
|
|
34
70
|
```bash
|
|
35
71
|
keyway login
|
|
36
72
|
```
|
|
37
73
|
|
|
38
|
-
If you
|
|
39
|
-
|
|
40
|
-
Fine-grained PAT alternative:
|
|
74
|
+
If you prefer using a fine-grained PAT:
|
|
41
75
|
|
|
42
76
|
```bash
|
|
43
77
|
keyway login --token
|
|
44
78
|
```
|
|
45
79
|
|
|
46
|
-
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.
|
|
47
|
-
|
|
48
80
|
### `keyway init`
|
|
49
81
|
|
|
50
82
|
Initialize a vault for the current repository.
|
|
@@ -53,18 +85,15 @@ Initialize a vault for the current repository.
|
|
|
53
85
|
keyway init
|
|
54
86
|
```
|
|
55
87
|
|
|
56
|
-
|
|
57
|
-
- Must be in a git repository
|
|
58
|
-
- Repository must have a GitHub remote
|
|
59
|
-
- Authenticated via `keyway login` (or provide `GITHUB_TOKEN`)
|
|
88
|
+
Creates a vault, pushes your `.env`, and sets everything up.
|
|
60
89
|
|
|
61
90
|
### `keyway push`
|
|
62
91
|
|
|
63
|
-
|
|
92
|
+
Push your local `.env` to the vault.
|
|
64
93
|
|
|
65
94
|
```bash
|
|
66
|
-
# Push
|
|
67
|
-
keyway push
|
|
95
|
+
# Push to development (default)
|
|
96
|
+
keyway push
|
|
68
97
|
|
|
69
98
|
# Push to a specific environment
|
|
70
99
|
keyway push --env production
|
|
@@ -73,35 +102,36 @@ keyway push --env production
|
|
|
73
102
|
keyway push --file .env.staging --env staging
|
|
74
103
|
```
|
|
75
104
|
|
|
76
|
-
|
|
77
|
-
-
|
|
78
|
-
-
|
|
105
|
+
Useful when:
|
|
106
|
+
- you added a new variable
|
|
107
|
+
- you rotated a key
|
|
108
|
+
- you fixed a staging/production mismatch
|
|
79
109
|
|
|
80
110
|
### `keyway pull`
|
|
81
111
|
|
|
82
|
-
|
|
112
|
+
Pull secrets from the vault and write them to `.env`.
|
|
83
113
|
|
|
84
114
|
```bash
|
|
85
|
-
# Pull development environment
|
|
86
|
-
keyway pull
|
|
115
|
+
# Pull development environment (default)
|
|
116
|
+
keyway pull
|
|
87
117
|
|
|
88
118
|
# Pull from a specific environment
|
|
89
119
|
keyway pull --env production
|
|
90
120
|
|
|
91
121
|
# Pull to a different file
|
|
92
|
-
keyway pull --file .env.local
|
|
122
|
+
keyway pull --file .env.local
|
|
93
123
|
```
|
|
94
124
|
|
|
95
|
-
|
|
96
|
-
-
|
|
97
|
-
-
|
|
125
|
+
Perfect for:
|
|
126
|
+
- onboarding a new dev
|
|
127
|
+
- syncing your local environment
|
|
128
|
+
- switching between machines
|
|
98
129
|
|
|
99
130
|
### `keyway doctor`
|
|
100
131
|
|
|
101
|
-
|
|
132
|
+
Diagnostic command to check your setup.
|
|
102
133
|
|
|
103
134
|
```bash
|
|
104
|
-
# Run all checks
|
|
105
135
|
keyway doctor
|
|
106
136
|
|
|
107
137
|
# Output as JSON (for CI/CD)
|
|
@@ -112,132 +142,106 @@ keyway doctor --strict
|
|
|
112
142
|
```
|
|
113
143
|
|
|
114
144
|
**Checks performed:**
|
|
115
|
-
-
|
|
116
|
-
-
|
|
117
|
-
-
|
|
118
|
-
-
|
|
119
|
-
-
|
|
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
|
|
120
151
|
|
|
121
|
-
|
|
152
|
+
### `keyway logout`
|
|
122
153
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
Keyway prefers the OAuth/device flow:
|
|
126
|
-
|
|
127
|
-
```bash
|
|
128
|
-
keyway login
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
This opens a browser (or gives you a code/URL) and stores a Keyway token in `~/.config/keyway/config.json`.
|
|
132
|
-
|
|
133
|
-
If you cannot use the login flow, set a GitHub token manually:
|
|
134
|
-
|
|
135
|
-
**Option 1: Environment Variable**
|
|
154
|
+
Clear stored Keyway credentials.
|
|
136
155
|
|
|
137
156
|
```bash
|
|
138
|
-
|
|
157
|
+
keyway logout
|
|
139
158
|
```
|
|
140
159
|
|
|
141
|
-
|
|
160
|
+
## Security
|
|
142
161
|
|
|
143
|
-
|
|
144
|
-
git config --global github.token your_github_personal_access_token
|
|
145
|
-
```
|
|
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.
|
|
146
163
|
|
|
147
|
-
**
|
|
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)
|
|
148
171
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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
|
|
153
176
|
|
|
154
|
-
|
|
177
|
+
More details: [keyway.sh/security](https://keyway.sh/security)
|
|
155
178
|
|
|
156
|
-
|
|
179
|
+
## Who is this for?
|
|
157
180
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
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
|
|
161
188
|
|
|
162
|
-
|
|
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
|
-
|
|
195
|
+
## Example Workflow
|
|
165
196
|
|
|
166
197
|
```bash
|
|
167
|
-
|
|
168
|
-
|
|
198
|
+
git clone git@github.com:acme/backend.git
|
|
199
|
+
cd backend
|
|
200
|
+
keyway pull
|
|
201
|
+
# ✓ secrets pulled
|
|
202
|
+
npm run dev
|
|
169
203
|
```
|
|
170
204
|
|
|
171
|
-
|
|
205
|
+
Add a new secret:
|
|
172
206
|
|
|
173
207
|
```bash
|
|
174
|
-
|
|
208
|
+
echo "STRIPE_KEY=sk_live_xxx" >> .env
|
|
209
|
+
keyway push
|
|
210
|
+
# ✓ 1 secret updated
|
|
175
211
|
```
|
|
176
212
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
**Privacy:** No secret names or values are ever sent to analytics.
|
|
180
|
-
|
|
181
|
-
## How It Works
|
|
213
|
+
## Configuration
|
|
182
214
|
|
|
183
|
-
|
|
184
|
-
2. **Authorization**: Checks if you're a collaborator/admin on the repository
|
|
185
|
-
3. **Encryption**: All secrets are encrypted server-side with AES-256-GCM
|
|
186
|
-
4. **Storage**: Encrypted secrets stored in PostgreSQL
|
|
187
|
-
5. **Retrieval**: Secrets are decrypted and returned only to authorized users
|
|
215
|
+
### GitHub Token (alternative to login)
|
|
188
216
|
|
|
189
|
-
|
|
217
|
+
If you cannot use the login flow, set a GitHub token manually:
|
|
190
218
|
|
|
191
219
|
```bash
|
|
192
|
-
#
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
# Run in dev mode
|
|
196
|
-
npm run dev
|
|
220
|
+
# Environment variable
|
|
221
|
+
export GITHUB_TOKEN=your_github_personal_access_token
|
|
197
222
|
|
|
198
|
-
#
|
|
199
|
-
|
|
223
|
+
# Or via git config
|
|
224
|
+
git config --global github.token your_github_personal_access_token
|
|
225
|
+
```
|
|
200
226
|
|
|
201
|
-
|
|
202
|
-
npm run build:watch
|
|
227
|
+
### API URL
|
|
203
228
|
|
|
204
|
-
|
|
205
|
-
npm test
|
|
229
|
+
By default, Keyway uses the production API. To point to another API:
|
|
206
230
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
keyway --version
|
|
231
|
+
```bash
|
|
232
|
+
export KEYWAY_API_URL=http://localhost:3000
|
|
210
233
|
```
|
|
211
234
|
|
|
212
|
-
|
|
235
|
+
### Disable Telemetry
|
|
213
236
|
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
├── cli.tsx # Main CLI entry point with commander
|
|
217
|
-
├── types.ts # TypeScript types and interfaces
|
|
218
|
-
├── ui/ # Ink React components
|
|
219
|
-
│ ├── Banner.tsx # Startup banner with gradient
|
|
220
|
-
│ └── Spinner.tsx # Loading spinner component
|
|
221
|
-
├── cmds/ # Command implementations
|
|
222
|
-
│ ├── init.ts # Initialize vault
|
|
223
|
-
│ ├── push.ts # Push secrets
|
|
224
|
-
│ ├── pull.ts # Pull secrets
|
|
225
|
-
│ └── doctor.tsx # Environment diagnostics
|
|
226
|
-
├── utils/ # Utility functions
|
|
227
|
-
│ ├── analytics.ts # PostHog integration
|
|
228
|
-
│ ├── api.ts # API client
|
|
229
|
-
│ └── git.ts # Git helpers
|
|
230
|
-
└── core/ # Core business logic
|
|
231
|
-
└── doctor.ts # Doctor checks implementations
|
|
237
|
+
```bash
|
|
238
|
+
export KEYWAY_DISABLE_TELEMETRY=1
|
|
232
239
|
```
|
|
233
240
|
|
|
234
|
-
## Privacy &
|
|
235
|
-
|
|
236
|
-
### Analytics Safety
|
|
241
|
+
## Privacy & Analytics
|
|
237
242
|
|
|
238
243
|
**NEVER tracked:**
|
|
239
|
-
- Secret names
|
|
240
|
-
- Secret values
|
|
244
|
+
- Secret names or values
|
|
241
245
|
- Environment variable content
|
|
242
246
|
- Access tokens
|
|
243
247
|
- File contents
|
|
@@ -245,21 +249,13 @@ src/
|
|
|
245
249
|
**Only tracked:**
|
|
246
250
|
- Command usage (init, push, pull)
|
|
247
251
|
- Repository names (public info)
|
|
248
|
-
- Environment names (e.g., "production")
|
|
249
|
-
- Number of variables (count only)
|
|
250
252
|
- Error messages (sanitized)
|
|
251
|
-
- Machine-specific anonymous ID
|
|
252
|
-
|
|
253
|
-
### Distinct ID
|
|
254
|
-
|
|
255
|
-
Each machine has a unique, anonymous identifier stored in `~/.config/keyway/id.json`. This ID is randomly generated and contains no personally identifiable information.
|
|
256
253
|
|
|
257
254
|
## Troubleshooting
|
|
258
255
|
|
|
259
256
|
### "Not in a git repository"
|
|
260
257
|
|
|
261
258
|
```bash
|
|
262
|
-
# Initialize git and add a remote
|
|
263
259
|
git init
|
|
264
260
|
git remote add origin git@github.com:your-org/your-repo.git
|
|
265
261
|
```
|
|
@@ -267,14 +263,14 @@ git remote add origin git@github.com:your-org/your-repo.git
|
|
|
267
263
|
### "GitHub token not found"
|
|
268
264
|
|
|
269
265
|
```bash
|
|
270
|
-
|
|
266
|
+
keyway login
|
|
267
|
+
# or
|
|
271
268
|
export GITHUB_TOKEN=your_token
|
|
272
269
|
```
|
|
273
270
|
|
|
274
271
|
### "Vault not found"
|
|
275
272
|
|
|
276
273
|
```bash
|
|
277
|
-
# Initialize the vault first
|
|
278
274
|
keyway init
|
|
279
275
|
```
|
|
280
276
|
|
|
@@ -282,36 +278,24 @@ keyway init
|
|
|
282
278
|
|
|
283
279
|
Make sure you're a collaborator or admin on the GitHub repository.
|
|
284
280
|
|
|
285
|
-
|
|
281
|
+
## TL;DR
|
|
286
282
|
|
|
287
283
|
```bash
|
|
288
|
-
|
|
289
|
-
keyway
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
export KEYWAY_NO_BANNER=1
|
|
293
|
-
keyway doctor
|
|
284
|
+
npm i -g @keywaysh/cli
|
|
285
|
+
keyway login
|
|
286
|
+
keyway init
|
|
287
|
+
keyway pull
|
|
294
288
|
```
|
|
295
289
|
|
|
296
|
-
|
|
290
|
+
No more Slack. No more outdated `.env`.
|
|
291
|
+
Your team stays perfectly in sync.
|
|
297
292
|
|
|
298
|
-
|
|
299
|
-
# Update version
|
|
300
|
-
npm version patch # or minor, or major
|
|
301
|
-
|
|
302
|
-
# Build
|
|
303
|
-
npm run build
|
|
293
|
+
## Support
|
|
304
294
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
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)
|
|
308
298
|
|
|
309
299
|
## License
|
|
310
300
|
|
|
311
301
|
MIT © Nicolas Ritouet
|
|
312
|
-
|
|
313
|
-
## Support
|
|
314
|
-
|
|
315
|
-
- **Issues**: https://github.com/keywaysh/cli/issues
|
|
316
|
-
- **Email**: unlock@keyway.sh
|
|
317
|
-
- **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
|
|
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
|
-
|
|
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
|
|
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
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
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
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
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.
|
|
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",
|
|
@@ -535,7 +555,7 @@ import path2 from "path";
|
|
|
535
555
|
import prompts2 from "prompts";
|
|
536
556
|
import chalk2 from "chalk";
|
|
537
557
|
function generateBadge(repo) {
|
|
538
|
-
return `[](https://keyway.sh/
|
|
558
|
+
return `[](https://www.keyway.sh/dashboard/vaults/${repo})`;
|
|
539
559
|
}
|
|
540
560
|
function insertBadgeIntoReadme(readmeContent, badge) {
|
|
541
561
|
if (readmeContent.includes("keyway.sh/badge.svg")) {
|
|
@@ -546,6 +566,9 @@ function insertBadgeIntoReadme(readmeContent, badge) {
|
|
|
546
566
|
if (titleIndex !== -1) {
|
|
547
567
|
const before = lines.slice(0, titleIndex + 1);
|
|
548
568
|
const after = lines.slice(titleIndex + 1);
|
|
569
|
+
while (after.length > 0 && after[0].trim() === "") {
|
|
570
|
+
after.shift();
|
|
571
|
+
}
|
|
549
572
|
const newLines = [...before, "", badge, "", ...after];
|
|
550
573
|
return newLines.join("\n");
|
|
551
574
|
}
|
|
@@ -828,6 +851,16 @@ async function pushCommand(options) {
|
|
|
828
851
|
console.log("\nUploading secrets...");
|
|
829
852
|
const response = await pushSecrets(repoFullName, environment, content, accessToken);
|
|
830
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
|
+
}
|
|
831
864
|
console.log(`
|
|
832
865
|
Your secrets are now encrypted and stored securely.`);
|
|
833
866
|
console.log(`To retrieve them, run: ${chalk4.cyan(`keyway pull --env ${environment}`)}`);
|
|
@@ -925,7 +958,7 @@ import { execSync as execSync2 } from "child_process";
|
|
|
925
958
|
import { writeFileSync, unlinkSync, readFileSync, existsSync } from "fs";
|
|
926
959
|
import { tmpdir } from "os";
|
|
927
960
|
import { join } from "path";
|
|
928
|
-
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`;
|
|
929
962
|
async function checkNode() {
|
|
930
963
|
const nodeVersion = process.versions.node;
|
|
931
964
|
const [major] = nodeVersion.split(".").map(Number);
|
|
@@ -1100,7 +1133,7 @@ async function checkSystemClock() {
|
|
|
1100
1133
|
try {
|
|
1101
1134
|
const controller = new AbortController();
|
|
1102
1135
|
const timeout = setTimeout(() => controller.abort(), 2e3);
|
|
1103
|
-
const response = await fetch("https://api.keyway.sh/", {
|
|
1136
|
+
const response = await fetch("https://api.keyway.sh/v1/health", {
|
|
1104
1137
|
method: "HEAD",
|
|
1105
1138
|
signal: controller.signal
|
|
1106
1139
|
});
|
|
@@ -1234,7 +1267,7 @@ Summary: ${formatSummary(results)}`);
|
|
|
1234
1267
|
// package.json with { type: 'json' }
|
|
1235
1268
|
var package_default2 = {
|
|
1236
1269
|
name: "@keywaysh/cli",
|
|
1237
|
-
version: "0.0.
|
|
1270
|
+
version: "0.0.4",
|
|
1238
1271
|
description: "One link to all your secrets",
|
|
1239
1272
|
type: "module",
|
|
1240
1273
|
bin: {
|
|
@@ -1250,7 +1283,10 @@ var package_default2 = {
|
|
|
1250
1283
|
"build:watch": "pnpm exec tsup --watch",
|
|
1251
1284
|
prepublishOnly: "pnpm run build",
|
|
1252
1285
|
test: "pnpm exec vitest run",
|
|
1253
|
-
"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"
|
|
1254
1290
|
},
|
|
1255
1291
|
keywords: [
|
|
1256
1292
|
"secrets",
|
|
@@ -1293,11 +1329,6 @@ var package_default2 = {
|
|
|
1293
1329
|
|
|
1294
1330
|
// src/cli.ts
|
|
1295
1331
|
var program = new Command();
|
|
1296
|
-
var shouldShowBanner = () => {
|
|
1297
|
-
if (process.env.KEYWAY_NO_BANNER === "1") return false;
|
|
1298
|
-
const argv = process.argv.slice(2);
|
|
1299
|
-
return !argv.includes("--no-banner") && argv.length > 0;
|
|
1300
|
-
};
|
|
1301
1332
|
var showBanner = () => {
|
|
1302
1333
|
const text = chalk7.cyan.bold("Keyway CLI");
|
|
1303
1334
|
const subtitle = chalk7.gray("GitHub-native secrets manager for dev teams");
|
|
@@ -1306,10 +1337,8 @@ ${text}
|
|
|
1306
1337
|
${subtitle}
|
|
1307
1338
|
`);
|
|
1308
1339
|
};
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
}
|
|
1312
|
-
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);
|
|
1313
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) => {
|
|
1314
1343
|
await initCommand(options);
|
|
1315
1344
|
});
|
|
@@ -1328,9 +1357,6 @@ program.command("logout").description("Clear stored Keyway credentials").action(
|
|
|
1328
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) => {
|
|
1329
1358
|
await doctorCommand(options);
|
|
1330
1359
|
});
|
|
1331
|
-
program.command("readme").description("README utilities").command("add-badge").description("Insert the Keyway badge into README").action(async () => {
|
|
1332
|
-
await addBadgeToReadme();
|
|
1333
|
-
});
|
|
1334
1360
|
program.parseAsync().catch((error) => {
|
|
1335
1361
|
console.error(chalk7.red("Error:"), error.message || error);
|
|
1336
1362
|
process.exit(1);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@keywaysh/cli",
|
|
3
|
-
"version": "0.0.
|
|
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
|
},
|
|
@@ -45,12 +57,5 @@
|
|
|
45
57
|
"tsx": "^4.20.3",
|
|
46
58
|
"typescript": "^5.9.2",
|
|
47
59
|
"vitest": "^3.2.4"
|
|
48
|
-
},
|
|
49
|
-
"scripts": {
|
|
50
|
-
"dev": "pnpm exec tsx src/cli.ts",
|
|
51
|
-
"build": "pnpm exec tsup",
|
|
52
|
-
"build:watch": "pnpm exec tsup --watch",
|
|
53
|
-
"test": "pnpm exec vitest run",
|
|
54
|
-
"test:watch": "pnpm exec vitest"
|
|
55
60
|
}
|
|
56
|
-
}
|
|
61
|
+
}
|