@appuo/orbit 1.0.5 → 1.0.10
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 +68 -2
- package/assets/orbit-header.gif +0 -0
- package/dist/index.js +574 -134
- package/dist/index.js.map +1 -1
- package/package.json +20 -2
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import { Command as
|
|
4
|
+
import { Command as Command9 } from "commander";
|
|
5
5
|
import { createRequire } from "module";
|
|
6
6
|
|
|
7
7
|
// src/providers/provider.interface.ts
|
|
@@ -21,45 +21,106 @@ var getProvider = (name) => {
|
|
|
21
21
|
};
|
|
22
22
|
|
|
23
23
|
// src/providers/vercel.ts
|
|
24
|
-
import
|
|
25
|
-
import
|
|
24
|
+
import fs3 from "fs";
|
|
25
|
+
import path3 from "path";
|
|
26
26
|
import os2 from "os";
|
|
27
27
|
|
|
28
28
|
// src/utils/paths.ts
|
|
29
29
|
import os from "os";
|
|
30
|
-
import
|
|
30
|
+
import path2 from "path";
|
|
31
|
+
import fs2 from "fs";
|
|
32
|
+
|
|
33
|
+
// src/utils/fileOps.ts
|
|
34
|
+
import crypto from "crypto";
|
|
31
35
|
import fs from "fs";
|
|
36
|
+
import path from "path";
|
|
37
|
+
var ensureFileMode = (filePath, mode) => {
|
|
38
|
+
if (!fs.existsSync(filePath)) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const currentMode = fs.statSync(filePath).mode & 511;
|
|
42
|
+
if (currentMode !== mode) {
|
|
43
|
+
fs.chmodSync(filePath, mode);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
var atomicWriteFile = (filePath, content, mode) => {
|
|
47
|
+
const directory = path.dirname(filePath);
|
|
48
|
+
if (!fs.existsSync(directory)) {
|
|
49
|
+
fs.mkdirSync(directory, { recursive: true, mode: 448 });
|
|
50
|
+
}
|
|
51
|
+
const tempName = `.${path.basename(filePath)}.${process.pid}.${crypto.randomUUID()}.tmp`;
|
|
52
|
+
const tempPath = path.join(directory, tempName);
|
|
53
|
+
try {
|
|
54
|
+
if (mode === void 0) {
|
|
55
|
+
fs.writeFileSync(tempPath, content, "utf-8");
|
|
56
|
+
} else {
|
|
57
|
+
fs.writeFileSync(tempPath, content, { encoding: "utf-8", mode });
|
|
58
|
+
}
|
|
59
|
+
fs.renameSync(tempPath, filePath);
|
|
60
|
+
if (mode !== void 0) {
|
|
61
|
+
fs.chmodSync(filePath, mode);
|
|
62
|
+
}
|
|
63
|
+
} finally {
|
|
64
|
+
if (fs.existsSync(tempPath)) {
|
|
65
|
+
fs.rmSync(tempPath, { force: true });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
var quarantineCorruptFile = (filePath, suffix) => {
|
|
70
|
+
if (!fs.existsSync(filePath)) {
|
|
71
|
+
return filePath;
|
|
72
|
+
}
|
|
73
|
+
const quarantinePath = `${filePath}.corrupt-${suffix}`;
|
|
74
|
+
try {
|
|
75
|
+
fs.renameSync(filePath, quarantinePath);
|
|
76
|
+
return quarantinePath;
|
|
77
|
+
} catch {
|
|
78
|
+
return filePath;
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// src/utils/paths.ts
|
|
32
83
|
var getConfigDir = () => {
|
|
33
|
-
|
|
84
|
+
const overrideDir = process.env["ORBIT_CONFIG_DIR"];
|
|
85
|
+
if (overrideDir && overrideDir.trim().length > 0) {
|
|
86
|
+
return overrideDir;
|
|
87
|
+
}
|
|
88
|
+
return path2.join(os.homedir(), ".orbit");
|
|
34
89
|
};
|
|
35
90
|
var getConfigPath = () => {
|
|
36
|
-
return
|
|
91
|
+
return path2.join(getConfigDir(), "config.json");
|
|
37
92
|
};
|
|
38
93
|
var ensureConfigDir = () => {
|
|
39
94
|
const dir = getConfigDir();
|
|
40
|
-
if (!
|
|
41
|
-
|
|
95
|
+
if (!fs2.existsSync(dir)) {
|
|
96
|
+
fs2.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
42
97
|
}
|
|
98
|
+
ensureFileMode(dir, 448);
|
|
43
99
|
};
|
|
44
100
|
var getAuthDir = (provider) => {
|
|
45
|
-
return
|
|
101
|
+
return path2.join(getConfigDir(), "auth", provider);
|
|
46
102
|
};
|
|
47
103
|
var getAuthFilePath = (provider, profile) => {
|
|
48
|
-
return
|
|
104
|
+
return path2.join(getAuthDir(provider), `${profile}.json`);
|
|
49
105
|
};
|
|
50
106
|
var ensureAuthDir = (provider) => {
|
|
51
107
|
const dir = getAuthDir(provider);
|
|
52
|
-
if (!
|
|
53
|
-
|
|
108
|
+
if (!fs2.existsSync(dir)) {
|
|
109
|
+
fs2.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
54
110
|
}
|
|
111
|
+
ensureFileMode(dir, 448);
|
|
55
112
|
};
|
|
56
113
|
|
|
57
114
|
// src/providers/vercel.ts
|
|
58
115
|
var getVercelAuthPath = () => {
|
|
59
116
|
const platform = os2.platform();
|
|
60
117
|
const home = os2.homedir();
|
|
118
|
+
const xdgDataHome = process.env["XDG_DATA_HOME"];
|
|
119
|
+
if (xdgDataHome && xdgDataHome.trim().length > 0) {
|
|
120
|
+
return path3.join(xdgDataHome, "com.vercel.cli", "auth.json");
|
|
121
|
+
}
|
|
61
122
|
if (platform === "darwin") {
|
|
62
|
-
return
|
|
123
|
+
return path3.join(
|
|
63
124
|
home,
|
|
64
125
|
"Library",
|
|
65
126
|
"Application Support",
|
|
@@ -67,13 +128,30 @@ var getVercelAuthPath = () => {
|
|
|
67
128
|
"auth.json"
|
|
68
129
|
);
|
|
69
130
|
} else if (platform === "win32") {
|
|
70
|
-
const appData = process.env["APPDATA"] ||
|
|
71
|
-
return
|
|
131
|
+
const appData = process.env["APPDATA"] || path3.join(home, "AppData", "Roaming");
|
|
132
|
+
return path3.join(appData, "com.vercel.cli", "auth.json");
|
|
72
133
|
} else {
|
|
73
|
-
const
|
|
74
|
-
return
|
|
134
|
+
const linuxDataHome = path3.join(home, ".local", "share");
|
|
135
|
+
return path3.join(linuxDataHome, "com.vercel.cli", "auth.json");
|
|
75
136
|
}
|
|
76
137
|
};
|
|
138
|
+
var ensureVercelAuthDir = (authPath) => {
|
|
139
|
+
const authDir = path3.dirname(authPath);
|
|
140
|
+
if (!fs3.existsSync(authDir)) {
|
|
141
|
+
fs3.mkdirSync(authDir, { recursive: true, mode: 448 });
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
var writeVercelAuthToken = (token) => {
|
|
145
|
+
const authPath = getVercelAuthPath();
|
|
146
|
+
if (!authPath) return false;
|
|
147
|
+
ensureVercelAuthDir(authPath);
|
|
148
|
+
fs3.writeFileSync(authPath, JSON.stringify({ token }, null, 2), {
|
|
149
|
+
encoding: "utf-8",
|
|
150
|
+
mode: 384
|
|
151
|
+
});
|
|
152
|
+
ensureFileMode(authPath, 384);
|
|
153
|
+
return true;
|
|
154
|
+
};
|
|
77
155
|
var vercelProvider = {
|
|
78
156
|
name: "vercel",
|
|
79
157
|
getEnvVar() {
|
|
@@ -88,8 +166,8 @@ var vercelProvider = {
|
|
|
88
166
|
async getStoredToken() {
|
|
89
167
|
try {
|
|
90
168
|
const configPath = getVercelAuthPath();
|
|
91
|
-
if (configPath &&
|
|
92
|
-
const content =
|
|
169
|
+
if (configPath && fs3.existsSync(configPath)) {
|
|
170
|
+
const content = fs3.readFileSync(configPath, "utf-8");
|
|
93
171
|
const config = JSON.parse(content);
|
|
94
172
|
if (config.token && typeof config.token === "string") {
|
|
95
173
|
return config.token;
|
|
@@ -127,57 +205,169 @@ var vercelProvider = {
|
|
|
127
205
|
},
|
|
128
206
|
async captureAuth(profile) {
|
|
129
207
|
const authPath = getVercelAuthPath();
|
|
130
|
-
if (!authPath || !
|
|
208
|
+
if (!authPath || !fs3.existsSync(authPath)) return;
|
|
131
209
|
ensureAuthDir("vercel");
|
|
132
210
|
const destPath = getAuthFilePath("vercel", profile);
|
|
133
|
-
|
|
211
|
+
fs3.copyFileSync(authPath, destPath);
|
|
212
|
+
ensureFileMode(destPath, 384);
|
|
134
213
|
},
|
|
135
214
|
async restoreAuth(profile) {
|
|
136
215
|
const sourcePath = getAuthFilePath("vercel", profile);
|
|
137
|
-
if (!
|
|
216
|
+
if (!fs3.existsSync(sourcePath)) return false;
|
|
138
217
|
const authPath = getVercelAuthPath();
|
|
139
218
|
if (!authPath) return false;
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
}
|
|
144
|
-
fs2.copyFileSync(sourcePath, authPath);
|
|
219
|
+
ensureVercelAuthDir(authPath);
|
|
220
|
+
fs3.copyFileSync(sourcePath, authPath);
|
|
221
|
+
ensureFileMode(authPath, 384);
|
|
145
222
|
return true;
|
|
223
|
+
},
|
|
224
|
+
async seedAuthFromToken(token) {
|
|
225
|
+
return writeVercelAuthToken(token);
|
|
226
|
+
},
|
|
227
|
+
async validateActiveAuth() {
|
|
228
|
+
const token = await this.getStoredToken?.();
|
|
229
|
+
if (!token) return false;
|
|
230
|
+
try {
|
|
231
|
+
const { execa: execa3 } = await import("execa");
|
|
232
|
+
const result = await execa3("vercel", ["whoami", "--token", token], {
|
|
233
|
+
stdio: "pipe",
|
|
234
|
+
reject: false
|
|
235
|
+
});
|
|
236
|
+
if (result.exitCode === 0) {
|
|
237
|
+
return true;
|
|
238
|
+
}
|
|
239
|
+
const output = `${result.stdout}
|
|
240
|
+
${result.stderr}`.toLowerCase();
|
|
241
|
+
if (output.includes("token is not valid") || output.includes("not valid")) {
|
|
242
|
+
return false;
|
|
243
|
+
}
|
|
244
|
+
return true;
|
|
245
|
+
} catch {
|
|
246
|
+
return true;
|
|
247
|
+
}
|
|
248
|
+
},
|
|
249
|
+
async removeAuthSnapshot(profile) {
|
|
250
|
+
const snapshotPath = getAuthFilePath("vercel", profile);
|
|
251
|
+
if (fs3.existsSync(snapshotPath)) {
|
|
252
|
+
fs3.rmSync(snapshotPath, { force: true });
|
|
253
|
+
}
|
|
146
254
|
}
|
|
147
255
|
};
|
|
148
256
|
|
|
149
257
|
// src/commands/add.ts
|
|
150
258
|
import { Command } from "commander";
|
|
151
|
-
import ora from "ora";
|
|
152
259
|
import chalk2 from "chalk";
|
|
153
260
|
import readline from "readline";
|
|
154
261
|
|
|
155
262
|
// src/storage/keychain.ts
|
|
156
|
-
import
|
|
157
|
-
import
|
|
158
|
-
import
|
|
263
|
+
import fs4 from "fs";
|
|
264
|
+
import path4 from "path";
|
|
265
|
+
import crypto2 from "crypto";
|
|
266
|
+
|
|
267
|
+
// src/utils/logger.ts
|
|
268
|
+
import chalk from "chalk";
|
|
269
|
+
var jsonMode = false;
|
|
270
|
+
var write = (stream, message) => {
|
|
271
|
+
stream.write(`${message}
|
|
272
|
+
`);
|
|
273
|
+
};
|
|
274
|
+
var writeJson = (stream, level, message) => {
|
|
275
|
+
write(
|
|
276
|
+
stream,
|
|
277
|
+
JSON.stringify({
|
|
278
|
+
level,
|
|
279
|
+
message
|
|
280
|
+
})
|
|
281
|
+
);
|
|
282
|
+
};
|
|
283
|
+
var setJsonMode = (enabled) => {
|
|
284
|
+
jsonMode = enabled;
|
|
285
|
+
};
|
|
286
|
+
var logger = {
|
|
287
|
+
isJsonMode: () => {
|
|
288
|
+
return jsonMode;
|
|
289
|
+
},
|
|
290
|
+
json: (payload) => {
|
|
291
|
+
write(process.stdout, JSON.stringify(payload));
|
|
292
|
+
},
|
|
293
|
+
info: (message) => {
|
|
294
|
+
if (jsonMode) {
|
|
295
|
+
writeJson(process.stdout, "info", message);
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
write(process.stdout, chalk.blue("\u2139") + ` ${message}`);
|
|
299
|
+
},
|
|
300
|
+
success: (message) => {
|
|
301
|
+
if (jsonMode) {
|
|
302
|
+
writeJson(process.stdout, "success", message);
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
write(process.stdout, chalk.green("\u2714") + ` ${message}`);
|
|
306
|
+
},
|
|
307
|
+
warn: (message) => {
|
|
308
|
+
if (jsonMode) {
|
|
309
|
+
writeJson(process.stderr, "warn", message);
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
write(process.stderr, chalk.yellow("\u26A0") + ` ${message}`);
|
|
313
|
+
},
|
|
314
|
+
error: (message) => {
|
|
315
|
+
if (jsonMode) {
|
|
316
|
+
writeJson(process.stderr, "error", message);
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
write(process.stderr, chalk.red("\u2716") + ` ${message}`);
|
|
320
|
+
},
|
|
321
|
+
plain: (message) => {
|
|
322
|
+
if (jsonMode) {
|
|
323
|
+
write(process.stdout, message);
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
write(process.stdout, message);
|
|
327
|
+
},
|
|
328
|
+
newline: () => {
|
|
329
|
+
if (jsonMode) {
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
write(process.stdout, "");
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
// src/storage/keychain.ts
|
|
159
337
|
var CREDENTIALS_FILE = "credentials.json";
|
|
160
338
|
var KEY_FILE = ".key";
|
|
161
339
|
var ALGORITHM = "aes-256-gcm";
|
|
340
|
+
var ENCRYPTED_FILE_MODE = 384;
|
|
162
341
|
var getCredentialsPath = () => {
|
|
163
|
-
return
|
|
342
|
+
return path4.join(getConfigDir(), CREDENTIALS_FILE);
|
|
164
343
|
};
|
|
165
344
|
var getKeyPath = () => {
|
|
166
|
-
return
|
|
345
|
+
return path4.join(getConfigDir(), KEY_FILE);
|
|
346
|
+
};
|
|
347
|
+
var writeKey = (keyPath, key) => {
|
|
348
|
+
atomicWriteFile(keyPath, key.toString("hex"), ENCRYPTED_FILE_MODE);
|
|
349
|
+
};
|
|
350
|
+
var readKey = (keyPath) => {
|
|
351
|
+
ensureFileMode(keyPath, ENCRYPTED_FILE_MODE);
|
|
352
|
+
const raw = fs4.readFileSync(keyPath, "utf-8").trim();
|
|
353
|
+
if (!/^[0-9a-fA-F]{64}$/.test(raw)) {
|
|
354
|
+
throw new Error("Invalid encryption key format.");
|
|
355
|
+
}
|
|
356
|
+
return Buffer.from(raw, "hex");
|
|
167
357
|
};
|
|
168
358
|
var getOrCreateKey = () => {
|
|
169
359
|
const keyPath = getKeyPath();
|
|
170
360
|
ensureConfigDir();
|
|
171
|
-
if (
|
|
172
|
-
return
|
|
361
|
+
if (fs4.existsSync(keyPath)) {
|
|
362
|
+
return readKey(keyPath);
|
|
173
363
|
}
|
|
174
|
-
const key =
|
|
175
|
-
|
|
364
|
+
const key = crypto2.randomBytes(32);
|
|
365
|
+
writeKey(keyPath, key);
|
|
176
366
|
return key;
|
|
177
367
|
};
|
|
178
368
|
var encrypt = (text, key) => {
|
|
179
|
-
const iv =
|
|
180
|
-
const cipher =
|
|
369
|
+
const iv = crypto2.randomBytes(16);
|
|
370
|
+
const cipher = crypto2.createCipheriv(ALGORITHM, key, iv);
|
|
181
371
|
let encrypted = cipher.update(text, "utf-8", "hex");
|
|
182
372
|
encrypted += cipher.final("hex");
|
|
183
373
|
const tag = cipher.getAuthTag();
|
|
@@ -188,7 +378,7 @@ var encrypt = (text, key) => {
|
|
|
188
378
|
};
|
|
189
379
|
};
|
|
190
380
|
var decrypt = (entry, key) => {
|
|
191
|
-
const decipher =
|
|
381
|
+
const decipher = crypto2.createDecipheriv(
|
|
192
382
|
ALGORITHM,
|
|
193
383
|
key,
|
|
194
384
|
Buffer.from(entry.iv, "hex")
|
|
@@ -200,17 +390,22 @@ var decrypt = (entry, key) => {
|
|
|
200
390
|
};
|
|
201
391
|
var loadStore = () => {
|
|
202
392
|
const filePath = getCredentialsPath();
|
|
203
|
-
if (!
|
|
393
|
+
if (!fs4.existsSync(filePath)) return {};
|
|
394
|
+
ensureFileMode(filePath, ENCRYPTED_FILE_MODE);
|
|
204
395
|
try {
|
|
205
|
-
return JSON.parse(
|
|
396
|
+
return JSON.parse(fs4.readFileSync(filePath, "utf-8"));
|
|
206
397
|
} catch {
|
|
398
|
+
const quarantinePath = quarantineCorruptFile(filePath, `${Date.now()}`);
|
|
399
|
+
logger.warn(
|
|
400
|
+
`Detected invalid credentials store. Backed it up to "${quarantinePath}" and re-initialized credentials.`
|
|
401
|
+
);
|
|
207
402
|
return {};
|
|
208
403
|
}
|
|
209
404
|
};
|
|
210
405
|
var saveStore = (store) => {
|
|
211
406
|
ensureConfigDir();
|
|
212
407
|
const filePath = getCredentialsPath();
|
|
213
|
-
|
|
408
|
+
atomicWriteFile(filePath, JSON.stringify(store, null, 2), ENCRYPTED_FILE_MODE);
|
|
214
409
|
};
|
|
215
410
|
var getKey = (provider, profile) => {
|
|
216
411
|
return `${provider}:${profile}`;
|
|
@@ -240,9 +435,41 @@ var deleteToken = async (provider, profile) => {
|
|
|
240
435
|
saveStore(store);
|
|
241
436
|
return true;
|
|
242
437
|
};
|
|
438
|
+
var rotateEncryptionKey = async () => {
|
|
439
|
+
const keyPath = getKeyPath();
|
|
440
|
+
const oldKey = getOrCreateKey();
|
|
441
|
+
const store = loadStore();
|
|
442
|
+
const decryptedEntries = /* @__PURE__ */ new Map();
|
|
443
|
+
for (const [entryKey, entryValue] of Object.entries(store)) {
|
|
444
|
+
decryptedEntries.set(entryKey, decrypt(entryValue, oldKey));
|
|
445
|
+
}
|
|
446
|
+
const newKey = crypto2.randomBytes(32);
|
|
447
|
+
const rotatedStore = {};
|
|
448
|
+
for (const [entryKey, token] of decryptedEntries.entries()) {
|
|
449
|
+
rotatedStore[entryKey] = encrypt(token, newKey);
|
|
450
|
+
}
|
|
451
|
+
const backupPath = `${keyPath}.bak-${Date.now()}`;
|
|
452
|
+
if (fs4.existsSync(keyPath)) {
|
|
453
|
+
fs4.copyFileSync(keyPath, backupPath);
|
|
454
|
+
}
|
|
455
|
+
try {
|
|
456
|
+
writeKey(keyPath, newKey);
|
|
457
|
+
saveStore(rotatedStore);
|
|
458
|
+
} catch (error) {
|
|
459
|
+
if (fs4.existsSync(backupPath)) {
|
|
460
|
+
fs4.copyFileSync(backupPath, keyPath);
|
|
461
|
+
ensureFileMode(keyPath, ENCRYPTED_FILE_MODE);
|
|
462
|
+
}
|
|
463
|
+
throw error;
|
|
464
|
+
} finally {
|
|
465
|
+
if (fs4.existsSync(backupPath)) {
|
|
466
|
+
fs4.rmSync(backupPath, { force: true });
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
};
|
|
243
470
|
|
|
244
471
|
// src/storage/configStore.ts
|
|
245
|
-
import
|
|
472
|
+
import fs5 from "fs";
|
|
246
473
|
|
|
247
474
|
// src/types/config.ts
|
|
248
475
|
import { z } from "zod";
|
|
@@ -259,21 +486,37 @@ var OrbitConfigSchema = z.object({
|
|
|
259
486
|
var defaultConfig = {
|
|
260
487
|
providers: {}
|
|
261
488
|
};
|
|
489
|
+
var writeDefaultConfig = (configPath) => {
|
|
490
|
+
ensureConfigDir();
|
|
491
|
+
atomicWriteFile(
|
|
492
|
+
configPath,
|
|
493
|
+
JSON.stringify(defaultConfig, null, 2),
|
|
494
|
+
384
|
|
495
|
+
);
|
|
496
|
+
return { providers: {} };
|
|
497
|
+
};
|
|
262
498
|
var loadConfig = () => {
|
|
263
499
|
const configPath = getConfigPath();
|
|
264
|
-
if (!
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
500
|
+
if (!fs5.existsSync(configPath)) {
|
|
501
|
+
return writeDefaultConfig(configPath);
|
|
502
|
+
}
|
|
503
|
+
ensureFileMode(configPath, 384);
|
|
504
|
+
try {
|
|
505
|
+
const raw = fs5.readFileSync(configPath, "utf-8");
|
|
506
|
+
const parsed = JSON.parse(raw);
|
|
507
|
+
return OrbitConfigSchema.parse(parsed);
|
|
508
|
+
} catch (error) {
|
|
509
|
+
const quarantinePath = quarantineCorruptFile(configPath, `${Date.now()}`);
|
|
510
|
+
logger.warn(
|
|
511
|
+
`Detected invalid config. Backed it up to "${quarantinePath}" and re-initialized Orbit config.`
|
|
512
|
+
);
|
|
513
|
+
return writeDefaultConfig(configPath);
|
|
268
514
|
}
|
|
269
|
-
const raw = fs4.readFileSync(configPath, "utf-8");
|
|
270
|
-
const parsed = JSON.parse(raw);
|
|
271
|
-
return OrbitConfigSchema.parse(parsed);
|
|
272
515
|
};
|
|
273
516
|
var saveConfig = (config) => {
|
|
274
517
|
ensureConfigDir();
|
|
275
518
|
const configPath = getConfigPath();
|
|
276
|
-
|
|
519
|
+
atomicWriteFile(configPath, JSON.stringify(config, null, 2), 384);
|
|
277
520
|
};
|
|
278
521
|
var addProfile = (provider, profile, metadata) => {
|
|
279
522
|
const config = loadConfig();
|
|
@@ -300,6 +543,12 @@ var removeProfile = (provider, profile) => {
|
|
|
300
543
|
if (providerConfig.current === profile) {
|
|
301
544
|
providerConfig.current = void 0;
|
|
302
545
|
}
|
|
546
|
+
if (providerConfig.metadata?.[profile]) {
|
|
547
|
+
delete providerConfig.metadata[profile];
|
|
548
|
+
if (Object.keys(providerConfig.metadata).length === 0) {
|
|
549
|
+
delete providerConfig.metadata;
|
|
550
|
+
}
|
|
551
|
+
}
|
|
303
552
|
if (providerConfig.profiles.length === 0) {
|
|
304
553
|
delete config.providers[provider];
|
|
305
554
|
}
|
|
@@ -336,50 +585,135 @@ var validateProfileName = (name) => {
|
|
|
336
585
|
return nameSchema.parse(name);
|
|
337
586
|
};
|
|
338
587
|
|
|
339
|
-
// src/utils/
|
|
340
|
-
import
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
588
|
+
// src/utils/errorHandler.ts
|
|
589
|
+
import { ZodError } from "zod";
|
|
590
|
+
|
|
591
|
+
// src/utils/cliError.ts
|
|
592
|
+
var CliError = class extends Error {
|
|
593
|
+
exitCode;
|
|
594
|
+
constructor(message, exitCode = 1) {
|
|
595
|
+
super(message);
|
|
596
|
+
this.name = "CliError";
|
|
597
|
+
this.exitCode = exitCode;
|
|
598
|
+
}
|
|
344
599
|
};
|
|
345
|
-
var
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
write(process.stdout, message);
|
|
360
|
-
},
|
|
361
|
-
newline: () => {
|
|
362
|
-
write(process.stdout, "");
|
|
600
|
+
var fail = (message, exitCode = 1) => {
|
|
601
|
+
throw new CliError(message, exitCode);
|
|
602
|
+
};
|
|
603
|
+
|
|
604
|
+
// src/utils/redact.ts
|
|
605
|
+
var PATTERNS = [
|
|
606
|
+
[/(Bearer\s+)[A-Za-z0-9._\-~=+/]+/g, "$1[REDACTED]"],
|
|
607
|
+
[/([A-Z_]*TOKEN=)[^\s"']+/g, "$1[REDACTED]"],
|
|
608
|
+
[/"token"\s*:\s*"[^"]+"/g, '"token":"[REDACTED]"']
|
|
609
|
+
];
|
|
610
|
+
var redactSecrets = (value) => {
|
|
611
|
+
let output = value;
|
|
612
|
+
for (const [pattern, replacement] of PATTERNS) {
|
|
613
|
+
output = output.replace(pattern, replacement);
|
|
363
614
|
}
|
|
615
|
+
return output;
|
|
364
616
|
};
|
|
365
617
|
|
|
366
618
|
// src/utils/errorHandler.ts
|
|
367
|
-
|
|
619
|
+
var printDebugStack = (error) => {
|
|
620
|
+
if (process.env["ORBIT_DEBUG"] !== "1") {
|
|
621
|
+
return;
|
|
622
|
+
}
|
|
623
|
+
logger.plain(redactSecrets(error.stack ?? ""));
|
|
624
|
+
};
|
|
368
625
|
var handleError = (error) => {
|
|
369
626
|
if (error instanceof ZodError) {
|
|
370
627
|
const messages = error.issues.map((e) => e.message).join(", ");
|
|
371
|
-
|
|
372
|
-
|
|
628
|
+
const message = `Validation error: ${messages}`;
|
|
629
|
+
if (logger.isJsonMode()) {
|
|
630
|
+
logger.json({
|
|
631
|
+
ok: false,
|
|
632
|
+
error: {
|
|
633
|
+
type: "validation_error",
|
|
634
|
+
message
|
|
635
|
+
}
|
|
636
|
+
});
|
|
637
|
+
} else {
|
|
638
|
+
logger.error(message);
|
|
639
|
+
}
|
|
640
|
+
process.exitCode = 1;
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
643
|
+
if (error instanceof CliError) {
|
|
644
|
+
if (logger.isJsonMode()) {
|
|
645
|
+
logger.json({
|
|
646
|
+
ok: false,
|
|
647
|
+
error: {
|
|
648
|
+
type: "cli_error",
|
|
649
|
+
message: error.message
|
|
650
|
+
}
|
|
651
|
+
});
|
|
652
|
+
} else {
|
|
653
|
+
logger.error(error.message);
|
|
654
|
+
}
|
|
655
|
+
process.exitCode = error.exitCode;
|
|
656
|
+
printDebugStack(error);
|
|
657
|
+
return;
|
|
373
658
|
}
|
|
374
659
|
if (error instanceof Error) {
|
|
375
|
-
|
|
376
|
-
if (
|
|
377
|
-
logger.
|
|
660
|
+
const message = redactSecrets(error.message);
|
|
661
|
+
if (logger.isJsonMode()) {
|
|
662
|
+
logger.json({
|
|
663
|
+
ok: false,
|
|
664
|
+
error: {
|
|
665
|
+
type: "runtime_error",
|
|
666
|
+
message
|
|
667
|
+
}
|
|
668
|
+
});
|
|
669
|
+
} else {
|
|
670
|
+
logger.error(message);
|
|
378
671
|
}
|
|
379
|
-
process.
|
|
672
|
+
process.exitCode = 1;
|
|
673
|
+
printDebugStack(error);
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
if (logger.isJsonMode()) {
|
|
677
|
+
logger.json({
|
|
678
|
+
ok: false,
|
|
679
|
+
error: {
|
|
680
|
+
type: "unknown_error",
|
|
681
|
+
message: "An unexpected error occurred."
|
|
682
|
+
}
|
|
683
|
+
});
|
|
684
|
+
} else {
|
|
685
|
+
logger.error("An unexpected error occurred.");
|
|
686
|
+
}
|
|
687
|
+
process.exitCode = 1;
|
|
688
|
+
};
|
|
689
|
+
|
|
690
|
+
// src/utils/spinner.ts
|
|
691
|
+
import ora from "ora";
|
|
692
|
+
var NoopSpinner = class {
|
|
693
|
+
text;
|
|
694
|
+
constructor(text) {
|
|
695
|
+
this.text = text;
|
|
696
|
+
}
|
|
697
|
+
start() {
|
|
698
|
+
return this;
|
|
699
|
+
}
|
|
700
|
+
stop() {
|
|
701
|
+
return this;
|
|
702
|
+
}
|
|
703
|
+
succeed(text) {
|
|
704
|
+
logger.success(text ?? this.text);
|
|
705
|
+
return this;
|
|
706
|
+
}
|
|
707
|
+
fail(text) {
|
|
708
|
+
logger.error(text ?? this.text);
|
|
709
|
+
return this;
|
|
380
710
|
}
|
|
381
|
-
|
|
382
|
-
|
|
711
|
+
};
|
|
712
|
+
var createSpinner = (text) => {
|
|
713
|
+
if (logger.isJsonMode()) {
|
|
714
|
+
return new NoopSpinner(text);
|
|
715
|
+
}
|
|
716
|
+
return ora(text).start();
|
|
383
717
|
};
|
|
384
718
|
|
|
385
719
|
// src/commands/add.ts
|
|
@@ -449,7 +783,7 @@ var addCommand = new Command("add").description("Add a new cloud provider profil
|
|
|
449
783
|
let token = "";
|
|
450
784
|
let storedTokenFound = false;
|
|
451
785
|
if (provider.getStoredToken) {
|
|
452
|
-
const spinner2 =
|
|
786
|
+
const spinner2 = createSpinner(`Checking for existing ${provider.name} credentials...`);
|
|
453
787
|
try {
|
|
454
788
|
const storedToken = await provider.getStoredToken();
|
|
455
789
|
spinner2.stop();
|
|
@@ -481,7 +815,7 @@ var addCommand = new Command("add").description("Add a new cloud provider profil
|
|
|
481
815
|
const loginSuccess = await provider.login();
|
|
482
816
|
if (loginSuccess) {
|
|
483
817
|
if (provider.getStoredToken) {
|
|
484
|
-
const spinner2 =
|
|
818
|
+
const spinner2 = createSpinner("Checking for new credentials...");
|
|
485
819
|
try {
|
|
486
820
|
const storedToken = await provider.getStoredToken();
|
|
487
821
|
spinner2.stop();
|
|
@@ -502,14 +836,12 @@ var addCommand = new Command("add").description("Add a new cloud provider profil
|
|
|
502
836
|
token = await promptHiddenInput(`\u{1F511} Enter token for ${provider.name}/${validProfile}: `);
|
|
503
837
|
}
|
|
504
838
|
if (!token.trim()) {
|
|
505
|
-
|
|
506
|
-
process.exit(1);
|
|
839
|
+
fail("Token cannot be empty.");
|
|
507
840
|
}
|
|
508
|
-
const spinner =
|
|
841
|
+
const spinner = createSpinner("Validating token...");
|
|
509
842
|
if (!provider.validateToken(token.trim())) {
|
|
510
843
|
spinner.fail("Token validation failed.");
|
|
511
|
-
|
|
512
|
-
process.exit(1);
|
|
844
|
+
fail(`Invalid token format for ${provider.name}. Please check your token and try again.`);
|
|
513
845
|
}
|
|
514
846
|
spinner.text = "Storing token securely...";
|
|
515
847
|
await storeToken(validProvider, validProfile, token.trim());
|
|
@@ -548,6 +880,25 @@ var listCommand = new Command2("list").description("List all profiles across pro
|
|
|
548
880
|
try {
|
|
549
881
|
const config = loadConfig();
|
|
550
882
|
const providers2 = Object.keys(config.providers);
|
|
883
|
+
if (logger.isJsonMode()) {
|
|
884
|
+
const payload = providers2.map((providerName) => {
|
|
885
|
+
const providerConfig = config.providers[providerName];
|
|
886
|
+
return {
|
|
887
|
+
provider: providerName,
|
|
888
|
+
current: providerConfig?.current ?? null,
|
|
889
|
+
profiles: (providerConfig?.profiles ?? []).map((profile) => ({
|
|
890
|
+
name: profile,
|
|
891
|
+
email: providerConfig?.metadata?.[profile]?.email ?? null,
|
|
892
|
+
isCurrent: providerConfig?.current === profile
|
|
893
|
+
}))
|
|
894
|
+
};
|
|
895
|
+
});
|
|
896
|
+
logger.json({
|
|
897
|
+
ok: true,
|
|
898
|
+
providers: payload
|
|
899
|
+
});
|
|
900
|
+
return;
|
|
901
|
+
}
|
|
551
902
|
if (providers2.length === 0) {
|
|
552
903
|
logger.info('No profiles configured yet. Use "orbit add <provider> <profile>" to get started.');
|
|
553
904
|
return;
|
|
@@ -578,19 +929,21 @@ var listCommand = new Command2("list").description("List all profiles across pro
|
|
|
578
929
|
|
|
579
930
|
// src/commands/remove.ts
|
|
580
931
|
import { Command as Command3 } from "commander";
|
|
581
|
-
import ora2 from "ora";
|
|
582
932
|
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) => {
|
|
583
933
|
try {
|
|
584
934
|
const validProvider = validateProviderName(providerName);
|
|
585
935
|
const validProfile = validateProfileName(profileName);
|
|
586
|
-
getProvider(validProvider);
|
|
936
|
+
const provider = getProvider(validProvider);
|
|
587
937
|
if (!profileExists(validProvider, validProfile)) {
|
|
588
|
-
|
|
589
|
-
process.exit(1);
|
|
938
|
+
fail(`Profile "${validProfile}" does not exist for provider "${validProvider}".`);
|
|
590
939
|
}
|
|
591
|
-
const spinner =
|
|
940
|
+
const spinner = createSpinner("Removing profile...");
|
|
592
941
|
spinner.text = "Deleting token from secure storage...";
|
|
593
942
|
await deleteToken(validProvider, validProfile);
|
|
943
|
+
if (provider.removeAuthSnapshot) {
|
|
944
|
+
spinner.text = "Removing local auth snapshot...";
|
|
945
|
+
await provider.removeAuthSnapshot(validProfile);
|
|
946
|
+
}
|
|
594
947
|
spinner.text = "Updating configuration...";
|
|
595
948
|
removeProfile(validProvider, validProfile);
|
|
596
949
|
spinner.succeed(`Profile "${validProfile}" removed from ${validProvider}.`);
|
|
@@ -601,32 +954,69 @@ var removeCommand = new Command3("remove").description("Remove a cloud provider
|
|
|
601
954
|
|
|
602
955
|
// src/commands/use.ts
|
|
603
956
|
import { Command as Command4 } from "commander";
|
|
604
|
-
import ora3 from "ora";
|
|
605
957
|
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) => {
|
|
606
958
|
try {
|
|
607
959
|
const validProvider = validateProviderName(providerName);
|
|
608
960
|
const validProfile = validateProfileName(profileName);
|
|
609
961
|
const provider = getProvider(validProvider);
|
|
610
962
|
if (!profileExists(validProvider, validProfile)) {
|
|
611
|
-
|
|
612
|
-
process.exit(1);
|
|
963
|
+
fail(`Profile "${validProfile}" does not exist for provider "${provider.name}".`);
|
|
613
964
|
}
|
|
614
|
-
const spinner =
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
965
|
+
const spinner = createSpinner("Switching profile...");
|
|
966
|
+
try {
|
|
967
|
+
const currentProfile = getCurrentProfile(validProvider);
|
|
968
|
+
if (currentProfile && currentProfile !== validProfile && provider.captureAuth) {
|
|
969
|
+
try {
|
|
970
|
+
await provider.captureAuth(currentProfile);
|
|
971
|
+
} catch {
|
|
972
|
+
}
|
|
620
973
|
}
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
974
|
+
if (provider.restoreAuth && currentProfile !== validProfile) {
|
|
975
|
+
const restoredSnapshot = await provider.restoreAuth(validProfile);
|
|
976
|
+
if (!restoredSnapshot) {
|
|
977
|
+
fail(
|
|
978
|
+
`No saved auth snapshot found for ${provider.name}/${validProfile}. Re-add the profile with "orbit add ${validProvider} ${validProfile}" to capture credentials.`
|
|
979
|
+
);
|
|
980
|
+
}
|
|
625
981
|
spinner.text = "Auth credentials restored...";
|
|
626
982
|
}
|
|
983
|
+
const authValid = provider.validateActiveAuth ? await provider.validateActiveAuth() : true;
|
|
984
|
+
if (!authValid) {
|
|
985
|
+
let recovered = false;
|
|
986
|
+
if (provider.seedAuthFromToken) {
|
|
987
|
+
const savedToken = await getToken(validProvider, validProfile);
|
|
988
|
+
if (savedToken && provider.validateToken(savedToken)) {
|
|
989
|
+
spinner.text = "Refreshing auth from saved token...";
|
|
990
|
+
const seeded = await provider.seedAuthFromToken(savedToken);
|
|
991
|
+
if (seeded) {
|
|
992
|
+
recovered = provider.validateActiveAuth ? await provider.validateActiveAuth() : true;
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
if (!recovered) {
|
|
997
|
+
if (currentProfile && currentProfile !== validProfile && provider.restoreAuth) {
|
|
998
|
+
try {
|
|
999
|
+
await provider.restoreAuth(currentProfile);
|
|
1000
|
+
} catch {
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
fail(
|
|
1004
|
+
`Credentials for ${provider.name}/${validProfile} are invalid or expired. Re-authenticate with "${provider.getCliName()} login", then refresh Orbit with "orbit add ${validProvider} ${validProfile}".`
|
|
1005
|
+
);
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
if (provider.captureAuth) {
|
|
1009
|
+
try {
|
|
1010
|
+
await provider.captureAuth(validProfile);
|
|
1011
|
+
} catch {
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
setCurrentProfile(validProvider, validProfile);
|
|
1015
|
+
spinner.succeed(`Now using profile "${validProfile}" for ${provider.name}.`);
|
|
1016
|
+
} catch (error) {
|
|
1017
|
+
spinner.fail("Failed to switch profile.");
|
|
1018
|
+
throw error;
|
|
627
1019
|
}
|
|
628
|
-
setCurrentProfile(validProvider, validProfile);
|
|
629
|
-
spinner.succeed(`Now using profile "${validProfile}" for ${provider.name}.`);
|
|
630
1020
|
} catch (error) {
|
|
631
1021
|
handleError(error);
|
|
632
1022
|
}
|
|
@@ -641,8 +1031,7 @@ var runCommand = new Command5("run").description("Run a command with a specific
|
|
|
641
1031
|
const validProfile = validateProfileName(profileName);
|
|
642
1032
|
const provider = getProvider(validProvider);
|
|
643
1033
|
if (!profileExists(validProvider, validProfile)) {
|
|
644
|
-
|
|
645
|
-
process.exit(1);
|
|
1034
|
+
fail(`Profile "${validProfile}" does not exist for provider "${provider.name}".`);
|
|
646
1035
|
}
|
|
647
1036
|
logger.info(`Running as ${provider.name}/${validProfile}...`);
|
|
648
1037
|
const currentProfile = getCurrentProfile(validProvider);
|
|
@@ -655,6 +1044,11 @@ var runCommand = new Command5("run").description("Run a command with a specific
|
|
|
655
1044
|
}
|
|
656
1045
|
}
|
|
657
1046
|
swapped = await provider.restoreAuth(validProfile);
|
|
1047
|
+
if (!swapped) {
|
|
1048
|
+
logger.warn(
|
|
1049
|
+
`No local auth snapshot found for ${provider.name}/${validProfile}. Using token fallback.`
|
|
1050
|
+
);
|
|
1051
|
+
}
|
|
658
1052
|
}
|
|
659
1053
|
try {
|
|
660
1054
|
if (swapped) {
|
|
@@ -667,15 +1061,18 @@ var runCommand = new Command5("run").description("Run a command with a specific
|
|
|
667
1061
|
} else {
|
|
668
1062
|
const token = await getToken(validProvider, validProfile);
|
|
669
1063
|
if (!token) {
|
|
670
|
-
|
|
671
|
-
|
|
1064
|
+
fail(
|
|
1065
|
+
`No token found for ${provider.name}/${validProfile}. Try adding it again with "orbit add".`
|
|
1066
|
+
);
|
|
1067
|
+
return;
|
|
672
1068
|
}
|
|
1069
|
+
const envToken = token;
|
|
673
1070
|
const cliName = provider.getCliName();
|
|
674
1071
|
const envVar = provider.getEnvVar();
|
|
675
1072
|
const result = await execa(cliName, args, {
|
|
676
1073
|
env: {
|
|
677
1074
|
...process.env,
|
|
678
|
-
[envVar]:
|
|
1075
|
+
[envVar]: envToken
|
|
679
1076
|
},
|
|
680
1077
|
stdio: "inherit",
|
|
681
1078
|
reject: false
|
|
@@ -690,7 +1087,6 @@ var runCommand = new Command5("run").description("Run a command with a specific
|
|
|
690
1087
|
}
|
|
691
1088
|
}
|
|
692
1089
|
}
|
|
693
|
-
process.exit(process.exitCode ?? 0);
|
|
694
1090
|
} catch (error) {
|
|
695
1091
|
handleError(error);
|
|
696
1092
|
}
|
|
@@ -705,40 +1101,47 @@ var execCommand = new Command6("exec").description("Execute a command using the
|
|
|
705
1101
|
const provider = getProvider(validProvider);
|
|
706
1102
|
const currentProfile = getCurrentProfile(validProvider);
|
|
707
1103
|
if (!currentProfile) {
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
);
|
|
711
|
-
process.exit(1);
|
|
1104
|
+
fail(`No active profile set for ${provider.name}. Use "orbit use ${validProvider} <profile>" first.`);
|
|
1105
|
+
return;
|
|
712
1106
|
}
|
|
1107
|
+
const activeProfile = currentProfile;
|
|
1108
|
+
const cliName = provider.getCliName();
|
|
1109
|
+
logger.info(`Running as ${provider.name}/${activeProfile}...`);
|
|
1110
|
+
let swapped = false;
|
|
713
1111
|
if (provider.restoreAuth) {
|
|
714
|
-
await provider.restoreAuth(
|
|
1112
|
+
swapped = await provider.restoreAuth(activeProfile);
|
|
715
1113
|
}
|
|
716
|
-
|
|
717
|
-
logger.info(`Running as ${provider.name}/${currentProfile}...`);
|
|
718
|
-
if (provider.getAuthConfigPath?.()) {
|
|
1114
|
+
if (swapped) {
|
|
719
1115
|
const result2 = await execa2(cliName, args, {
|
|
720
1116
|
stdio: "inherit",
|
|
721
1117
|
reject: false
|
|
722
1118
|
});
|
|
723
|
-
process.
|
|
1119
|
+
process.exitCode = result2.exitCode ?? 0;
|
|
1120
|
+
return;
|
|
1121
|
+
}
|
|
1122
|
+
if (provider.restoreAuth) {
|
|
1123
|
+
logger.warn(
|
|
1124
|
+
`Unable to restore local auth snapshot for ${provider.name}/${activeProfile}. Falling back to token-based execution.`
|
|
1125
|
+
);
|
|
724
1126
|
}
|
|
725
|
-
const token = await getToken(validProvider,
|
|
1127
|
+
const token = await getToken(validProvider, activeProfile);
|
|
726
1128
|
if (!token) {
|
|
727
|
-
|
|
728
|
-
`No token found for ${provider.name}/${
|
|
1129
|
+
fail(
|
|
1130
|
+
`No token found for ${provider.name}/${activeProfile}. Cannot verify identity; try "orbit add ${validProvider} ${activeProfile}" first.`
|
|
729
1131
|
);
|
|
730
|
-
|
|
1132
|
+
return;
|
|
731
1133
|
}
|
|
1134
|
+
const envToken = token;
|
|
732
1135
|
const envVar = provider.getEnvVar();
|
|
733
1136
|
const result = await execa2(cliName, args, {
|
|
734
1137
|
env: {
|
|
735
1138
|
...process.env,
|
|
736
|
-
[envVar]:
|
|
1139
|
+
[envVar]: envToken
|
|
737
1140
|
},
|
|
738
1141
|
stdio: "inherit",
|
|
739
1142
|
reject: false
|
|
740
1143
|
});
|
|
741
|
-
process.
|
|
1144
|
+
process.exitCode = result.exitCode ?? 0;
|
|
742
1145
|
} catch (error) {
|
|
743
1146
|
handleError(error);
|
|
744
1147
|
}
|
|
@@ -751,6 +1154,23 @@ var currentCommand = new Command7("current").description("Show current active pr
|
|
|
751
1154
|
try {
|
|
752
1155
|
const config = loadConfig();
|
|
753
1156
|
const providers2 = Object.keys(config.providers);
|
|
1157
|
+
if (logger.isJsonMode()) {
|
|
1158
|
+
const current = providers2.map((providerName) => {
|
|
1159
|
+
const providerConfig = config.providers[providerName];
|
|
1160
|
+
if (!providerConfig?.current) {
|
|
1161
|
+
return null;
|
|
1162
|
+
}
|
|
1163
|
+
return {
|
|
1164
|
+
provider: providerName,
|
|
1165
|
+
profile: providerConfig.current
|
|
1166
|
+
};
|
|
1167
|
+
}).filter((entry) => entry !== null);
|
|
1168
|
+
logger.json({
|
|
1169
|
+
ok: true,
|
|
1170
|
+
current
|
|
1171
|
+
});
|
|
1172
|
+
return;
|
|
1173
|
+
}
|
|
754
1174
|
if (providers2.length === 0) {
|
|
755
1175
|
logger.info("No profiles configured yet.");
|
|
756
1176
|
return;
|
|
@@ -773,12 +1193,31 @@ var currentCommand = new Command7("current").description("Show current active pr
|
|
|
773
1193
|
}
|
|
774
1194
|
});
|
|
775
1195
|
|
|
1196
|
+
// src/commands/rotate-key.ts
|
|
1197
|
+
import { Command as Command8 } from "commander";
|
|
1198
|
+
var rotateKeyCommand = new Command8("rotate-key").description("Rotate local encryption key and re-encrypt stored tokens").action(async () => {
|
|
1199
|
+
const spinner = createSpinner("Rotating encryption key...");
|
|
1200
|
+
try {
|
|
1201
|
+
await rotateEncryptionKey();
|
|
1202
|
+
spinner.succeed("Encryption key rotated successfully.");
|
|
1203
|
+
} catch (error) {
|
|
1204
|
+
spinner.fail("Failed to rotate encryption key.");
|
|
1205
|
+
handleError(error);
|
|
1206
|
+
}
|
|
1207
|
+
});
|
|
1208
|
+
|
|
776
1209
|
// src/index.ts
|
|
777
1210
|
var require2 = createRequire(import.meta.url);
|
|
778
1211
|
var pkg = require2("../package.json");
|
|
1212
|
+
if (process.argv.includes("--json")) {
|
|
1213
|
+
setJsonMode(true);
|
|
1214
|
+
}
|
|
1215
|
+
if (process.argv.includes("--debug")) {
|
|
1216
|
+
process.env["ORBIT_DEBUG"] = "1";
|
|
1217
|
+
}
|
|
779
1218
|
registerProvider(vercelProvider);
|
|
780
|
-
var program = new
|
|
781
|
-
program.name("orbit").description("
|
|
1219
|
+
var program = new Command9();
|
|
1220
|
+
program.name("orbit").description("Switch Vercel identities instantly. No logout required.").version(pkg.version).option("--json", "Output machine-readable JSON logs").option("--debug", "Enable debug diagnostics (with secret redaction)").enablePositionalOptions();
|
|
782
1221
|
program.addCommand(addCommand);
|
|
783
1222
|
program.addCommand(listCommand);
|
|
784
1223
|
program.addCommand(removeCommand);
|
|
@@ -786,6 +1225,7 @@ program.addCommand(useCommand);
|
|
|
786
1225
|
program.addCommand(runCommand);
|
|
787
1226
|
program.addCommand(execCommand);
|
|
788
1227
|
program.addCommand(currentCommand);
|
|
1228
|
+
program.addCommand(rotateKeyCommand);
|
|
789
1229
|
try {
|
|
790
1230
|
await program.parseAsync(process.argv);
|
|
791
1231
|
} catch (error) {
|