@keywaysh/cli 0.1.5 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli.js +306 -276
  2. package/package.json +4 -1
package/dist/cli.js CHANGED
@@ -8,12 +8,11 @@ import {
8
8
 
9
9
  // src/cli.ts
10
10
  import { Command } from "commander";
11
- import pc10 from "picocolors";
11
+ import pc11 from "picocolors";
12
12
 
13
13
  // src/cmds/init.ts
14
- import pc5 from "picocolors";
14
+ import pc6 from "picocolors";
15
15
  import prompts4 from "prompts";
16
- import open2 from "open";
17
16
 
18
17
  // src/utils/git.ts
19
18
  import { execSync } from "child_process";
@@ -102,7 +101,7 @@ var INTERNAL_POSTHOG_HOST = "https://eu.i.posthog.com";
102
101
  // package.json
103
102
  var package_default = {
104
103
  name: "@keywaysh/cli",
105
- version: "0.1.5",
104
+ version: "0.1.6",
106
105
  description: "One link to all your secrets",
107
106
  type: "module",
108
107
  bin: {
@@ -120,6 +119,7 @@ var package_default = {
120
119
  prepublishOnly: "pnpm run build",
121
120
  test: "pnpm exec vitest run",
122
121
  "test:watch": "pnpm exec vitest",
122
+ "test:coverage": "pnpm exec vitest run --coverage",
123
123
  release: "npm version patch && git push && git push --tags",
124
124
  "release:minor": "npm version minor && git push && git push --tags",
125
125
  "release:major": "npm version major && git push && git push --tags"
@@ -158,6 +158,8 @@ var package_default = {
158
158
  "@types/balanced-match": "^3.0.2",
159
159
  "@types/node": "^24.2.0",
160
160
  "@types/prompts": "^2.4.9",
161
+ "@vitest/coverage-v8": "^3.0.0",
162
+ msw: "^2.12.4",
161
163
  tsup: "^8.5.0",
162
164
  tsx: "^4.20.3",
163
165
  typescript: "^5.9.2",
@@ -776,8 +778,8 @@ function findReadmePath(cwd) {
776
778
  async function ensureReadme(repoName, cwd) {
777
779
  const existing = findReadmePath(cwd);
778
780
  if (existing) return existing;
779
- const isInteractive3 = process.stdin.isTTY && process.stdout.isTTY;
780
- if (!isInteractive3) {
781
+ const isInteractive2 = process.stdin.isTTY && process.stdout.isTTY;
782
+ if (!isInteractive2) {
781
783
  console.log(pc2.yellow('No README found. Run "keyway readme add-badge" from a repo with a README.'));
782
784
  return null;
783
785
  }
@@ -828,22 +830,49 @@ async function addBadgeToReadme(silent = false) {
828
830
  }
829
831
 
830
832
  // src/cmds/push.ts
831
- import pc4 from "picocolors";
833
+ import pc5 from "picocolors";
832
834
  import fs4 from "fs";
833
835
  import path4 from "path";
834
836
  import prompts3 from "prompts";
835
837
 
836
838
  // src/cmds/login.ts
837
- import pc3 from "picocolors";
839
+ import pc4 from "picocolors";
838
840
  import readline from "readline";
839
- import open from "open";
840
841
  import prompts2 from "prompts";
842
+
843
+ // src/utils/helpers.ts
844
+ import pc3 from "picocolors";
845
+ import open from "open";
841
846
  function sleep(ms) {
842
847
  return new Promise((resolve) => setTimeout(resolve, ms));
843
848
  }
849
+ async function openUrl(url) {
850
+ console.log(pc3.gray(`
851
+ Open this URL in your browser:
852
+ ${url}
853
+ `));
854
+ await open(url).catch(() => {
855
+ });
856
+ }
844
857
  function isInteractive() {
845
858
  return Boolean(process.stdout.isTTY && process.stdin.isTTY && !process.env.CI);
846
859
  }
860
+ function showUpgradePrompt(message, upgradeUrl) {
861
+ console.log("");
862
+ console.log(pc3.dim("\u2500".repeat(50)));
863
+ console.log("");
864
+ console.log(` ${pc3.yellow("\u26A1")} ${pc3.bold("Plan Limit Reached")}`);
865
+ console.log("");
866
+ console.log(pc3.white(` ${message}`));
867
+ console.log("");
868
+ console.log(` ${pc3.cyan("Upgrade now \u2192")} ${pc3.underline(upgradeUrl)}`);
869
+ console.log("");
870
+ console.log(pc3.dim("\u2500".repeat(50)));
871
+ console.log("");
872
+ }
873
+ var MAX_CONSECUTIVE_ERRORS = 5;
874
+
875
+ // src/cmds/login.ts
847
876
  async function promptYesNo(question, defaultYes = true) {
848
877
  return new Promise((resolve) => {
849
878
  const rl = readline.createInterface({
@@ -861,52 +890,60 @@ async function promptYesNo(question, defaultYes = true) {
861
890
  });
862
891
  }
863
892
  async function runLoginFlow() {
864
- console.log(pc3.blue("\u{1F510} Starting Keyway login...\n"));
893
+ console.log(pc4.blue("\u{1F510} Starting Keyway login...\n"));
865
894
  const repoName = detectGitRepo();
866
895
  const start = await startDeviceLogin(repoName);
867
896
  const verifyUrl = start.verificationUriComplete || start.verificationUri;
868
897
  if (!verifyUrl) {
869
898
  throw new Error("Missing verification URL from the auth server.");
870
899
  }
871
- console.log(`Code: ${pc3.bold(pc3.green(start.userCode))}`);
900
+ console.log(`Code: ${pc4.bold(pc4.green(start.userCode))}`);
901
+ await openUrl(verifyUrl);
872
902
  console.log("Waiting for auth...");
873
- open(verifyUrl).catch(() => {
874
- console.log(pc3.gray(`Open this URL in your browser: ${verifyUrl}`));
875
- });
876
903
  const pollIntervalMs = (start.interval ?? 5) * 1e3;
877
904
  const maxTimeoutMs = Math.min((start.expiresIn ?? 900) * 1e3, 30 * 60 * 1e3);
878
905
  const startTime = Date.now();
906
+ let consecutiveErrors = 0;
879
907
  while (true) {
880
908
  if (Date.now() - startTime > maxTimeoutMs) {
881
909
  throw new Error('Login timed out. Please run "keyway login" again.');
882
910
  }
883
911
  await sleep(pollIntervalMs);
884
- const result = await pollDeviceLogin(start.deviceCode);
885
- if (result.status === "pending") {
886
- continue;
887
- }
888
- if (result.status === "approved" && result.keywayToken) {
889
- await saveAuthToken(result.keywayToken, {
890
- githubLogin: result.githubLogin,
891
- expiresAt: result.expiresAt
892
- });
893
- trackEvent(AnalyticsEvents.CLI_LOGIN, {
894
- method: "device",
895
- repo: repoName
896
- });
897
- if (result.githubLogin) {
898
- identifyUser(result.githubLogin, {
899
- github_username: result.githubLogin,
900
- login_method: "device"
912
+ try {
913
+ const result = await pollDeviceLogin(start.deviceCode);
914
+ consecutiveErrors = 0;
915
+ if (result.status === "pending") {
916
+ continue;
917
+ }
918
+ if (result.status === "approved" && result.keywayToken) {
919
+ await saveAuthToken(result.keywayToken, {
920
+ githubLogin: result.githubLogin,
921
+ expiresAt: result.expiresAt
922
+ });
923
+ trackEvent(AnalyticsEvents.CLI_LOGIN, {
924
+ method: "device",
925
+ repo: repoName
901
926
  });
927
+ if (result.githubLogin) {
928
+ identifyUser(result.githubLogin, {
929
+ github_username: result.githubLogin,
930
+ login_method: "device"
931
+ });
932
+ }
933
+ console.log(pc4.green("\n\u2713 Login successful"));
934
+ if (result.githubLogin) {
935
+ console.log(`Authenticated GitHub user: ${pc4.cyan(result.githubLogin)}`);
936
+ }
937
+ return result.keywayToken;
902
938
  }
903
- console.log(pc3.green("\n\u2713 Login successful"));
904
- if (result.githubLogin) {
905
- console.log(`Authenticated GitHub user: ${pc3.cyan(result.githubLogin)}`);
939
+ throw new Error(result.message || "Authentication failed");
940
+ } catch (error) {
941
+ consecutiveErrors++;
942
+ if (consecutiveErrors >= MAX_CONSECUTIVE_ERRORS) {
943
+ const errorMsg = error instanceof Error ? error.message : "Unknown error";
944
+ throw new Error(`Login failed after ${MAX_CONSECUTIVE_ERRORS} consecutive errors: ${errorMsg}`);
906
945
  }
907
- return result.keywayToken;
908
946
  }
909
- throw new Error(result.message || "Authentication failed");
910
947
  }
911
948
  }
912
949
  async function ensureLogin(options = {}) {
@@ -915,7 +952,7 @@ async function ensureLogin(options = {}) {
915
952
  return envToken;
916
953
  }
917
954
  if (process.env.GITHUB_TOKEN && !process.env.KEYWAY_TOKEN) {
918
- console.warn(pc3.yellow("Note: GITHUB_TOKEN found but not used. Set KEYWAY_TOKEN for Keyway authentication."));
955
+ console.warn(pc4.yellow("Note: GITHUB_TOKEN found but not used. Set KEYWAY_TOKEN for Keyway authentication."));
919
956
  }
920
957
  const stored = await getStoredAuth();
921
958
  if (stored?.keywayToken) {
@@ -935,16 +972,13 @@ async function ensureLogin(options = {}) {
935
972
  async function runTokenLogin() {
936
973
  const repoName = detectGitRepo();
937
974
  if (repoName) {
938
- console.log(`\u{1F4C1} Detected: ${pc3.cyan(repoName)}`);
975
+ console.log(`\u{1F4C1} Detected: ${pc4.cyan(repoName)}`);
939
976
  }
940
977
  const description = repoName ? `Keyway CLI for ${repoName}` : "Keyway CLI";
941
978
  const url = `https://github.com/settings/personal-access-tokens/new?description=${encodeURIComponent(description)}`;
942
- console.log("Opening GitHub...");
943
- open(url).catch(() => {
944
- console.log(pc3.gray(`Open this URL in your browser: ${url}`));
945
- });
946
- console.log(pc3.gray("Select the detected repo (or scope manually)."));
947
- console.log(pc3.gray("Permissions: Metadata \u2192 Read-only; Account permissions: None."));
979
+ await openUrl(url);
980
+ console.log(pc4.gray("Select the detected repo (or scope manually)."));
981
+ console.log(pc4.gray("Permissions: Metadata \u2192 Read-only; Account permissions: None."));
948
982
  const { token } = await prompts2(
949
983
  {
950
984
  type: "password",
@@ -981,7 +1015,7 @@ async function runTokenLogin() {
981
1015
  github_username: validation.username,
982
1016
  login_method: "pat"
983
1017
  });
984
- console.log(pc3.green("\u2705 Authenticated"), `as ${pc3.cyan(`@${validation.username}`)}`);
1018
+ console.log(pc4.green("\u2705 Authenticated"), `as ${pc4.cyan(`@${validation.username}`)}`);
985
1019
  return trimmedToken;
986
1020
  }
987
1021
  async function loginCommand(options = {}) {
@@ -997,15 +1031,15 @@ async function loginCommand(options = {}) {
997
1031
  command: "login",
998
1032
  error: truncateMessage(message)
999
1033
  });
1000
- console.error(pc3.red(`
1034
+ console.error(pc4.red(`
1001
1035
  \u2717 ${message}`));
1002
1036
  process.exit(1);
1003
1037
  }
1004
1038
  }
1005
1039
  async function logoutCommand() {
1006
1040
  clearAuth();
1007
- console.log(pc3.green("\u2713 Logged out of Keyway"));
1008
- console.log(pc3.gray(`Auth cache cleared: ${getAuthFilePath()}`));
1041
+ console.log(pc4.green("\u2713 Logged out of Keyway"));
1042
+ console.log(pc4.gray(`Auth cache cleared: ${getAuthFilePath()}`));
1009
1043
  }
1010
1044
 
1011
1045
  // src/cmds/push.ts
@@ -1022,7 +1056,7 @@ function discoverEnvCandidates(cwd) {
1022
1056
  const entries = fs4.readdirSync(cwd);
1023
1057
  const hasEnvLocal = entries.includes(".env.local");
1024
1058
  if (hasEnvLocal) {
1025
- console.log(pc4.gray("\u2139\uFE0F Detected .env.local \u2014 not synced by design (machine-specific secrets)"));
1059
+ console.log(pc5.gray("\u2139\uFE0F Detected .env.local \u2014 not synced by design (machine-specific secrets)"));
1026
1060
  }
1027
1061
  const candidates = entries.filter((name) => name.startsWith(".env") && name !== ".env.local").map((name) => {
1028
1062
  const fullPath = path4.join(cwd, name);
@@ -1048,8 +1082,8 @@ function discoverEnvCandidates(cwd) {
1048
1082
  }
1049
1083
  async function pushCommand(options) {
1050
1084
  try {
1051
- console.log(pc4.blue("\u{1F510} Pushing secrets to Keyway...\n"));
1052
- const isInteractive3 = process.stdin.isTTY && process.stdout.isTTY;
1085
+ console.log(pc5.blue("\u{1F510} Pushing secrets to Keyway...\n"));
1086
+ const isInteractive2 = process.stdin.isTTY && process.stdout.isTTY;
1053
1087
  let environment = options.env;
1054
1088
  let envFile = options.file;
1055
1089
  const candidates = discoverEnvCandidates(process.cwd());
@@ -1059,7 +1093,7 @@ async function pushCommand(options) {
1059
1093
  envFile = match.file;
1060
1094
  }
1061
1095
  }
1062
- if (!environment && !envFile && isInteractive3 && candidates.length > 0) {
1096
+ if (!environment && !envFile && isInteractive2 && candidates.length > 0) {
1063
1097
  const { choice } = await prompts3(
1064
1098
  {
1065
1099
  type: "select",
@@ -1113,7 +1147,7 @@ async function pushCommand(options) {
1113
1147
  }
1114
1148
  let envFilePath = path4.resolve(process.cwd(), envFile);
1115
1149
  if (!fs4.existsSync(envFilePath)) {
1116
- if (!isInteractive3) {
1150
+ if (!isInteractive2) {
1117
1151
  throw new Error(`File not found: ${envFile}. Provide --file <path> or run interactively to choose a file.`);
1118
1152
  }
1119
1153
  const { newPath } = await prompts3(
@@ -1148,14 +1182,14 @@ async function pushCommand(options) {
1148
1182
  const trimmed = line.trim();
1149
1183
  return trimmed.length > 0 && !trimmed.startsWith("#");
1150
1184
  });
1151
- console.log(`File: ${pc4.cyan(envFile)}`);
1152
- console.log(`Environment: ${pc4.cyan(environment)}`);
1153
- console.log(`Variables: ${pc4.cyan(lines.length.toString())}`);
1185
+ console.log(`File: ${pc5.cyan(envFile)}`);
1186
+ console.log(`Environment: ${pc5.cyan(environment)}`);
1187
+ console.log(`Variables: ${pc5.cyan(lines.length.toString())}`);
1154
1188
  const repoFullName = getCurrentRepoFullName();
1155
- console.log(`Repository: ${pc4.cyan(repoFullName)}`);
1189
+ console.log(`Repository: ${pc5.cyan(repoFullName)}`);
1156
1190
  if (!options.yes) {
1157
- const isInteractive4 = process.stdin.isTTY && process.stdout.isTTY;
1158
- if (!isInteractive4) {
1191
+ const isInteractive3 = process.stdin.isTTY && process.stdout.isTTY;
1192
+ if (!isInteractive3) {
1159
1193
  throw new Error("Confirmation required. Re-run with --yes in non-interactive environments.");
1160
1194
  }
1161
1195
  const { confirm } = await prompts3(
@@ -1172,7 +1206,7 @@ async function pushCommand(options) {
1172
1206
  }
1173
1207
  );
1174
1208
  if (!confirm) {
1175
- console.log(pc4.yellow("Push aborted."));
1209
+ console.log(pc5.yellow("Push aborted."));
1176
1210
  return;
1177
1211
  }
1178
1212
  }
@@ -1184,20 +1218,20 @@ async function pushCommand(options) {
1184
1218
  });
1185
1219
  console.log("\nUploading secrets...");
1186
1220
  const response = await pushSecrets(repoFullName, environment, content, accessToken);
1187
- console.log(pc4.green("\n\u2713 " + response.message));
1221
+ console.log(pc5.green("\n\u2713 " + response.message));
1188
1222
  if (response.stats) {
1189
1223
  const { created, updated, deleted } = response.stats;
1190
1224
  const parts = [];
1191
- if (created > 0) parts.push(pc4.green(`+${created} created`));
1192
- if (updated > 0) parts.push(pc4.yellow(`~${updated} updated`));
1193
- if (deleted > 0) parts.push(pc4.red(`-${deleted} deleted`));
1225
+ if (created > 0) parts.push(pc5.green(`+${created} created`));
1226
+ if (updated > 0) parts.push(pc5.yellow(`~${updated} updated`));
1227
+ if (deleted > 0) parts.push(pc5.red(`-${deleted} deleted`));
1194
1228
  if (parts.length > 0) {
1195
1229
  console.log(`Stats: ${parts.join(", ")}`);
1196
1230
  }
1197
1231
  }
1198
1232
  console.log(`
1199
1233
  Your secrets are now encrypted and stored securely.`);
1200
- console.log(`To retrieve them, run: ${pc4.cyan(`keyway pull --env ${environment}`)}`);
1234
+ console.log(`To retrieve them, run: ${pc5.cyan(`keyway pull --env ${environment}`)}`);
1201
1235
  await shutdownAnalytics();
1202
1236
  } catch (error) {
1203
1237
  let message;
@@ -1210,13 +1244,18 @@ Your secrets are now encrypted and stored securely.`);
1210
1244
  const availableEnvs = envNotFoundMatch[2];
1211
1245
  message = `Environment '${requestedEnv}' does not exist in this vault.`;
1212
1246
  hint = `Available environments: ${availableEnvs}
1213
- Use ${pc4.cyan(`keyway push --env <environment>`)} to specify one, or create '${requestedEnv}' via the dashboard.`;
1247
+ Use ${pc5.cyan(`keyway push --env <environment>`)} to specify one, or create '${requestedEnv}' via the dashboard.`;
1214
1248
  }
1215
- if (error.statusCode === 403 && error.upgradeUrl) {
1216
- hint = `${pc4.yellow("\u26A1")} Upgrade to Pro: ${pc4.cyan(error.upgradeUrl)}`;
1217
- } else if (error.statusCode === 403 && message.toLowerCase().includes("read-only")) {
1218
- message = "This vault is read-only on your current plan.";
1219
- hint = `Upgrade to Pro to unlock editing: ${pc4.cyan("https://keyway.sh/settings")}`;
1249
+ if (error.statusCode === 403 && (error.upgradeUrl || message.toLowerCase().includes("read-only"))) {
1250
+ const upgradeMessage = message.toLowerCase().includes("read-only") ? "This vault is read-only on your current plan." : message;
1251
+ const upgradeUrl = error.upgradeUrl || "https://keyway.sh/settings";
1252
+ trackEvent(AnalyticsEvents.CLI_ERROR, {
1253
+ command: "push",
1254
+ error: upgradeMessage
1255
+ });
1256
+ await shutdownAnalytics();
1257
+ showUpgradePrompt(upgradeMessage, upgradeUrl);
1258
+ process.exit(1);
1220
1259
  }
1221
1260
  } else if (error instanceof Error) {
1222
1261
  message = truncateMessage(error.message);
@@ -1228,10 +1267,10 @@ Use ${pc4.cyan(`keyway push --env <environment>`)} to specify one, or create '${
1228
1267
  error: message
1229
1268
  });
1230
1269
  await shutdownAnalytics();
1231
- console.error(pc4.red(`
1270
+ console.error(pc5.red(`
1232
1271
  \u2717 ${message}`));
1233
1272
  if (hint) {
1234
- console.error(pc4.gray(`
1273
+ console.error(pc5.gray(`
1235
1274
  ${hint}`));
1236
1275
  }
1237
1276
  process.exit(1);
@@ -1242,12 +1281,6 @@ ${hint}`));
1242
1281
  var DASHBOARD_URL = "https://www.keyway.sh/dashboard/vaults";
1243
1282
  var POLL_INTERVAL_MS = 3e3;
1244
1283
  var POLL_TIMEOUT_MS = 12e4;
1245
- function sleep2(ms) {
1246
- return new Promise((resolve) => setTimeout(resolve, ms));
1247
- }
1248
- function isInteractive2() {
1249
- return Boolean(process.stdout.isTTY && process.stdin.isTTY && !process.env.CI);
1250
- }
1251
1284
  async function ensureLoginAndGitHubApp(repoFullName, options = {}) {
1252
1285
  const [repoOwner, repoName] = repoFullName.split("/");
1253
1286
  const envToken = process.env.KEYWAY_TOKEN;
@@ -1266,12 +1299,12 @@ async function ensureLoginAndGitHubApp(repoFullName, options = {}) {
1266
1299
  }
1267
1300
  }
1268
1301
  const allowPrompt = options.allowPrompt !== false;
1269
- if (!allowPrompt || !isInteractive2()) {
1302
+ if (!allowPrompt || !isInteractive()) {
1270
1303
  throw new Error('No Keyway session found. Run "keyway login" to authenticate.');
1271
1304
  }
1272
1305
  console.log("");
1273
- console.log(pc5.gray(" Keyway uses a GitHub App for secure access."));
1274
- console.log(pc5.gray(" Installing the app will also log you in."));
1306
+ console.log(pc6.gray(" Keyway uses a GitHub App for secure access."));
1307
+ console.log(pc6.gray(" Installing the app will also log you in."));
1275
1308
  console.log("");
1276
1309
  const { shouldProceed } = await prompts4({
1277
1310
  type: "confirm",
@@ -1284,16 +1317,15 @@ async function ensureLoginAndGitHubApp(repoFullName, options = {}) {
1284
1317
  }
1285
1318
  const deviceStart = await startDeviceLogin(repoFullName);
1286
1319
  const installUrl = deviceStart.githubAppInstallUrl || "https://github.com/apps/keyway/installations/new";
1287
- console.log(pc5.gray("\n Opening browser..."));
1288
- await open2(installUrl);
1289
- console.log("");
1290
- console.log(pc5.blue("\u23F3 Waiting for installation & authorization..."));
1291
- console.log(pc5.gray(" (Press Ctrl+C to cancel)\n"));
1320
+ await openUrl(installUrl);
1321
+ console.log(pc6.blue("\u23F3 Waiting for installation & authorization..."));
1322
+ console.log(pc6.gray(" (Press Ctrl+C to cancel)\n"));
1292
1323
  const pollIntervalMs = Math.max((deviceStart.interval ?? 5) * 1e3, POLL_INTERVAL_MS);
1293
1324
  const startTime = Date.now();
1294
1325
  let accessToken = null;
1326
+ let consecutiveErrors = 0;
1295
1327
  while (Date.now() - startTime < POLL_TIMEOUT_MS) {
1296
- await sleep2(pollIntervalMs);
1328
+ await sleep(pollIntervalMs);
1297
1329
  try {
1298
1330
  if (!accessToken) {
1299
1331
  const result = await pollDeviceLogin(deviceStart.deviceCode);
@@ -1303,7 +1335,7 @@ async function ensureLoginAndGitHubApp(repoFullName, options = {}) {
1303
1335
  githubLogin: result.githubLogin,
1304
1336
  expiresAt: result.expiresAt
1305
1337
  });
1306
- console.log(pc5.green("\u2713 Signed in!"));
1338
+ console.log(pc6.green("\u2713 Signed in!"));
1307
1339
  if (result.githubLogin) {
1308
1340
  identifyUser(result.githubLogin, {
1309
1341
  github_username: result.githubLogin,
@@ -1315,18 +1347,24 @@ async function ensureLoginAndGitHubApp(repoFullName, options = {}) {
1315
1347
  if (accessToken) {
1316
1348
  const installStatus = await checkGitHubAppInstallation(repoOwner, repoName, accessToken);
1317
1349
  if (installStatus.installed) {
1318
- console.log(pc5.green("\u2713 GitHub App installed!"));
1350
+ console.log(pc6.green("\u2713 GitHub App installed!"));
1319
1351
  console.log("");
1320
1352
  return accessToken;
1321
1353
  }
1322
1354
  }
1323
- process.stdout.write(pc5.gray("."));
1324
- } catch {
1355
+ consecutiveErrors = 0;
1356
+ process.stdout.write(pc6.gray("."));
1357
+ } catch (error) {
1358
+ consecutiveErrors++;
1359
+ if (consecutiveErrors >= MAX_CONSECUTIVE_ERRORS) {
1360
+ const errorMsg = error instanceof Error ? error.message : "Unknown error";
1361
+ throw new Error(`Setup failed after ${MAX_CONSECUTIVE_ERRORS} consecutive errors: ${errorMsg}`);
1362
+ }
1325
1363
  }
1326
1364
  }
1327
1365
  console.log("");
1328
- console.log(pc5.yellow("\u26A0 Timed out waiting for setup."));
1329
- console.log(pc5.gray(` Install the GitHub App: ${installUrl}`));
1366
+ console.log(pc6.yellow("\u26A0 Timed out waiting for setup."));
1367
+ console.log(pc6.gray(` Install the GitHub App: ${installUrl}`));
1330
1368
  throw new Error("Setup timed out. Please try again.");
1331
1369
  }
1332
1370
  async function ensureGitHubAppInstalledOnly(repoFullName, accessToken) {
@@ -1336,7 +1374,7 @@ async function ensureGitHubAppInstalledOnly(repoFullName, accessToken) {
1336
1374
  status = await checkGitHubAppInstallation(repoOwner, repoName, accessToken);
1337
1375
  } catch (error) {
1338
1376
  if (error instanceof APIError && error.statusCode === 401) {
1339
- console.log(pc5.yellow("\n\u26A0 Session expired or invalid. Clearing credentials..."));
1377
+ console.log(pc6.yellow("\n\u26A0 Session expired or invalid. Clearing credentials..."));
1340
1378
  const { clearAuth: clearAuth2 } = await import("./auth-QLPQ24HZ.js");
1341
1379
  clearAuth2();
1342
1380
  return null;
@@ -1347,13 +1385,13 @@ async function ensureGitHubAppInstalledOnly(repoFullName, accessToken) {
1347
1385
  return accessToken;
1348
1386
  }
1349
1387
  console.log("");
1350
- console.log(pc5.yellow("\u26A0 GitHub App not installed for this repository"));
1388
+ console.log(pc6.yellow("\u26A0 GitHub App not installed for this repository"));
1351
1389
  console.log("");
1352
- console.log(pc5.gray(" The Keyway GitHub App is required to securely manage secrets."));
1353
- console.log(pc5.gray(" It only requests minimal permissions (repository metadata)."));
1390
+ console.log(pc6.gray(" The Keyway GitHub App is required to securely manage secrets."));
1391
+ console.log(pc6.gray(" It only requests minimal permissions (repository metadata)."));
1354
1392
  console.log("");
1355
- if (!isInteractive2()) {
1356
- console.log(pc5.gray(` Install the Keyway GitHub App: ${status.installUrl}`));
1393
+ if (!isInteractive()) {
1394
+ console.log(pc6.gray(` Install the Keyway GitHub App: ${status.installUrl}`));
1357
1395
  throw new Error("GitHub App installation required.");
1358
1396
  }
1359
1397
  const { shouldInstall } = await prompts4({
@@ -1363,67 +1401,72 @@ async function ensureGitHubAppInstalledOnly(repoFullName, accessToken) {
1363
1401
  initial: true
1364
1402
  });
1365
1403
  if (!shouldInstall) {
1366
- console.log(pc5.gray(`
1404
+ console.log(pc6.gray(`
1367
1405
  You can install later: ${status.installUrl}`));
1368
1406
  throw new Error("GitHub App installation required.");
1369
1407
  }
1370
- console.log(pc5.gray("\n Opening browser..."));
1371
- await open2(status.installUrl);
1372
- console.log("");
1373
- console.log(pc5.blue("\u23F3 Waiting for GitHub App installation..."));
1374
- console.log(pc5.gray(" (Press Ctrl+C to cancel)\n"));
1408
+ await openUrl(status.installUrl);
1409
+ console.log(pc6.blue("\u23F3 Waiting for GitHub App installation..."));
1410
+ console.log(pc6.gray(" (Press Ctrl+C to cancel)\n"));
1375
1411
  const startTime = Date.now();
1412
+ let consecutiveErrors = 0;
1376
1413
  while (Date.now() - startTime < POLL_TIMEOUT_MS) {
1377
- await sleep2(POLL_INTERVAL_MS);
1414
+ await sleep(POLL_INTERVAL_MS);
1378
1415
  try {
1379
1416
  const pollStatus = await checkGitHubAppInstallation(repoOwner, repoName, accessToken);
1380
1417
  if (pollStatus.installed) {
1381
- console.log(pc5.green("\u2713 GitHub App installed!"));
1418
+ console.log(pc6.green("\u2713 GitHub App installed!"));
1382
1419
  console.log("");
1383
1420
  return accessToken;
1384
1421
  }
1385
- process.stdout.write(pc5.gray("."));
1386
- } catch {
1422
+ consecutiveErrors = 0;
1423
+ process.stdout.write(pc6.gray("."));
1424
+ } catch (error) {
1425
+ consecutiveErrors++;
1426
+ if (consecutiveErrors >= MAX_CONSECUTIVE_ERRORS) {
1427
+ const errorMsg = error instanceof Error ? error.message : "Unknown error";
1428
+ throw new Error(`Installation check failed after ${MAX_CONSECUTIVE_ERRORS} consecutive errors: ${errorMsg}`);
1429
+ }
1387
1430
  }
1388
1431
  }
1389
1432
  console.log("");
1390
- console.log(pc5.yellow("\u26A0 Timed out waiting for installation."));
1391
- console.log(pc5.gray(` You can install the GitHub App later: ${status.installUrl}`));
1433
+ console.log(pc6.yellow("\u26A0 Timed out waiting for installation."));
1434
+ console.log(pc6.gray(` You can install the GitHub App later: ${status.installUrl}`));
1392
1435
  throw new Error("GitHub App installation timed out.");
1393
1436
  }
1394
1437
  async function initCommand(options = {}) {
1395
1438
  try {
1396
1439
  const repoFullName = getCurrentRepoFullName();
1397
1440
  const dashboardLink = `${DASHBOARD_URL}/${repoFullName}`;
1398
- console.log(pc5.blue("\u{1F510} Initializing Keyway vault...\n"));
1399
- console.log(` ${pc5.gray("Repository:")} ${pc5.white(repoFullName)}`);
1441
+ console.log(pc6.blue("\u{1F510} Initializing Keyway vault...\n"));
1442
+ console.log(` ${pc6.gray("Repository:")} ${pc6.white(repoFullName)}`);
1400
1443
  const accessToken = await ensureLoginAndGitHubApp(repoFullName, {
1401
1444
  allowPrompt: options.loginPrompt !== false
1402
1445
  });
1403
1446
  trackEvent(AnalyticsEvents.CLI_INIT, { repoFullName, githubAppInstalled: true });
1404
1447
  const vaultExists = await checkVaultExists(accessToken, repoFullName);
1405
1448
  if (vaultExists) {
1406
- console.log(pc5.green("\n\u2713 Already initialized!\n"));
1407
- console.log(` ${pc5.yellow("\u2192")} Run ${pc5.cyan("keyway push")} to sync your secrets`);
1408
- console.log(` ${pc5.blue("\u2394")} Dashboard: ${pc5.underline(dashboardLink)}`);
1449
+ console.log(pc6.green("\n\u2713 Already initialized!\n"));
1450
+ console.log(` ${pc6.yellow("\u2192")} Run ${pc6.cyan("keyway push")} to sync your secrets`);
1451
+ console.log(` ${pc6.blue("\u2394")} Dashboard: ${pc6.underline(dashboardLink)}`);
1409
1452
  console.log("");
1410
1453
  await shutdownAnalytics();
1411
1454
  return;
1412
1455
  }
1413
1456
  await initVault(repoFullName, accessToken);
1414
- console.log(pc5.green("\u2713 Vault created!"));
1457
+ console.log(pc6.green("\u2713 Vault created!"));
1415
1458
  try {
1416
1459
  const badgeAdded = await addBadgeToReadme(true);
1417
1460
  if (badgeAdded) {
1418
- console.log(pc5.green("\u2713 Badge added to README.md"));
1461
+ console.log(pc6.green("\u2713 Badge added to README.md"));
1419
1462
  }
1420
1463
  } catch {
1421
1464
  }
1422
1465
  console.log("");
1423
1466
  const envCandidates = discoverEnvCandidates(process.cwd());
1424
- const isInteractive3 = process.stdin.isTTY && process.stdout.isTTY;
1425
- if (envCandidates.length > 0 && isInteractive3) {
1426
- console.log(pc5.gray(` Found ${envCandidates.length} env file(s): ${envCandidates.map((c) => c.file).join(", ")}
1467
+ const isInteractive2 = process.stdin.isTTY && process.stdout.isTTY;
1468
+ if (envCandidates.length > 0 && isInteractive2) {
1469
+ console.log(pc6.gray(` Found ${envCandidates.length} env file(s): ${envCandidates.map((c) => c.file).join(", ")}
1427
1470
  `));
1428
1471
  const { shouldPush } = await prompts4({
1429
1472
  type: "confirm",
@@ -1437,42 +1480,32 @@ async function initCommand(options = {}) {
1437
1480
  return;
1438
1481
  }
1439
1482
  }
1440
- console.log(pc5.dim("\u2500".repeat(50)));
1483
+ console.log(pc6.dim("\u2500".repeat(50)));
1441
1484
  console.log("");
1442
1485
  if (envCandidates.length === 0) {
1443
- console.log(` ${pc5.yellow("\u2192")} Create a ${pc5.cyan(".env")} file with your secrets`);
1444
- console.log(` ${pc5.yellow("\u2192")} Run ${pc5.cyan("keyway push")} to sync them
1486
+ console.log(`${pc6.yellow("\u26A0")} No .env file found - your vault is empty`);
1487
+ console.log(` Next: Create ${pc6.cyan(".env")} and run ${pc6.cyan("keyway push")}
1445
1488
  `);
1446
1489
  } else {
1447
- console.log(` ${pc5.yellow("\u2192")} Run ${pc5.cyan("keyway push")} to sync your secrets
1490
+ console.log(` ${pc6.yellow("\u2192")} Run ${pc6.cyan("keyway push")} to sync your secrets
1448
1491
  `);
1449
1492
  }
1450
- console.log(` ${pc5.blue("\u2394")} Dashboard: ${pc5.underline(dashboardLink)}`);
1493
+ console.log(` ${pc6.blue("\u2394")} Dashboard: ${pc6.underline(dashboardLink)}`);
1451
1494
  console.log("");
1452
1495
  await shutdownAnalytics();
1453
1496
  } catch (error) {
1454
1497
  if (error instanceof APIError) {
1455
1498
  if (error.statusCode === 409) {
1456
- console.log(pc5.green("\n\u2713 Already initialized!\n"));
1457
- console.log(` ${pc5.yellow("\u2192")} Run ${pc5.cyan("keyway push")} to sync your secrets`);
1458
- console.log(` ${pc5.blue("\u2394")} Dashboard: ${pc5.underline(`${DASHBOARD_URL}/${getCurrentRepoFullName()}`)}`);
1499
+ console.log(pc6.green("\n\u2713 Already initialized!\n"));
1500
+ console.log(` ${pc6.yellow("\u2192")} Run ${pc6.cyan("keyway push")} to sync your secrets`);
1501
+ console.log(` ${pc6.blue("\u2394")} Dashboard: ${pc6.underline(`${DASHBOARD_URL}/${getCurrentRepoFullName()}`)}`);
1459
1502
  console.log("");
1460
1503
  await shutdownAnalytics();
1461
1504
  return;
1462
1505
  }
1463
1506
  if (error.error === "Plan Limit Reached" || error.upgradeUrl) {
1464
1507
  const upgradeUrl = error.upgradeUrl || "https://keyway.sh/pricing";
1465
- console.log("");
1466
- console.log(pc5.dim("\u2500".repeat(50)));
1467
- console.log("");
1468
- console.log(` ${pc5.yellow("\u26A1")} ${pc5.bold("Plan Limit Reached")}`);
1469
- console.log("");
1470
- console.log(pc5.white(` ${error.message}`));
1471
- console.log("");
1472
- console.log(` ${pc5.cyan("Upgrade now \u2192")} ${pc5.underline(upgradeUrl)}`);
1473
- console.log("");
1474
- console.log(pc5.dim("\u2500".repeat(50)));
1475
- console.log("");
1508
+ showUpgradePrompt(error.message, upgradeUrl);
1476
1509
  await shutdownAnalytics();
1477
1510
  process.exit(1);
1478
1511
  }
@@ -1483,14 +1516,14 @@ async function initCommand(options = {}) {
1483
1516
  error: message
1484
1517
  });
1485
1518
  await shutdownAnalytics();
1486
- console.error(pc5.red(`
1519
+ console.error(pc6.red(`
1487
1520
  \u2717 ${message}`));
1488
1521
  process.exit(1);
1489
1522
  }
1490
1523
  }
1491
1524
 
1492
1525
  // src/cmds/pull.ts
1493
- import pc6 from "picocolors";
1526
+ import pc7 from "picocolors";
1494
1527
  import fs5 from "fs";
1495
1528
  import path5 from "path";
1496
1529
  import prompts5 from "prompts";
@@ -1498,10 +1531,10 @@ async function pullCommand(options) {
1498
1531
  try {
1499
1532
  const environment = options.env || "development";
1500
1533
  const envFile = options.file || ".env";
1501
- console.log(pc6.blue("\u{1F510} Pulling secrets from Keyway...\n"));
1502
- console.log(`Environment: ${pc6.cyan(environment)}`);
1534
+ console.log(pc7.blue("\u{1F510} Pulling secrets from Keyway...\n"));
1535
+ console.log(`Environment: ${pc7.cyan(environment)}`);
1503
1536
  const repoFullName = getCurrentRepoFullName();
1504
- console.log(`Repository: ${pc6.cyan(repoFullName)}`);
1537
+ console.log(`Repository: ${pc7.cyan(repoFullName)}`);
1505
1538
  const accessToken = await ensureLogin({ allowPrompt: options.loginPrompt !== false });
1506
1539
  trackEvent(AnalyticsEvents.CLI_PULL, {
1507
1540
  repoFullName,
@@ -1511,11 +1544,11 @@ async function pullCommand(options) {
1511
1544
  const response = await pullSecrets(repoFullName, environment, accessToken);
1512
1545
  const envFilePath = path5.resolve(process.cwd(), envFile);
1513
1546
  if (fs5.existsSync(envFilePath)) {
1514
- const isInteractive3 = process.stdin.isTTY && process.stdout.isTTY;
1547
+ const isInteractive2 = process.stdin.isTTY && process.stdout.isTTY;
1515
1548
  if (options.yes) {
1516
- console.log(pc6.yellow(`
1549
+ console.log(pc7.yellow(`
1517
1550
  \u26A0 Overwriting existing file: ${envFile}`));
1518
- } else if (!isInteractive3) {
1551
+ } else if (!isInteractive2) {
1519
1552
  throw new Error(`File ${envFile} exists. Re-run with --yes to overwrite or choose a different --file.`);
1520
1553
  } else {
1521
1554
  const { confirm } = await prompts5(
@@ -1532,7 +1565,7 @@ async function pullCommand(options) {
1532
1565
  }
1533
1566
  );
1534
1567
  if (!confirm) {
1535
- console.log(pc6.yellow("Pull aborted."));
1568
+ console.log(pc7.yellow("Pull aborted."));
1536
1569
  return;
1537
1570
  }
1538
1571
  }
@@ -1542,11 +1575,11 @@ async function pullCommand(options) {
1542
1575
  const trimmed = line.trim();
1543
1576
  return trimmed.length > 0 && !trimmed.startsWith("#");
1544
1577
  });
1545
- console.log(pc6.green(`
1578
+ console.log(pc7.green(`
1546
1579
  \u2713 Secrets downloaded successfully`));
1547
1580
  console.log(`
1548
- File: ${pc6.cyan(envFile)}`);
1549
- console.log(`Variables: ${pc6.cyan(lines.length.toString())}`);
1581
+ File: ${pc7.cyan(envFile)}`);
1582
+ console.log(`Variables: ${pc7.cyan(lines.length.toString())}`);
1550
1583
  await shutdownAnalytics();
1551
1584
  } catch (error) {
1552
1585
  const message = error instanceof APIError ? `API ${error.statusCode}: ${error.message}` : error instanceof Error ? truncateMessage(error.message) : "Unknown error";
@@ -1555,14 +1588,14 @@ File: ${pc6.cyan(envFile)}`);
1555
1588
  error: message
1556
1589
  });
1557
1590
  await shutdownAnalytics();
1558
- console.error(pc6.red(`
1591
+ console.error(pc7.red(`
1559
1592
  \u2717 ${message}`));
1560
1593
  process.exit(1);
1561
1594
  }
1562
1595
  }
1563
1596
 
1564
1597
  // src/cmds/doctor.ts
1565
- import pc7 from "picocolors";
1598
+ import pc8 from "picocolors";
1566
1599
 
1567
1600
  // src/core/doctor.ts
1568
1601
  import { execSync as execSync2 } from "child_process";
@@ -1816,9 +1849,9 @@ async function runAllChecks(options = {}) {
1816
1849
  // src/cmds/doctor.ts
1817
1850
  function formatSummary(results) {
1818
1851
  const parts = [
1819
- pc7.green(`${results.summary.pass} passed`),
1820
- results.summary.warn > 0 ? pc7.yellow(`${results.summary.warn} warnings`) : null,
1821
- results.summary.fail > 0 ? pc7.red(`${results.summary.fail} failed`) : null
1852
+ pc8.green(`${results.summary.pass} passed`),
1853
+ results.summary.warn > 0 ? pc8.yellow(`${results.summary.warn} warnings`) : null,
1854
+ results.summary.fail > 0 ? pc8.red(`${results.summary.fail} failed`) : null
1822
1855
  ].filter(Boolean);
1823
1856
  return parts.join(", ");
1824
1857
  }
@@ -1835,20 +1868,20 @@ async function doctorCommand(options = {}) {
1835
1868
  process.stdout.write(JSON.stringify(results, null, 0) + "\n");
1836
1869
  process.exit(results.exitCode);
1837
1870
  }
1838
- console.log(pc7.cyan("\n\u{1F50D} Keyway Doctor - Environment Check\n"));
1871
+ console.log(pc8.cyan("\n\u{1F50D} Keyway Doctor - Environment Check\n"));
1839
1872
  results.checks.forEach((check) => {
1840
- const icon = check.status === "pass" ? pc7.green("\u2713") : check.status === "warn" ? pc7.yellow("!") : pc7.red("\u2717");
1841
- const detail = check.detail ? pc7.dim(` \u2014 ${check.detail}`) : "";
1873
+ const icon = check.status === "pass" ? pc8.green("\u2713") : check.status === "warn" ? pc8.yellow("!") : pc8.red("\u2717");
1874
+ const detail = check.detail ? pc8.dim(` \u2014 ${check.detail}`) : "";
1842
1875
  console.log(` ${icon} ${check.name}${detail}`);
1843
1876
  });
1844
1877
  console.log(`
1845
1878
  Summary: ${formatSummary(results)}`);
1846
1879
  if (results.summary.fail > 0) {
1847
- console.log(pc7.red("\u26A0 Some checks failed. Please resolve the issues above before using Keyway."));
1880
+ console.log(pc8.red("\u26A0 Some checks failed. Please resolve the issues above before using Keyway."));
1848
1881
  } else if (results.summary.warn > 0) {
1849
- console.log(pc7.yellow("\u26A0 Some warnings detected. Keyway should work but consider addressing them."));
1882
+ console.log(pc8.yellow("\u26A0 Some warnings detected. Keyway should work but consider addressing them."));
1850
1883
  } else {
1851
- console.log(pc7.green("\u2728 All checks passed! Your environment is ready for Keyway."));
1884
+ console.log(pc8.green("\u2728 All checks passed! Your environment is ready for Keyway."));
1852
1885
  }
1853
1886
  process.exit(results.exitCode);
1854
1887
  } catch (error) {
@@ -1869,7 +1902,7 @@ Summary: ${formatSummary(results)}`);
1869
1902
  };
1870
1903
  process.stdout.write(JSON.stringify(errorResult, null, 0) + "\n");
1871
1904
  } else {
1872
- console.error(pc7.red(`
1905
+ console.error(pc8.red(`
1873
1906
  \u2717 ${message}`));
1874
1907
  }
1875
1908
  process.exit(1);
@@ -1877,8 +1910,7 @@ Summary: ${formatSummary(results)}`);
1877
1910
  }
1878
1911
 
1879
1912
  // src/cmds/connect.ts
1880
- import pc8 from "picocolors";
1881
- import open3 from "open";
1913
+ import pc9 from "picocolors";
1882
1914
  import prompts6 from "prompts";
1883
1915
  var TOKEN_AUTH_PROVIDERS = ["railway"];
1884
1916
  function getTokenCreationUrl(provider) {
@@ -1891,41 +1923,38 @@ function getTokenCreationUrl(provider) {
1891
1923
  }
1892
1924
  async function connectWithTokenFlow(accessToken, provider, displayName) {
1893
1925
  const tokenUrl = getTokenCreationUrl(provider);
1894
- console.log(pc8.gray(`Create a ${displayName} API Token at:`));
1895
- console.log(pc8.cyan(`\u2192 ${tokenUrl}
1896
- `));
1897
1926
  if (provider === "railway") {
1898
- console.log(pc8.yellow("Tip: Select the workspace containing your projects."));
1899
- console.log(pc8.yellow(` Do NOT use "No workspace" - it won't have access to your projects.
1900
- `));
1927
+ console.log(pc9.yellow("\nTip: Select the workspace containing your projects."));
1928
+ console.log(pc9.yellow(` Do NOT use "No workspace" - it won't have access to your projects.`));
1901
1929
  }
1930
+ await openUrl(tokenUrl);
1902
1931
  const { token } = await prompts6({
1903
1932
  type: "password",
1904
1933
  name: "token",
1905
1934
  message: `${displayName} API Token:`
1906
1935
  });
1907
1936
  if (!token) {
1908
- console.log(pc8.gray("Cancelled."));
1937
+ console.log(pc9.gray("Cancelled."));
1909
1938
  return false;
1910
1939
  }
1911
- console.log(pc8.gray("\nValidating token..."));
1940
+ console.log(pc9.gray("\nValidating token..."));
1912
1941
  try {
1913
1942
  const result = await connectWithToken(accessToken, provider, token);
1914
1943
  if (result.success) {
1915
- console.log(pc8.green(`
1944
+ console.log(pc9.green(`
1916
1945
  \u2713 Connected to ${displayName}!`));
1917
- console.log(pc8.gray(` Account: ${result.user.username}`));
1946
+ console.log(pc9.gray(` Account: ${result.user.username}`));
1918
1947
  if (result.user.teamName) {
1919
- console.log(pc8.gray(` Team: ${result.user.teamName}`));
1948
+ console.log(pc9.gray(` Team: ${result.user.teamName}`));
1920
1949
  }
1921
1950
  return true;
1922
1951
  } else {
1923
- console.log(pc8.red("\n\u2717 Connection failed."));
1952
+ console.log(pc9.red("\n\u2717 Connection failed."));
1924
1953
  return false;
1925
1954
  }
1926
1955
  } catch (error) {
1927
1956
  const message = error instanceof Error ? error.message : "Token validation failed";
1928
- console.log(pc8.red(`
1957
+ console.log(pc9.red(`
1929
1958
  \u2717 ${message}`));
1930
1959
  return false;
1931
1960
  }
@@ -1933,11 +1962,8 @@ async function connectWithTokenFlow(accessToken, provider, displayName) {
1933
1962
  async function connectWithOAuthFlow(accessToken, provider, displayName) {
1934
1963
  const authUrl = getProviderAuthUrl(provider);
1935
1964
  const startTime = /* @__PURE__ */ new Date();
1936
- console.log(pc8.gray("Opening browser for authorization..."));
1937
- console.log(pc8.gray(`If the browser doesn't open, visit: ${authUrl}`));
1938
- await open3(authUrl).catch(() => {
1939
- });
1940
- console.log(pc8.gray("Waiting for authorization..."));
1965
+ await openUrl(authUrl);
1966
+ console.log(pc9.gray("Waiting for authorization..."));
1941
1967
  const maxAttempts = 60;
1942
1968
  let attempts = 0;
1943
1969
  while (attempts < maxAttempts) {
@@ -1949,15 +1975,15 @@ async function connectWithOAuthFlow(accessToken, provider, displayName) {
1949
1975
  (c) => c.provider === provider && new Date(c.createdAt) > startTime
1950
1976
  );
1951
1977
  if (newConn) {
1952
- console.log(pc8.green(`
1978
+ console.log(pc9.green(`
1953
1979
  \u2713 Connected to ${displayName}!`));
1954
1980
  return true;
1955
1981
  }
1956
1982
  } catch {
1957
1983
  }
1958
1984
  }
1959
- console.log(pc8.red("\n\u2717 Authorization timeout."));
1960
- console.log(pc8.gray("Run `keyway connections` to check if the connection was established."));
1985
+ console.log(pc9.red("\n\u2717 Authorization timeout."));
1986
+ console.log(pc9.gray("Run `keyway connections` to check if the connection was established."));
1961
1987
  return false;
1962
1988
  }
1963
1989
  async function connectCommand(provider, options = {}) {
@@ -1967,13 +1993,13 @@ async function connectCommand(provider, options = {}) {
1967
1993
  const providerInfo = providers.find((p) => p.name === provider.toLowerCase());
1968
1994
  if (!providerInfo) {
1969
1995
  const available = providers.map((p) => p.name).join(", ");
1970
- console.error(pc8.red(`Unknown provider: ${provider}`));
1971
- console.log(pc8.gray(`Available providers: ${available || "none"}`));
1996
+ console.error(pc9.red(`Unknown provider: ${provider}`));
1997
+ console.log(pc9.gray(`Available providers: ${available || "none"}`));
1972
1998
  process.exit(1);
1973
1999
  }
1974
2000
  if (!providerInfo.configured) {
1975
- console.error(pc8.red(`Provider ${providerInfo.displayName} is not configured on the server.`));
1976
- console.log(pc8.gray("Contact your administrator to enable this integration."));
2001
+ console.error(pc9.red(`Provider ${providerInfo.displayName} is not configured on the server.`));
2002
+ console.log(pc9.gray("Contact your administrator to enable this integration."));
1977
2003
  process.exit(1);
1978
2004
  }
1979
2005
  const { connections } = await getConnections(accessToken);
@@ -1986,11 +2012,11 @@ async function connectCommand(provider, options = {}) {
1986
2012
  initial: false
1987
2013
  });
1988
2014
  if (!reconnect) {
1989
- console.log(pc8.gray("Keeping existing connection."));
2015
+ console.log(pc9.gray("Keeping existing connection."));
1990
2016
  return;
1991
2017
  }
1992
2018
  }
1993
- console.log(pc8.blue(`
2019
+ console.log(pc9.blue(`
1994
2020
  Connecting to ${providerInfo.displayName}...
1995
2021
  `));
1996
2022
  let connected = false;
@@ -2009,7 +2035,7 @@ Connecting to ${providerInfo.displayName}...
2009
2035
  command: "connect",
2010
2036
  error: truncateMessage(message)
2011
2037
  });
2012
- console.error(pc8.red(`
2038
+ console.error(pc9.red(`
2013
2039
  \u2717 ${message}`));
2014
2040
  process.exit(1);
2015
2041
  }
@@ -2019,24 +2045,24 @@ async function connectionsCommand(options = {}) {
2019
2045
  const accessToken = await ensureLogin({ allowPrompt: options.loginPrompt !== false });
2020
2046
  const { connections } = await getConnections(accessToken);
2021
2047
  if (connections.length === 0) {
2022
- console.log(pc8.gray("No provider connections found."));
2023
- console.log(pc8.gray("\nConnect to a provider with: keyway connect <provider>"));
2024
- console.log(pc8.gray("Available providers: vercel, railway"));
2048
+ console.log(pc9.gray("No provider connections found."));
2049
+ console.log(pc9.gray("\nConnect to a provider with: keyway connect <provider>"));
2050
+ console.log(pc9.gray("Available providers: vercel, railway"));
2025
2051
  return;
2026
2052
  }
2027
- console.log(pc8.blue("\n\u{1F4E1} Provider Connections\n"));
2053
+ console.log(pc9.blue("\n\u{1F4E1} Provider Connections\n"));
2028
2054
  for (const conn of connections) {
2029
2055
  const providerName = conn.provider.charAt(0).toUpperCase() + conn.provider.slice(1);
2030
- const teamInfo = conn.providerTeamId ? pc8.gray(` (Team: ${conn.providerTeamId})`) : "";
2056
+ const teamInfo = conn.providerTeamId ? pc9.gray(` (Team: ${conn.providerTeamId})`) : "";
2031
2057
  const date = new Date(conn.createdAt).toLocaleDateString();
2032
- console.log(` ${pc8.green("\u25CF")} ${pc8.bold(providerName)}${teamInfo}`);
2033
- console.log(pc8.gray(` Connected: ${date}`));
2034
- console.log(pc8.gray(` ID: ${conn.id}`));
2058
+ console.log(` ${pc9.green("\u25CF")} ${pc9.bold(providerName)}${teamInfo}`);
2059
+ console.log(pc9.gray(` Connected: ${date}`));
2060
+ console.log(pc9.gray(` ID: ${conn.id}`));
2035
2061
  console.log("");
2036
2062
  }
2037
2063
  } catch (error) {
2038
2064
  const message = error instanceof Error ? error.message : "Failed to list connections";
2039
- console.error(pc8.red(`
2065
+ console.error(pc9.red(`
2040
2066
  \u2717 ${message}`));
2041
2067
  process.exit(1);
2042
2068
  }
@@ -2047,7 +2073,7 @@ async function disconnectCommand(provider, options = {}) {
2047
2073
  const { connections } = await getConnections(accessToken);
2048
2074
  const connection = connections.find((c) => c.provider === provider.toLowerCase());
2049
2075
  if (!connection) {
2050
- console.log(pc8.gray(`No connection found for provider: ${provider}`));
2076
+ console.log(pc9.gray(`No connection found for provider: ${provider}`));
2051
2077
  return;
2052
2078
  }
2053
2079
  const providerName = provider.charAt(0).toUpperCase() + provider.slice(1);
@@ -2058,11 +2084,11 @@ async function disconnectCommand(provider, options = {}) {
2058
2084
  initial: false
2059
2085
  });
2060
2086
  if (!confirm) {
2061
- console.log(pc8.gray("Cancelled."));
2087
+ console.log(pc9.gray("Cancelled."));
2062
2088
  return;
2063
2089
  }
2064
2090
  await deleteConnection(accessToken, connection.id);
2065
- console.log(pc8.green(`
2091
+ console.log(pc9.green(`
2066
2092
  \u2713 Disconnected from ${providerName}`));
2067
2093
  trackEvent(AnalyticsEvents.CLI_DISCONNECT, {
2068
2094
  provider: provider.toLowerCase()
@@ -2073,14 +2099,14 @@ async function disconnectCommand(provider, options = {}) {
2073
2099
  command: "disconnect",
2074
2100
  error: truncateMessage(message)
2075
2101
  });
2076
- console.error(pc8.red(`
2102
+ console.error(pc9.red(`
2077
2103
  \u2717 ${message}`));
2078
2104
  process.exit(1);
2079
2105
  }
2080
2106
  }
2081
2107
 
2082
2108
  // src/cmds/sync.ts
2083
- import pc9 from "picocolors";
2109
+ import pc10 from "picocolors";
2084
2110
  import prompts7 from "prompts";
2085
2111
  function mapToVercelEnvironment(keywayEnv) {
2086
2112
  const mapping = {
@@ -2149,11 +2175,11 @@ async function promptProjectSelection(projects, repoFullName) {
2149
2175
  let title = p.name;
2150
2176
  const badges = [];
2151
2177
  if (p.linkedRepo?.toLowerCase() === repoFullName.toLowerCase()) {
2152
- badges.push(pc9.green("\u2190 linked"));
2178
+ badges.push(pc10.green("\u2190 linked"));
2153
2179
  } else if (p.name.toLowerCase() === repoName) {
2154
- badges.push(pc9.green("\u2190 same name"));
2180
+ badges.push(pc10.green("\u2190 same name"));
2155
2181
  } else if (p.linkedRepo) {
2156
- badges.push(pc9.gray(`\u2192 ${p.linkedRepo}`));
2182
+ badges.push(pc10.gray(`\u2192 ${p.linkedRepo}`));
2157
2183
  }
2158
2184
  if (badges.length > 0) {
2159
2185
  title = `${p.name} ${badges.join(" ")}`;
@@ -2167,7 +2193,7 @@ async function promptProjectSelection(projects, repoFullName) {
2167
2193
  choices
2168
2194
  });
2169
2195
  if (!projectChoice) {
2170
- console.log(pc9.gray("Cancelled."));
2196
+ console.log(pc10.gray("Cancelled."));
2171
2197
  process.exit(0);
2172
2198
  }
2173
2199
  return projects.find((p) => p.id === projectChoice);
@@ -2175,23 +2201,23 @@ async function promptProjectSelection(projects, repoFullName) {
2175
2201
  async function syncCommand(provider, options = {}) {
2176
2202
  try {
2177
2203
  if (options.pull && options.allowDelete) {
2178
- console.error(pc9.red("Error: --allow-delete cannot be used with --pull"));
2179
- console.log(pc9.gray("The --allow-delete flag is only for push operations."));
2204
+ console.error(pc10.red("Error: --allow-delete cannot be used with --pull"));
2205
+ console.log(pc10.gray("The --allow-delete flag is only for push operations."));
2180
2206
  process.exit(1);
2181
2207
  }
2182
2208
  const accessToken = await ensureLogin({ allowPrompt: options.loginPrompt !== false });
2183
2209
  const repoFullName = detectGitRepo();
2184
2210
  if (!repoFullName) {
2185
- console.error(pc9.red("Could not detect Git repository."));
2186
- console.log(pc9.gray("Run this command from a Git repository directory."));
2211
+ console.error(pc10.red("Could not detect Git repository."));
2212
+ console.log(pc10.gray("Run this command from a Git repository directory."));
2187
2213
  process.exit(1);
2188
2214
  }
2189
- console.log(pc9.gray(`Repository: ${repoFullName}`));
2215
+ console.log(pc10.gray(`Repository: ${repoFullName}`));
2190
2216
  let { connections } = await getConnections(accessToken);
2191
2217
  let connection = connections.find((c) => c.provider === provider.toLowerCase());
2192
2218
  if (!connection) {
2193
2219
  const providerDisplayName = provider.charAt(0).toUpperCase() + provider.slice(1);
2194
- console.log(pc9.yellow(`
2220
+ console.log(pc10.yellow(`
2195
2221
  Not connected to ${providerDisplayName}.`));
2196
2222
  const { shouldConnect } = await prompts7({
2197
2223
  type: "confirm",
@@ -2200,7 +2226,7 @@ Not connected to ${providerDisplayName}.`));
2200
2226
  initial: true
2201
2227
  });
2202
2228
  if (!shouldConnect) {
2203
- console.log(pc9.gray("Cancelled."));
2229
+ console.log(pc10.gray("Cancelled."));
2204
2230
  process.exit(0);
2205
2231
  }
2206
2232
  await connectCommand(provider, { loginPrompt: false });
@@ -2208,7 +2234,7 @@ Not connected to ${providerDisplayName}.`));
2208
2234
  connections = refreshed.connections;
2209
2235
  connection = connections.find((c) => c.provider === provider.toLowerCase());
2210
2236
  if (!connection) {
2211
- console.error(pc9.red(`
2237
+ console.error(pc10.red(`
2212
2238
  Connection to ${providerDisplayName} failed.`));
2213
2239
  process.exit(1);
2214
2240
  }
@@ -2216,7 +2242,7 @@ Connection to ${providerDisplayName} failed.`));
2216
2242
  }
2217
2243
  const { projects } = await getConnectionProjects(accessToken, connection.id);
2218
2244
  if (projects.length === 0) {
2219
- console.error(pc9.red(`No projects found in your ${provider} account.`));
2245
+ console.error(pc10.red(`No projects found in your ${provider} account.`));
2220
2246
  process.exit(1);
2221
2247
  }
2222
2248
  let selectedProject;
@@ -2225,21 +2251,21 @@ Connection to ${providerDisplayName} failed.`));
2225
2251
  (p) => p.id === options.project || p.name.toLowerCase() === options.project?.toLowerCase()
2226
2252
  );
2227
2253
  if (!found) {
2228
- console.error(pc9.red(`Project not found: ${options.project}`));
2229
- console.log(pc9.gray("Available projects:"));
2230
- projects.forEach((p) => console.log(pc9.gray(` - ${p.name}`)));
2254
+ console.error(pc10.red(`Project not found: ${options.project}`));
2255
+ console.log(pc10.gray("Available projects:"));
2256
+ projects.forEach((p) => console.log(pc10.gray(` - ${p.name}`)));
2231
2257
  process.exit(1);
2232
2258
  }
2233
2259
  selectedProject = found;
2234
2260
  if (!projectMatchesRepo(selectedProject, repoFullName)) {
2235
2261
  console.log("");
2236
- console.log(pc9.yellow("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510"));
2237
- console.log(pc9.yellow("\u2502 \u26A0\uFE0F WARNING: Project does not match current repository \u2502"));
2238
- console.log(pc9.yellow("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"));
2239
- console.log(pc9.yellow(` Current repo: ${repoFullName}`));
2240
- console.log(pc9.yellow(` Selected project: ${selectedProject.name}`));
2262
+ console.log(pc10.yellow("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510"));
2263
+ console.log(pc10.yellow("\u2502 \u26A0\uFE0F WARNING: Project does not match current repository \u2502"));
2264
+ console.log(pc10.yellow("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"));
2265
+ console.log(pc10.yellow(` Current repo: ${repoFullName}`));
2266
+ console.log(pc10.yellow(` Selected project: ${selectedProject.name}`));
2241
2267
  if (selectedProject.linkedRepo) {
2242
- console.log(pc9.yellow(` Project linked to: ${selectedProject.linkedRepo}`));
2268
+ console.log(pc10.yellow(` Project linked to: ${selectedProject.linkedRepo}`));
2243
2269
  }
2244
2270
  console.log("");
2245
2271
  }
@@ -2248,9 +2274,9 @@ Connection to ${providerDisplayName} failed.`));
2248
2274
  if (autoMatch && (autoMatch.matchType === "linked_repo" || autoMatch.matchType === "exact_name")) {
2249
2275
  selectedProject = autoMatch.project;
2250
2276
  const matchReason = autoMatch.matchType === "linked_repo" ? `linked to ${repoFullName}` : "exact name match";
2251
- console.log(pc9.green(`\u2713 Auto-selected project: ${selectedProject.name} (${matchReason})`));
2277
+ console.log(pc10.green(`\u2713 Auto-selected project: ${selectedProject.name} (${matchReason})`));
2252
2278
  } else if (autoMatch && autoMatch.matchType === "partial_name") {
2253
- console.log(pc9.yellow(`Detected project: ${autoMatch.project.name} (partial match)`));
2279
+ console.log(pc10.yellow(`Detected project: ${autoMatch.project.name} (partial match)`));
2254
2280
  const { useDetected } = await prompts7({
2255
2281
  type: "confirm",
2256
2282
  name: "useDetected",
@@ -2266,13 +2292,13 @@ Connection to ${providerDisplayName} failed.`));
2266
2292
  selectedProject = projects[0];
2267
2293
  if (!projectMatchesRepo(selectedProject, repoFullName)) {
2268
2294
  console.log("");
2269
- console.log(pc9.yellow("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510"));
2270
- console.log(pc9.yellow("\u2502 \u26A0\uFE0F WARNING: Project does not match current repository \u2502"));
2271
- console.log(pc9.yellow("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"));
2272
- console.log(pc9.yellow(` Current repo: ${repoFullName}`));
2273
- console.log(pc9.yellow(` Only project: ${selectedProject.name}`));
2295
+ console.log(pc10.yellow("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510"));
2296
+ console.log(pc10.yellow("\u2502 \u26A0\uFE0F WARNING: Project does not match current repository \u2502"));
2297
+ console.log(pc10.yellow("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"));
2298
+ console.log(pc10.yellow(` Current repo: ${repoFullName}`));
2299
+ console.log(pc10.yellow(` Only project: ${selectedProject.name}`));
2274
2300
  if (selectedProject.linkedRepo) {
2275
- console.log(pc9.yellow(` Project linked to: ${selectedProject.linkedRepo}`));
2301
+ console.log(pc10.yellow(` Project linked to: ${selectedProject.linkedRepo}`));
2276
2302
  }
2277
2303
  console.log("");
2278
2304
  const { continueAnyway } = await prompts7({
@@ -2282,14 +2308,14 @@ Connection to ${providerDisplayName} failed.`));
2282
2308
  initial: false
2283
2309
  });
2284
2310
  if (!continueAnyway) {
2285
- console.log(pc9.gray("Cancelled."));
2311
+ console.log(pc10.gray("Cancelled."));
2286
2312
  process.exit(0);
2287
2313
  }
2288
2314
  }
2289
2315
  } else {
2290
- console.log(pc9.yellow(`
2316
+ console.log(pc10.yellow(`
2291
2317
  \u26A0\uFE0F No matching project found for ${repoFullName}`));
2292
- console.log(pc9.gray("Select a project manually:\n"));
2318
+ console.log(pc10.gray("Select a project manually:\n"));
2293
2319
  selectedProject = await promptProjectSelection(projects, repoFullName);
2294
2320
  }
2295
2321
  }
@@ -2297,13 +2323,13 @@ Connection to ${providerDisplayName} failed.`));
2297
2323
  const autoMatch = findMatchingProject(projects, repoFullName);
2298
2324
  if (autoMatch && autoMatch.project.id !== selectedProject.id) {
2299
2325
  console.log("");
2300
- console.log(pc9.yellow("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510"));
2301
- console.log(pc9.yellow("\u2502 \u26A0\uFE0F WARNING: You selected a different project \u2502"));
2302
- console.log(pc9.yellow("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"));
2303
- console.log(pc9.yellow(` Current repo: ${repoFullName}`));
2304
- console.log(pc9.yellow(` Selected project: ${selectedProject.name}`));
2326
+ console.log(pc10.yellow("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510"));
2327
+ console.log(pc10.yellow("\u2502 \u26A0\uFE0F WARNING: You selected a different project \u2502"));
2328
+ console.log(pc10.yellow("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"));
2329
+ console.log(pc10.yellow(` Current repo: ${repoFullName}`));
2330
+ console.log(pc10.yellow(` Selected project: ${selectedProject.name}`));
2305
2331
  if (selectedProject.linkedRepo) {
2306
- console.log(pc9.yellow(` Project linked to: ${selectedProject.linkedRepo}`));
2332
+ console.log(pc10.yellow(` Project linked to: ${selectedProject.linkedRepo}`));
2307
2333
  }
2308
2334
  console.log("");
2309
2335
  const { continueAnyway } = await prompts7({
@@ -2313,7 +2339,7 @@ Connection to ${providerDisplayName} failed.`));
2313
2339
  initial: false
2314
2340
  });
2315
2341
  if (!continueAnyway) {
2316
- console.log(pc9.gray("Cancelled."));
2342
+ console.log(pc10.gray("Cancelled."));
2317
2343
  process.exit(0);
2318
2344
  }
2319
2345
  }
@@ -2335,7 +2361,7 @@ Connection to ${providerDisplayName} failed.`));
2335
2361
  initial: Math.max(0, vaultEnvs.indexOf("production"))
2336
2362
  });
2337
2363
  if (!selectedEnv) {
2338
- console.log(pc9.gray("Cancelled."));
2364
+ console.log(pc10.gray("Cancelled."));
2339
2365
  process.exit(0);
2340
2366
  }
2341
2367
  keywayEnv = selectedEnv;
@@ -2354,7 +2380,7 @@ Connection to ${providerDisplayName} failed.`));
2354
2380
  ]
2355
2381
  });
2356
2382
  if (!selectedDirection) {
2357
- console.log(pc9.gray("Cancelled."));
2383
+ console.log(pc10.gray("Cancelled."));
2358
2384
  process.exit(0);
2359
2385
  }
2360
2386
  direction = selectedDirection;
@@ -2371,9 +2397,9 @@ Connection to ${providerDisplayName} failed.`));
2371
2397
  keywayEnv
2372
2398
  );
2373
2399
  if (status.isFirstSync && direction === "push" && status.vaultIsEmpty && status.providerHasSecrets) {
2374
- console.log(pc9.yellow(`
2400
+ console.log(pc10.yellow(`
2375
2401
  \u26A0\uFE0F Your Keyway vault is empty for "${keywayEnv}", but ${providerName} has ${status.providerSecretCount} secrets.`));
2376
- console.log(pc9.gray(` (Use --environment to sync a different environment)`));
2402
+ console.log(pc10.gray(` (Use --environment to sync a different environment)`));
2377
2403
  const { importFirst } = await prompts7({
2378
2404
  type: "confirm",
2379
2405
  name: "importFirst",
@@ -2415,7 +2441,7 @@ Connection to ${providerDisplayName} failed.`));
2415
2441
  command: "sync",
2416
2442
  error: truncateMessage(message)
2417
2443
  });
2418
- console.error(pc9.red(`
2444
+ console.error(pc10.red(`
2419
2445
  \u2717 ${message}`));
2420
2446
  process.exit(1);
2421
2447
  }
@@ -2432,33 +2458,33 @@ async function executeSyncOperation(accessToken, repoFullName, connectionId, pro
2432
2458
  });
2433
2459
  const totalChanges = preview.toCreate.length + preview.toUpdate.length + preview.toDelete.length;
2434
2460
  if (totalChanges === 0) {
2435
- console.log(pc9.green("\n\u2713 Already in sync. No changes needed."));
2461
+ console.log(pc10.green("\n\u2713 Already in sync. No changes needed."));
2436
2462
  return;
2437
2463
  }
2438
- console.log(pc9.blue("\n\u{1F4CB} Sync Preview\n"));
2464
+ console.log(pc10.blue("\n\u{1F4CB} Sync Preview\n"));
2439
2465
  if (preview.toCreate.length > 0) {
2440
- console.log(pc9.green(` + ${preview.toCreate.length} to create`));
2441
- preview.toCreate.slice(0, 5).forEach((key) => console.log(pc9.gray(` ${key}`)));
2466
+ console.log(pc10.green(` + ${preview.toCreate.length} to create`));
2467
+ preview.toCreate.slice(0, 5).forEach((key) => console.log(pc10.gray(` ${key}`)));
2442
2468
  if (preview.toCreate.length > 5) {
2443
- console.log(pc9.gray(` ... and ${preview.toCreate.length - 5} more`));
2469
+ console.log(pc10.gray(` ... and ${preview.toCreate.length - 5} more`));
2444
2470
  }
2445
2471
  }
2446
2472
  if (preview.toUpdate.length > 0) {
2447
- console.log(pc9.yellow(` ~ ${preview.toUpdate.length} to update`));
2448
- preview.toUpdate.slice(0, 5).forEach((key) => console.log(pc9.gray(` ${key}`)));
2473
+ console.log(pc10.yellow(` ~ ${preview.toUpdate.length} to update`));
2474
+ preview.toUpdate.slice(0, 5).forEach((key) => console.log(pc10.gray(` ${key}`)));
2449
2475
  if (preview.toUpdate.length > 5) {
2450
- console.log(pc9.gray(` ... and ${preview.toUpdate.length - 5} more`));
2476
+ console.log(pc10.gray(` ... and ${preview.toUpdate.length - 5} more`));
2451
2477
  }
2452
2478
  }
2453
2479
  if (preview.toDelete.length > 0) {
2454
- console.log(pc9.red(` - ${preview.toDelete.length} to delete`));
2455
- preview.toDelete.slice(0, 5).forEach((key) => console.log(pc9.gray(` ${key}`)));
2480
+ console.log(pc10.red(` - ${preview.toDelete.length} to delete`));
2481
+ preview.toDelete.slice(0, 5).forEach((key) => console.log(pc10.gray(` ${key}`)));
2456
2482
  if (preview.toDelete.length > 5) {
2457
- console.log(pc9.gray(` ... and ${preview.toDelete.length - 5} more`));
2483
+ console.log(pc10.gray(` ... and ${preview.toDelete.length - 5} more`));
2458
2484
  }
2459
2485
  }
2460
2486
  if (preview.toSkip.length > 0) {
2461
- console.log(pc9.gray(` \u25CB ${preview.toSkip.length} unchanged`));
2487
+ console.log(pc10.gray(` \u25CB ${preview.toSkip.length} unchanged`));
2462
2488
  }
2463
2489
  console.log("");
2464
2490
  if (!skipConfirm) {
@@ -2470,11 +2496,11 @@ async function executeSyncOperation(accessToken, repoFullName, connectionId, pro
2470
2496
  initial: true
2471
2497
  });
2472
2498
  if (!confirm) {
2473
- console.log(pc9.gray("Cancelled."));
2499
+ console.log(pc10.gray("Cancelled."));
2474
2500
  return;
2475
2501
  }
2476
2502
  }
2477
- console.log(pc9.blue("\n\u23F3 Syncing...\n"));
2503
+ console.log(pc10.blue("\n\u23F3 Syncing...\n"));
2478
2504
  const result = await executeSync(accessToken, repoFullName, {
2479
2505
  connectionId,
2480
2506
  projectId: project.id,
@@ -2484,11 +2510,11 @@ async function executeSyncOperation(accessToken, repoFullName, connectionId, pro
2484
2510
  allowDelete
2485
2511
  });
2486
2512
  if (result.success) {
2487
- console.log(pc9.green("\u2713 Sync complete"));
2488
- console.log(pc9.gray(` Created: ${result.stats.created}`));
2489
- console.log(pc9.gray(` Updated: ${result.stats.updated}`));
2513
+ console.log(pc10.green("\u2713 Sync complete"));
2514
+ console.log(pc10.gray(` Created: ${result.stats.created}`));
2515
+ console.log(pc10.gray(` Updated: ${result.stats.updated}`));
2490
2516
  if (result.stats.deleted > 0) {
2491
- console.log(pc9.gray(` Deleted: ${result.stats.deleted}`));
2517
+ console.log(pc10.gray(` Deleted: ${result.stats.deleted}`));
2492
2518
  }
2493
2519
  trackEvent(AnalyticsEvents.CLI_SYNC, {
2494
2520
  provider,
@@ -2498,20 +2524,24 @@ async function executeSyncOperation(accessToken, repoFullName, connectionId, pro
2498
2524
  deleted: result.stats.deleted
2499
2525
  });
2500
2526
  } else {
2501
- console.error(pc9.red(`
2527
+ console.error(pc10.red(`
2502
2528
  \u2717 ${result.error}`));
2503
2529
  process.exit(1);
2504
2530
  }
2505
2531
  }
2506
2532
 
2507
2533
  // src/cli.ts
2534
+ process.on("unhandledRejection", (reason) => {
2535
+ console.error(pc11.red("Unhandled error:"), reason);
2536
+ process.exit(1);
2537
+ });
2508
2538
  var program = new Command();
2509
2539
  var TAGLINE = "Sync secrets with your team and infra";
2510
2540
  var showBanner = () => {
2511
- const text = pc10.bold(pc10.cyan("Keyway CLI"));
2541
+ const text = pc11.bold(pc11.cyan("Keyway CLI"));
2512
2542
  console.log(`
2513
2543
  ${text}
2514
- ${pc10.gray(TAGLINE)}
2544
+ ${pc11.gray(TAGLINE)}
2515
2545
  `);
2516
2546
  };
2517
2547
  showBanner();
@@ -2548,6 +2578,6 @@ program.command("sync <provider>").description("Sync secrets with a provider (e.
2548
2578
  await syncCommand(provider, options);
2549
2579
  });
2550
2580
  program.parseAsync().catch((error) => {
2551
- console.error(pc10.red("Error:"), error.message || error);
2581
+ console.error(pc11.red("Error:"), error.message || error);
2552
2582
  process.exit(1);
2553
2583
  });