@secretstash/cli 0.1.2 → 0.1.4

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/index.js +104 -96
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -8,8 +8,9 @@ import { fileURLToPath } from "url";
8
8
  import { dirname as dirname2, join as join2 } from "path";
9
9
 
10
10
  // src/commands/auth.ts
11
- import { prompt } from "enquirer";
11
+ import enquirer from "enquirer";
12
12
  import qrcode from "qrcode-terminal";
13
+ import { execFile } from "child_process";
13
14
 
14
15
  // src/config.ts
15
16
  import Conf from "conf";
@@ -385,6 +386,18 @@ var ApiClient = class {
385
386
  requireAuth: false
386
387
  });
387
388
  }
389
+ // Browser-based CLI auth endpoints
390
+ async initBrowserAuth() {
391
+ return this.request("/api/v1/cli/auth/init", {
392
+ method: "POST",
393
+ requireAuth: false
394
+ });
395
+ }
396
+ async checkBrowserAuthStatus(code) {
397
+ return this.request(`/api/v1/cli/auth/status/${code}`, {
398
+ requireAuth: false
399
+ });
400
+ }
388
401
  async verify2FA(tempToken, code) {
389
402
  return this.request("/api/auth/2fa/verify", {
390
403
  method: "POST",
@@ -817,7 +830,81 @@ var ui = {
817
830
  };
818
831
 
819
832
  // src/commands/auth.ts
833
+ var { prompt } = enquirer;
820
834
  var SERVICE_TOKEN_PREFIX = "stk_";
835
+ function openBrowser(url) {
836
+ const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "powershell" : "xdg-open";
837
+ const args = process.platform === "win32" ? ["-Command", "Start-Process", url] : [url];
838
+ execFile(opener, args, (error) => {
839
+ if (error) {
840
+ ui.warning("Could not open browser automatically");
841
+ ui.info(`Please open this URL manually: ${colors.highlight(url)}`);
842
+ }
843
+ });
844
+ }
845
+ async function handleBrowserLogin() {
846
+ ui.heading("SecretStash Login");
847
+ ui.br();
848
+ const initSpinner = ui.spinner("Starting authentication...");
849
+ const initResult = await apiClient.initBrowserAuth();
850
+ if (initResult.error) {
851
+ initSpinner.fail();
852
+ ui.error(initResult.error.message);
853
+ process.exit(1);
854
+ }
855
+ initSpinner.succeed("Authentication session started");
856
+ const { code, authUrl, expiresIn } = initResult.data;
857
+ ui.br();
858
+ ui.box("Browser Authentication", [
859
+ "A browser window will open for you to sign in.",
860
+ "If it doesn't open automatically, visit:",
861
+ "",
862
+ ` ${colors.highlight(authUrl)}`,
863
+ "",
864
+ `Code: ${colors.primary.bold(code)}`,
865
+ "",
866
+ colors.muted(`This code expires in ${Math.floor(expiresIn / 60)} minutes`)
867
+ ]);
868
+ openBrowser(authUrl);
869
+ ui.br();
870
+ const pollSpinner = ui.spinner("Waiting for authentication...");
871
+ const pollInterval = 2e3;
872
+ const maxAttempts = Math.floor(expiresIn * 1e3 / pollInterval);
873
+ let attempts = 0;
874
+ while (attempts < maxAttempts) {
875
+ await new Promise((resolve) => setTimeout(resolve, pollInterval));
876
+ attempts++;
877
+ const statusResult = await apiClient.checkBrowserAuthStatus(code);
878
+ if (statusResult.error) {
879
+ if (statusResult.error.code === "EXPIRED" || statusResult.error.code === "NOT_FOUND") {
880
+ pollSpinner.fail();
881
+ ui.error("Authentication session expired. Please try again.");
882
+ process.exit(1);
883
+ }
884
+ continue;
885
+ }
886
+ const { status, accessToken, refreshToken, expiresIn: tokenExpiry, user } = statusResult.data;
887
+ if (status === "completed" && accessToken && refreshToken && user) {
888
+ pollSpinner.succeed("Authentication successful!");
889
+ configManager.setTokens(accessToken, refreshToken, tokenExpiry || 3600);
890
+ configManager.setUser(user.id, user.email);
891
+ ui.br();
892
+ ui.keyValue("Email", user.email);
893
+ ui.keyValue("Config", configManager.getConfigPath());
894
+ ui.br();
895
+ ui.info(`Run ${ui.code("sstash teams")} to see your teams`);
896
+ return;
897
+ }
898
+ if (status === "expired") {
899
+ pollSpinner.fail();
900
+ ui.error("Authentication session expired. Please try again.");
901
+ process.exit(1);
902
+ }
903
+ }
904
+ pollSpinner.fail();
905
+ ui.error("Authentication timed out. Please try again.");
906
+ process.exit(1);
907
+ }
821
908
  async function handleServiceTokenLogin(token, fromEnvVar = false) {
822
909
  ui.heading("SecretStash Service Token Login");
823
910
  if (!token.startsWith(SERVICE_TOKEN_PREFIX)) {
@@ -865,7 +952,7 @@ async function handleServiceTokenLogin(token, fromEnvVar = false) {
865
952
  ]);
866
953
  }
867
954
  function registerAuthCommands(program2) {
868
- program2.command("login").description("Authenticate with SecretStash").option("-e, --email <email>", "Email address").option("-t, --token <token>", "Service token for CI/CD authentication (must start with stk_)").action(async (options) => {
955
+ program2.command("login").description("Authenticate with SecretStash via browser (supports passkeys and 2FA)").option("-t, --token <token>", "Service token for CI/CD authentication (must start with stk_)").action(async (options) => {
869
956
  if (options.token) {
870
957
  await handleServiceTokenLogin(options.token);
871
958
  return;
@@ -874,93 +961,7 @@ function registerAuthCommands(program2) {
874
961
  await handleServiceTokenLogin(process.env.SECRETSTASH_TOKEN, true);
875
962
  return;
876
963
  }
877
- ui.heading("SecretStash Login");
878
- let email = options.email;
879
- if (!email) {
880
- const response = await prompt({
881
- type: "input",
882
- name: "email",
883
- message: "Email:",
884
- validate: (value) => {
885
- if (!value) return "Email is required";
886
- if (!value.includes("@")) return "Invalid email format";
887
- return true;
888
- }
889
- });
890
- email = response.email;
891
- }
892
- const { password } = await prompt({
893
- type: "password",
894
- name: "password",
895
- message: "Password:",
896
- validate: (value) => value ? true : "Password is required"
897
- });
898
- const spinner = ui.spinner("Authenticating...");
899
- const result = await apiClient.login(email, password);
900
- if (result.error) {
901
- spinner.fail();
902
- if (result.error.code === "CLI_2FA_REQUIRED") {
903
- ui.br();
904
- ui.box("Two-Factor Authentication Required", [
905
- "CLI access requires two-factor authentication.",
906
- "",
907
- "To set up 2FA:",
908
- ` 1. Visit ${colors.highlight("https://app.secretstash.dev/settings/security")}`,
909
- " 2. Enable two-factor authentication",
910
- " 3. Return here and try again",
911
- "",
912
- colors.muted("For CI/CD pipelines, use service tokens instead:"),
913
- ` ${colors.highlight("sstash login --token <service-token>")}`
914
- ]);
915
- process.exit(1);
916
- }
917
- ui.error(result.error.message);
918
- process.exit(1);
919
- }
920
- if (result.data?.requires2FA) {
921
- spinner.stop();
922
- ui.info("Two-factor authentication required");
923
- const { code } = await prompt({
924
- type: "input",
925
- name: "code",
926
- message: "2FA Code:",
927
- validate: (value) => {
928
- if (!value) return "2FA code is required";
929
- if (!/^\d{6}$/.test(value)) return "Code must be 6 digits";
930
- return true;
931
- }
932
- });
933
- const spinner2 = ui.spinner("Verifying...");
934
- const twoFAResult = await apiClient.verify2FA(
935
- result.data.tempToken,
936
- code
937
- );
938
- if (twoFAResult.error) {
939
- spinner2.fail();
940
- ui.error(twoFAResult.error.message);
941
- process.exit(1);
942
- }
943
- configManager.setTokens(
944
- twoFAResult.data.accessToken,
945
- twoFAResult.data.refreshToken,
946
- twoFAResult.data.expiresIn
947
- );
948
- configManager.setUser(twoFAResult.data.user.id, twoFAResult.data.user.email);
949
- spinner2.succeed("Logged in successfully");
950
- } else {
951
- configManager.setTokens(
952
- result.data.accessToken,
953
- result.data.refreshToken,
954
- result.data.expiresIn
955
- );
956
- configManager.setUser(result.data.user.id, result.data.user.email);
957
- spinner.succeed("Logged in successfully");
958
- }
959
- ui.br();
960
- ui.keyValue("Email", email);
961
- ui.keyValue("Config", configManager.getConfigPath());
962
- ui.br();
963
- ui.info(`Run ${ui.code("sstash teams")} to see your teams`);
964
+ await handleBrowserLogin();
964
965
  });
965
966
  program2.command("logout").description("Log out from SecretStash").action(async () => {
966
967
  if (!configManager.isAuthenticated()) {
@@ -1134,7 +1135,8 @@ function registerAuthCommands(program2) {
1134
1135
  }
1135
1136
 
1136
1137
  // src/commands/teams.ts
1137
- import { prompt as prompt2 } from "enquirer";
1138
+ import enquirer2 from "enquirer";
1139
+ var { prompt: prompt2 } = enquirer2;
1138
1140
  function registerTeamCommands(program2) {
1139
1141
  const teams = program2.command("teams").description("Manage teams");
1140
1142
  teams.command("list").alias("ls").description("List all teams you belong to").action(async () => {
@@ -1449,7 +1451,8 @@ function registerTeamCommands(program2) {
1449
1451
  }
1450
1452
 
1451
1453
  // src/commands/projects.ts
1452
- import { prompt as prompt3 } from "enquirer";
1454
+ import enquirer3 from "enquirer";
1455
+ var { prompt: prompt3 } = enquirer3;
1453
1456
  function requireTeam() {
1454
1457
  const team = configManager.getCurrentTeam();
1455
1458
  if (!team) {
@@ -1644,7 +1647,8 @@ function registerProjectCommands(program2) {
1644
1647
  }
1645
1648
 
1646
1649
  // src/commands/environments.ts
1647
- import { prompt as prompt4 } from "enquirer";
1650
+ import enquirer4 from "enquirer";
1651
+ var { prompt: prompt4 } = enquirer4;
1648
1652
  function requireProject() {
1649
1653
  const project = configManager.getCurrentProject();
1650
1654
  if (!project) {
@@ -1943,10 +1947,11 @@ function registerEnvironmentCommands(program2) {
1943
1947
  }
1944
1948
 
1945
1949
  // src/commands/secrets.ts
1946
- import { prompt as prompt5 } from "enquirer";
1950
+ import enquirer5 from "enquirer";
1947
1951
  import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, existsSync as existsSync2 } from "fs";
1948
1952
  import { spawn } from "child_process";
1949
1953
  import yaml from "js-yaml";
1954
+ var { prompt: prompt5 } = enquirer5;
1950
1955
  function requireContext() {
1951
1956
  const teamSlug = projectConfig.getEffectiveTeam();
1952
1957
  const projectSlug = projectConfig.getEffectiveProject();
@@ -2987,7 +2992,8 @@ function registerSecretCommands(program2) {
2987
2992
  }
2988
2993
 
2989
2994
  // src/commands/diff.ts
2990
- import { prompt as prompt6 } from "enquirer";
2995
+ import enquirer6 from "enquirer";
2996
+ var { prompt: prompt6 } = enquirer6;
2991
2997
  function computeDiff(secrets1, secrets2) {
2992
2998
  const map1 = new Map(secrets1.map((s) => [s.key, s.value]));
2993
2999
  const map2 = new Map(secrets2.map((s) => [s.key, s.value]));
@@ -3141,7 +3147,8 @@ function registerDiffCommand(program2) {
3141
3147
  }
3142
3148
 
3143
3149
  // src/commands/share.ts
3144
- import { prompt as prompt7 } from "enquirer";
3150
+ import enquirer7 from "enquirer";
3151
+ var { prompt: prompt7 } = enquirer7;
3145
3152
  function requireContext2() {
3146
3153
  const teamSlug = projectConfig.getEffectiveTeam();
3147
3154
  const projectSlug = projectConfig.getEffectiveProject();
@@ -3693,8 +3700,9 @@ function registerDoctorCommand(program2) {
3693
3700
  }
3694
3701
 
3695
3702
  // src/commands/tags.ts
3696
- import { prompt as prompt8 } from "enquirer";
3703
+ import enquirer8 from "enquirer";
3697
3704
  import chalk2 from "chalk";
3705
+ var { prompt: prompt8 } = enquirer8;
3698
3706
  function registerTagCommands(program2) {
3699
3707
  const tags = program2.command("tags").description("Manage tags for organizing secrets");
3700
3708
  tags.command("list").alias("ls").description("List all tags in the current team").action(async () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@secretstash/cli",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "CLI tool for SecretStash - secure team secrets management",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",