@appuo/orbit 1.0.0
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/LICENSE +21 -0
- package/README.md +372 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +719 -0
- package/dist/index.js.map +1 -0
- package/package.json +48 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Appuo
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
# 🌍 Orbit
|
|
2
|
+
|
|
3
|
+
**A fully CLI-based multi-cloud profile manager.**
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@appuo/orbit)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
[](https://nodejs.org)
|
|
8
|
+
|
|
9
|
+
Orbit lets you manage multiple cloud provider accounts and switch between them instantly. No shell modifications. No global environment changes. No OS-specific hacks.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Why Orbit?
|
|
14
|
+
|
|
15
|
+
If you deploy to **multiple Vercel accounts** (personal, company, client projects), switching between them is painful — you have to `vercel logout`, `vercel login`, verify email, wait...
|
|
16
|
+
|
|
17
|
+
Orbit fixes this:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
orbit use vercel company
|
|
21
|
+
vercel deploy # ← Deploys as company. Done.
|
|
22
|
+
|
|
23
|
+
orbit use vercel personal
|
|
24
|
+
vercel deploy # ← Deploys as personal. Instant.
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
One command. Instant switch. Plain `vercel` commands just work.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## ✨ Features
|
|
32
|
+
|
|
33
|
+
- 🔄 **Instant Profile Switching** — `orbit use` swaps credentials natively, plain CLI commands work immediately
|
|
34
|
+
- 🔐 **Secure Token Storage** — Credentials stored in your OS keychain (Keychain / libsecret / Credential Vault)
|
|
35
|
+
- 🔑 **Integrated Login** — Orbit launches `vercel login` for you during setup, no manual token pasting
|
|
36
|
+
- 📧 **Email Display** — See which account each profile belongs to at a glance
|
|
37
|
+
- 🌐 **Multi-cloud Ready** — Extensible provider architecture (Vercel built-in, more coming)
|
|
38
|
+
- 🖥️ **Cross-platform** — macOS, Linux, and Windows
|
|
39
|
+
- ⚡ **Zero Config Pollution** — No `.bashrc`, `.zshrc`, or shell modifications
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## 📦 Installation
|
|
44
|
+
|
|
45
|
+
### Global Install (Recommended)
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
npm install -g @appuo/orbit
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
or with pnpm:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
pnpm add -g @appuo/orbit
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### No Install (via npx)
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
npx @appuo/orbit list
|
|
61
|
+
npx @appuo/orbit add vercel personal
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### From Source
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
git clone https://github.com/appuo/orbit.git
|
|
68
|
+
cd orbit
|
|
69
|
+
pnpm install
|
|
70
|
+
pnpm build
|
|
71
|
+
npm link
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Prerequisites
|
|
75
|
+
|
|
76
|
+
- **Node.js 18+**
|
|
77
|
+
- **Vercel CLI** installed globally (`npm i -g vercel`) for Vercel provider
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## 🚀 Quick Start
|
|
82
|
+
|
|
83
|
+
### 1. Add your first account
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
orbit add vercel personal
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Orbit will detect if you're already logged into Vercel CLI:
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
Found existing token (you@gmail.com) from vercel CLI. Use this? (Y/n) y
|
|
93
|
+
✔ Profile "personal" added for vercel.
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
If no token is found, Orbit will offer to log you in:
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
No existing credentials found. Login with vercel CLI now? (Y/n) y
|
|
100
|
+
Visit vercel.com/device and enter XXXX-XXXX
|
|
101
|
+
Congratulations! You are now signed in.
|
|
102
|
+
✔ New credentials found!
|
|
103
|
+
✔ Profile "personal" added for vercel.
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### 2. Add a second account
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
orbit add vercel company
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Orbit shows whose token it found, so you can decide:
|
|
113
|
+
|
|
114
|
+
```
|
|
115
|
+
Found existing token (you@gmail.com) from vercel CLI. Use this? (Y/n) n
|
|
116
|
+
Login to a different account with vercel CLI? (Y/n) y
|
|
117
|
+
Visit vercel.com/device and enter YYYY-YYYY
|
|
118
|
+
Congratulations! You are now signed in.
|
|
119
|
+
✔ New credentials found!
|
|
120
|
+
✔ Profile "company" added for vercel.
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### 3. See all profiles
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
orbit list
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
```
|
|
130
|
+
Provider Profile Email Status
|
|
131
|
+
vercel personal you@gmail.com ★ current
|
|
132
|
+
vercel company work@company.com —
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### 4. Switch instantly
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
orbit use vercel company
|
|
139
|
+
vercel deploy # ← Deploys as company
|
|
140
|
+
vercel whoami # ← Shows company account
|
|
141
|
+
|
|
142
|
+
orbit use vercel personal
|
|
143
|
+
vercel deploy # ← Deploys as personal
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
No `vercel logout`. No `vercel login`. Just switch and go.
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## 📖 Commands
|
|
151
|
+
|
|
152
|
+
| Command | Description |
|
|
153
|
+
|---------|-------------|
|
|
154
|
+
| `orbit add <provider> <profile>` | Add a new profile (with guided login) |
|
|
155
|
+
| `orbit list` | Show all profiles with email and status |
|
|
156
|
+
| `orbit use <provider> <profile>` | Switch active profile (swaps credentials) |
|
|
157
|
+
| `orbit exec <provider> <command...>` | Run command with the current profile |
|
|
158
|
+
| `orbit run <provider> <profile> <command...>` | Run command with a specific profile |
|
|
159
|
+
| `orbit current` | Show currently active profiles |
|
|
160
|
+
| `orbit remove <provider> <profile>` | Remove a profile and its credentials |
|
|
161
|
+
|
|
162
|
+
### `orbit add <provider> <profile>`
|
|
163
|
+
|
|
164
|
+
Add a new cloud provider profile. Orbit guides you through the process:
|
|
165
|
+
|
|
166
|
+
1. **Auto-discovery** — Checks for existing CLI credentials and shows the associated email
|
|
167
|
+
2. **Integrated login** — Offers to run `vercel login` if no credentials found or if you want a different account
|
|
168
|
+
3. **Manual token** — Falls back to secure token input if needed
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
orbit add vercel staging
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### `orbit use <provider> <profile>`
|
|
175
|
+
|
|
176
|
+
Switch the active profile. This **swaps the provider's auth credentials** so that native CLI commands work immediately.
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
orbit use vercel company
|
|
180
|
+
# ✔ Now using profile "company" for vercel.
|
|
181
|
+
|
|
182
|
+
vercel deploy # ← Runs as company, no wrapper needed!
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### `orbit exec <provider> <command...>`
|
|
186
|
+
|
|
187
|
+
Execute a command using the current active profile.
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
orbit use vercel personal
|
|
191
|
+
orbit exec vercel deploy --prod
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### `orbit run <provider> <profile> <command...>`
|
|
195
|
+
|
|
196
|
+
Run a command with a specific profile without changing the active selection.
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
orbit run vercel company deploy --prod
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### `orbit list`
|
|
203
|
+
|
|
204
|
+
Display all profiles with provider, name, email, and active status.
|
|
205
|
+
|
|
206
|
+
```bash
|
|
207
|
+
orbit list
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### `orbit current`
|
|
211
|
+
|
|
212
|
+
Show the currently active profile for each provider.
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
orbit current
|
|
216
|
+
# ✔ vercel: personal
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### `orbit remove <provider> <profile>`
|
|
220
|
+
|
|
221
|
+
Remove a profile, its stored token, and auth snapshot.
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
orbit remove vercel staging
|
|
225
|
+
# ✔ Profile "staging" removed from vercel.
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## 🏗️ How It Works
|
|
231
|
+
|
|
232
|
+
### Auth File Swap (the magic ✨)
|
|
233
|
+
|
|
234
|
+
When you run `orbit use vercel company`, Orbit:
|
|
235
|
+
|
|
236
|
+
1. **Saves** the current profile's `auth.json` to `~/.orbit/auth/vercel/personal.json`
|
|
237
|
+
2. **Restores** the target profile's `auth.json` from `~/.orbit/auth/vercel/company.json`
|
|
238
|
+
3. **Updates** the config to mark `company` as current
|
|
239
|
+
|
|
240
|
+
This means `vercel` CLI reads its own native `auth.json` — no environment variable hacks, no token injection. The CLI thinks you just logged in normally.
|
|
241
|
+
|
|
242
|
+
### Storage Architecture
|
|
243
|
+
|
|
244
|
+
```
|
|
245
|
+
~/.orbit/
|
|
246
|
+
├── config.json # Profile names, current selections, metadata
|
|
247
|
+
└── auth/
|
|
248
|
+
└── vercel/
|
|
249
|
+
├── personal.json # Auth snapshot for personal profile
|
|
250
|
+
└── company.json # Auth snapshot for company profile
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
Tokens are additionally stored in your **OS keychain** via Keytar as a secure backup.
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## 🖥️ Cross-Platform Support
|
|
258
|
+
|
|
259
|
+
| Feature | macOS | Linux | Windows |
|
|
260
|
+
|---------|-------|-------|---------|
|
|
261
|
+
| Keychain storage | ✅ Keychain | ✅ libsecret | ✅ Credential Vault |
|
|
262
|
+
| Auth file swap | ✅ | ✅ | ✅ |
|
|
263
|
+
| CLI execution | ✅ | ✅ | ✅ |
|
|
264
|
+
| Config storage | `~/.orbit/` | `~/.orbit/` | `%USERPROFILE%\.orbit\` |
|
|
265
|
+
|
|
266
|
+
Orbit uses `os.homedir()`, `path.join()`, and `execa` — no platform-specific hacks.
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
270
|
+
## 🔐 Security
|
|
271
|
+
|
|
272
|
+
### Token Storage
|
|
273
|
+
|
|
274
|
+
All tokens are stored in your OS's native keychain:
|
|
275
|
+
|
|
276
|
+
| OS | Backend |
|
|
277
|
+
|----|---------|
|
|
278
|
+
| macOS | Keychain |
|
|
279
|
+
| Linux | libsecret (GNOME Keyring) |
|
|
280
|
+
| Windows | Credential Vault |
|
|
281
|
+
|
|
282
|
+
**Tokens are never:**
|
|
283
|
+
- Written to plain text config files
|
|
284
|
+
- Logged to stdout/stderr
|
|
285
|
+
- Stored in environment variables permanently
|
|
286
|
+
|
|
287
|
+
### Config File
|
|
288
|
+
|
|
289
|
+
Only metadata is stored in `~/.orbit/config.json`:
|
|
290
|
+
|
|
291
|
+
```json
|
|
292
|
+
{
|
|
293
|
+
"providers": {
|
|
294
|
+
"vercel": {
|
|
295
|
+
"profiles": ["personal", "company"],
|
|
296
|
+
"current": "personal",
|
|
297
|
+
"metadata": {
|
|
298
|
+
"personal": { "email": "you@gmail.com" },
|
|
299
|
+
"company": { "email": "work@company.com" }
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
No secrets. No tokens. Just profile names, selections, and display metadata.
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
## 🛠️ Tech Stack
|
|
311
|
+
|
|
312
|
+
| Component | Technology |
|
|
313
|
+
|-----------|------------|
|
|
314
|
+
| Runtime | Node.js 18+ |
|
|
315
|
+
| Language | TypeScript (strict mode) |
|
|
316
|
+
| Module System | ESM only |
|
|
317
|
+
| CLI Framework | Commander.js |
|
|
318
|
+
| Styling | Chalk |
|
|
319
|
+
| Spinners | Ora |
|
|
320
|
+
| Process Execution | Execa |
|
|
321
|
+
| Secure Storage | Keytar |
|
|
322
|
+
| Validation | Zod |
|
|
323
|
+
| Bundler | tsup |
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
## 🛠️ Development
|
|
328
|
+
|
|
329
|
+
```bash
|
|
330
|
+
# Clone and install
|
|
331
|
+
git clone https://github.com/appuo/orbit.git
|
|
332
|
+
cd orbit
|
|
333
|
+
pnpm install
|
|
334
|
+
|
|
335
|
+
# Build
|
|
336
|
+
pnpm build
|
|
337
|
+
|
|
338
|
+
# Type check
|
|
339
|
+
pnpm typecheck
|
|
340
|
+
|
|
341
|
+
# Watch mode
|
|
342
|
+
pnpm dev
|
|
343
|
+
|
|
344
|
+
# Link for local testing
|
|
345
|
+
npm link
|
|
346
|
+
orbit list
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
---
|
|
350
|
+
|
|
351
|
+
## 🗺️ Roadmap
|
|
352
|
+
|
|
353
|
+
- [ ] **AWS Provider** — Support for AWS CLI with named profiles
|
|
354
|
+
- [ ] **GCP Provider** — Google Cloud SDK integration
|
|
355
|
+
- [ ] **Azure Provider** — Azure CLI support
|
|
356
|
+
- [ ] **Netlify Provider** — Netlify CLI support
|
|
357
|
+
- [ ] **Profile import/export** — Migrate profiles between machines
|
|
358
|
+
- [ ] **Token rotation** — Automatic token refresh support
|
|
359
|
+
- [ ] **Shell completions** — Auto-complete for bash, zsh, fish, PowerShell
|
|
360
|
+
- [ ] **Plugin system** — Community-driven provider plugins
|
|
361
|
+
|
|
362
|
+
---
|
|
363
|
+
|
|
364
|
+
## 🤝 Contributing
|
|
365
|
+
|
|
366
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
|
367
|
+
|
|
368
|
+
---
|
|
369
|
+
|
|
370
|
+
## 📄 License
|
|
371
|
+
|
|
372
|
+
MIT — see [LICENSE](LICENSE) for details.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,719 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
#!/usr/bin/env node
|
|
3
|
+
|
|
4
|
+
// src/index.ts
|
|
5
|
+
import { Command as Command8 } from "commander";
|
|
6
|
+
|
|
7
|
+
// src/providers/provider.interface.ts
|
|
8
|
+
var providers = /* @__PURE__ */ new Map();
|
|
9
|
+
var registerProvider = (provider) => {
|
|
10
|
+
providers.set(provider.name.toLowerCase(), provider);
|
|
11
|
+
};
|
|
12
|
+
var getProvider = (name) => {
|
|
13
|
+
const provider = providers.get(name.toLowerCase());
|
|
14
|
+
if (!provider) {
|
|
15
|
+
const available = Array.from(providers.keys()).join(", ");
|
|
16
|
+
throw new Error(
|
|
17
|
+
`Unknown provider "${name}". Available providers: ${available || "none"}`
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
return provider;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
// src/providers/vercel.ts
|
|
24
|
+
import fs2 from "fs";
|
|
25
|
+
import path2 from "path";
|
|
26
|
+
import os2 from "os";
|
|
27
|
+
|
|
28
|
+
// src/utils/paths.ts
|
|
29
|
+
import os from "os";
|
|
30
|
+
import path from "path";
|
|
31
|
+
import fs from "fs";
|
|
32
|
+
var getConfigDir = () => {
|
|
33
|
+
return path.join(os.homedir(), ".orbit");
|
|
34
|
+
};
|
|
35
|
+
var getConfigPath = () => {
|
|
36
|
+
return path.join(getConfigDir(), "config.json");
|
|
37
|
+
};
|
|
38
|
+
var ensureConfigDir = () => {
|
|
39
|
+
const dir = getConfigDir();
|
|
40
|
+
if (!fs.existsSync(dir)) {
|
|
41
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
var getAuthDir = (provider) => {
|
|
45
|
+
return path.join(getConfigDir(), "auth", provider);
|
|
46
|
+
};
|
|
47
|
+
var getAuthFilePath = (provider, profile) => {
|
|
48
|
+
return path.join(getAuthDir(provider), `${profile}.json`);
|
|
49
|
+
};
|
|
50
|
+
var ensureAuthDir = (provider) => {
|
|
51
|
+
const dir = getAuthDir(provider);
|
|
52
|
+
if (!fs.existsSync(dir)) {
|
|
53
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// src/providers/vercel.ts
|
|
58
|
+
var getVercelAuthPath = () => {
|
|
59
|
+
const platform = os2.platform();
|
|
60
|
+
const home = os2.homedir();
|
|
61
|
+
if (platform === "darwin") {
|
|
62
|
+
return path2.join(
|
|
63
|
+
home,
|
|
64
|
+
"Library",
|
|
65
|
+
"Application Support",
|
|
66
|
+
"com.vercel.cli",
|
|
67
|
+
"auth.json"
|
|
68
|
+
);
|
|
69
|
+
} else if (platform === "win32") {
|
|
70
|
+
const appData = process.env["APPDATA"] || path2.join(home, "AppData", "Roaming");
|
|
71
|
+
return path2.join(appData, "com.vercel.cli", "auth.json");
|
|
72
|
+
} else {
|
|
73
|
+
const xdgDataHome = process.env["XDG_DATA_HOME"] || path2.join(home, ".local", "share");
|
|
74
|
+
return path2.join(xdgDataHome, "com.vercel.cli", "auth.json");
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
var vercelProvider = {
|
|
78
|
+
name: "vercel",
|
|
79
|
+
getEnvVar() {
|
|
80
|
+
return "VERCEL_TOKEN";
|
|
81
|
+
},
|
|
82
|
+
getCliName() {
|
|
83
|
+
return "vercel";
|
|
84
|
+
},
|
|
85
|
+
validateToken(token) {
|
|
86
|
+
return token.length > 20;
|
|
87
|
+
},
|
|
88
|
+
async getStoredToken() {
|
|
89
|
+
try {
|
|
90
|
+
const configPath = getVercelAuthPath();
|
|
91
|
+
if (configPath && fs2.existsSync(configPath)) {
|
|
92
|
+
const content = fs2.readFileSync(configPath, "utf-8");
|
|
93
|
+
const config = JSON.parse(content);
|
|
94
|
+
if (config.token && typeof config.token === "string") {
|
|
95
|
+
return config.token;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
} catch {
|
|
99
|
+
}
|
|
100
|
+
return null;
|
|
101
|
+
},
|
|
102
|
+
async getUserEmail(token) {
|
|
103
|
+
try {
|
|
104
|
+
const response = await fetch("https://api.vercel.com/v2/user", {
|
|
105
|
+
headers: {
|
|
106
|
+
Authorization: `Bearer ${token}`
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
if (!response.ok) return void 0;
|
|
110
|
+
const data = await response.json();
|
|
111
|
+
return data.user.email;
|
|
112
|
+
} catch {
|
|
113
|
+
return void 0;
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
async login() {
|
|
117
|
+
try {
|
|
118
|
+
const { execa: execa3 } = await import("execa");
|
|
119
|
+
await execa3("vercel", ["login"], { stdio: "inherit" });
|
|
120
|
+
return true;
|
|
121
|
+
} catch {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
getAuthConfigPath() {
|
|
126
|
+
return getVercelAuthPath();
|
|
127
|
+
},
|
|
128
|
+
async captureAuth(profile) {
|
|
129
|
+
const authPath = getVercelAuthPath();
|
|
130
|
+
if (!authPath || !fs2.existsSync(authPath)) return;
|
|
131
|
+
ensureAuthDir("vercel");
|
|
132
|
+
const destPath = getAuthFilePath("vercel", profile);
|
|
133
|
+
fs2.copyFileSync(authPath, destPath);
|
|
134
|
+
},
|
|
135
|
+
async restoreAuth(profile) {
|
|
136
|
+
const sourcePath = getAuthFilePath("vercel", profile);
|
|
137
|
+
if (!fs2.existsSync(sourcePath)) return false;
|
|
138
|
+
const authPath = getVercelAuthPath();
|
|
139
|
+
if (!authPath) return false;
|
|
140
|
+
const authDir = path2.dirname(authPath);
|
|
141
|
+
if (!fs2.existsSync(authDir)) {
|
|
142
|
+
fs2.mkdirSync(authDir, { recursive: true });
|
|
143
|
+
}
|
|
144
|
+
fs2.copyFileSync(sourcePath, authPath);
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
// src/commands/add.ts
|
|
150
|
+
import { Command } from "commander";
|
|
151
|
+
import ora from "ora";
|
|
152
|
+
import chalk2 from "chalk";
|
|
153
|
+
import readline from "readline";
|
|
154
|
+
|
|
155
|
+
// src/storage/keychain.ts
|
|
156
|
+
import keytar from "keytar";
|
|
157
|
+
var SERVICE_NAME = "appuo-orbit";
|
|
158
|
+
var getKey = (provider, profile) => {
|
|
159
|
+
return `${provider}:${profile}`;
|
|
160
|
+
};
|
|
161
|
+
var storeToken = async (provider, profile, token) => {
|
|
162
|
+
await keytar.setPassword(SERVICE_NAME, getKey(provider, profile), token);
|
|
163
|
+
};
|
|
164
|
+
var getToken = async (provider, profile) => {
|
|
165
|
+
return keytar.getPassword(SERVICE_NAME, getKey(provider, profile));
|
|
166
|
+
};
|
|
167
|
+
var deleteToken = async (provider, profile) => {
|
|
168
|
+
return keytar.deletePassword(SERVICE_NAME, getKey(provider, profile));
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
// src/storage/configStore.ts
|
|
172
|
+
import fs3 from "fs";
|
|
173
|
+
|
|
174
|
+
// src/types/config.ts
|
|
175
|
+
import { z } from "zod";
|
|
176
|
+
var ProviderConfigSchema = z.object({
|
|
177
|
+
profiles: z.array(z.string()),
|
|
178
|
+
current: z.string().optional(),
|
|
179
|
+
metadata: z.record(z.string(), z.object({ email: z.string().optional() })).optional()
|
|
180
|
+
});
|
|
181
|
+
var OrbitConfigSchema = z.object({
|
|
182
|
+
providers: z.record(z.string(), ProviderConfigSchema)
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// src/storage/configStore.ts
|
|
186
|
+
var defaultConfig = {
|
|
187
|
+
providers: {}
|
|
188
|
+
};
|
|
189
|
+
var loadConfig = () => {
|
|
190
|
+
const configPath = getConfigPath();
|
|
191
|
+
if (!fs3.existsSync(configPath)) {
|
|
192
|
+
ensureConfigDir();
|
|
193
|
+
fs3.writeFileSync(configPath, JSON.stringify(defaultConfig, null, 2), "utf-8");
|
|
194
|
+
return { ...defaultConfig };
|
|
195
|
+
}
|
|
196
|
+
const raw = fs3.readFileSync(configPath, "utf-8");
|
|
197
|
+
const parsed = JSON.parse(raw);
|
|
198
|
+
return OrbitConfigSchema.parse(parsed);
|
|
199
|
+
};
|
|
200
|
+
var saveConfig = (config) => {
|
|
201
|
+
ensureConfigDir();
|
|
202
|
+
const configPath = getConfigPath();
|
|
203
|
+
fs3.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
204
|
+
};
|
|
205
|
+
var addProfile = (provider, profile, metadata) => {
|
|
206
|
+
const config = loadConfig();
|
|
207
|
+
if (!config.providers[provider]) {
|
|
208
|
+
config.providers[provider] = { profiles: [], current: void 0 };
|
|
209
|
+
}
|
|
210
|
+
const providerConfig = config.providers[provider];
|
|
211
|
+
if (providerConfig && !providerConfig.profiles.includes(profile)) {
|
|
212
|
+
providerConfig.profiles.push(profile);
|
|
213
|
+
}
|
|
214
|
+
if (metadata) {
|
|
215
|
+
if (!providerConfig.metadata) {
|
|
216
|
+
providerConfig.metadata = {};
|
|
217
|
+
}
|
|
218
|
+
providerConfig.metadata[profile] = metadata;
|
|
219
|
+
}
|
|
220
|
+
saveConfig(config);
|
|
221
|
+
};
|
|
222
|
+
var removeProfile = (provider, profile) => {
|
|
223
|
+
const config = loadConfig();
|
|
224
|
+
const providerConfig = config.providers[provider];
|
|
225
|
+
if (!providerConfig) return;
|
|
226
|
+
providerConfig.profiles = providerConfig.profiles.filter((p) => p !== profile);
|
|
227
|
+
if (providerConfig.current === profile) {
|
|
228
|
+
providerConfig.current = void 0;
|
|
229
|
+
}
|
|
230
|
+
if (providerConfig.profiles.length === 0) {
|
|
231
|
+
delete config.providers[provider];
|
|
232
|
+
}
|
|
233
|
+
saveConfig(config);
|
|
234
|
+
};
|
|
235
|
+
var setCurrentProfile = (provider, profile) => {
|
|
236
|
+
const config = loadConfig();
|
|
237
|
+
const providerConfig = config.providers[provider];
|
|
238
|
+
if (!providerConfig) {
|
|
239
|
+
throw new Error(`No profiles found for provider "${provider}".`);
|
|
240
|
+
}
|
|
241
|
+
if (!providerConfig.profiles.includes(profile)) {
|
|
242
|
+
throw new Error(`Profile "${profile}" does not exist for provider "${provider}".`);
|
|
243
|
+
}
|
|
244
|
+
providerConfig.current = profile;
|
|
245
|
+
saveConfig(config);
|
|
246
|
+
};
|
|
247
|
+
var getCurrentProfile = (provider) => {
|
|
248
|
+
const config = loadConfig();
|
|
249
|
+
return config.providers[provider]?.current;
|
|
250
|
+
};
|
|
251
|
+
var profileExists = (provider, profile) => {
|
|
252
|
+
const config = loadConfig();
|
|
253
|
+
return config.providers[provider]?.profiles.includes(profile) ?? false;
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
// src/utils/validator.ts
|
|
257
|
+
import { z as z2 } from "zod";
|
|
258
|
+
var nameSchema = z2.string().min(1, "Name cannot be empty").max(64, "Name is too long").regex(/^[a-zA-Z0-9_-]+$/, "Name must be alphanumeric (hyphens and underscores allowed)");
|
|
259
|
+
var validateProviderName = (name) => {
|
|
260
|
+
return nameSchema.parse(name);
|
|
261
|
+
};
|
|
262
|
+
var validateProfileName = (name) => {
|
|
263
|
+
return nameSchema.parse(name);
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
// src/utils/logger.ts
|
|
267
|
+
import chalk from "chalk";
|
|
268
|
+
var write = (stream, message) => {
|
|
269
|
+
stream.write(`${message}
|
|
270
|
+
`);
|
|
271
|
+
};
|
|
272
|
+
var logger = {
|
|
273
|
+
info: (message) => {
|
|
274
|
+
write(process.stdout, chalk.blue("\u2139") + ` ${message}`);
|
|
275
|
+
},
|
|
276
|
+
success: (message) => {
|
|
277
|
+
write(process.stdout, chalk.green("\u2714") + ` ${message}`);
|
|
278
|
+
},
|
|
279
|
+
warn: (message) => {
|
|
280
|
+
write(process.stderr, chalk.yellow("\u26A0") + ` ${message}`);
|
|
281
|
+
},
|
|
282
|
+
error: (message) => {
|
|
283
|
+
write(process.stderr, chalk.red("\u2716") + ` ${message}`);
|
|
284
|
+
},
|
|
285
|
+
plain: (message) => {
|
|
286
|
+
write(process.stdout, message);
|
|
287
|
+
},
|
|
288
|
+
newline: () => {
|
|
289
|
+
write(process.stdout, "");
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
// src/utils/errorHandler.ts
|
|
294
|
+
import { ZodError } from "zod";
|
|
295
|
+
var handleError = (error) => {
|
|
296
|
+
if (error instanceof ZodError) {
|
|
297
|
+
const messages = error.errors.map((e) => e.message).join(", ");
|
|
298
|
+
logger.error(`Validation error: ${messages}`);
|
|
299
|
+
process.exit(1);
|
|
300
|
+
}
|
|
301
|
+
if (error instanceof Error) {
|
|
302
|
+
logger.error(error.message);
|
|
303
|
+
if (process.env["ORBIT_DEBUG"] === "1") {
|
|
304
|
+
logger.plain(error.stack ?? "");
|
|
305
|
+
}
|
|
306
|
+
process.exit(1);
|
|
307
|
+
}
|
|
308
|
+
logger.error("An unexpected error occurred.");
|
|
309
|
+
process.exit(1);
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
// src/commands/add.ts
|
|
313
|
+
var promptHiddenInput = (prompt) => {
|
|
314
|
+
return new Promise((resolve, reject) => {
|
|
315
|
+
const rl = readline.createInterface({
|
|
316
|
+
input: process.stdin,
|
|
317
|
+
output: process.stdout
|
|
318
|
+
});
|
|
319
|
+
if (process.stdin.isTTY) {
|
|
320
|
+
process.stdout.write(prompt);
|
|
321
|
+
const stdin = process.stdin;
|
|
322
|
+
stdin.setRawMode(true);
|
|
323
|
+
stdin.resume();
|
|
324
|
+
let input = "";
|
|
325
|
+
const onData = (data) => {
|
|
326
|
+
const char = data.toString("utf-8");
|
|
327
|
+
if (char === "\n" || char === "\r" || char === "") {
|
|
328
|
+
stdin.setRawMode(false);
|
|
329
|
+
stdin.removeListener("data", onData);
|
|
330
|
+
stdin.pause();
|
|
331
|
+
rl.close();
|
|
332
|
+
process.stdout.write("\n");
|
|
333
|
+
resolve(input);
|
|
334
|
+
} else if (char === "") {
|
|
335
|
+
stdin.setRawMode(false);
|
|
336
|
+
stdin.removeListener("data", onData);
|
|
337
|
+
stdin.pause();
|
|
338
|
+
rl.close();
|
|
339
|
+
reject(new Error("User cancelled input."));
|
|
340
|
+
} else if (char === "\x7F" || char === "\b") {
|
|
341
|
+
input = input.slice(0, -1);
|
|
342
|
+
} else {
|
|
343
|
+
input += char;
|
|
344
|
+
}
|
|
345
|
+
};
|
|
346
|
+
stdin.on("data", onData);
|
|
347
|
+
} else {
|
|
348
|
+
rl.question(prompt, (answer) => {
|
|
349
|
+
rl.close();
|
|
350
|
+
resolve(answer);
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
});
|
|
354
|
+
};
|
|
355
|
+
var confirmInput = (prompt) => {
|
|
356
|
+
return new Promise((resolve) => {
|
|
357
|
+
const rl = readline.createInterface({
|
|
358
|
+
input: process.stdin,
|
|
359
|
+
output: process.stdout
|
|
360
|
+
});
|
|
361
|
+
rl.question(prompt, (answer) => {
|
|
362
|
+
rl.close();
|
|
363
|
+
const normalized = answer.trim().toLowerCase();
|
|
364
|
+
resolve(normalized === "y" || normalized === "yes" || normalized === "");
|
|
365
|
+
});
|
|
366
|
+
});
|
|
367
|
+
};
|
|
368
|
+
var addCommand = new Command("add").description("Add a new cloud provider profile").argument("<provider>", "Cloud provider name (e.g., vercel)").argument("<profile>", "Profile name (e.g., personal, company)").action(async (providerName, profileName) => {
|
|
369
|
+
try {
|
|
370
|
+
const validProvider = validateProviderName(providerName);
|
|
371
|
+
const validProfile = validateProfileName(profileName);
|
|
372
|
+
const provider = getProvider(validProvider);
|
|
373
|
+
if (profileExists(validProvider, validProfile)) {
|
|
374
|
+
logger.warn(`Profile "${validProfile}" already exists for ${provider.name}. It will be overwritten.`);
|
|
375
|
+
}
|
|
376
|
+
let token = "";
|
|
377
|
+
let storedTokenFound = false;
|
|
378
|
+
if (provider.getStoredToken) {
|
|
379
|
+
const spinner2 = ora(`Checking for existing ${provider.name} credentials...`).start();
|
|
380
|
+
try {
|
|
381
|
+
const storedToken = await provider.getStoredToken();
|
|
382
|
+
spinner2.stop();
|
|
383
|
+
if (storedToken) {
|
|
384
|
+
storedTokenFound = true;
|
|
385
|
+
let emailDisplay = "";
|
|
386
|
+
if (provider.getUserEmail) {
|
|
387
|
+
try {
|
|
388
|
+
const email = await provider.getUserEmail(storedToken);
|
|
389
|
+
if (email) emailDisplay = ` (${chalk2.cyan(email)})`;
|
|
390
|
+
} catch {
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
const useStored = await confirmInput(
|
|
394
|
+
`Found existing token${emailDisplay} from ${provider.name} CLI. Use this? (Y/n) `
|
|
395
|
+
);
|
|
396
|
+
if (useStored) {
|
|
397
|
+
token = storedToken;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
} catch {
|
|
401
|
+
spinner2.stop();
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
if (!token && provider.login) {
|
|
405
|
+
const promptMsg = storedTokenFound ? `Login to a different account with ${provider.name} CLI? (Y/n) ` : `No existing credentials found. Login with ${provider.name} CLI now? (Y/n) `;
|
|
406
|
+
const shouldLogin = await confirmInput(promptMsg);
|
|
407
|
+
if (shouldLogin) {
|
|
408
|
+
const loginSuccess = await provider.login();
|
|
409
|
+
if (loginSuccess) {
|
|
410
|
+
if (provider.getStoredToken) {
|
|
411
|
+
const spinner2 = ora("Checking for new credentials...").start();
|
|
412
|
+
try {
|
|
413
|
+
const storedToken = await provider.getStoredToken();
|
|
414
|
+
spinner2.stop();
|
|
415
|
+
if (storedToken) {
|
|
416
|
+
token = storedToken;
|
|
417
|
+
logger.success("New credentials found!");
|
|
418
|
+
}
|
|
419
|
+
} catch {
|
|
420
|
+
spinner2.stop();
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
} else {
|
|
424
|
+
logger.warn("Login failed or was cancelled.");
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
if (!token) {
|
|
429
|
+
token = await promptHiddenInput(`\u{1F511} Enter token for ${provider.name}/${validProfile}: `);
|
|
430
|
+
}
|
|
431
|
+
if (!token.trim()) {
|
|
432
|
+
logger.error("Token cannot be empty.");
|
|
433
|
+
process.exit(1);
|
|
434
|
+
}
|
|
435
|
+
const spinner = ora("Validating token...").start();
|
|
436
|
+
if (!provider.validateToken(token.trim())) {
|
|
437
|
+
spinner.fail("Token validation failed.");
|
|
438
|
+
logger.error(`Invalid token format for ${provider.name}. Please check your token and try again.`);
|
|
439
|
+
process.exit(1);
|
|
440
|
+
}
|
|
441
|
+
spinner.text = "Storing token securely...";
|
|
442
|
+
await storeToken(validProvider, validProfile, token.trim());
|
|
443
|
+
if (provider.getUserEmail) {
|
|
444
|
+
spinner.text = "Fetching user info...";
|
|
445
|
+
try {
|
|
446
|
+
const email = await provider.getUserEmail(token.trim());
|
|
447
|
+
if (email) {
|
|
448
|
+
spinner.text = `Found user: ${email}. Updating configuration...`;
|
|
449
|
+
addProfile(validProvider, validProfile, { email });
|
|
450
|
+
} else {
|
|
451
|
+
addProfile(validProvider, validProfile);
|
|
452
|
+
}
|
|
453
|
+
} catch {
|
|
454
|
+
addProfile(validProvider, validProfile);
|
|
455
|
+
}
|
|
456
|
+
} else {
|
|
457
|
+
addProfile(validProvider, validProfile);
|
|
458
|
+
}
|
|
459
|
+
spinner.succeed(`Profile "${validProfile}" added for ${provider.name}.`);
|
|
460
|
+
if (provider.captureAuth) {
|
|
461
|
+
try {
|
|
462
|
+
await provider.captureAuth(validProfile);
|
|
463
|
+
} catch {
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
} catch (error) {
|
|
467
|
+
handleError(error);
|
|
468
|
+
}
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
// src/commands/list.ts
|
|
472
|
+
import { Command as Command2 } from "commander";
|
|
473
|
+
import chalk3 from "chalk";
|
|
474
|
+
var listCommand = new Command2("list").description("List all profiles across providers").action(() => {
|
|
475
|
+
try {
|
|
476
|
+
const config = loadConfig();
|
|
477
|
+
const providers2 = Object.keys(config.providers);
|
|
478
|
+
if (providers2.length === 0) {
|
|
479
|
+
logger.info('No profiles configured yet. Use "orbit add <provider> <profile>" to get started.');
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
logger.newline();
|
|
483
|
+
logger.plain(
|
|
484
|
+
` ${chalk3.bold.underline("Provider".padEnd(15))} ${chalk3.bold.underline("Profile".padEnd(20))} ${chalk3.bold.underline("Email".padEnd(30))} ${chalk3.bold.underline("Status")}`
|
|
485
|
+
);
|
|
486
|
+
logger.newline();
|
|
487
|
+
for (const providerName of providers2) {
|
|
488
|
+
const providerConfig = config.providers[providerName];
|
|
489
|
+
if (!providerConfig) continue;
|
|
490
|
+
for (const profile of providerConfig.profiles) {
|
|
491
|
+
const isCurrent = providerConfig.current === profile;
|
|
492
|
+
const email = providerConfig.metadata?.[profile]?.email ?? "-";
|
|
493
|
+
const status = isCurrent ? chalk3.green("\u2605 current") : chalk3.dim("\u2014");
|
|
494
|
+
const providerDisplay = isCurrent ? chalk3.cyan(providerName.padEnd(15)) : chalk3.white(providerName.padEnd(15));
|
|
495
|
+
const profileDisplay = isCurrent ? chalk3.cyan(profile.padEnd(20)) : chalk3.white(profile.padEnd(20));
|
|
496
|
+
const emailDisplay = isCurrent ? chalk3.cyan(email.padEnd(30)) : chalk3.gray(email.padEnd(30));
|
|
497
|
+
logger.plain(` ${providerDisplay} ${profileDisplay} ${emailDisplay} ${status}`);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
logger.newline();
|
|
501
|
+
} catch (error) {
|
|
502
|
+
handleError(error);
|
|
503
|
+
}
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
// src/commands/remove.ts
|
|
507
|
+
import { Command as Command3 } from "commander";
|
|
508
|
+
import ora2 from "ora";
|
|
509
|
+
var removeCommand = new Command3("remove").description("Remove a cloud provider profile").argument("<provider>", "Cloud provider name").argument("<profile>", "Profile name to remove").action(async (providerName, profileName) => {
|
|
510
|
+
try {
|
|
511
|
+
const validProvider = validateProviderName(providerName);
|
|
512
|
+
const validProfile = validateProfileName(profileName);
|
|
513
|
+
getProvider(validProvider);
|
|
514
|
+
if (!profileExists(validProvider, validProfile)) {
|
|
515
|
+
logger.error(`Profile "${validProfile}" does not exist for provider "${validProvider}".`);
|
|
516
|
+
process.exit(1);
|
|
517
|
+
}
|
|
518
|
+
const spinner = ora2("Removing profile...").start();
|
|
519
|
+
spinner.text = "Deleting token from secure storage...";
|
|
520
|
+
await deleteToken(validProvider, validProfile);
|
|
521
|
+
spinner.text = "Updating configuration...";
|
|
522
|
+
removeProfile(validProvider, validProfile);
|
|
523
|
+
spinner.succeed(`Profile "${validProfile}" removed from ${validProvider}.`);
|
|
524
|
+
} catch (error) {
|
|
525
|
+
handleError(error);
|
|
526
|
+
}
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
// src/commands/use.ts
|
|
530
|
+
import { Command as Command4 } from "commander";
|
|
531
|
+
import ora3 from "ora";
|
|
532
|
+
var useCommand = new Command4("use").description("Set the current active profile for a provider").argument("<provider>", "Cloud provider name").argument("<profile>", "Profile name to activate").action(async (providerName, profileName) => {
|
|
533
|
+
try {
|
|
534
|
+
const validProvider = validateProviderName(providerName);
|
|
535
|
+
const validProfile = validateProfileName(profileName);
|
|
536
|
+
const provider = getProvider(validProvider);
|
|
537
|
+
if (!profileExists(validProvider, validProfile)) {
|
|
538
|
+
logger.error(`Profile "${validProfile}" does not exist for provider "${provider.name}".`);
|
|
539
|
+
process.exit(1);
|
|
540
|
+
}
|
|
541
|
+
const spinner = ora3("Switching profile...").start();
|
|
542
|
+
const currentProfile = getCurrentProfile(validProvider);
|
|
543
|
+
if (currentProfile && currentProfile !== validProfile && provider.captureAuth) {
|
|
544
|
+
try {
|
|
545
|
+
await provider.captureAuth(currentProfile);
|
|
546
|
+
} catch {
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
if (provider.restoreAuth) {
|
|
550
|
+
const restored = await provider.restoreAuth(validProfile);
|
|
551
|
+
if (restored) {
|
|
552
|
+
spinner.text = "Auth credentials restored...";
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
setCurrentProfile(validProvider, validProfile);
|
|
556
|
+
spinner.succeed(`Now using profile "${validProfile}" for ${provider.name}.`);
|
|
557
|
+
} catch (error) {
|
|
558
|
+
handleError(error);
|
|
559
|
+
}
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
// src/commands/run.ts
|
|
563
|
+
import { Command as Command5 } from "commander";
|
|
564
|
+
import { execa } from "execa";
|
|
565
|
+
var runCommand = new Command5("run").description("Run a command with a specific provider profile").argument("<provider>", "Cloud provider name").argument("<profile>", "Profile name to use").argument("<args...>", "Command arguments to pass to the provider CLI").passThroughOptions().allowUnknownOption().action(async (providerName, profileName, args) => {
|
|
566
|
+
try {
|
|
567
|
+
const validProvider = validateProviderName(providerName);
|
|
568
|
+
const validProfile = validateProfileName(profileName);
|
|
569
|
+
const provider = getProvider(validProvider);
|
|
570
|
+
if (!profileExists(validProvider, validProfile)) {
|
|
571
|
+
logger.error(`Profile "${validProfile}" does not exist for provider "${provider.name}".`);
|
|
572
|
+
process.exit(1);
|
|
573
|
+
}
|
|
574
|
+
logger.info(`Running as ${provider.name}/${validProfile}...`);
|
|
575
|
+
const currentProfile = getCurrentProfile(validProvider);
|
|
576
|
+
let swapped = false;
|
|
577
|
+
if (provider.restoreAuth) {
|
|
578
|
+
if (currentProfile && currentProfile !== validProfile && provider.captureAuth) {
|
|
579
|
+
try {
|
|
580
|
+
await provider.captureAuth(currentProfile);
|
|
581
|
+
} catch {
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
swapped = await provider.restoreAuth(validProfile);
|
|
585
|
+
}
|
|
586
|
+
try {
|
|
587
|
+
if (swapped) {
|
|
588
|
+
const cliName = provider.getCliName();
|
|
589
|
+
const result = await execa(cliName, args, {
|
|
590
|
+
stdio: "inherit",
|
|
591
|
+
reject: false
|
|
592
|
+
});
|
|
593
|
+
process.exitCode = result.exitCode ?? 0;
|
|
594
|
+
} else {
|
|
595
|
+
const token = await getToken(validProvider, validProfile);
|
|
596
|
+
if (!token) {
|
|
597
|
+
logger.error(`No token found for ${provider.name}/${validProfile}. Try adding it again with "orbit add".`);
|
|
598
|
+
process.exit(1);
|
|
599
|
+
}
|
|
600
|
+
const cliName = provider.getCliName();
|
|
601
|
+
const envVar = provider.getEnvVar();
|
|
602
|
+
const result = await execa(cliName, args, {
|
|
603
|
+
env: {
|
|
604
|
+
...process.env,
|
|
605
|
+
[envVar]: token
|
|
606
|
+
},
|
|
607
|
+
stdio: "inherit",
|
|
608
|
+
reject: false
|
|
609
|
+
});
|
|
610
|
+
process.exitCode = result.exitCode ?? 0;
|
|
611
|
+
}
|
|
612
|
+
} finally {
|
|
613
|
+
if (swapped && currentProfile && currentProfile !== validProfile && provider.restoreAuth) {
|
|
614
|
+
try {
|
|
615
|
+
await provider.restoreAuth(currentProfile);
|
|
616
|
+
} catch {
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
process.exit(process.exitCode ?? 0);
|
|
621
|
+
} catch (error) {
|
|
622
|
+
handleError(error);
|
|
623
|
+
}
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
// src/commands/exec.ts
|
|
627
|
+
import { Command as Command6 } from "commander";
|
|
628
|
+
import { execa as execa2 } from "execa";
|
|
629
|
+
var execCommand = new Command6("exec").description("Execute a command using the current active profile").argument("<provider>", "Cloud provider name").argument("<args...>", "Command arguments to pass to the provider CLI").passThroughOptions().allowUnknownOption().action(async (providerName, args) => {
|
|
630
|
+
try {
|
|
631
|
+
const validProvider = validateProviderName(providerName);
|
|
632
|
+
const provider = getProvider(validProvider);
|
|
633
|
+
const currentProfile = getCurrentProfile(validProvider);
|
|
634
|
+
if (!currentProfile) {
|
|
635
|
+
logger.error(
|
|
636
|
+
`No active profile set for ${provider.name}. Use "orbit use ${validProvider} <profile>" first.`
|
|
637
|
+
);
|
|
638
|
+
process.exit(1);
|
|
639
|
+
}
|
|
640
|
+
if (provider.restoreAuth) {
|
|
641
|
+
await provider.restoreAuth(currentProfile);
|
|
642
|
+
}
|
|
643
|
+
const cliName = provider.getCliName();
|
|
644
|
+
logger.info(`Running as ${provider.name}/${currentProfile}...`);
|
|
645
|
+
if (provider.getAuthConfigPath?.()) {
|
|
646
|
+
const result2 = await execa2(cliName, args, {
|
|
647
|
+
stdio: "inherit",
|
|
648
|
+
reject: false
|
|
649
|
+
});
|
|
650
|
+
process.exit(result2.exitCode ?? 0);
|
|
651
|
+
}
|
|
652
|
+
const token = await getToken(validProvider, currentProfile);
|
|
653
|
+
if (!token) {
|
|
654
|
+
logger.error(
|
|
655
|
+
`No token found for ${provider.name}/${currentProfile}. Try adding it again with "orbit add".`
|
|
656
|
+
);
|
|
657
|
+
process.exit(1);
|
|
658
|
+
}
|
|
659
|
+
const envVar = provider.getEnvVar();
|
|
660
|
+
const result = await execa2(cliName, args, {
|
|
661
|
+
env: {
|
|
662
|
+
...process.env,
|
|
663
|
+
[envVar]: token
|
|
664
|
+
},
|
|
665
|
+
stdio: "inherit",
|
|
666
|
+
reject: false
|
|
667
|
+
});
|
|
668
|
+
process.exit(result.exitCode ?? 0);
|
|
669
|
+
} catch (error) {
|
|
670
|
+
handleError(error);
|
|
671
|
+
}
|
|
672
|
+
});
|
|
673
|
+
|
|
674
|
+
// src/commands/current.ts
|
|
675
|
+
import { Command as Command7 } from "commander";
|
|
676
|
+
import chalk4 from "chalk";
|
|
677
|
+
var currentCommand = new Command7("current").description("Show current active profiles").action(() => {
|
|
678
|
+
try {
|
|
679
|
+
const config = loadConfig();
|
|
680
|
+
const providers2 = Object.keys(config.providers);
|
|
681
|
+
if (providers2.length === 0) {
|
|
682
|
+
logger.info("No profiles configured yet.");
|
|
683
|
+
return;
|
|
684
|
+
}
|
|
685
|
+
let hasActive = false;
|
|
686
|
+
for (const providerName of providers2) {
|
|
687
|
+
const providerConfig = config.providers[providerName];
|
|
688
|
+
if (providerConfig?.current) {
|
|
689
|
+
logger.success(
|
|
690
|
+
`${chalk4.bold(providerName)}: ${chalk4.cyan(providerConfig.current)}`
|
|
691
|
+
);
|
|
692
|
+
hasActive = true;
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
if (!hasActive) {
|
|
696
|
+
logger.info('No active profiles set. Use "orbit use <provider> <profile>" to set one.');
|
|
697
|
+
}
|
|
698
|
+
} catch (error) {
|
|
699
|
+
handleError(error);
|
|
700
|
+
}
|
|
701
|
+
});
|
|
702
|
+
|
|
703
|
+
// src/index.ts
|
|
704
|
+
registerProvider(vercelProvider);
|
|
705
|
+
var program = new Command8();
|
|
706
|
+
program.name("orbit").description("\u{1F30D} A fully CLI-based multi-cloud profile manager").version("1.0.0").enablePositionalOptions();
|
|
707
|
+
program.addCommand(addCommand);
|
|
708
|
+
program.addCommand(listCommand);
|
|
709
|
+
program.addCommand(removeCommand);
|
|
710
|
+
program.addCommand(useCommand);
|
|
711
|
+
program.addCommand(runCommand);
|
|
712
|
+
program.addCommand(execCommand);
|
|
713
|
+
program.addCommand(currentCommand);
|
|
714
|
+
try {
|
|
715
|
+
await program.parseAsync(process.argv);
|
|
716
|
+
} catch (error) {
|
|
717
|
+
handleError(error);
|
|
718
|
+
}
|
|
719
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/providers/provider.interface.ts","../src/providers/vercel.ts","../src/utils/paths.ts","../src/commands/add.ts","../src/storage/keychain.ts","../src/storage/configStore.ts","../src/types/config.ts","../src/utils/validator.ts","../src/utils/logger.ts","../src/utils/errorHandler.ts","../src/commands/list.ts","../src/commands/remove.ts","../src/commands/use.ts","../src/commands/run.ts","../src/commands/exec.ts","../src/commands/current.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { Command } from 'commander';\nimport { registerProvider } from './providers/provider.interface.js';\nimport { vercelProvider } from './providers/vercel.js';\nimport { addCommand } from './commands/add.js';\nimport { listCommand } from './commands/list.js';\nimport { removeCommand } from './commands/remove.js';\nimport { useCommand } from './commands/use.js';\nimport { runCommand } from './commands/run.js';\nimport { execCommand } from './commands/exec.js';\nimport { currentCommand } from './commands/current.js';\nimport { handleError } from './utils/errorHandler.js';\n\n// Register providers\nregisterProvider(vercelProvider);\n\nconst program = new Command();\n\nprogram\n .name('orbit')\n .description('🌍 A fully CLI-based multi-cloud profile manager')\n .version('1.0.0')\n .enablePositionalOptions();\n\nprogram.addCommand(addCommand);\nprogram.addCommand(listCommand);\nprogram.addCommand(removeCommand);\nprogram.addCommand(useCommand);\nprogram.addCommand(runCommand);\nprogram.addCommand(execCommand);\nprogram.addCommand(currentCommand);\n\ntry {\n await program.parseAsync(process.argv);\n} catch (error: unknown) {\n handleError(error);\n}\n","export interface Provider {\n name: string;\n getEnvVar(): string;\n getCliName(): string;\n validateToken(token: string): boolean;\n getStoredToken?(): Promise<string | null>;\n getUserEmail?(token: string): Promise<string | undefined>;\n login?(): Promise<boolean>;\n getAuthConfigPath?(): string | null;\n captureAuth?(profile: string): Promise<void>;\n restoreAuth?(profile: string): Promise<boolean>;\n}\n\nconst providers = new Map<string, Provider>();\n\nexport const registerProvider = (provider: Provider): void => {\n providers.set(provider.name.toLowerCase(), provider);\n};\n\nexport const getProvider = (name: string): Provider => {\n const provider = providers.get(name.toLowerCase());\n\n if (!provider) {\n const available = Array.from(providers.keys()).join(', ');\n throw new Error(\n `Unknown provider \"${name}\". Available providers: ${available || 'none'}`,\n );\n }\n\n return provider;\n};\n\nexport const getAllProviders = (): Provider[] => {\n return Array.from(providers.values());\n};\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport os from 'node:os';\nimport type { Provider } from './provider.interface.js';\nimport { getAuthFilePath, ensureAuthDir } from '../utils/paths.js';\n\nconst getVercelAuthPath = (): string | null => {\n const platform = os.platform();\n const home = os.homedir();\n\n if (platform === 'darwin') {\n return path.join(\n home,\n 'Library',\n 'Application Support',\n 'com.vercel.cli',\n 'auth.json',\n );\n } else if (platform === 'win32') {\n const appData = process.env['APPDATA'] || path.join(home, 'AppData', 'Roaming');\n return path.join(appData, 'com.vercel.cli', 'auth.json');\n } else {\n const xdgDataHome =\n process.env['XDG_DATA_HOME'] || path.join(home, '.local', 'share');\n return path.join(xdgDataHome, 'com.vercel.cli', 'auth.json');\n }\n};\n\nexport const vercelProvider: Provider = {\n name: 'vercel',\n\n getEnvVar(): string {\n return 'VERCEL_TOKEN';\n },\n\n getCliName(): string {\n return 'vercel';\n },\n\n validateToken(token: string): boolean {\n return token.length > 20;\n },\n\n async getStoredToken(): Promise<string | null> {\n try {\n const configPath = getVercelAuthPath();\n if (configPath && fs.existsSync(configPath)) {\n const content = fs.readFileSync(configPath, 'utf-8');\n const config = JSON.parse(content) as { token?: string };\n if (config.token && typeof config.token === 'string') {\n return config.token;\n }\n }\n } catch {\n // Ignore errors\n }\n return null;\n },\n\n async getUserEmail(token: string): Promise<string | undefined> {\n try {\n const response = await fetch('https://api.vercel.com/v2/user', {\n headers: {\n Authorization: `Bearer ${token}`,\n },\n });\n\n if (!response.ok) return undefined;\n\n const data = (await response.json()) as { user: { email: string } };\n return data.user.email;\n } catch {\n return undefined;\n }\n },\n\n async login(): Promise<boolean> {\n try {\n const { execa } = await import('execa');\n await execa('vercel', ['login'], { stdio: 'inherit' });\n return true;\n } catch {\n return false;\n }\n },\n\n getAuthConfigPath(): string | null {\n return getVercelAuthPath();\n },\n\n async captureAuth(profile: string): Promise<void> {\n const authPath = getVercelAuthPath();\n if (!authPath || !fs.existsSync(authPath)) return;\n\n ensureAuthDir('vercel');\n const destPath = getAuthFilePath('vercel', profile);\n fs.copyFileSync(authPath, destPath);\n },\n\n async restoreAuth(profile: string): Promise<boolean> {\n const sourcePath = getAuthFilePath('vercel', profile);\n if (!fs.existsSync(sourcePath)) return false;\n\n const authPath = getVercelAuthPath();\n if (!authPath) return false;\n\n // Ensure the Vercel config directory exists\n const authDir = path.dirname(authPath);\n if (!fs.existsSync(authDir)) {\n fs.mkdirSync(authDir, { recursive: true });\n }\n\n fs.copyFileSync(sourcePath, authPath);\n return true;\n },\n};\n","import os from 'node:os';\nimport path from 'node:path';\nimport fs from 'node:fs';\n\nexport const getConfigDir = (): string => {\n return path.join(os.homedir(), '.orbit');\n};\n\nexport const getConfigPath = (): string => {\n return path.join(getConfigDir(), 'config.json');\n};\n\nexport const ensureConfigDir = (): void => {\n const dir = getConfigDir();\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n};\n\nexport const getAuthDir = (provider: string): string => {\n return path.join(getConfigDir(), 'auth', provider);\n};\n\nexport const getAuthFilePath = (provider: string, profile: string): string => {\n return path.join(getAuthDir(provider), `${profile}.json`);\n};\n\nexport const ensureAuthDir = (provider: string): void => {\n const dir = getAuthDir(provider);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n};\n","import { Command } from 'commander';\nimport ora from 'ora';\nimport chalk from 'chalk';\nimport readline from 'node:readline';\nimport { getProvider } from '../providers/provider.interface.js';\nimport { storeToken } from '../storage/keychain.js';\nimport { addProfile, profileExists } from '../storage/configStore.js';\nimport { validateProviderName, validateProfileName } from '../utils/validator.js';\nimport { logger } from '../utils/logger.js';\nimport { handleError } from '../utils/errorHandler.js';\n\nconst promptHiddenInput = (prompt: string): Promise<string> => {\n return new Promise((resolve, reject) => {\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n // Disable echo for hidden input\n if (process.stdin.isTTY) {\n process.stdout.write(prompt);\n const stdin = process.stdin;\n stdin.setRawMode(true);\n stdin.resume();\n\n let input = '';\n\n const onData = (data: Buffer): void => {\n const char = data.toString('utf-8');\n\n if (char === '\\n' || char === '\\r' || char === '\\u0004') {\n stdin.setRawMode(false);\n stdin.removeListener('data', onData);\n stdin.pause();\n rl.close();\n process.stdout.write('\\n');\n resolve(input);\n } else if (char === '\\u0003') {\n // Ctrl+C\n stdin.setRawMode(false);\n stdin.removeListener('data', onData);\n stdin.pause();\n rl.close();\n reject(new Error('User cancelled input.'));\n } else if (char === '\\u007F' || char === '\\b') {\n // Backspace\n input = input.slice(0, -1);\n } else {\n input += char;\n }\n };\n\n stdin.on('data', onData);\n } else {\n // Non-TTY fallback (piped input)\n rl.question(prompt, (answer) => {\n rl.close();\n resolve(answer);\n });\n }\n });\n};\n\nconst confirmInput = (prompt: string): Promise<boolean> => {\n return new Promise((resolve) => {\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n rl.question(prompt, (answer) => {\n rl.close();\n const normalized = answer.trim().toLowerCase();\n resolve(normalized === 'y' || normalized === 'yes' || normalized === '');\n });\n });\n};\n\nexport const addCommand = new Command('add')\n .description('Add a new cloud provider profile')\n .argument('<provider>', 'Cloud provider name (e.g., vercel)')\n .argument('<profile>', 'Profile name (e.g., personal, company)')\n .action(async (providerName: string, profileName: string) => {\n try {\n const validProvider = validateProviderName(providerName);\n const validProfile = validateProfileName(profileName);\n const provider = getProvider(validProvider);\n\n if (profileExists(validProvider, validProfile)) {\n logger.warn(`Profile \"${validProfile}\" already exists for ${provider.name}. It will be overwritten.`);\n }\n\n let token = '';\n let storedTokenFound = false;\n\n if (provider.getStoredToken) {\n const spinner = ora(`Checking for existing ${provider.name} credentials...`).start();\n try {\n const storedToken = await provider.getStoredToken();\n spinner.stop();\n\n if (storedToken) {\n storedTokenFound = true;\n let emailDisplay = '';\n if (provider.getUserEmail) {\n try {\n const email = await provider.getUserEmail(storedToken);\n if (email) emailDisplay = ` (${chalk.cyan(email)})`;\n } catch {\n // Ignore email fetch error\n }\n }\n\n const useStored = await confirmInput(\n `Found existing token${emailDisplay} from ${provider.name} CLI. Use this? (Y/n) `,\n );\n if (useStored) {\n token = storedToken;\n }\n }\n } catch {\n spinner.stop();\n // Ignore errors during auto-discovery\n }\n }\n\n if (!token && provider.login) {\n const promptMsg = storedTokenFound\n ? `Login to a different account with ${provider.name} CLI? (Y/n) `\n : `No existing credentials found. Login with ${provider.name} CLI now? (Y/n) `;\n\n const shouldLogin = await confirmInput(promptMsg);\n\n if (shouldLogin) {\n const loginSuccess = await provider.login();\n if (loginSuccess) {\n // Re-check for token\n if (provider.getStoredToken) {\n const spinner = ora('Checking for new credentials...').start();\n try {\n const storedToken = await provider.getStoredToken();\n spinner.stop();\n if (storedToken) {\n token = storedToken;\n logger.success('New credentials found!');\n }\n } catch {\n spinner.stop();\n }\n }\n } else {\n logger.warn('Login failed or was cancelled.');\n }\n }\n }\n\n if (!token) {\n token = await promptHiddenInput(`🔑 Enter token for ${provider.name}/${validProfile}: `);\n }\n\n if (!token.trim()) {\n logger.error('Token cannot be empty.');\n process.exit(1);\n }\n\n const spinner = ora('Validating token...').start();\n\n if (!provider.validateToken(token.trim())) {\n spinner.fail('Token validation failed.');\n logger.error(`Invalid token format for ${provider.name}. Please check your token and try again.`);\n process.exit(1);\n }\n\n spinner.text = 'Storing token securely...';\n await storeToken(validProvider, validProfile, token.trim());\n\n if (provider.getUserEmail) {\n spinner.text = 'Fetching user info...';\n try {\n const email = await provider.getUserEmail(token.trim());\n if (email) {\n spinner.text = `Found user: ${email}. Updating configuration...`;\n addProfile(validProvider, validProfile, { email });\n } else {\n addProfile(validProvider, validProfile);\n }\n } catch {\n addProfile(validProvider, validProfile);\n }\n } else {\n addProfile(validProvider, validProfile);\n }\n\n spinner.succeed(`Profile \"${validProfile}\" added for ${provider.name}.`);\n\n // Capture auth.json snapshot for this profile\n if (provider.captureAuth) {\n try {\n await provider.captureAuth(validProfile);\n } catch {\n // Non-critical: auth capture failure doesn't block profile creation\n }\n }\n } catch (error: unknown) {\n handleError(error);\n }\n });\n","import keytar from 'keytar';\n\nconst SERVICE_NAME = 'appuo-orbit';\n\nconst getKey = (provider: string, profile: string): string => {\n return `${provider}:${profile}`;\n};\n\nexport const storeToken = async (\n provider: string,\n profile: string,\n token: string,\n): Promise<void> => {\n await keytar.setPassword(SERVICE_NAME, getKey(provider, profile), token);\n};\n\nexport const getToken = async (\n provider: string,\n profile: string,\n): Promise<string | null> => {\n return keytar.getPassword(SERVICE_NAME, getKey(provider, profile));\n};\n\nexport const deleteToken = async (\n provider: string,\n profile: string,\n): Promise<boolean> => {\n return keytar.deletePassword(SERVICE_NAME, getKey(provider, profile));\n};\n","import fs from 'node:fs';\nimport { OrbitConfigSchema } from '../types/config.js';\nimport type { OrbitConfig } from '../types/config.js';\nimport { getConfigPath, ensureConfigDir } from '../utils/paths.js';\n\nconst defaultConfig: OrbitConfig = {\n providers: {},\n};\n\nexport const loadConfig = (): OrbitConfig => {\n const configPath = getConfigPath();\n\n if (!fs.existsSync(configPath)) {\n ensureConfigDir();\n fs.writeFileSync(configPath, JSON.stringify(defaultConfig, null, 2), 'utf-8');\n return { ...defaultConfig };\n }\n\n const raw = fs.readFileSync(configPath, 'utf-8');\n const parsed: unknown = JSON.parse(raw);\n return OrbitConfigSchema.parse(parsed);\n};\n\nexport const saveConfig = (config: OrbitConfig): void => {\n ensureConfigDir();\n const configPath = getConfigPath();\n fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');\n};\n\nexport const addProfile = (\n provider: string,\n profile: string,\n metadata?: { email?: string },\n): void => {\n const config = loadConfig();\n\n if (!config.providers[provider]) {\n config.providers[provider] = { profiles: [], current: undefined };\n }\n\n const providerConfig = config.providers[provider];\n if (providerConfig && !providerConfig.profiles.includes(profile)) {\n providerConfig.profiles.push(profile);\n }\n\n if (metadata) {\n if (!providerConfig.metadata) {\n providerConfig.metadata = {};\n }\n providerConfig.metadata[profile] = metadata;\n }\n\n saveConfig(config);\n};\n\nexport const removeProfile = (provider: string, profile: string): void => {\n const config = loadConfig();\n const providerConfig = config.providers[provider];\n\n if (!providerConfig) return;\n\n providerConfig.profiles = providerConfig.profiles.filter((p) => p !== profile);\n\n if (providerConfig.current === profile) {\n providerConfig.current = undefined;\n }\n\n if (providerConfig.profiles.length === 0) {\n delete config.providers[provider];\n }\n\n saveConfig(config);\n};\n\nexport const setCurrentProfile = (provider: string, profile: string): void => {\n const config = loadConfig();\n const providerConfig = config.providers[provider];\n\n if (!providerConfig) {\n throw new Error(`No profiles found for provider \"${provider}\".`);\n }\n\n if (!providerConfig.profiles.includes(profile)) {\n throw new Error(`Profile \"${profile}\" does not exist for provider \"${provider}\".`);\n }\n\n providerConfig.current = profile;\n saveConfig(config);\n};\n\nexport const getCurrentProfile = (provider: string): string | undefined => {\n const config = loadConfig();\n return config.providers[provider]?.current;\n};\n\nexport const profileExists = (provider: string, profile: string): boolean => {\n const config = loadConfig();\n return config.providers[provider]?.profiles.includes(profile) ?? false;\n};\n","import { z } from 'zod';\n\nconst ProviderConfigSchema = z.object({\n profiles: z.array(z.string()),\n current: z.string().optional(),\n metadata: z.record(z.string(), z.object({ email: z.string().optional() })).optional(),\n});\n\nexport const OrbitConfigSchema = z.object({\n providers: z.record(z.string(), ProviderConfigSchema),\n});\n\nexport type ProviderConfig = z.infer<typeof ProviderConfigSchema>;\nexport type OrbitConfig = z.infer<typeof OrbitConfigSchema>;\n","import { z } from 'zod';\n\nconst nameSchema = z\n .string()\n .min(1, 'Name cannot be empty')\n .max(64, 'Name is too long')\n .regex(/^[a-zA-Z0-9_-]+$/, 'Name must be alphanumeric (hyphens and underscores allowed)');\n\nexport const validateProviderName = (name: string): string => {\n return nameSchema.parse(name);\n};\n\nexport const validateProfileName = (name: string): string => {\n return nameSchema.parse(name);\n};\n","import chalk from 'chalk';\n\nconst write = (stream: NodeJS.WriteStream, message: string): void => {\n stream.write(`${message}\\n`);\n};\n\nexport const logger = {\n info: (message: string): void => {\n write(process.stdout, chalk.blue('ℹ') + ` ${message}`);\n },\n\n success: (message: string): void => {\n write(process.stdout, chalk.green('✔') + ` ${message}`);\n },\n\n warn: (message: string): void => {\n write(process.stderr, chalk.yellow('⚠') + ` ${message}`);\n },\n\n error: (message: string): void => {\n write(process.stderr, chalk.red('✖') + ` ${message}`);\n },\n\n plain: (message: string): void => {\n write(process.stdout, message);\n },\n\n newline: (): void => {\n write(process.stdout, '');\n },\n};\n","import { logger } from './logger.js';\nimport { ZodError } from 'zod';\n\nexport const handleError = (error: unknown): never => {\n if (error instanceof ZodError) {\n const messages = error.errors.map((e) => e.message).join(', ');\n logger.error(`Validation error: ${messages}`);\n process.exit(1);\n }\n\n if (error instanceof Error) {\n logger.error(error.message);\n\n if (process.env['ORBIT_DEBUG'] === '1') {\n logger.plain(error.stack ?? '');\n }\n\n process.exit(1);\n }\n\n logger.error('An unexpected error occurred.');\n process.exit(1);\n};\n","import { Command } from 'commander';\nimport chalk from 'chalk';\nimport { loadConfig } from '../storage/configStore.js';\nimport { logger } from '../utils/logger.js';\nimport { handleError } from '../utils/errorHandler.js';\n\nexport const listCommand = new Command('list')\n .description('List all profiles across providers')\n .action(() => {\n try {\n const config = loadConfig();\n const providers = Object.keys(config.providers);\n\n if (providers.length === 0) {\n logger.info('No profiles configured yet. Use \"orbit add <provider> <profile>\" to get started.');\n return;\n }\n\n logger.newline();\n logger.plain(\n ` ${chalk.bold.underline('Provider'.padEnd(15))} ${chalk.bold.underline('Profile'.padEnd(20))} ${chalk.bold.underline('Email'.padEnd(30))} ${chalk.bold.underline('Status')}`,\n );\n logger.newline();\n\n for (const providerName of providers) {\n const providerConfig = config.providers[providerName];\n if (!providerConfig) continue;\n\n for (const profile of providerConfig.profiles) {\n const isCurrent = providerConfig.current === profile;\n const email = providerConfig.metadata?.[profile]?.email ?? '-';\n const status = isCurrent ? chalk.green('★ current') : chalk.dim('—');\n\n const providerDisplay = isCurrent\n ? chalk.cyan(providerName.padEnd(15))\n : chalk.white(providerName.padEnd(15));\n const profileDisplay = isCurrent\n ? chalk.cyan(profile.padEnd(20))\n : chalk.white(profile.padEnd(20));\n const emailDisplay = isCurrent\n ? chalk.cyan(email.padEnd(30))\n : chalk.gray(email.padEnd(30));\n\n logger.plain(` ${providerDisplay} ${profileDisplay} ${emailDisplay} ${status}`);\n }\n }\n\n logger.newline();\n } catch (error: unknown) {\n handleError(error);\n }\n });\n","import { Command } from 'commander';\nimport ora from 'ora';\nimport { getProvider } from '../providers/provider.interface.js';\nimport { deleteToken } from '../storage/keychain.js';\nimport { removeProfile, profileExists } from '../storage/configStore.js';\nimport { validateProviderName, validateProfileName } from '../utils/validator.js';\nimport { logger } from '../utils/logger.js';\nimport { handleError } from '../utils/errorHandler.js';\n\nexport const removeCommand = new Command('remove')\n .description('Remove a cloud provider profile')\n .argument('<provider>', 'Cloud provider name')\n .argument('<profile>', 'Profile name to remove')\n .action(async (providerName: string, profileName: string) => {\n try {\n const validProvider = validateProviderName(providerName);\n const validProfile = validateProfileName(profileName);\n getProvider(validProvider); // Ensure provider is registered\n\n if (!profileExists(validProvider, validProfile)) {\n logger.error(`Profile \"${validProfile}\" does not exist for provider \"${validProvider}\".`);\n process.exit(1);\n }\n\n const spinner = ora('Removing profile...').start();\n\n spinner.text = 'Deleting token from secure storage...';\n await deleteToken(validProvider, validProfile);\n\n spinner.text = 'Updating configuration...';\n removeProfile(validProvider, validProfile);\n\n spinner.succeed(`Profile \"${validProfile}\" removed from ${validProvider}.`);\n } catch (error: unknown) {\n handleError(error);\n }\n });\n","import { Command } from 'commander';\nimport ora from 'ora';\nimport { getProvider } from '../providers/provider.interface.js';\nimport { setCurrentProfile, profileExists, getCurrentProfile } from '../storage/configStore.js';\nimport { validateProviderName, validateProfileName } from '../utils/validator.js';\nimport { logger } from '../utils/logger.js';\nimport { handleError } from '../utils/errorHandler.js';\n\nexport const useCommand = new Command('use')\n .description('Set the current active profile for a provider')\n .argument('<provider>', 'Cloud provider name')\n .argument('<profile>', 'Profile name to activate')\n .action(async (providerName: string, profileName: string) => {\n try {\n const validProvider = validateProviderName(providerName);\n const validProfile = validateProfileName(profileName);\n const provider = getProvider(validProvider);\n\n if (!profileExists(validProvider, validProfile)) {\n logger.error(`Profile \"${validProfile}\" does not exist for provider \"${provider.name}\".`);\n process.exit(1);\n }\n\n const spinner = ora('Switching profile...').start();\n\n // Save current profile's auth state before switching\n const currentProfile = getCurrentProfile(validProvider);\n if (currentProfile && currentProfile !== validProfile && provider.captureAuth) {\n try {\n await provider.captureAuth(currentProfile);\n } catch {\n // Non-critical\n }\n }\n\n // Restore target profile's auth state\n if (provider.restoreAuth) {\n const restored = await provider.restoreAuth(validProfile);\n if (restored) {\n spinner.text = 'Auth credentials restored...';\n }\n }\n\n setCurrentProfile(validProvider, validProfile);\n spinner.succeed(`Now using profile \"${validProfile}\" for ${provider.name}.`);\n } catch (error: unknown) {\n handleError(error);\n }\n });\n","import { Command } from 'commander';\nimport { execa } from 'execa';\nimport { getProvider } from '../providers/provider.interface.js';\nimport { getToken } from '../storage/keychain.js';\nimport { profileExists, getCurrentProfile } from '../storage/configStore.js';\nimport { validateProviderName, validateProfileName } from '../utils/validator.js';\nimport { logger } from '../utils/logger.js';\nimport { handleError } from '../utils/errorHandler.js';\n\nexport const runCommand = new Command('run')\n .description('Run a command with a specific provider profile')\n .argument('<provider>', 'Cloud provider name')\n .argument('<profile>', 'Profile name to use')\n .argument('<args...>', 'Command arguments to pass to the provider CLI')\n .passThroughOptions()\n .allowUnknownOption()\n .action(async (providerName: string, profileName: string, args: string[]) => {\n try {\n const validProvider = validateProviderName(providerName);\n const validProfile = validateProfileName(profileName);\n const provider = getProvider(validProvider);\n\n if (!profileExists(validProvider, validProfile)) {\n logger.error(`Profile \"${validProfile}\" does not exist for provider \"${provider.name}\".`);\n process.exit(1);\n }\n\n logger.info(`Running as ${provider.name}/${validProfile}...`);\n\n // Save current auth state and swap to target profile\n const currentProfile = getCurrentProfile(validProvider);\n let swapped = false;\n\n if (provider.restoreAuth) {\n // Save current auth state first\n if (currentProfile && currentProfile !== validProfile && provider.captureAuth) {\n try {\n await provider.captureAuth(currentProfile);\n } catch {\n // Non-critical\n }\n }\n\n // Restore target profile's auth\n swapped = await provider.restoreAuth(validProfile);\n }\n\n try {\n if (swapped) {\n // Auth swapped — run natively (no env injection needed)\n const cliName = provider.getCliName();\n const result = await execa(cliName, args, {\n stdio: 'inherit',\n reject: false,\n });\n process.exitCode = result.exitCode ?? 0;\n } else {\n // Fallback: inject VERCEL_TOKEN (for providers without auth swap)\n const token = await getToken(validProvider, validProfile);\n if (!token) {\n logger.error(`No token found for ${provider.name}/${validProfile}. Try adding it again with \"orbit add\".`);\n process.exit(1);\n }\n\n const cliName = provider.getCliName();\n const envVar = provider.getEnvVar();\n\n const result = await execa(cliName, args, {\n env: {\n ...process.env,\n [envVar]: token,\n },\n stdio: 'inherit',\n reject: false,\n });\n process.exitCode = result.exitCode ?? 0;\n }\n } finally {\n // Restore previous profile's auth\n if (swapped && currentProfile && currentProfile !== validProfile && provider.restoreAuth) {\n try {\n await provider.restoreAuth(currentProfile);\n } catch {\n // Non-critical\n }\n }\n }\n\n process.exit(process.exitCode ?? 0);\n } catch (error: unknown) {\n handleError(error);\n }\n });\n","import { Command } from 'commander';\nimport { execa } from 'execa';\nimport { getProvider } from '../providers/provider.interface.js';\nimport { getToken } from '../storage/keychain.js';\nimport { getCurrentProfile } from '../storage/configStore.js';\nimport { validateProviderName } from '../utils/validator.js';\nimport { logger } from '../utils/logger.js';\nimport { handleError } from '../utils/errorHandler.js';\n\nexport const execCommand = new Command('exec')\n .description('Execute a command using the current active profile')\n .argument('<provider>', 'Cloud provider name')\n .argument('<args...>', 'Command arguments to pass to the provider CLI')\n .passThroughOptions()\n .allowUnknownOption()\n .action(async (providerName: string, args: string[]) => {\n try {\n const validProvider = validateProviderName(providerName);\n const provider = getProvider(validProvider);\n\n const currentProfile = getCurrentProfile(validProvider);\n\n if (!currentProfile) {\n logger.error(\n `No active profile set for ${provider.name}. Use \"orbit use ${validProvider} <profile>\" first.`,\n );\n process.exit(1);\n }\n\n // Ensure current profile's auth is active\n if (provider.restoreAuth) {\n await provider.restoreAuth(currentProfile);\n }\n\n const cliName = provider.getCliName();\n\n logger.info(`Running as ${provider.name}/${currentProfile}...`);\n\n // Try native execution first (auth.json is already swapped)\n if (provider.getAuthConfigPath?.()) {\n const result = await execa(cliName, args, {\n stdio: 'inherit',\n reject: false,\n });\n process.exit(result.exitCode ?? 0);\n }\n\n // Fallback: inject env var\n const token = await getToken(validProvider, currentProfile);\n\n if (!token) {\n logger.error(\n `No token found for ${provider.name}/${currentProfile}. Try adding it again with \"orbit add\".`,\n );\n process.exit(1);\n }\n\n const envVar = provider.getEnvVar();\n\n const result = await execa(cliName, args, {\n env: {\n ...process.env,\n [envVar]: token,\n },\n stdio: 'inherit',\n reject: false,\n });\n\n process.exit(result.exitCode ?? 0);\n } catch (error: unknown) {\n handleError(error);\n }\n });\n","import { Command } from 'commander';\nimport chalk from 'chalk';\nimport { loadConfig } from '../storage/configStore.js';\nimport { logger } from '../utils/logger.js';\nimport { handleError } from '../utils/errorHandler.js';\n\nexport const currentCommand = new Command('current')\n .description('Show current active profiles')\n .action(() => {\n try {\n const config = loadConfig();\n const providers = Object.keys(config.providers);\n\n if (providers.length === 0) {\n logger.info('No profiles configured yet.');\n return;\n }\n\n let hasActive = false;\n\n for (const providerName of providers) {\n const providerConfig = config.providers[providerName];\n if (providerConfig?.current) {\n logger.success(\n `${chalk.bold(providerName)}: ${chalk.cyan(providerConfig.current)}`,\n );\n hasActive = true;\n }\n }\n\n if (!hasActive) {\n logger.info('No active profiles set. Use \"orbit use <provider> <profile>\" to set one.');\n }\n } catch (error: unknown) {\n handleError(error);\n }\n });\n"],"mappings":"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","names":["Command","fs","path","os","os","path","fs","execa","chalk","fs","fs","z","spinner","chalk","Command","chalk","Command","providers","chalk","Command","ora","Command","ora","Command","ora","Command","ora","Command","Command","Command","execa","Command","result","execa","Command","chalk","Command","providers","chalk","Command"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@appuo/orbit",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A fully CLI-based multi-cloud profile manager",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"orbit": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"files": [
|
|
12
|
+
"dist"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsup src/index.ts --format esm --dts",
|
|
16
|
+
"dev": "tsup src/index.ts --format esm --dts --watch",
|
|
17
|
+
"typecheck": "tsc --noEmit",
|
|
18
|
+
"prepublishOnly": "pnpm build"
|
|
19
|
+
},
|
|
20
|
+
"keywords": [
|
|
21
|
+
"cli",
|
|
22
|
+
"cloud",
|
|
23
|
+
"profile",
|
|
24
|
+
"manager",
|
|
25
|
+
"vercel",
|
|
26
|
+
"multi-cloud",
|
|
27
|
+
"credentials",
|
|
28
|
+
"orbit"
|
|
29
|
+
],
|
|
30
|
+
"author": "Appuo",
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=18"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"chalk": "^5.6.2",
|
|
37
|
+
"commander": "^14.0.3",
|
|
38
|
+
"execa": "^9.6.1",
|
|
39
|
+
"keytar": "^7.9.0",
|
|
40
|
+
"ora": "^9.3.0",
|
|
41
|
+
"zod": "^4.3.6"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@types/node": "^25.2.3",
|
|
45
|
+
"tsup": "^8.5.1",
|
|
46
|
+
"typescript": "^5.9.3"
|
|
47
|
+
}
|
|
48
|
+
}
|