@keywaysh/cli 0.1.11 → 0.1.13
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/dist/cli.js +191 -71
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -12,13 +12,14 @@ import pc11 from "picocolors";
|
|
|
12
12
|
|
|
13
13
|
// src/cmds/init.ts
|
|
14
14
|
import pc6 from "picocolors";
|
|
15
|
-
import
|
|
15
|
+
import prompts5 from "prompts";
|
|
16
16
|
|
|
17
17
|
// src/utils/git.ts
|
|
18
18
|
import { execSync } from "child_process";
|
|
19
19
|
import fs from "fs";
|
|
20
20
|
import path from "path";
|
|
21
21
|
import pc from "picocolors";
|
|
22
|
+
import prompts from "prompts";
|
|
22
23
|
function getCurrentRepoFullName() {
|
|
23
24
|
try {
|
|
24
25
|
if (!isGitRepository()) {
|
|
@@ -87,9 +88,47 @@ function checkEnvGitignore() {
|
|
|
87
88
|
return true;
|
|
88
89
|
}
|
|
89
90
|
}
|
|
90
|
-
function
|
|
91
|
-
|
|
92
|
-
|
|
91
|
+
function addEnvToGitignore() {
|
|
92
|
+
try {
|
|
93
|
+
const gitRoot = execSync("git rev-parse --show-toplevel", {
|
|
94
|
+
encoding: "utf-8",
|
|
95
|
+
stdio: "pipe"
|
|
96
|
+
}).trim();
|
|
97
|
+
const gitignorePath = path.join(gitRoot, ".gitignore");
|
|
98
|
+
const envEntry = ".env*";
|
|
99
|
+
if (fs.existsSync(gitignorePath)) {
|
|
100
|
+
const content = fs.readFileSync(gitignorePath, "utf-8");
|
|
101
|
+
const newContent = content.endsWith("\n") ? `${content}${envEntry}
|
|
102
|
+
` : `${content}
|
|
103
|
+
${envEntry}
|
|
104
|
+
`;
|
|
105
|
+
fs.writeFileSync(gitignorePath, newContent);
|
|
106
|
+
} else {
|
|
107
|
+
fs.writeFileSync(gitignorePath, `${envEntry}
|
|
108
|
+
`);
|
|
109
|
+
}
|
|
110
|
+
return true;
|
|
111
|
+
} catch {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
async function warnIfEnvNotGitignored() {
|
|
116
|
+
if (checkEnvGitignore()) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
console.log(pc.yellow("\u26A0\uFE0F .env files are not in .gitignore - secrets may be committed"));
|
|
120
|
+
const { addToGitignore } = await prompts({
|
|
121
|
+
type: "confirm",
|
|
122
|
+
name: "addToGitignore",
|
|
123
|
+
message: "Add .env* to .gitignore?",
|
|
124
|
+
initial: true
|
|
125
|
+
});
|
|
126
|
+
if (addToGitignore) {
|
|
127
|
+
if (addEnvToGitignore()) {
|
|
128
|
+
console.log(pc.green("\u2713 Added .env* to .gitignore"));
|
|
129
|
+
} else {
|
|
130
|
+
console.log(pc.red("\u2717 Failed to update .gitignore"));
|
|
131
|
+
}
|
|
93
132
|
}
|
|
94
133
|
}
|
|
95
134
|
|
|
@@ -101,7 +140,7 @@ var INTERNAL_POSTHOG_HOST = "https://eu.i.posthog.com";
|
|
|
101
140
|
// package.json
|
|
102
141
|
var package_default = {
|
|
103
142
|
name: "@keywaysh/cli",
|
|
104
|
-
version: "0.1.
|
|
143
|
+
version: "0.1.13",
|
|
105
144
|
description: "One link to all your secrets",
|
|
106
145
|
type: "module",
|
|
107
146
|
bin: {
|
|
@@ -409,9 +448,10 @@ async function deleteConnection(accessToken, connectionId) {
|
|
|
409
448
|
});
|
|
410
449
|
await handleResponse(response);
|
|
411
450
|
}
|
|
412
|
-
function getProviderAuthUrl(provider, redirectUri) {
|
|
413
|
-
const params =
|
|
414
|
-
|
|
451
|
+
function getProviderAuthUrl(provider, accessToken, redirectUri) {
|
|
452
|
+
const params = new URLSearchParams({ token: accessToken });
|
|
453
|
+
if (redirectUri) params.set("redirect_uri", redirectUri);
|
|
454
|
+
return `${API_BASE_URL}/v1/integrations/${provider}/authorize?${params}`;
|
|
415
455
|
}
|
|
416
456
|
async function getConnectionProjects(accessToken, connectionId) {
|
|
417
457
|
const response = await fetchWithTimeout(`${API_BASE_URL}/v1/integrations/connections/${connectionId}/projects`, {
|
|
@@ -718,7 +758,7 @@ var AnalyticsEvents = {
|
|
|
718
758
|
// src/cmds/readme.ts
|
|
719
759
|
import fs3 from "fs";
|
|
720
760
|
import path3 from "path";
|
|
721
|
-
import
|
|
761
|
+
import prompts2 from "prompts";
|
|
722
762
|
import pc2 from "picocolors";
|
|
723
763
|
import balanced from "balanced-match";
|
|
724
764
|
function generateBadge(repo) {
|
|
@@ -813,7 +853,7 @@ async function ensureReadme(repoName, cwd) {
|
|
|
813
853
|
console.log(pc2.yellow('No README found. Run "keyway readme add-badge" from a repo with a README.'));
|
|
814
854
|
return null;
|
|
815
855
|
}
|
|
816
|
-
const { confirm } = await
|
|
856
|
+
const { confirm } = await prompts2(
|
|
817
857
|
{
|
|
818
858
|
type: "confirm",
|
|
819
859
|
name: "confirm",
|
|
@@ -863,12 +903,12 @@ async function addBadgeToReadme(silent = false) {
|
|
|
863
903
|
import pc5 from "picocolors";
|
|
864
904
|
import fs4 from "fs";
|
|
865
905
|
import path4 from "path";
|
|
866
|
-
import
|
|
906
|
+
import prompts4 from "prompts";
|
|
867
907
|
|
|
868
908
|
// src/cmds/login.ts
|
|
869
909
|
import pc4 from "picocolors";
|
|
870
910
|
import readline from "readline";
|
|
871
|
-
import
|
|
911
|
+
import prompts3 from "prompts";
|
|
872
912
|
|
|
873
913
|
// src/utils/helpers.ts
|
|
874
914
|
import pc3 from "picocolors";
|
|
@@ -1009,7 +1049,7 @@ async function runTokenLogin() {
|
|
|
1009
1049
|
await openUrl(url);
|
|
1010
1050
|
console.log(pc4.gray("Select the detected repo (or scope manually)."));
|
|
1011
1051
|
console.log(pc4.gray("Permissions: Metadata \u2192 Read-only; Account permissions: None."));
|
|
1012
|
-
const { token } = await
|
|
1052
|
+
const { token } = await prompts3(
|
|
1013
1053
|
{
|
|
1014
1054
|
type: "password",
|
|
1015
1055
|
name: "token",
|
|
@@ -1127,7 +1167,7 @@ async function pushCommand(options) {
|
|
|
1127
1167
|
}
|
|
1128
1168
|
}
|
|
1129
1169
|
if (!environment && !envFile && isInteractive2 && candidates.length > 0) {
|
|
1130
|
-
const { choice } = await
|
|
1170
|
+
const { choice } = await prompts4(
|
|
1131
1171
|
{
|
|
1132
1172
|
type: "select",
|
|
1133
1173
|
name: "choice",
|
|
@@ -1150,7 +1190,7 @@ async function pushCommand(options) {
|
|
|
1150
1190
|
envFile = choice.file;
|
|
1151
1191
|
environment = choice.env;
|
|
1152
1192
|
} else if (choice === "custom") {
|
|
1153
|
-
const { fileInput } = await
|
|
1193
|
+
const { fileInput } = await prompts4(
|
|
1154
1194
|
{
|
|
1155
1195
|
type: "text",
|
|
1156
1196
|
name: "fileInput",
|
|
@@ -1200,7 +1240,7 @@ async function pushCommand(options) {
|
|
|
1200
1240
|
if (!isInteractive3) {
|
|
1201
1241
|
throw new Error("Confirmation required. Re-run with --yes in non-interactive environments.");
|
|
1202
1242
|
}
|
|
1203
|
-
const { confirm } = await
|
|
1243
|
+
const { confirm } = await prompts4(
|
|
1204
1244
|
{
|
|
1205
1245
|
type: "confirm",
|
|
1206
1246
|
name: "confirm",
|
|
@@ -1312,23 +1352,20 @@ async function ensureLoginAndGitHubApp(repoFullName, options = {}) {
|
|
|
1312
1352
|
if (!allowPrompt || !isInteractive()) {
|
|
1313
1353
|
throw new Error('No Keyway session found. Run "keyway login" to authenticate.');
|
|
1314
1354
|
}
|
|
1355
|
+
const deviceStart = await startDeviceLogin(repoFullName);
|
|
1356
|
+
const installUrl = deviceStart.githubAppInstallUrl || "https://github.com/apps/keyway/installations/new";
|
|
1315
1357
|
console.log("");
|
|
1316
|
-
|
|
1317
|
-
console.log(pc6.gray(" Installing the app will also log you in."));
|
|
1318
|
-
console.log("");
|
|
1319
|
-
const { shouldProceed } = await prompts4({
|
|
1358
|
+
const { shouldProceed } = await prompts5({
|
|
1320
1359
|
type: "confirm",
|
|
1321
1360
|
name: "shouldProceed",
|
|
1322
|
-
message: "Open browser to
|
|
1361
|
+
message: "Open browser to sign in?",
|
|
1323
1362
|
initial: true
|
|
1324
1363
|
});
|
|
1325
1364
|
if (!shouldProceed) {
|
|
1326
1365
|
throw new Error('Setup required. Run "keyway init" when ready.');
|
|
1327
1366
|
}
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
await openUrl(installUrl);
|
|
1331
|
-
console.log(pc6.blue("\u23F3 Waiting for installation & authorization..."));
|
|
1367
|
+
await openUrl(deviceStart.verificationUriComplete);
|
|
1368
|
+
console.log(pc6.blue("\u23F3 Waiting for authorization..."));
|
|
1332
1369
|
console.log(pc6.gray(" (Press Ctrl+C to cancel)\n"));
|
|
1333
1370
|
const pollIntervalMs = Math.max((deviceStart.interval ?? 5) * 1e3, POLL_INTERVAL_MS);
|
|
1334
1371
|
const startTime = Date.now();
|
|
@@ -1337,30 +1374,73 @@ async function ensureLoginAndGitHubApp(repoFullName, options = {}) {
|
|
|
1337
1374
|
while (Date.now() - startTime < POLL_TIMEOUT_MS) {
|
|
1338
1375
|
await sleep(pollIntervalMs);
|
|
1339
1376
|
try {
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1377
|
+
const result = await pollDeviceLogin(deviceStart.deviceCode);
|
|
1378
|
+
if (result.status === "approved" && result.keywayToken) {
|
|
1379
|
+
accessToken = result.keywayToken;
|
|
1380
|
+
await saveAuthToken(result.keywayToken, {
|
|
1381
|
+
githubLogin: result.githubLogin,
|
|
1382
|
+
expiresAt: result.expiresAt
|
|
1383
|
+
});
|
|
1384
|
+
console.log(pc6.green("\u2713 Signed in!"));
|
|
1385
|
+
if (result.githubLogin) {
|
|
1386
|
+
identifyUser(result.githubLogin, {
|
|
1387
|
+
github_username: result.githubLogin,
|
|
1388
|
+
login_method: "device_flow"
|
|
1347
1389
|
});
|
|
1348
|
-
console.log(pc6.green("\u2713 Signed in!"));
|
|
1349
|
-
if (result.githubLogin) {
|
|
1350
|
-
identifyUser(result.githubLogin, {
|
|
1351
|
-
github_username: result.githubLogin,
|
|
1352
|
-
login_method: "github_app"
|
|
1353
|
-
});
|
|
1354
|
-
}
|
|
1355
1390
|
}
|
|
1391
|
+
break;
|
|
1356
1392
|
}
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
}
|
|
1393
|
+
consecutiveErrors = 0;
|
|
1394
|
+
process.stdout.write(pc6.gray("."));
|
|
1395
|
+
} catch (error) {
|
|
1396
|
+
consecutiveErrors++;
|
|
1397
|
+
if (consecutiveErrors >= MAX_CONSECUTIVE_ERRORS) {
|
|
1398
|
+
const errorMsg = error instanceof Error ? error.message : "Unknown error";
|
|
1399
|
+
throw new Error(`Login failed after ${MAX_CONSECUTIVE_ERRORS} consecutive errors: ${errorMsg}`);
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
if (!accessToken) {
|
|
1404
|
+
console.log("");
|
|
1405
|
+
console.log(pc6.yellow("\u26A0 Timed out waiting for sign in."));
|
|
1406
|
+
throw new Error("Sign in timed out. Please try again.");
|
|
1407
|
+
}
|
|
1408
|
+
const installStatus = await checkGitHubAppInstallation(repoOwner, repoName, accessToken);
|
|
1409
|
+
if (installStatus.installed) {
|
|
1410
|
+
console.log(pc6.green("\u2713 GitHub App installed"));
|
|
1411
|
+
console.log("");
|
|
1412
|
+
return accessToken;
|
|
1413
|
+
}
|
|
1414
|
+
console.log("");
|
|
1415
|
+
console.log(pc6.yellow("\u26A0 GitHub App not installed on this repository"));
|
|
1416
|
+
console.log(pc6.gray(" The Keyway GitHub App is required for secure access."));
|
|
1417
|
+
console.log("");
|
|
1418
|
+
const { shouldInstall } = await prompts5({
|
|
1419
|
+
type: "confirm",
|
|
1420
|
+
name: "shouldInstall",
|
|
1421
|
+
message: "Open browser to install GitHub App?",
|
|
1422
|
+
initial: true
|
|
1423
|
+
});
|
|
1424
|
+
if (!shouldInstall) {
|
|
1425
|
+
console.log(pc6.gray(`
|
|
1426
|
+
Install later: ${installUrl}`));
|
|
1427
|
+
throw new Error("GitHub App installation required.");
|
|
1428
|
+
}
|
|
1429
|
+
await openUrl(installUrl);
|
|
1430
|
+
console.log(pc6.blue("\u23F3 Waiting for GitHub App installation..."));
|
|
1431
|
+
console.log(pc6.gray(' Add this repository and click "Install"'));
|
|
1432
|
+
console.log(pc6.gray(" Then return here - the CLI will detect it automatically"));
|
|
1433
|
+
console.log(pc6.gray(" (Press Ctrl+C to cancel)\n"));
|
|
1434
|
+
const installStartTime = Date.now();
|
|
1435
|
+
consecutiveErrors = 0;
|
|
1436
|
+
while (Date.now() - installStartTime < POLL_TIMEOUT_MS) {
|
|
1437
|
+
await sleep(POLL_INTERVAL_MS);
|
|
1438
|
+
try {
|
|
1439
|
+
const pollStatus = await checkGitHubAppInstallation(repoOwner, repoName, accessToken);
|
|
1440
|
+
if (pollStatus.installed) {
|
|
1441
|
+
console.log(pc6.green("\u2713 GitHub App installed!"));
|
|
1442
|
+
console.log("");
|
|
1443
|
+
return accessToken;
|
|
1364
1444
|
}
|
|
1365
1445
|
consecutiveErrors = 0;
|
|
1366
1446
|
process.stdout.write(pc6.gray("."));
|
|
@@ -1368,14 +1448,14 @@ async function ensureLoginAndGitHubApp(repoFullName, options = {}) {
|
|
|
1368
1448
|
consecutiveErrors++;
|
|
1369
1449
|
if (consecutiveErrors >= MAX_CONSECUTIVE_ERRORS) {
|
|
1370
1450
|
const errorMsg = error instanceof Error ? error.message : "Unknown error";
|
|
1371
|
-
throw new Error(`
|
|
1451
|
+
throw new Error(`Installation check failed after ${MAX_CONSECUTIVE_ERRORS} consecutive errors: ${errorMsg}`);
|
|
1372
1452
|
}
|
|
1373
1453
|
}
|
|
1374
1454
|
}
|
|
1375
1455
|
console.log("");
|
|
1376
|
-
console.log(pc6.yellow("\u26A0 Timed out waiting for
|
|
1456
|
+
console.log(pc6.yellow("\u26A0 Timed out waiting for installation."));
|
|
1377
1457
|
console.log(pc6.gray(` Install the GitHub App: ${installUrl}`));
|
|
1378
|
-
throw new Error("
|
|
1458
|
+
throw new Error("GitHub App installation timed out.");
|
|
1379
1459
|
}
|
|
1380
1460
|
async function ensureGitHubAppInstalledOnly(repoFullName, accessToken) {
|
|
1381
1461
|
const [repoOwner, repoName] = repoFullName.split("/");
|
|
@@ -1404,7 +1484,7 @@ async function ensureGitHubAppInstalledOnly(repoFullName, accessToken) {
|
|
|
1404
1484
|
console.log(pc6.gray(` Install the Keyway GitHub App: ${status.installUrl}`));
|
|
1405
1485
|
throw new Error("GitHub App installation required.");
|
|
1406
1486
|
}
|
|
1407
|
-
const { shouldInstall } = await
|
|
1487
|
+
const { shouldInstall } = await prompts5({
|
|
1408
1488
|
type: "confirm",
|
|
1409
1489
|
name: "shouldInstall",
|
|
1410
1490
|
message: "Open browser to install Keyway GitHub App?",
|
|
@@ -1478,7 +1558,7 @@ async function initCommand(options = {}) {
|
|
|
1478
1558
|
if (envCandidates.length > 0 && isInteractive2) {
|
|
1479
1559
|
console.log(pc6.gray(` Found ${envCandidates.length} env file(s): ${envCandidates.map((c) => c.file).join(", ")}
|
|
1480
1560
|
`));
|
|
1481
|
-
const { shouldPush } = await
|
|
1561
|
+
const { shouldPush } = await prompts5({
|
|
1482
1562
|
type: "confirm",
|
|
1483
1563
|
name: "shouldPush",
|
|
1484
1564
|
message: "Push secrets now?",
|
|
@@ -1536,7 +1616,7 @@ async function initCommand(options = {}) {
|
|
|
1536
1616
|
import pc7 from "picocolors";
|
|
1537
1617
|
import fs5 from "fs";
|
|
1538
1618
|
import path5 from "path";
|
|
1539
|
-
import
|
|
1619
|
+
import prompts6 from "prompts";
|
|
1540
1620
|
async function pullCommand(options) {
|
|
1541
1621
|
try {
|
|
1542
1622
|
const environment = options.env || "development";
|
|
@@ -1561,7 +1641,7 @@ async function pullCommand(options) {
|
|
|
1561
1641
|
} else if (!isInteractive2) {
|
|
1562
1642
|
throw new Error(`File ${envFile} exists. Re-run with --yes to overwrite or choose a different --file.`);
|
|
1563
1643
|
} else {
|
|
1564
|
-
const { confirm } = await
|
|
1644
|
+
const { confirm } = await prompts6(
|
|
1565
1645
|
{
|
|
1566
1646
|
type: "confirm",
|
|
1567
1647
|
name: "confirm",
|
|
@@ -1921,7 +2001,7 @@ Summary: ${formatSummary(results)}`);
|
|
|
1921
2001
|
|
|
1922
2002
|
// src/cmds/connect.ts
|
|
1923
2003
|
import pc9 from "picocolors";
|
|
1924
|
-
import
|
|
2004
|
+
import prompts7 from "prompts";
|
|
1925
2005
|
var TOKEN_AUTH_PROVIDERS = ["railway"];
|
|
1926
2006
|
function getTokenCreationUrl(provider) {
|
|
1927
2007
|
switch (provider) {
|
|
@@ -1938,7 +2018,7 @@ async function connectWithTokenFlow(accessToken, provider, displayName) {
|
|
|
1938
2018
|
console.log(pc9.yellow(` Do NOT use "No workspace" - it won't have access to your projects.`));
|
|
1939
2019
|
}
|
|
1940
2020
|
await openUrl(tokenUrl);
|
|
1941
|
-
const { token } = await
|
|
2021
|
+
const { token } = await prompts7({
|
|
1942
2022
|
type: "password",
|
|
1943
2023
|
name: "token",
|
|
1944
2024
|
message: `${displayName} API Token:`
|
|
@@ -1970,7 +2050,7 @@ async function connectWithTokenFlow(accessToken, provider, displayName) {
|
|
|
1970
2050
|
}
|
|
1971
2051
|
}
|
|
1972
2052
|
async function connectWithOAuthFlow(accessToken, provider, displayName) {
|
|
1973
|
-
const authUrl = getProviderAuthUrl(provider);
|
|
2053
|
+
const authUrl = getProviderAuthUrl(provider, accessToken);
|
|
1974
2054
|
const startTime = /* @__PURE__ */ new Date();
|
|
1975
2055
|
await openUrl(authUrl);
|
|
1976
2056
|
console.log(pc9.gray("Waiting for authorization..."));
|
|
@@ -2015,7 +2095,7 @@ async function connectCommand(provider, options = {}) {
|
|
|
2015
2095
|
const { connections } = await getConnections(accessToken);
|
|
2016
2096
|
const existingConnection = connections.find((c) => c.provider === provider.toLowerCase());
|
|
2017
2097
|
if (existingConnection) {
|
|
2018
|
-
const { reconnect } = await
|
|
2098
|
+
const { reconnect } = await prompts7({
|
|
2019
2099
|
type: "confirm",
|
|
2020
2100
|
name: "reconnect",
|
|
2021
2101
|
message: `You're already connected to ${providerInfo.displayName}. Reconnect?`,
|
|
@@ -2087,7 +2167,7 @@ async function disconnectCommand(provider, options = {}) {
|
|
|
2087
2167
|
return;
|
|
2088
2168
|
}
|
|
2089
2169
|
const providerName = provider.charAt(0).toUpperCase() + provider.slice(1);
|
|
2090
|
-
const { confirm } = await
|
|
2170
|
+
const { confirm } = await prompts7({
|
|
2091
2171
|
type: "confirm",
|
|
2092
2172
|
name: "confirm",
|
|
2093
2173
|
message: `Disconnect from ${providerName}?`,
|
|
@@ -2117,7 +2197,7 @@ async function disconnectCommand(provider, options = {}) {
|
|
|
2117
2197
|
|
|
2118
2198
|
// src/cmds/sync.ts
|
|
2119
2199
|
import pc10 from "picocolors";
|
|
2120
|
-
import
|
|
2200
|
+
import prompts8 from "prompts";
|
|
2121
2201
|
function mapToVercelEnvironment(keywayEnv) {
|
|
2122
2202
|
const mapping = {
|
|
2123
2203
|
production: "production",
|
|
@@ -2136,12 +2216,24 @@ function mapToRailwayEnvironment(keywayEnv) {
|
|
|
2136
2216
|
};
|
|
2137
2217
|
return mapping[keywayEnv.toLowerCase()] || "production";
|
|
2138
2218
|
}
|
|
2219
|
+
function mapToNetlifyEnvironment(keywayEnv) {
|
|
2220
|
+
const mapping = {
|
|
2221
|
+
production: "production",
|
|
2222
|
+
staging: "branch-deploy",
|
|
2223
|
+
preview: "deploy-preview",
|
|
2224
|
+
dev: "dev",
|
|
2225
|
+
development: "dev"
|
|
2226
|
+
};
|
|
2227
|
+
return mapping[keywayEnv.toLowerCase()] || "production";
|
|
2228
|
+
}
|
|
2139
2229
|
function mapToProviderEnvironment(provider, keywayEnv) {
|
|
2140
2230
|
switch (provider.toLowerCase()) {
|
|
2141
2231
|
case "vercel":
|
|
2142
2232
|
return mapToVercelEnvironment(keywayEnv);
|
|
2143
2233
|
case "railway":
|
|
2144
2234
|
return mapToRailwayEnvironment(keywayEnv);
|
|
2235
|
+
case "netlify":
|
|
2236
|
+
return mapToNetlifyEnvironment(keywayEnv);
|
|
2145
2237
|
default:
|
|
2146
2238
|
return keywayEnv;
|
|
2147
2239
|
}
|
|
@@ -2236,7 +2328,7 @@ async function promptProjectSelection(projects, repoFullName) {
|
|
|
2236
2328
|
}
|
|
2237
2329
|
return { title, value: p.id };
|
|
2238
2330
|
});
|
|
2239
|
-
const { projectChoice } = await
|
|
2331
|
+
const { projectChoice } = await prompts8({
|
|
2240
2332
|
type: "select",
|
|
2241
2333
|
name: "projectChoice",
|
|
2242
2334
|
message: "Select a project:",
|
|
@@ -2263,13 +2355,39 @@ async function syncCommand(provider, options = {}) {
|
|
|
2263
2355
|
process.exit(1);
|
|
2264
2356
|
}
|
|
2265
2357
|
console.log(pc10.gray(`Repository: ${repoFullName}`));
|
|
2358
|
+
const vaultExists = await checkVaultExists(accessToken, repoFullName);
|
|
2359
|
+
if (!vaultExists) {
|
|
2360
|
+
console.log(pc10.yellow(`
|
|
2361
|
+
No vault found for ${repoFullName}.`));
|
|
2362
|
+
const { shouldCreate } = await prompts8({
|
|
2363
|
+
type: "confirm",
|
|
2364
|
+
name: "shouldCreate",
|
|
2365
|
+
message: "Create vault now?",
|
|
2366
|
+
initial: true
|
|
2367
|
+
});
|
|
2368
|
+
if (!shouldCreate) {
|
|
2369
|
+
console.log(pc10.gray("Cancelled. Run `keyway init` to create a vault first."));
|
|
2370
|
+
process.exit(0);
|
|
2371
|
+
}
|
|
2372
|
+
console.log(pc10.gray("\nCreating vault..."));
|
|
2373
|
+
try {
|
|
2374
|
+
await initVault(repoFullName, accessToken);
|
|
2375
|
+
console.log(pc10.green(`\u2713 Vault created for ${repoFullName}
|
|
2376
|
+
`));
|
|
2377
|
+
} catch (error) {
|
|
2378
|
+
const message = error instanceof Error ? error.message : "Failed to create vault";
|
|
2379
|
+
console.error(pc10.red(`
|
|
2380
|
+
\u2717 ${message}`));
|
|
2381
|
+
process.exit(1);
|
|
2382
|
+
}
|
|
2383
|
+
}
|
|
2266
2384
|
let { connections } = await getConnections(accessToken);
|
|
2267
2385
|
let connection = connections.find((c) => c.provider === provider.toLowerCase());
|
|
2268
2386
|
if (!connection) {
|
|
2269
2387
|
const providerDisplayName = provider.charAt(0).toUpperCase() + provider.slice(1);
|
|
2270
2388
|
console.log(pc10.yellow(`
|
|
2271
2389
|
Not connected to ${providerDisplayName}.`));
|
|
2272
|
-
const { shouldConnect } = await
|
|
2390
|
+
const { shouldConnect } = await prompts8({
|
|
2273
2391
|
type: "confirm",
|
|
2274
2392
|
name: "shouldConnect",
|
|
2275
2393
|
message: `Connect to ${providerDisplayName} now?`,
|
|
@@ -2328,7 +2446,7 @@ Connection to ${providerDisplayName} failed.`));
|
|
|
2328
2446
|
} else if (autoMatch && autoMatch.matchType === "partial_name") {
|
|
2329
2447
|
const partialDisplayName = getProjectDisplayName(autoMatch.project);
|
|
2330
2448
|
console.log(pc10.yellow(`Detected project: ${partialDisplayName} (partial match)`));
|
|
2331
|
-
const { useDetected } = await
|
|
2449
|
+
const { useDetected } = await prompts8({
|
|
2332
2450
|
type: "confirm",
|
|
2333
2451
|
name: "useDetected",
|
|
2334
2452
|
message: `Use ${partialDisplayName}?`,
|
|
@@ -2352,7 +2470,7 @@ Connection to ${providerDisplayName} failed.`));
|
|
|
2352
2470
|
console.log(pc10.yellow(` Project linked to: ${selectedProject.linkedRepo}`));
|
|
2353
2471
|
}
|
|
2354
2472
|
console.log("");
|
|
2355
|
-
const { continueAnyway } = await
|
|
2473
|
+
const { continueAnyway } = await prompts8({
|
|
2356
2474
|
type: "confirm",
|
|
2357
2475
|
name: "continueAnyway",
|
|
2358
2476
|
message: "Continue anyway?",
|
|
@@ -2383,7 +2501,7 @@ Connection to ${providerDisplayName} failed.`));
|
|
|
2383
2501
|
console.log(pc10.yellow(` Project linked to: ${selectedProject.linkedRepo}`));
|
|
2384
2502
|
}
|
|
2385
2503
|
console.log("");
|
|
2386
|
-
const { continueAnyway } = await
|
|
2504
|
+
const { continueAnyway } = await prompts8({
|
|
2387
2505
|
type: "confirm",
|
|
2388
2506
|
name: "continueAnyway",
|
|
2389
2507
|
message: "Are you sure you want to sync with this project?",
|
|
@@ -2404,7 +2522,7 @@ Connection to ${providerDisplayName} failed.`));
|
|
|
2404
2522
|
if (needsEnvPrompt || needsDirectionPrompt) {
|
|
2405
2523
|
if (needsEnvPrompt) {
|
|
2406
2524
|
const vaultEnvs = await getVaultEnvironments(accessToken, repoFullName);
|
|
2407
|
-
const { selectedEnv } = await
|
|
2525
|
+
const { selectedEnv } = await prompts8({
|
|
2408
2526
|
type: "select",
|
|
2409
2527
|
name: "selectedEnv",
|
|
2410
2528
|
message: "Keyway environment:",
|
|
@@ -2428,7 +2546,7 @@ Connection to ${providerDisplayName} failed.`));
|
|
|
2428
2546
|
providerEnv = selectedProject.environments[0];
|
|
2429
2547
|
console.log(pc10.gray(`Using ${providerName} environment: ${providerEnv}`));
|
|
2430
2548
|
} else {
|
|
2431
|
-
const { selectedProviderEnv } = await
|
|
2549
|
+
const { selectedProviderEnv } = await prompts8({
|
|
2432
2550
|
type: "select",
|
|
2433
2551
|
name: "selectedProviderEnv",
|
|
2434
2552
|
message: `${providerName} environment:`,
|
|
@@ -2474,7 +2592,7 @@ Connection to ${providerDisplayName} failed.`));
|
|
|
2474
2592
|
} else if (diff.providerCount === 0 && diff.keywayCount > 0) {
|
|
2475
2593
|
defaultDirection = 0;
|
|
2476
2594
|
}
|
|
2477
|
-
const { selectedDirection } = await
|
|
2595
|
+
const { selectedDirection } = await prompts8({
|
|
2478
2596
|
type: "select",
|
|
2479
2597
|
name: "selectedDirection",
|
|
2480
2598
|
message: "Sync direction:",
|
|
@@ -2505,7 +2623,7 @@ Connection to ${providerDisplayName} failed.`));
|
|
|
2505
2623
|
console.log(pc10.yellow(`
|
|
2506
2624
|
\u26A0\uFE0F Your Keyway vault is empty for "${keywayEnv}", but ${providerName} has ${status.providerSecretCount} secrets.`));
|
|
2507
2625
|
console.log(pc10.gray(` (Use --environment to sync a different environment)`));
|
|
2508
|
-
const { importFirst } = await
|
|
2626
|
+
const { importFirst } = await prompts8({
|
|
2509
2627
|
type: "confirm",
|
|
2510
2628
|
name: "importFirst",
|
|
2511
2629
|
message: `Import secrets from ${providerName} first?`,
|
|
@@ -2596,7 +2714,7 @@ async function executeSyncOperation(accessToken, repoFullName, connectionId, pro
|
|
|
2596
2714
|
console.log("");
|
|
2597
2715
|
if (!skipConfirm) {
|
|
2598
2716
|
const target = direction === "push" ? providerName : "Keyway";
|
|
2599
|
-
const { confirm } = await
|
|
2717
|
+
const { confirm } = await prompts8({
|
|
2600
2718
|
type: "confirm",
|
|
2601
2719
|
name: "confirm",
|
|
2602
2720
|
message: `Apply ${totalChanges} changes to ${target}?`,
|
|
@@ -2654,7 +2772,6 @@ ${pc11.gray(TAGLINE)}
|
|
|
2654
2772
|
`);
|
|
2655
2773
|
};
|
|
2656
2774
|
showBanner();
|
|
2657
|
-
warnIfEnvNotGitignored();
|
|
2658
2775
|
program.name("keyway").description(TAGLINE).version(package_default.version);
|
|
2659
2776
|
program.command("init").description("Initialize a vault for the current repository").option("--no-login-prompt", "Fail instead of prompting to login if unauthenticated").action(async (options) => {
|
|
2660
2777
|
await initCommand(options);
|
|
@@ -2686,7 +2803,10 @@ program.command("disconnect <provider>").description("Disconnect from a provider
|
|
|
2686
2803
|
program.command("sync <provider>").description("Sync secrets with a provider (e.g., vercel)").option("--push", "Export secrets from Keyway to provider").option("--pull", "Import secrets from provider to Keyway").option("-e, --environment <env>", "Keyway environment (default: production)").option("--provider-env <env>", "Provider environment (default: production)").option("--project <project>", "Provider project name or ID").option("--allow-delete", "Allow deleting secrets not in source").option("-y, --yes", "Skip confirmation prompt").option("--no-login-prompt", "Fail instead of prompting to login if unauthenticated").action(async (provider, options) => {
|
|
2687
2804
|
await syncCommand(provider, options);
|
|
2688
2805
|
});
|
|
2689
|
-
|
|
2806
|
+
(async () => {
|
|
2807
|
+
await warnIfEnvNotGitignored();
|
|
2808
|
+
await program.parseAsync();
|
|
2809
|
+
})().catch((error) => {
|
|
2690
2810
|
console.error(pc11.red("Error:"), error.message || error);
|
|
2691
2811
|
process.exit(1);
|
|
2692
2812
|
});
|