@keywaysh/cli 0.0.3 → 0.0.6
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 +96 -50
- 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
|
@@ -66,7 +66,11 @@ var INTERNAL_POSTHOG_KEY = "phc_duG0qqI5z8LeHrS9pNxR5KaD4djgD0nmzUxuD3zP0ov";
|
|
|
66
66
|
var INTERNAL_POSTHOG_HOST = "https://eu.i.posthog.com";
|
|
67
67
|
|
|
68
68
|
// src/utils/api.ts
|
|
69
|
+
import { createRequire } from "module";
|
|
70
|
+
var require2 = createRequire(import.meta.url);
|
|
71
|
+
var pkg = require2("../../package.json");
|
|
69
72
|
var API_BASE_URL = process.env.KEYWAY_API_URL || INTERNAL_API_URL;
|
|
73
|
+
var USER_AGENT = `keyway-cli/${pkg.version}`;
|
|
70
74
|
var APIError = class extends Error {
|
|
71
75
|
constructor(statusCode, error, message) {
|
|
72
76
|
super(message);
|
|
@@ -102,70 +106,103 @@ async function handleResponse(response) {
|
|
|
102
106
|
}
|
|
103
107
|
async function initVault(repoFullName, accessToken) {
|
|
104
108
|
const body = { repoFullName };
|
|
105
|
-
const headers = {
|
|
109
|
+
const headers = {
|
|
110
|
+
"Content-Type": "application/json",
|
|
111
|
+
"User-Agent": USER_AGENT
|
|
112
|
+
};
|
|
106
113
|
if (accessToken) {
|
|
107
114
|
headers.Authorization = `Bearer ${accessToken}`;
|
|
108
115
|
}
|
|
109
|
-
const response = await fetch(`${API_BASE_URL}/vaults
|
|
116
|
+
const response = await fetch(`${API_BASE_URL}/v1/vaults`, {
|
|
110
117
|
method: "POST",
|
|
111
118
|
headers,
|
|
112
119
|
body: JSON.stringify(body)
|
|
113
120
|
});
|
|
114
|
-
|
|
121
|
+
const result = await handleResponse(response);
|
|
122
|
+
return result.data;
|
|
123
|
+
}
|
|
124
|
+
function parseEnvContent(content) {
|
|
125
|
+
const result = {};
|
|
126
|
+
const lines = content.split("\n");
|
|
127
|
+
for (const line of lines) {
|
|
128
|
+
const trimmed = line.trim();
|
|
129
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
130
|
+
const eqIndex = trimmed.indexOf("=");
|
|
131
|
+
if (eqIndex === -1) continue;
|
|
132
|
+
const key = trimmed.substring(0, eqIndex).trim();
|
|
133
|
+
let value = trimmed.substring(eqIndex + 1);
|
|
134
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
135
|
+
value = value.slice(1, -1);
|
|
136
|
+
}
|
|
137
|
+
if (key) result[key] = value;
|
|
138
|
+
}
|
|
139
|
+
return result;
|
|
115
140
|
}
|
|
116
141
|
async function pushSecrets(repoFullName, environment, content, accessToken) {
|
|
117
|
-
const
|
|
118
|
-
const
|
|
142
|
+
const secrets = parseEnvContent(content);
|
|
143
|
+
const body = { repoFullName, environment, secrets };
|
|
144
|
+
const headers = {
|
|
145
|
+
"Content-Type": "application/json",
|
|
146
|
+
"User-Agent": USER_AGENT
|
|
147
|
+
};
|
|
119
148
|
if (accessToken) {
|
|
120
149
|
headers.Authorization = `Bearer ${accessToken}`;
|
|
121
150
|
}
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
130
|
-
);
|
|
131
|
-
return handleResponse(response);
|
|
151
|
+
const response = await fetch(`${API_BASE_URL}/v1/secrets/push`, {
|
|
152
|
+
method: "POST",
|
|
153
|
+
headers,
|
|
154
|
+
body: JSON.stringify(body)
|
|
155
|
+
});
|
|
156
|
+
const result = await handleResponse(response);
|
|
157
|
+
return result.data;
|
|
132
158
|
}
|
|
133
159
|
async function pullSecrets(repoFullName, environment, accessToken) {
|
|
134
|
-
const
|
|
135
|
-
|
|
160
|
+
const headers = {
|
|
161
|
+
"Content-Type": "application/json",
|
|
162
|
+
"User-Agent": USER_AGENT
|
|
163
|
+
};
|
|
136
164
|
if (accessToken) {
|
|
137
165
|
headers.Authorization = `Bearer ${accessToken}`;
|
|
138
166
|
}
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
167
|
+
const params = new URLSearchParams({
|
|
168
|
+
repo: repoFullName,
|
|
169
|
+
environment
|
|
170
|
+
});
|
|
171
|
+
const response = await fetch(`${API_BASE_URL}/v1/secrets/pull?${params}`, {
|
|
172
|
+
method: "GET",
|
|
173
|
+
headers
|
|
174
|
+
});
|
|
175
|
+
const result = await handleResponse(response);
|
|
176
|
+
return { content: result.data.content };
|
|
147
177
|
}
|
|
148
178
|
async function startDeviceLogin(repository) {
|
|
149
|
-
const response = await fetch(`${API_BASE_URL}/auth/device/start`, {
|
|
179
|
+
const response = await fetch(`${API_BASE_URL}/v1/auth/device/start`, {
|
|
150
180
|
method: "POST",
|
|
151
|
-
headers: {
|
|
181
|
+
headers: {
|
|
182
|
+
"Content-Type": "application/json",
|
|
183
|
+
"User-Agent": USER_AGENT
|
|
184
|
+
},
|
|
152
185
|
body: JSON.stringify(repository ? { repository } : {})
|
|
153
186
|
});
|
|
154
187
|
return handleResponse(response);
|
|
155
188
|
}
|
|
156
189
|
async function pollDeviceLogin(deviceCode) {
|
|
157
|
-
const response = await fetch(`${API_BASE_URL}/auth/device/poll`, {
|
|
190
|
+
const response = await fetch(`${API_BASE_URL}/v1/auth/device/poll`, {
|
|
158
191
|
method: "POST",
|
|
159
|
-
headers: {
|
|
192
|
+
headers: {
|
|
193
|
+
"Content-Type": "application/json",
|
|
194
|
+
"User-Agent": USER_AGENT
|
|
195
|
+
},
|
|
160
196
|
body: JSON.stringify({ deviceCode })
|
|
161
197
|
});
|
|
162
198
|
return handleResponse(response);
|
|
163
199
|
}
|
|
164
200
|
async function validateToken(token) {
|
|
165
|
-
const response = await fetch(`${API_BASE_URL}/auth/token/validate`, {
|
|
201
|
+
const response = await fetch(`${API_BASE_URL}/v1/auth/token/validate`, {
|
|
166
202
|
method: "POST",
|
|
167
203
|
headers: {
|
|
168
204
|
"Content-Type": "application/json",
|
|
205
|
+
"User-Agent": USER_AGENT,
|
|
169
206
|
Authorization: `Bearer ${token}`
|
|
170
207
|
},
|
|
171
208
|
body: JSON.stringify({})
|
|
@@ -183,7 +220,7 @@ import fs from "fs";
|
|
|
183
220
|
// package.json
|
|
184
221
|
var package_default = {
|
|
185
222
|
name: "@keywaysh/cli",
|
|
186
|
-
version: "0.0.
|
|
223
|
+
version: "0.0.6",
|
|
187
224
|
description: "One link to all your secrets",
|
|
188
225
|
type: "module",
|
|
189
226
|
bin: {
|
|
@@ -199,7 +236,10 @@ var package_default = {
|
|
|
199
236
|
"build:watch": "pnpm exec tsup --watch",
|
|
200
237
|
prepublishOnly: "pnpm run build",
|
|
201
238
|
test: "pnpm exec vitest run",
|
|
202
|
-
"test:watch": "pnpm exec vitest"
|
|
239
|
+
"test:watch": "pnpm exec vitest",
|
|
240
|
+
release: "npm version patch && git push && git push --tags",
|
|
241
|
+
"release:minor": "npm version minor && git push && git push --tags",
|
|
242
|
+
"release:major": "npm version major && git push && git push --tags"
|
|
203
243
|
},
|
|
204
244
|
keywords: [
|
|
205
245
|
"secrets",
|
|
@@ -535,17 +575,20 @@ import path2 from "path";
|
|
|
535
575
|
import prompts2 from "prompts";
|
|
536
576
|
import chalk2 from "chalk";
|
|
537
577
|
function generateBadge(repo) {
|
|
538
|
-
return `[](https://keyway.sh/
|
|
578
|
+
return `[](https://www.keyway.sh/dashboard/vaults/${repo})`;
|
|
539
579
|
}
|
|
540
580
|
function insertBadgeIntoReadme(readmeContent, badge) {
|
|
541
581
|
if (readmeContent.includes("keyway.sh/badge.svg")) {
|
|
542
582
|
return readmeContent;
|
|
543
583
|
}
|
|
544
584
|
const lines = readmeContent.split(/\r?\n/);
|
|
545
|
-
const titleIndex = lines.findIndex((line) =>
|
|
585
|
+
const titleIndex = lines.findIndex((line) => /^#(?!#)\s+/.test(line.trim()));
|
|
546
586
|
if (titleIndex !== -1) {
|
|
547
587
|
const before = lines.slice(0, titleIndex + 1);
|
|
548
588
|
const after = lines.slice(titleIndex + 1);
|
|
589
|
+
while (after.length > 0 && after[0].trim() === "") {
|
|
590
|
+
after.shift();
|
|
591
|
+
}
|
|
549
592
|
const newLines = [...before, "", badge, "", ...after];
|
|
550
593
|
return newLines.join("\n");
|
|
551
594
|
}
|
|
@@ -828,6 +871,16 @@ async function pushCommand(options) {
|
|
|
828
871
|
console.log("\nUploading secrets...");
|
|
829
872
|
const response = await pushSecrets(repoFullName, environment, content, accessToken);
|
|
830
873
|
console.log(chalk4.green("\n\u2713 " + response.message));
|
|
874
|
+
if (response.stats) {
|
|
875
|
+
const { created, updated, deleted } = response.stats;
|
|
876
|
+
const parts = [];
|
|
877
|
+
if (created > 0) parts.push(chalk4.green(`+${created} created`));
|
|
878
|
+
if (updated > 0) parts.push(chalk4.yellow(`~${updated} updated`));
|
|
879
|
+
if (deleted > 0) parts.push(chalk4.red(`-${deleted} deleted`));
|
|
880
|
+
if (parts.length > 0) {
|
|
881
|
+
console.log(`Stats: ${parts.join(", ")}`);
|
|
882
|
+
}
|
|
883
|
+
}
|
|
831
884
|
console.log(`
|
|
832
885
|
Your secrets are now encrypted and stored securely.`);
|
|
833
886
|
console.log(`To retrieve them, run: ${chalk4.cyan(`keyway pull --env ${environment}`)}`);
|
|
@@ -925,7 +978,7 @@ import { execSync as execSync2 } from "child_process";
|
|
|
925
978
|
import { writeFileSync, unlinkSync, readFileSync, existsSync } from "fs";
|
|
926
979
|
import { tmpdir } from "os";
|
|
927
980
|
import { join } from "path";
|
|
928
|
-
var API_HEALTH_URL = `${process.env.KEYWAY_API_URL || INTERNAL_API_URL}
|
|
981
|
+
var API_HEALTH_URL = `${process.env.KEYWAY_API_URL || INTERNAL_API_URL}/v1/health`;
|
|
929
982
|
async function checkNode() {
|
|
930
983
|
const nodeVersion = process.versions.node;
|
|
931
984
|
const [major] = nodeVersion.split(".").map(Number);
|
|
@@ -1100,7 +1153,7 @@ async function checkSystemClock() {
|
|
|
1100
1153
|
try {
|
|
1101
1154
|
const controller = new AbortController();
|
|
1102
1155
|
const timeout = setTimeout(() => controller.abort(), 2e3);
|
|
1103
|
-
const response = await fetch("https://api.keyway.sh/", {
|
|
1156
|
+
const response = await fetch("https://api.keyway.sh/v1/health", {
|
|
1104
1157
|
method: "HEAD",
|
|
1105
1158
|
signal: controller.signal
|
|
1106
1159
|
});
|
|
@@ -1234,7 +1287,7 @@ Summary: ${formatSummary(results)}`);
|
|
|
1234
1287
|
// package.json with { type: 'json' }
|
|
1235
1288
|
var package_default2 = {
|
|
1236
1289
|
name: "@keywaysh/cli",
|
|
1237
|
-
version: "0.0.
|
|
1290
|
+
version: "0.0.6",
|
|
1238
1291
|
description: "One link to all your secrets",
|
|
1239
1292
|
type: "module",
|
|
1240
1293
|
bin: {
|
|
@@ -1250,7 +1303,10 @@ var package_default2 = {
|
|
|
1250
1303
|
"build:watch": "pnpm exec tsup --watch",
|
|
1251
1304
|
prepublishOnly: "pnpm run build",
|
|
1252
1305
|
test: "pnpm exec vitest run",
|
|
1253
|
-
"test:watch": "pnpm exec vitest"
|
|
1306
|
+
"test:watch": "pnpm exec vitest",
|
|
1307
|
+
release: "npm version patch && git push && git push --tags",
|
|
1308
|
+
"release:minor": "npm version minor && git push && git push --tags",
|
|
1309
|
+
"release:major": "npm version major && git push && git push --tags"
|
|
1254
1310
|
},
|
|
1255
1311
|
keywords: [
|
|
1256
1312
|
"secrets",
|
|
@@ -1293,11 +1349,6 @@ var package_default2 = {
|
|
|
1293
1349
|
|
|
1294
1350
|
// src/cli.ts
|
|
1295
1351
|
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
1352
|
var showBanner = () => {
|
|
1302
1353
|
const text = chalk7.cyan.bold("Keyway CLI");
|
|
1303
1354
|
const subtitle = chalk7.gray("GitHub-native secrets manager for dev teams");
|
|
@@ -1306,10 +1357,8 @@ ${text}
|
|
|
1306
1357
|
${subtitle}
|
|
1307
1358
|
`);
|
|
1308
1359
|
};
|
|
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");
|
|
1360
|
+
showBanner();
|
|
1361
|
+
program.name("keyway").description("GitHub-native secrets manager for dev teams").version(package_default2.version);
|
|
1313
1362
|
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
1363
|
await initCommand(options);
|
|
1315
1364
|
});
|
|
@@ -1328,9 +1377,6 @@ program.command("logout").description("Clear stored Keyway credentials").action(
|
|
|
1328
1377
|
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
1378
|
await doctorCommand(options);
|
|
1330
1379
|
});
|
|
1331
|
-
program.command("readme").description("README utilities").command("add-badge").description("Insert the Keyway badge into README").action(async () => {
|
|
1332
|
-
await addBadgeToReadme();
|
|
1333
|
-
});
|
|
1334
1380
|
program.parseAsync().catch((error) => {
|
|
1335
1381
|
console.error(chalk7.red("Error:"), error.message || error);
|
|
1336
1382
|
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.6",
|
|
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
|
+
}
|