learn-secrets-sdk 1.6.9 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -362,7 +362,7 @@ Log in on ${this.baseUrl}`);
362
362
  /**
363
363
  * Make authenticated request
364
364
  */
365
- async request(method, path2, body) {
365
+ async request(method, path3, body) {
366
366
  const token = this.getAccessToken();
367
367
  const options = {
368
368
  method,
@@ -374,7 +374,7 @@ Log in on ${this.baseUrl}`);
374
374
  if (body) {
375
375
  options.body = JSON.stringify(body);
376
376
  }
377
- const response = await fetch(`${this.baseUrl}${path2}`, options);
377
+ const response = await fetch(`${this.baseUrl}${path3}`, options);
378
378
  if (!response.ok) {
379
379
  const errorData = await response.json().catch(() => ({}));
380
380
  throw new SecretsSDKError(
@@ -539,7 +539,7 @@ function parseEnvFile(filePath) {
539
539
  if (!trimmed || trimmed.startsWith("#")) {
540
540
  continue;
541
541
  }
542
- const match = trimmed.match(/^([A-Z0-9_]+)\s*=\s*(['"]?)(.+?)\2$/i);
542
+ const match = trimmed.match(/^([A-Z0-9_]+)\s*=\s*(['"]?)(.+)\2\s*$/i);
543
543
  if (match) {
544
544
  const key = match[1];
545
545
  const value = match[3];
@@ -585,45 +585,43 @@ var PROVIDER_PATTERNS = [
585
585
  baseUrl: "https://www.googleapis.com",
586
586
  authHeader: "Authorization",
587
587
  authPrefix: "Bearer "
588
+ },
589
+ {
590
+ pattern: /^AUTH0_.*_SECRET$/i,
591
+ provider: "auth0",
592
+ baseUrl: "",
593
+ authHeader: "Authorization",
594
+ authPrefix: "Bearer "
595
+ },
596
+ {
597
+ pattern: /^SPOTIFY_(CLIENT_ID|CLIENT_SECRET|API_KEY)$/i,
598
+ provider: "spotify",
599
+ baseUrl: "https://api.spotify.com",
600
+ authHeader: "Authorization",
601
+ authPrefix: "Bearer "
602
+ },
603
+ {
604
+ pattern: /^APPLE_(KEY_ID|TEAM_ID|P8_KEY)$/i,
605
+ provider: "apple",
606
+ baseUrl: "https://api.music.apple.com",
607
+ authHeader: "Authorization",
608
+ authPrefix: "Bearer "
609
+ },
610
+ {
611
+ pattern: /^LICHESS_KEY$/i,
612
+ provider: "lichess",
613
+ baseUrl: "https://lichess.org/api",
614
+ authHeader: "Authorization",
615
+ authPrefix: "Bearer "
616
+ },
617
+ {
618
+ pattern: /^GITHUB_PAT$/i,
619
+ provider: "github",
620
+ baseUrl: "https://api.github.com",
621
+ authHeader: "Authorization",
622
+ authPrefix: "Bearer "
588
623
  }
589
624
  ];
590
- function isLikelyApiKey(key, value) {
591
- const keyPatterns = [
592
- /_API_KEY$/i,
593
- /_SECRET$/i,
594
- /_TOKEN$/i,
595
- /^API_KEY_/i,
596
- /^SECRET_/i,
597
- /^TOKEN_/i
598
- ];
599
- for (const pattern of keyPatterns) {
600
- if (pattern.test(key)) {
601
- return true;
602
- }
603
- }
604
- const valuePatterns = [
605
- /^sk-[a-zA-Z0-9_-]+$/,
606
- // OpenAI, Stripe
607
- /^sk-ant-[a-zA-Z0-9_-]+$/,
608
- // Anthropic
609
- /^ghp_[a-zA-Z0-9]+$/,
610
- // GitHub
611
- /^ya29\.[a-zA-Z0-9_-]+$/,
612
- // Google OAuth
613
- /^[a-f0-9]{32}$/,
614
- // 32-char hex
615
- /^[a-f0-9]{40}$/,
616
- // 40-char hex (like GitHub)
617
- /^[a-zA-Z0-9_-]{40,}$/
618
- // Long random string
619
- ];
620
- for (const pattern of valuePatterns) {
621
- if (pattern.test(value)) {
622
- return true;
623
- }
624
- }
625
- return false;
626
- }
627
625
  function detectProvider(key) {
628
626
  for (const pattern of PROVIDER_PATTERNS) {
629
627
  if (pattern.pattern.test(key)) {
@@ -633,12 +631,12 @@ function detectProvider(key) {
633
631
  return null;
634
632
  }
635
633
  function envKeyToSecretName(key) {
636
- return key.toLowerCase().replace(/_api_key$/i, "").replace(/_secret$/i, "").replace(/_token$/i, "").replace(/_key$/i, "").replace(/_/g, "-");
634
+ return key.toLowerCase().replace(/_/g, "-");
637
635
  }
638
- function detectApiKeys(envVars) {
636
+ function convertAllEnvVarsToSecrets(envVars) {
639
637
  const secrets = [];
640
638
  for (const { key, value } of envVars) {
641
- if (!isLikelyApiKey(key, value)) {
639
+ if (!value || value.trim() === "") {
642
640
  continue;
643
641
  }
644
642
  const providerInfo = detectProvider(key);
@@ -658,7 +656,6 @@ function detectApiKeys(envVars) {
658
656
  provider: "custom",
659
657
  api_key: value,
660
658
  base_url: "",
661
- // User must configure
662
659
  auth_header: "Authorization",
663
660
  auth_prefix: "Bearer "
664
661
  });
@@ -668,16 +665,117 @@ function detectApiKeys(envVars) {
668
665
  }
669
666
  function summarizeDetectedSecrets(secrets) {
670
667
  if (secrets.length === 0) {
671
- return "No API keys detected in .env file";
668
+ return "No secrets found in .env file";
672
669
  }
673
- const lines = ["Detected API keys:"];
670
+ const lines = ["Detected secrets:"];
674
671
  for (const secret of secrets) {
675
- const masked = secret.api_key.substring(0, 8) + "...";
672
+ const masked = secret.api_key.length > 8 ? secret.api_key.substring(0, 8) + "..." : "***";
676
673
  lines.push(` - ${secret.name} (${secret.provider}): ${masked}`);
677
674
  }
678
675
  return lines.join("\n");
679
676
  }
680
677
 
678
+ // src/cli/commands/connect-cloudflare.ts
679
+ function getApiUrl(baseUrl) {
680
+ return baseUrl || "https://cloudprototype.org";
681
+ }
682
+ async function connectCloudflare(apiToken, baseUrl) {
683
+ if (!apiToken.match(/^[A-Za-z0-9_-]{40,}$/)) {
684
+ return {
685
+ success: false,
686
+ error: "Invalid API token format. Token should be 40+ alphanumeric characters"
687
+ };
688
+ }
689
+ const validation = await validateCloudflareToken(apiToken);
690
+ if (!validation.valid) {
691
+ return {
692
+ success: false,
693
+ error: validation.error || "Token validation failed"
694
+ };
695
+ }
696
+ try {
697
+ const apiUrl = getApiUrl(baseUrl);
698
+ const response = await fetch(`${apiUrl}/api/cloudflare/connect`, {
699
+ method: "POST",
700
+ headers: {
701
+ "Content-Type": "application/json"
702
+ },
703
+ credentials: "include",
704
+ body: JSON.stringify({
705
+ apiToken,
706
+ accountId: validation.accountId,
707
+ accountName: validation.accountName
708
+ })
709
+ });
710
+ if (!response.ok) {
711
+ const error = await response.json();
712
+ return {
713
+ success: false,
714
+ error: error.message || "Failed to store token"
715
+ };
716
+ }
717
+ return {
718
+ success: true,
719
+ accountId: validation.accountId,
720
+ accountName: validation.accountName
721
+ };
722
+ } catch (error) {
723
+ return {
724
+ success: false,
725
+ error: error.message || "Failed to connect Cloudflare account"
726
+ };
727
+ }
728
+ }
729
+ async function validateCloudflareToken(token) {
730
+ try {
731
+ const response = await fetch("https://api.cloudflare.com/client/v4/user", {
732
+ headers: {
733
+ Authorization: `Bearer ${token}`,
734
+ "Content-Type": "application/json"
735
+ }
736
+ });
737
+ if (!response.ok) {
738
+ const error = await response.json();
739
+ return {
740
+ valid: false,
741
+ error: error.errors?.[0]?.message || "Invalid token"
742
+ };
743
+ }
744
+ const data = await response.json();
745
+ const accountsResponse = await fetch("https://api.cloudflare.com/client/v4/accounts", {
746
+ headers: {
747
+ Authorization: `Bearer ${token}`,
748
+ "Content-Type": "application/json"
749
+ }
750
+ });
751
+ if (!accountsResponse.ok) {
752
+ return {
753
+ valid: false,
754
+ error: "Token does not have access to accounts"
755
+ };
756
+ }
757
+ const accountsData = await accountsResponse.json();
758
+ const accounts = accountsData.result || [];
759
+ if (accounts.length === 0) {
760
+ return {
761
+ valid: false,
762
+ error: "No accounts found for this token"
763
+ };
764
+ }
765
+ const account = accounts[0];
766
+ return {
767
+ valid: true,
768
+ accountId: account.id,
769
+ accountName: account.name
770
+ };
771
+ } catch (error) {
772
+ return {
773
+ valid: false,
774
+ error: error.message || "Failed to validate token"
775
+ };
776
+ }
777
+ }
778
+
681
779
  // src/cli/commands/init.ts
682
780
  async function initCommand(options) {
683
781
  if (!hasValidCredentials()) {
@@ -720,12 +818,10 @@ Onboarding site for project: ${projectId}`);
720
818
  try {
721
819
  console.log(`Reading ${envFile}...`);
722
820
  const envVars = parseEnvFile(envFile);
723
- secrets = detectApiKeys(envVars);
821
+ secrets = convertAllEnvVarsToSecrets(envVars);
724
822
  if (secrets.length === 0) {
725
- console.log("\nNo API keys detected in .env file.");
726
- console.log("Make sure your .env contains variables like:");
727
- console.log(" OPENAI_API_KEY=sk-...");
728
- console.log(" ANTHROPIC_API_KEY=sk-ant-...");
823
+ console.log("\nNo environment variables found in .env file.");
824
+ console.log("Make sure your .env file is not empty.");
729
825
  process.exit(0);
730
826
  }
731
827
  console.log("\n" + summarizeDetectedSecrets(secrets));
@@ -773,15 +869,67 @@ Onboarding site for project: ${projectId}`);
773
869
  console.log("\nAdd to your static site code:");
774
870
  console.log(" const sdk = new SecretsSDK();");
775
871
  console.log(" // Uses origin-based auth - no token needed!");
872
+ console.log("\n--- Cloudflare Worker Setup ---");
776
873
  const dashboardUrl = options.baseUrl || "https://cloudprototype.org";
874
+ let cloudflareConnected = false;
875
+ try {
876
+ const statusResponse = await fetch(`${dashboardUrl}/api/cloudflare/status`, {
877
+ credentials: "include"
878
+ });
879
+ cloudflareConnected = statusResponse.ok;
880
+ } catch {
881
+ cloudflareConnected = false;
882
+ }
883
+ if (cloudflareConnected) {
884
+ console.log("Cloudflare account is already connected.");
885
+ } else {
886
+ console.log("To deploy secrets via Cloudflare Worker, we need your Cloudflare API token.");
887
+ console.log("\nRequired permissions:");
888
+ console.log(" - Workers Scripts: Edit");
889
+ console.log(" - Workers Routes: Edit");
890
+ console.log(" - Account Settings: Read");
891
+ console.log("\nCreate token at: https://dash.cloudflare.com/profile/api-tokens");
892
+ const readline = await import("readline");
893
+ const rl = readline.createInterface({
894
+ input: process.stdin,
895
+ output: process.stdout
896
+ });
897
+ const apiToken = await new Promise((resolve2) => {
898
+ rl.question("\nCloudflare API Token (or press Enter to skip): ", (answer) => {
899
+ rl.close();
900
+ resolve2(answer.trim());
901
+ });
902
+ });
903
+ if (apiToken) {
904
+ console.log("\nValidating token...");
905
+ const result2 = await connectCloudflare(apiToken, options.baseUrl);
906
+ if (result2.success) {
907
+ console.log("Cloudflare account connected");
908
+ console.log(` Account: ${result2.accountName}`);
909
+ cloudflareConnected = true;
910
+ } else {
911
+ console.log(`
912
+ Warning: Failed to connect Cloudflare: ${result2.error}`);
913
+ console.log("You can connect later by running: learn-secrets deploy");
914
+ }
915
+ } else {
916
+ console.log("\nSkipped Cloudflare connection.");
917
+ console.log("You can connect later when you run: learn-secrets deploy");
918
+ }
919
+ }
777
920
  console.log(`
778
921
  Next steps:`);
779
922
  console.log(` 1. Run: learn-secrets deploy`);
780
- console.log(` (Push these secrets to dashboard at ${dashboardUrl}/dashboard/secrets/api_keys)`);
923
+ if (cloudflareConnected) {
924
+ console.log(` (Deploy Worker to Cloudflare with your secrets)`);
925
+ } else {
926
+ console.log(` (Connect Cloudflare and deploy Worker with your secrets)`);
927
+ }
781
928
  console.log(` 2. Visit: ${dashboardUrl}/dashboard to manage secrets`);
782
929
  const config = {
783
930
  project: projectId,
784
- origins
931
+ origins,
932
+ env: envFile
785
933
  };
786
934
  fs3.writeFileSync(configFile, JSON.stringify(config, null, 2));
787
935
  console.log(`
@@ -798,212 +946,6 @@ Updated local config: ${configFile}`);
798
946
  }
799
947
  }
800
948
 
801
- // src/cli/commands/setup.ts
802
- async function setupCommand(options) {
803
- const baseUrl = options.baseUrl || "https://cloudprototype.org";
804
- const envFile = options.env || ".env";
805
- const projectName = options.projectName || `Project - ${options.origin}`;
806
- console.log("\n\u{1F680} Learn Secrets Setup\n");
807
- console.log(`Email: ${options.email}`);
808
- console.log(`Origin: ${options.origin}`);
809
- console.log(`Project: ${projectName}`);
810
- console.log(`Env File: ${envFile}
811
- `);
812
- let secrets;
813
- try {
814
- console.log(`\u{1F4D6} Reading ${envFile}...`);
815
- const envVars = parseEnvFile(envFile);
816
- secrets = detectApiKeys(envVars);
817
- if (secrets.length === 0) {
818
- console.log("\n\u26A0\uFE0F No API keys detected in .env file.");
819
- console.log("Make sure your .env contains variables like:");
820
- console.log(" OPENAI_API_KEY=sk-...");
821
- console.log(" STRIPE_API_KEY=sk_live_...");
822
- console.log(" SPOTIFY_CLIENT_ID=abc123...");
823
- process.exit(0);
824
- }
825
- console.log("\n" + summarizeDetectedSecrets(secrets));
826
- console.log("");
827
- } catch (error) {
828
- console.error(`\u274C Error reading ${envFile}:`, error.message);
829
- process.exit(1);
830
- }
831
- if (!options.yes) {
832
- const readline = await import("readline");
833
- const rl = readline.createInterface({
834
- input: process.stdin,
835
- output: process.stdout
836
- });
837
- const answer = await new Promise((resolve2) => {
838
- rl.question("Continue with setup? (y/N): ", resolve2);
839
- });
840
- rl.close();
841
- if (answer.toLowerCase() !== "y" && answer.toLowerCase() !== "yes") {
842
- console.log("\u274C Cancelled.");
843
- process.exit(0);
844
- }
845
- }
846
- console.log("\n\u{1F510} Authenticating...");
847
- let cookies;
848
- try {
849
- const loginResponse = await fetch(`${baseUrl}/api/login`, {
850
- method: "POST",
851
- headers: {
852
- "Content-Type": "application/json"
853
- },
854
- body: JSON.stringify({
855
- email: options.email,
856
- password: options.password
857
- })
858
- });
859
- if (!loginResponse.ok) {
860
- const errorData = await loginResponse.json().catch(() => ({}));
861
- throw new Error(errorData.error || errorData.message || "Authentication failed");
862
- }
863
- const setCookieHeader = loginResponse.headers.get("set-cookie");
864
- if (!setCookieHeader) {
865
- throw new Error("No session cookie received");
866
- }
867
- const cookieParts = setCookieHeader.split(";")[0];
868
- cookies = cookieParts;
869
- console.log("\u2705 Authenticated successfully");
870
- } catch (error) {
871
- console.error(`\u274C Authentication failed: ${error.message}`);
872
- console.log("\n\u{1F4A1} Make sure:");
873
- console.log(" - Email and password are correct");
874
- console.log(" - User account exists in the system");
875
- console.log(" - Backend is accessible at", baseUrl);
876
- process.exit(1);
877
- }
878
- console.log("\n\u{1F4E6} Creating project...");
879
- let projectAppId;
880
- try {
881
- const createProjectResponse = await fetch(`${baseUrl}/api/projects`, {
882
- method: "POST",
883
- headers: {
884
- "Content-Type": "application/json",
885
- "Cookie": cookies
886
- },
887
- body: JSON.stringify({
888
- name: projectName,
889
- description: `Auto-provisioned for ${options.origin}`
890
- })
891
- });
892
- if (!createProjectResponse.ok) {
893
- const errorData = await createProjectResponse.json().catch(() => ({}));
894
- throw new Error(errorData.message || "Failed to create project");
895
- }
896
- const projectData = await createProjectResponse.json();
897
- projectAppId = projectData.project.appid;
898
- console.log(`\u2705 Project created: ${projectAppId}`);
899
- } catch (error) {
900
- console.error(`\u274C Project creation failed: ${error.message}`);
901
- process.exit(1);
902
- }
903
- console.log("\n\u{1F511} Generating SDK token...");
904
- let sdkToken;
905
- try {
906
- const tokenResponse = await fetch(`${baseUrl}/api/projects/${projectAppId}/sdk-tokens`, {
907
- method: "POST",
908
- headers: {
909
- "Content-Type": "application/json",
910
- "Cookie": cookies
911
- },
912
- body: JSON.stringify({
913
- label: `Production - ${options.origin}`,
914
- origins: [options.origin],
915
- expiresInDays: 365
916
- })
917
- });
918
- if (!tokenResponse.ok) {
919
- const errorData = await tokenResponse.json().catch(() => ({}));
920
- throw new Error(errorData.message || "Failed to generate SDK token");
921
- }
922
- const tokenData = await tokenResponse.json();
923
- sdkToken = tokenData.token;
924
- console.log(`\u2705 SDK token generated`);
925
- console.log(` Token: ${sdkToken.substring(0, 15)}...${sdkToken.slice(-4)}`);
926
- } catch (error) {
927
- console.error(`\u274C Token generation failed: ${error.message}`);
928
- process.exit(1);
929
- }
930
- console.log("\n\u{1F4E4} Uploading secrets...");
931
- try {
932
- const importResponse = await fetch(`${baseUrl}/api/projects/${projectAppId}/import-secrets`, {
933
- method: "POST",
934
- headers: {
935
- "Content-Type": "application/json",
936
- "Cookie": cookies
937
- },
938
- body: JSON.stringify({
939
- version: "1.0",
940
- secrets
941
- })
942
- });
943
- if (!importResponse.ok) {
944
- const errorData = await importResponse.json().catch(() => ({}));
945
- throw new Error(errorData.message || "Failed to upload secrets");
946
- }
947
- const result = await importResponse.json();
948
- console.log("\u2705 Secrets uploaded:");
949
- console.log(` Created: ${result.results.created}`);
950
- console.log(` Updated: ${result.results.updated}`);
951
- if (result.results.failed > 0) {
952
- console.log(` \u26A0\uFE0F Failed: ${result.results.failed}`);
953
- for (const failure of result.results.details.failed) {
954
- console.log(` - ${failure.name}: ${failure.error}`);
955
- }
956
- }
957
- } catch (error) {
958
- console.error(`\u274C Upload failed: ${error.message}`);
959
- }
960
- console.log("\n" + "=".repeat(60));
961
- console.log("\u2705 SETUP COMPLETE!");
962
- console.log("=".repeat(60));
963
- console.log("\nConfiguration:");
964
- console.log(` LEARN_TENANT_URL: ${baseUrl}/api/auth`);
965
- console.log(` LEARN_PROJECT_APPID: ${projectAppId}`);
966
- console.log(` LEARN_SDK_TOKEN: ${sdkToken}`);
967
- console.log(` LEARN_ORIGIN: ${options.origin}`);
968
- console.log("\nUsage in your static site:");
969
- console.log(' <script src="https://unpkg.com/learn-secrets-sdk/dist/index.global.js"></script>');
970
- console.log(" <script>");
971
- console.log(" // Zero-config mode - no tokens needed in client code!");
972
- console.log(" const sdk = new SecretsSDK.SecretsSDK();");
973
- console.log(` sdk.setAppId('${projectAppId}');`);
974
- console.log(" ");
975
- console.log(" // Use your secrets");
976
- console.log(' const data = await sdk.get("newsapi", "/v2/top-headlines");');
977
- console.log(" </script>");
978
- console.log("\nManage secrets:");
979
- console.log(` Visit: ${baseUrl}`);
980
- console.log(` Or use CLI: learn-secrets init --project ${projectAppId} --origins ${options.origin}`);
981
- console.log("\n" + "=".repeat(60));
982
- const configPath = ".env.learn";
983
- const configContent = `# Learn Platform Configuration
984
- # Generated on ${(/* @__PURE__ */ new Date()).toISOString()}
985
- # DO NOT COMMIT THIS FILE TO GIT
986
-
987
- LEARN_TENANT_URL=${baseUrl}/api/auth
988
- LEARN_PROJECT_APPID=${projectAppId}
989
- LEARN_SDK_TOKEN=${sdkToken}
990
- LEARN_ORIGIN=${options.origin}
991
-
992
- # Token expires: 365 days
993
- # Token prefix: ${sdkToken.slice(-4)}
994
- `;
995
- try {
996
- const fs6 = await import("fs");
997
- fs6.writeFileSync(configPath, configContent, "utf-8");
998
- console.log(`
999
- \u{1F4BE} Configuration saved to: ${configPath}`);
1000
- } catch (error) {
1001
- console.error(`
1002
- \u26A0\uFE0F Could not save configuration file: ${error.message}`);
1003
- }
1004
- console.log("\n\u{1F389} Your site is ready to use Learn Secrets!\n");
1005
- }
1006
-
1007
949
  // src/cli/commands/config.ts
1008
950
  import fs4 from "fs";
1009
951
  async function configCommand(options) {
@@ -1014,15 +956,17 @@ async function configCommand(options) {
1014
956
  process.exit(1);
1015
957
  }
1016
958
  const config = JSON.parse(fs4.readFileSync(configFile, "utf8"));
1017
- if (!options.project && !options.origins) {
959
+ if (!options.project && !options.origins && !options.env) {
1018
960
  console.log("\nCurrent configuration:");
1019
961
  console.log(` Project ID: ${config.project || "(not set)"}`);
1020
962
  console.log(` Origins: ${config.origins?.length > 0 ? config.origins.join(", ") : "(not set)"}`);
963
+ console.log(` Env file: ${config.env || ".env (default)"}`);
1021
964
  console.log(`
1022
965
  Config file: ${configFile}`);
1023
966
  console.log("\nTo update:");
1024
967
  console.log(" learn-secrets config --project <appid>");
1025
968
  console.log(" learn-secrets config --origins domain1.com,domain2.com");
969
+ console.log(" learn-secrets config --env .custom_env");
1026
970
  return;
1027
971
  }
1028
972
  let updated = false;
@@ -1036,6 +980,11 @@ Config file: ${configFile}`);
1036
980
  console.log(`Updated origins: ${config.origins.join(", ")}`);
1037
981
  updated = true;
1038
982
  }
983
+ if (options.env) {
984
+ config.env = options.env;
985
+ console.log(`Updated env file path: ${options.env}`);
986
+ updated = true;
987
+ }
1039
988
  if (updated) {
1040
989
  fs4.writeFileSync(configFile, JSON.stringify(config, null, 2));
1041
990
  console.log(`
@@ -1045,6 +994,87 @@ Configuration saved to: ${configFile}`);
1045
994
 
1046
995
  // src/cli/commands/deploy.ts
1047
996
  import fs5 from "fs";
997
+
998
+ // src/cli/commands/provision-worker.ts
999
+ import path2 from "path";
1000
+ import { fileURLToPath } from "url";
1001
+ var __filename = fileURLToPath(import.meta.url);
1002
+ var __dirname = path2.dirname(__filename);
1003
+ function getApiUrl2(baseUrl) {
1004
+ return baseUrl || "https://cloudprototype.org";
1005
+ }
1006
+ async function provisionWorker(workerName, origins, envVars, baseUrl) {
1007
+ const publicVars = [];
1008
+ const secretVars = [];
1009
+ for (const { key, value } of envVars) {
1010
+ if (isPublicConfig(key)) {
1011
+ publicVars.push({ key, value });
1012
+ } else {
1013
+ secretVars.push({ key, value });
1014
+ }
1015
+ }
1016
+ try {
1017
+ const apiUrl = getApiUrl2(baseUrl);
1018
+ const deployResponse = await fetch(`${apiUrl}/api/cloudflare/provision-worker`, {
1019
+ method: "POST",
1020
+ headers: {
1021
+ "Content-Type": "application/json"
1022
+ },
1023
+ credentials: "include",
1024
+ body: JSON.stringify({
1025
+ workerName,
1026
+ allowedOrigins: origins,
1027
+ publicVars: publicVars.map((v) => ({
1028
+ key: `PUBLIC_${v.key}`,
1029
+ value: v.value
1030
+ })),
1031
+ secrets: secretVars.map((v) => ({
1032
+ key: `SECRET_${v.key}`,
1033
+ value: v.value
1034
+ }))
1035
+ })
1036
+ });
1037
+ if (!deployResponse.ok) {
1038
+ const error = await deployResponse.json();
1039
+ return {
1040
+ success: false,
1041
+ error: error.message || "Failed to deploy Worker"
1042
+ };
1043
+ }
1044
+ const result = await deployResponse.json();
1045
+ return {
1046
+ success: true,
1047
+ workerUrl: result.workerUrl,
1048
+ workerName: result.workerName
1049
+ };
1050
+ } catch (error) {
1051
+ return {
1052
+ success: false,
1053
+ error: error.message || "Failed to provision Worker"
1054
+ };
1055
+ }
1056
+ }
1057
+ function isPublicConfig(key) {
1058
+ const publicPatterns = [
1059
+ /_DOMAIN$/,
1060
+ /_CLIENT_ID$/,
1061
+ /_AUDIENCE$/,
1062
+ /_CALLBACK_URL$/,
1063
+ /_ISSUER$/,
1064
+ /_TEAM_ID$/,
1065
+ /_KEY_ID$/,
1066
+ /^API_BASE_URL$/,
1067
+ /^PUBLIC_/
1068
+ ];
1069
+ for (const pattern of publicPatterns) {
1070
+ if (pattern.test(key)) {
1071
+ return true;
1072
+ }
1073
+ }
1074
+ return false;
1075
+ }
1076
+
1077
+ // src/cli/commands/deploy.ts
1048
1078
  async function deployCommand(options = {}) {
1049
1079
  if (!hasValidCredentials()) {
1050
1080
  console.error('Error: Not authenticated. Run "learn-secrets login" first.');
@@ -1071,17 +1101,16 @@ async function deployCommand(options = {}) {
1071
1101
  Deploying secrets to project: ${config.project}`);
1072
1102
  console.log(`Origins: ${config.origins.join(", ")}
1073
1103
  `);
1074
- const envFile = options.env || ".env";
1104
+ const envFile = options.env || config.env || ".env";
1075
1105
  let secrets;
1106
+ let envVars = [];
1076
1107
  try {
1077
1108
  console.log(`Reading ${envFile}...`);
1078
- const envVars = parseEnvFile(envFile);
1079
- secrets = detectApiKeys(envVars);
1109
+ envVars = parseEnvFile(envFile);
1110
+ secrets = convertAllEnvVarsToSecrets(envVars);
1080
1111
  if (secrets.length === 0) {
1081
- console.log("\nNo API keys detected in .env file.");
1082
- console.log("Make sure your .env contains variables like:");
1083
- console.log(" OPENAI_API_KEY=sk-...");
1084
- console.log(" ANTHROPIC_API_KEY=sk-ant-...");
1112
+ console.log("\nNo environment variables found in .env file.");
1113
+ console.log("Make sure your .env file is not empty.");
1085
1114
  process.exit(0);
1086
1115
  }
1087
1116
  console.log("\n" + summarizeDetectedSecrets(secrets));
@@ -1127,7 +1156,93 @@ Deploying secrets to project: ${config.project}`);
1127
1156
  }
1128
1157
  const dashboardUrl = options.baseUrl || "https://cloudprototype.org";
1129
1158
  console.log(`
1130
- Secrets deployed! View them at: ${dashboardUrl}/dashboard/secrets/api_keys`);
1159
+ Secrets deployed! View them at: ${dashboardUrl}/dashboard/collections/secrets`);
1160
+ console.log("\n--- Cloudflare Worker Deployment ---");
1161
+ let cloudflareConnected = false;
1162
+ try {
1163
+ const statusResponse = await fetch(`${dashboardUrl}/api/cloudflare/status`, {
1164
+ credentials: "include"
1165
+ });
1166
+ cloudflareConnected = statusResponse.ok;
1167
+ } catch {
1168
+ cloudflareConnected = false;
1169
+ }
1170
+ if (!cloudflareConnected) {
1171
+ console.log("Cloudflare account not connected.");
1172
+ console.log("\nTo deploy Worker, we need your Cloudflare API token.");
1173
+ console.log("Required permissions:");
1174
+ console.log(" - Workers Scripts: Edit");
1175
+ console.log(" - Workers Routes: Edit");
1176
+ console.log(" - Account Settings: Read");
1177
+ console.log("\nCreate token at: https://dash.cloudflare.com/profile/api-tokens");
1178
+ const readline = await import("readline");
1179
+ const rl = readline.createInterface({
1180
+ input: process.stdin,
1181
+ output: process.stdout
1182
+ });
1183
+ const apiToken = await new Promise((resolve2) => {
1184
+ rl.question("\nCloudflare API Token (or press Enter to skip): ", (answer) => {
1185
+ rl.close();
1186
+ resolve2(answer.trim());
1187
+ });
1188
+ });
1189
+ if (apiToken) {
1190
+ console.log("\nValidating token...");
1191
+ const result2 = await connectCloudflare(apiToken, options.baseUrl);
1192
+ if (result2.success) {
1193
+ console.log("Cloudflare account connected");
1194
+ console.log(` Account: ${result2.accountName}`);
1195
+ cloudflareConnected = true;
1196
+ } else {
1197
+ console.log(`
1198
+ Error: Failed to connect Cloudflare: ${result2.error}`);
1199
+ console.log("Worker deployment skipped.");
1200
+ }
1201
+ } else {
1202
+ console.log("\nSkipped Cloudflare connection. Worker deployment skipped.");
1203
+ }
1204
+ } else {
1205
+ console.log("Cloudflare account is connected.");
1206
+ }
1207
+ if (cloudflareConnected) {
1208
+ const readline = await import("readline");
1209
+ const rl = readline.createInterface({
1210
+ input: process.stdin,
1211
+ output: process.stdout
1212
+ });
1213
+ const shouldProvision = await new Promise((resolve2) => {
1214
+ rl.question("\nDeploy/update Worker to Cloudflare? (y/N): ", (answer) => {
1215
+ rl.close();
1216
+ resolve2(answer.trim());
1217
+ });
1218
+ });
1219
+ if (shouldProvision.toLowerCase() === "y" || shouldProvision.toLowerCase() === "yes") {
1220
+ console.log("\nDeploying Worker to Cloudflare...");
1221
+ const workerName = `learn-secrets-${config.project.toLowerCase()}`;
1222
+ const provisionResult = await provisionWorker(
1223
+ workerName,
1224
+ config.origins,
1225
+ envVars,
1226
+ options.baseUrl
1227
+ );
1228
+ if (provisionResult.success) {
1229
+ console.log("\nWorker deployed successfully");
1230
+ console.log(` Name: ${provisionResult.workerName}`);
1231
+ console.log(` URL: ${provisionResult.workerUrl}`);
1232
+ config.workerUrl = provisionResult.workerUrl;
1233
+ fs5.writeFileSync(configFile, JSON.stringify(config, null, 2));
1234
+ console.log("\nNext steps:");
1235
+ console.log(` 1. Test Worker: curl ${provisionResult.workerUrl}/health`);
1236
+ console.log(` 2. Update your frontend to use Worker URL`);
1237
+ console.log(` 3. Test origin validation from your site`);
1238
+ } else {
1239
+ console.log(`
1240
+ Error: Worker deployment failed: ${provisionResult.error}`);
1241
+ }
1242
+ } else {
1243
+ console.log("\nSkipped Worker deployment.");
1244
+ }
1245
+ }
1131
1246
  } catch (error) {
1132
1247
  console.error("\nDeploy failed:", error.message);
1133
1248
  if (error.status === 401) {
@@ -1142,17 +1257,14 @@ Secrets deployed! View them at: ${dashboardUrl}/dashboard/secrets/api_keys`);
1142
1257
 
1143
1258
  // src/cli/index.ts
1144
1259
  var program = new Command();
1145
- program.name("learn-secrets").description("CLI tool for managing API secrets in static sites").version("1.6.9");
1146
- program.command("setup").description("Complete setup: create project, generate token, upload secrets").requiredOption("-e, --email <email>", "Your Learn account email").requiredOption("-p, --password <password>", "Your Learn account password").requiredOption("-o, --origin <domain>", "Your site origin (e.g., mysite.com)").option("--project-name <name>", 'Project name (default: "Project - <origin>")').option("--env <file>", "Path to .env file (default: .env)", ".env").option("--base-url <url>", "Custom base URL for API").option("-y, --yes", "Skip confirmation prompt").action(async (options) => {
1147
- await setupCommand(options);
1148
- });
1260
+ program.name("learn-secrets").description("CLI tool for managing API secrets with Cloudflare Worker deployment").version("1.9.0");
1149
1261
  program.command("login").description("Authenticate with Learn Secrets via browser").option("--base-url <url>", "Custom base URL for API").action(async (options) => {
1150
1262
  await loginCommand(options);
1151
1263
  });
1152
1264
  program.command("init").description("Initialize secrets for a new site").requiredOption("-o, --origins <domains>", "Comma-separated list of allowed origins (e.g., mysite.com,localhost)").option("-p, --project <appid>", "Project app ID (auto-detected from login if not specified)").option("-e, --env <file>", "Path to .env file (default: .env)", ".env").option("--base-url <url>", "Custom base URL for API").option("-y, --yes", "Skip confirmation prompt").action(async (options) => {
1153
1265
  await initCommand(options);
1154
1266
  });
1155
- program.command("config").description("View or update project configuration").option("-p, --project <appid>", "Change project ID").option("--origins <origins>", "Comma separated origins").action(async (options) => {
1267
+ program.command("config").description("View or update project configuration").option("-p, --project <appid>", "Change project ID").option("--origins <origins>", "Comma separated origins").option("-e, --env <file>", "Path to .env file").action(async (options) => {
1156
1268
  await configCommand(options);
1157
1269
  });
1158
1270
  program.command("deploy").description("Deploy secrets from .env to dashboard").option("-e, --env <file>", "Path to .env file (default: .env)", ".env").option("--base-url <url>", "Custom base URL for API").option("-y, --yes", "Skip confirmation prompt").action(async (options) => {
package/dist/index.d.mts CHANGED
@@ -18,14 +18,14 @@ interface SecretsSDKOptions {
18
18
  timeout?: number;
19
19
  retryOn429?: boolean;
20
20
  }
21
- interface ProxyRequest {
21
+ interface ProxyRequest$1 {
22
22
  keyName: string;
23
23
  endpoint: string;
24
24
  method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
25
25
  body?: any;
26
26
  headers?: Record<string, string>;
27
27
  }
28
- interface ProxyResponse<T = any> {
28
+ interface ProxyResponse$1<T = any> {
29
29
  success: boolean;
30
30
  status: number;
31
31
  data: T;
@@ -157,7 +157,7 @@ declare class SecretsSDK {
157
157
  * @param options - Request options (method, body, headers)
158
158
  * @returns Promise with the API response
159
159
  */
160
- call<T = any>(keyName: string, endpoint: string, options?: Omit<ProxyRequest, 'keyName' | 'endpoint'>): Promise<T>;
160
+ call<T = any>(keyName: string, endpoint: string, options?: Omit<ProxyRequest$1, 'keyName' | 'endpoint'>): Promise<T>;
161
161
  /**
162
162
  * Make a GET request
163
163
  */
@@ -367,6 +367,116 @@ declare class SecretsManagement {
367
367
  }>;
368
368
  }
369
369
 
370
+ /**
371
+ * Worker Client - Frontend SDK for calling Learn Secrets Worker
372
+ *
373
+ * Use this client to call your deployed Cloudflare Worker from static sites
374
+ */
375
+ interface WorkerClientOptions {
376
+ workerUrl: string;
377
+ }
378
+ interface ProxyRequest {
379
+ secret: string;
380
+ url: string;
381
+ method?: string;
382
+ headers?: Record<string, string>;
383
+ body?: any;
384
+ authHeader?: string;
385
+ authPrefix?: string;
386
+ }
387
+ interface ProxyResponse<T = any> {
388
+ success: boolean;
389
+ status: number;
390
+ data: T;
391
+ }
392
+ interface PublicConfig {
393
+ [key: string]: string;
394
+ }
395
+ interface AppleJwtRequest {
396
+ teamId?: string;
397
+ keyId?: string;
398
+ privateKey: string;
399
+ }
400
+ interface OAuthExchangeRequest {
401
+ provider: string;
402
+ code: string;
403
+ redirectUri: string;
404
+ }
405
+ /**
406
+ * Worker Client for calling Learn Secrets Worker
407
+ *
408
+ * SECURITY:
409
+ * - All requests include Origin header (automatically added by browser)
410
+ * - Worker validates origin before processing
411
+ * - Secrets never exposed to frontend
412
+ * - Only public config returned to frontend
413
+ *
414
+ * Example:
415
+ * ```typescript
416
+ * const client = new WorkerClient({
417
+ * workerUrl: 'https://learn-secrets-myapp.myaccount.workers.dev'
418
+ * });
419
+ *
420
+ * // Get public config
421
+ * const config = await client.getConfig();
422
+ *
423
+ * // Make API call with secret
424
+ * const response = await client.proxy({
425
+ * secret: 'openai-api-key',
426
+ * url: 'https://api.openai.com/v1/chat/completions',
427
+ * method: 'POST',
428
+ * body: { model: 'gpt-4', messages: [...] }
429
+ * });
430
+ * ```
431
+ */
432
+ declare class WorkerClient {
433
+ private workerUrl;
434
+ constructor(options: WorkerClientOptions);
435
+ /**
436
+ * Get public configuration
437
+ *
438
+ * Returns only PUBLIC_* environment variables from Worker
439
+ * Safe for frontend use
440
+ */
441
+ getConfig(): Promise<PublicConfig>;
442
+ /**
443
+ * Proxy API request with secret injection
444
+ *
445
+ * Makes API call through Worker which injects secret server-side
446
+ * Secret never reaches browser
447
+ *
448
+ * @param request - Proxy request configuration
449
+ * @returns API response data
450
+ */
451
+ proxy<T = any>(request: ProxyRequest): Promise<ProxyResponse<T>>;
452
+ /**
453
+ * Generate Apple Music JWT
454
+ *
455
+ * Private key stays in Worker, only JWT returned to frontend
456
+ *
457
+ * @param request - Apple JWT request with secret name
458
+ * @returns JWT token
459
+ */
460
+ generateAppleJwt(request: AppleJwtRequest): Promise<string>;
461
+ /**
462
+ * Exchange OAuth authorization code for access token
463
+ *
464
+ * Client secret stays in Worker, tokens returned to frontend
465
+ *
466
+ * @param request - OAuth exchange request
467
+ * @returns OAuth token response
468
+ */
469
+ exchangeOAuthCode(request: OAuthExchangeRequest): Promise<any>;
470
+ /**
471
+ * Health check
472
+ */
473
+ health(): Promise<{
474
+ status: string;
475
+ version: string;
476
+ timestamp: string;
477
+ }>;
478
+ }
479
+
370
480
  /**
371
481
  * Resolve environment variable references in a string
372
482
  * Supports ${VAR} and $VAR syntax
@@ -429,4 +539,4 @@ declare function deleteSDKCredentials(sdkName: SDKName): void;
429
539
  */
430
540
  declare function hasSDKCredentials(sdkName: SDKName): boolean;
431
541
 
432
- export { type CLICredentials, InvalidTokenError, type MachineInfo, type MaskedSecret, OriginMismatchError, type ProxyRequest, type ProxyResponse, RateLimitError, type RateLimitInfo, type SDKCredentialsStore, type SDKName, type SecretConfig, SecretsManagement, type SecretsManagementOptions, SecretsSDK, SecretsSDKError, type SecretsSDKOptions, type SyncOptions, type SyncResult, deleteSDKCredentials, getMachineId, hasSDKCredentials, loadSecretsConfig, readAllCredentials, readSDKCredentials, resolveEnvInSecret, resolveEnvInSecrets, resolveEnvString, writeAllCredentials, writeSDKCredentials };
542
+ export { type CLICredentials, InvalidTokenError, type MachineInfo, type MaskedSecret, OriginMismatchError, type ProxyRequest$1 as ProxyRequest, type ProxyResponse$1 as ProxyResponse, RateLimitError, type RateLimitInfo, type SDKCredentialsStore, type SDKName, type SecretConfig, SecretsManagement, type SecretsManagementOptions, SecretsSDK, SecretsSDKError, type SecretsSDKOptions, type SyncOptions, type SyncResult, WorkerClient, deleteSDKCredentials, getMachineId, hasSDKCredentials, loadSecretsConfig, readAllCredentials, readSDKCredentials, resolveEnvInSecret, resolveEnvInSecrets, resolveEnvString, writeAllCredentials, writeSDKCredentials };
package/dist/index.d.ts CHANGED
@@ -18,14 +18,14 @@ interface SecretsSDKOptions {
18
18
  timeout?: number;
19
19
  retryOn429?: boolean;
20
20
  }
21
- interface ProxyRequest {
21
+ interface ProxyRequest$1 {
22
22
  keyName: string;
23
23
  endpoint: string;
24
24
  method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
25
25
  body?: any;
26
26
  headers?: Record<string, string>;
27
27
  }
28
- interface ProxyResponse<T = any> {
28
+ interface ProxyResponse$1<T = any> {
29
29
  success: boolean;
30
30
  status: number;
31
31
  data: T;
@@ -157,7 +157,7 @@ declare class SecretsSDK {
157
157
  * @param options - Request options (method, body, headers)
158
158
  * @returns Promise with the API response
159
159
  */
160
- call<T = any>(keyName: string, endpoint: string, options?: Omit<ProxyRequest, 'keyName' | 'endpoint'>): Promise<T>;
160
+ call<T = any>(keyName: string, endpoint: string, options?: Omit<ProxyRequest$1, 'keyName' | 'endpoint'>): Promise<T>;
161
161
  /**
162
162
  * Make a GET request
163
163
  */
@@ -367,6 +367,116 @@ declare class SecretsManagement {
367
367
  }>;
368
368
  }
369
369
 
370
+ /**
371
+ * Worker Client - Frontend SDK for calling Learn Secrets Worker
372
+ *
373
+ * Use this client to call your deployed Cloudflare Worker from static sites
374
+ */
375
+ interface WorkerClientOptions {
376
+ workerUrl: string;
377
+ }
378
+ interface ProxyRequest {
379
+ secret: string;
380
+ url: string;
381
+ method?: string;
382
+ headers?: Record<string, string>;
383
+ body?: any;
384
+ authHeader?: string;
385
+ authPrefix?: string;
386
+ }
387
+ interface ProxyResponse<T = any> {
388
+ success: boolean;
389
+ status: number;
390
+ data: T;
391
+ }
392
+ interface PublicConfig {
393
+ [key: string]: string;
394
+ }
395
+ interface AppleJwtRequest {
396
+ teamId?: string;
397
+ keyId?: string;
398
+ privateKey: string;
399
+ }
400
+ interface OAuthExchangeRequest {
401
+ provider: string;
402
+ code: string;
403
+ redirectUri: string;
404
+ }
405
+ /**
406
+ * Worker Client for calling Learn Secrets Worker
407
+ *
408
+ * SECURITY:
409
+ * - All requests include Origin header (automatically added by browser)
410
+ * - Worker validates origin before processing
411
+ * - Secrets never exposed to frontend
412
+ * - Only public config returned to frontend
413
+ *
414
+ * Example:
415
+ * ```typescript
416
+ * const client = new WorkerClient({
417
+ * workerUrl: 'https://learn-secrets-myapp.myaccount.workers.dev'
418
+ * });
419
+ *
420
+ * // Get public config
421
+ * const config = await client.getConfig();
422
+ *
423
+ * // Make API call with secret
424
+ * const response = await client.proxy({
425
+ * secret: 'openai-api-key',
426
+ * url: 'https://api.openai.com/v1/chat/completions',
427
+ * method: 'POST',
428
+ * body: { model: 'gpt-4', messages: [...] }
429
+ * });
430
+ * ```
431
+ */
432
+ declare class WorkerClient {
433
+ private workerUrl;
434
+ constructor(options: WorkerClientOptions);
435
+ /**
436
+ * Get public configuration
437
+ *
438
+ * Returns only PUBLIC_* environment variables from Worker
439
+ * Safe for frontend use
440
+ */
441
+ getConfig(): Promise<PublicConfig>;
442
+ /**
443
+ * Proxy API request with secret injection
444
+ *
445
+ * Makes API call through Worker which injects secret server-side
446
+ * Secret never reaches browser
447
+ *
448
+ * @param request - Proxy request configuration
449
+ * @returns API response data
450
+ */
451
+ proxy<T = any>(request: ProxyRequest): Promise<ProxyResponse<T>>;
452
+ /**
453
+ * Generate Apple Music JWT
454
+ *
455
+ * Private key stays in Worker, only JWT returned to frontend
456
+ *
457
+ * @param request - Apple JWT request with secret name
458
+ * @returns JWT token
459
+ */
460
+ generateAppleJwt(request: AppleJwtRequest): Promise<string>;
461
+ /**
462
+ * Exchange OAuth authorization code for access token
463
+ *
464
+ * Client secret stays in Worker, tokens returned to frontend
465
+ *
466
+ * @param request - OAuth exchange request
467
+ * @returns OAuth token response
468
+ */
469
+ exchangeOAuthCode(request: OAuthExchangeRequest): Promise<any>;
470
+ /**
471
+ * Health check
472
+ */
473
+ health(): Promise<{
474
+ status: string;
475
+ version: string;
476
+ timestamp: string;
477
+ }>;
478
+ }
479
+
370
480
  /**
371
481
  * Resolve environment variable references in a string
372
482
  * Supports ${VAR} and $VAR syntax
@@ -429,4 +539,4 @@ declare function deleteSDKCredentials(sdkName: SDKName): void;
429
539
  */
430
540
  declare function hasSDKCredentials(sdkName: SDKName): boolean;
431
541
 
432
- export { type CLICredentials, InvalidTokenError, type MachineInfo, type MaskedSecret, OriginMismatchError, type ProxyRequest, type ProxyResponse, RateLimitError, type RateLimitInfo, type SDKCredentialsStore, type SDKName, type SecretConfig, SecretsManagement, type SecretsManagementOptions, SecretsSDK, SecretsSDKError, type SecretsSDKOptions, type SyncOptions, type SyncResult, deleteSDKCredentials, getMachineId, hasSDKCredentials, loadSecretsConfig, readAllCredentials, readSDKCredentials, resolveEnvInSecret, resolveEnvInSecrets, resolveEnvString, writeAllCredentials, writeSDKCredentials };
542
+ export { type CLICredentials, InvalidTokenError, type MachineInfo, type MaskedSecret, OriginMismatchError, type ProxyRequest$1 as ProxyRequest, type ProxyResponse$1 as ProxyResponse, RateLimitError, type RateLimitInfo, type SDKCredentialsStore, type SDKName, type SecretConfig, SecretsManagement, type SecretsManagementOptions, SecretsSDK, SecretsSDKError, type SecretsSDKOptions, type SyncOptions, type SyncResult, WorkerClient, deleteSDKCredentials, getMachineId, hasSDKCredentials, loadSecretsConfig, readAllCredentials, readSDKCredentials, resolveEnvInSecret, resolveEnvInSecrets, resolveEnvString, writeAllCredentials, writeSDKCredentials };
@@ -1,10 +1,10 @@
1
- "use strict";var SecretsSDK=(()=>{var X=Object.create;var _=Object.defineProperty;var Z=Object.getOwnPropertyDescriptor;var Q=Object.getOwnPropertyNames;var Y=Object.getPrototypeOf,ee=Object.prototype.hasOwnProperty;var d=(t=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(t,{get:(e,r)=>(typeof require<"u"?require:e)[r]}):t)(function(t){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+t+'" is not supported')});var te=(t,e)=>()=>(t&&(e=t(t=0)),e);var z=(t,e)=>{for(var r in e)_(t,r,{get:e[r],enumerable:!0})},F=(t,e,r,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of Q(e))!ee.call(t,n)&&n!==r&&_(t,n,{get:()=>e[n],enumerable:!(s=Z(e,n))||s.enumerable});return t};var y=(t,e,r)=>(r=t!=null?X(Y(t)):{},F(e||!t||!t.__esModule?_(r,"default",{value:t,enumerable:!0}):r,t)),re=t=>F(_({},"__esModule",{value:!0}),t);var V={};z(V,{loadSecretsConfig:()=>q,resolveEnvInSecret:()=>E,resolveEnvInSecrets:()=>L,resolveEnvString:()=>p});function p(t){return t.replace(/\$\{([A-Z0-9_]+)\}|\$([A-Z0-9_]+)/g,(e,r,s)=>{let n=r||s,i=process.env[n];if(i===void 0)throw new Error(`Environment variable ${n} is not defined`);return i})}function E(t){let e={name:p(t.name),provider:p(t.provider),api_key:p(t.api_key)};return t.base_url&&(e.base_url=p(t.base_url)),t.auth_header&&(e.auth_header=p(t.auth_header)),t.auth_prefix&&(e.auth_prefix=p(t.auth_prefix)),e}function L(t){return t.map(E)}function q(t){let e=d("fs"),s=d("path").resolve(t);if(!e.existsSync(s))throw new Error(`Config file not found: ${s}`);let n=e.readFileSync(s,"utf-8"),i=JSON.parse(n);if(!i.version)throw new Error('Config file missing "version" field');if(!i.project)throw new Error('Config file missing "project" field');if(!Array.isArray(i.secrets))throw new Error('Config file missing "secrets" array');let a=L(i.secrets);return{version:i.version,project:i.project,secrets:a}}var N=te(()=>{"use strict"});var ae={};z(ae,{InvalidTokenError:()=>b,OriginMismatchError:()=>S,RateLimitError:()=>x,SecretsManagement:()=>$,SecretsSDK:()=>T,SecretsSDKError:()=>o,deleteSDKCredentials:()=>J,getMachineId:()=>O,hasSDKCredentials:()=>H,loadSecretsConfig:()=>q,readAllCredentials:()=>I,readSDKCredentials:()=>v,resolveEnvInSecret:()=>E,resolveEnvInSecrets:()=>L,resolveEnvString:()=>p,writeAllCredentials:()=>R,writeSDKCredentials:()=>P});var o=class extends Error{constructor(r,s,n){super(r);this.status=s;this.response=n;this.name="SecretsSDKError"}},S=class extends o{constructor(e="Origin not allowed for this token"){super(e,403),this.name="OriginMismatchError"}},x=class extends o{constructor(e="Rate limit exceeded",r=60,s=0,n=100){super(e,429),this.name="RateLimitError",this.retryAfter=r,this.remaining=s,this.limit=n}},b=class extends o{constructor(e="Invalid or expired token"){super(e,401),this.name="InvalidTokenError"}};var T=class{constructor(e={}){this.rateLimitInfo=null;this.appId=e.appId||null,this.token=e.token||e.sessionToken||null,this.baseUrl=e.baseUrl||"https://cloudprototype.org",this.timeout=e.timeout||3e4,this.retryOn429=e.retryOn429!==void 0?e.retryOn429:!0,this.zeroConfigMode=!this.appId&&!this.token}getUsage(){return this.rateLimitInfo}parseRateLimitHeaders(e){let r=e.get("X-RateLimit-Limit"),s=e.get("X-RateLimit-Remaining"),n=e.get("X-RateLimit-Reset");r&&s&&n&&(this.rateLimitInfo={limit:parseInt(r),remaining:parseInt(s),reset:parseInt(n)})}sleep(e){return new Promise(r=>setTimeout(r,e))}async fetchWithTimeout(e,r,s){let n=new AbortController,i=setTimeout(()=>n.abort(),s);try{return await fetch(e,{...r,signal:n.signal})}finally{clearTimeout(i)}}async call(e,r,s={}){let n=0,i=this.retryOn429?3:0;for(;;)try{let a=this.zeroConfigMode?`${this.baseUrl}/api/sdk/proxy`:`${this.baseUrl}/api/sdk/${this.appId}/proxy`,m={"Content-Type":"application/json"};!this.zeroConfigMode&&this.token&&(m.Authorization=`Bearer ${this.token}`);let c=await this.fetchWithTimeout(a,{method:"POST",headers:m,body:JSON.stringify({keyName:e,endpoint:r,method:s.method||"GET",body:s.body,headers:s.headers})},this.timeout);this.parseRateLimitHeaders(c.headers);let w=await c.json();if(!c.ok){let h=w.data?.message||w?.message||`Request failed with status ${c.status}`;if(c.status===403&&h.includes("Origin"))throw new S(h);if(c.status===401)throw new b(h);if(c.status===429){let k=this.rateLimitInfo?this.rateLimitInfo.reset-Math.floor(Date.now()/1e3):60,u=new x(h,k,this.rateLimitInfo?.remaining||0,this.rateLimitInfo?.limit||100);if(this.retryOn429&&n<i){n++;let f=Math.min(1e3*Math.pow(2,n-1),8e3);await this.sleep(f);continue}throw u}throw new o(h,c.status,w)}return w.data}catch(a){throw a instanceof o?a:a instanceof Error&&a.name==="AbortError"?new o("Request timeout",408):new o(a instanceof Error?a.message:"Unknown error occurred",500)}}async get(e,r,s){return this.call(e,r,{method:"GET",headers:s})}async post(e,r,s,n){return this.call(e,r,{method:"POST",body:s,headers:n})}async put(e,r,s,n){return this.call(e,r,{method:"PUT",body:s,headers:n})}async delete(e,r,s){return this.call(e,r,{method:"DELETE",headers:s})}async patch(e,r,s,n){return this.call(e,r,{method:"PATCH",body:s,headers:n})}setAppId(e){this.appId=e}async secretsRequest(e,r){if(!this.appId)throw new o("App ID required for secrets management. Call setAppId() first.",400);let s=await this.fetchWithTimeout(`${this.baseUrl}/api/projects/${this.appId}/secrets/manage?action=${e}`,{method:"POST",headers:{"Content-Type":"application/json"},body:r?JSON.stringify(r):void 0},this.timeout);if(!s.ok){let n=await s.json().catch(()=>({}));throw new o(n.message||`Secrets management failed: ${s.statusText}`,s.status,n)}return s.json()}async listSecrets(){return this.secretsRequest("list")}async createSecret(e){return this.secretsRequest("create",e)}async updateSecret(e){return this.secretsRequest("update",e)}async deleteSecret(e){return this.secretsRequest("delete",{name:e})}async syncEnv(e,r){return this.secretsRequest("sync",{secrets:e,provider:r})}};var l=y(d("fs")),D=y(d("path")),A=y(d("os"));function M(){let t=A.homedir(),e=D.join(t,".learn");return D.join(e,"credentials.json")}function se(){let t=A.homedir(),e=D.join(t,".learn");l.existsSync(e)||l.mkdirSync(e,{recursive:!0,mode:448})}function I(){try{let t=M();if(!l.existsSync(t))return null;let e=l.readFileSync(t,"utf-8"),r=JSON.parse(e);return r.access_token&&!r["learn-secrets-sdk"]&&!r["learn-auth-sdk"]?{"learn-secrets-sdk":r}:r}catch{return null}}function R(t){se();let e=M(),r=JSON.stringify(t,null,2);l.writeFileSync(e,r,{mode:384})}function v(t){let e=I();return e&&e[t]||null}function P(t,e){let r=I()||{};r[t]=e,R(r)}function J(t){let e=I();e&&(delete e[t],Object.keys(e).length===0?K():R(e))}function j(){return v("learn-secrets-sdk")}function K(){try{let t=M();l.existsSync(t)&&l.unlinkSync(t)}catch{}}function H(t){let e=v(t);return e!==null&&!!e.access_token}function W(){let t=j();return t?t.expires_at?new Date(t.expires_at)>new Date:!0:!1}var B=d("child_process"),G=d("util"),U=d("crypto"),g=y(d("os")),C=(0,G.promisify)(B.exec);async function O(){let t=process.platform;try{let e;if(t==="darwin")e=await ne();else if(t==="linux")e=await ie();else if(t==="win32")e=await oe();else throw new Error(`Unsupported platform: ${t}`);let r=(0,U.createHash)("sha256").update(e).digest("hex").substring(0,32),s=JSON.stringify({machineId:r,cpus:g.cpus().length,arch:g.arch(),platform:g.platform(),homedir:g.homedir()}),n=(0,U.createHash)("sha256").update(s).digest("hex");return{machineId:r,fingerprint:n}}catch(e){throw new Error(`Failed to get machine ID: ${e.message}`)}}async function ne(){try{let{stdout:t}=await C(`ioreg -l | grep IOPlatformSerialNumber | awk '{print $4}' | sed 's/"//g'`),{stdout:e}=await C("hostname"),r=t.trim(),s=e.trim();if(r&&s)return`${r}-${s}`;let{stdout:n}=await C(`ioreg -rd1 -c IOPlatformExpertDevice | grep IOPlatformUUID | awk '{print $3}' | sed 's/"//g'`);return n.trim()}catch{throw new Error("Failed to get macOS machine ID")}}async function ie(){try{let{stdout:t}=await C("cat /etc/machine-id 2>/dev/null || cat /var/lib/dbus/machine-id 2>/dev/null"),e=t.trim();if(e)return e;throw new Error("Machine ID file not found")}catch{throw new Error("Failed to get Linux machine ID")}}async function oe(){try{let{stdout:t}=await C("wmic csproduct get uuid"),r=t.split(`
2
- `).map(s=>s.trim()).find(s=>s&&s!=="UUID");if(r)return r;throw new Error("Failed to parse machine UUID from WMIC")}catch{throw new Error("Failed to get Windows machine ID")}}var $=class{constructor(e){this.appId=e.appId,this.baseUrl=e.baseUrl||"https://cloudprototype.org",this.timeout=e.timeout||3e4}async openBrowser(e){let{exec:r}=await import("child_process"),{promisify:s}=await import("util"),n=s(r),i=process.platform;try{i==="darwin"?await n(`open "${e}"`):i==="win32"?await n(`start "${e}"`):await n(`xdg-open "${e}"`)}catch(a){throw console.error("Failed to open browser:",a),new o("Failed to open browser",500)}}sleep(e){return new Promise(r=>setTimeout(r,e))}async login(){let e="learn-secrets-sdk";try{console.log(`Authenticating with Learn (${e})...
3
- `);let r=await O(),s=v(e);if(s&&s.access_token){let u=await fetch(`${this.baseUrl}/api/auth/cli/check-authorization`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({machine_id:r.machineId,sdk_name:e})});if(u.ok){let f=await u.json();if(f.authorized)return console.log("This machine is already authorized."),console.log(`Logged in as: ${f.user_email||f.user_id}`),{success:!0,user:f.user_email||f.user_id}}}let n=await fetch(`${this.baseUrl}/api/auth/cli/device`,{method:"POST",headers:{"Content-Type":"application/json"}});if(!n.ok)throw new o("Failed to generate device code",n.status);let i=await n.json();console.log(`
1
+ "use strict";var SecretsSDK=(()=>{var Z=Object.create;var T=Object.defineProperty;var Q=Object.getOwnPropertyDescriptor;var Y=Object.getOwnPropertyNames;var ee=Object.getPrototypeOf,te=Object.prototype.hasOwnProperty;var l=(r=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(r,{get:(e,t)=>(typeof require<"u"?require:e)[t]}):r)(function(r){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+r+'" is not supported')});var re=(r,e)=>()=>(r&&(e=r(r=0)),e);var z=(r,e)=>{for(var t in e)T(r,t,{get:e[t],enumerable:!0})},F=(r,e,t,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of Y(e))!te.call(r,n)&&n!==t&&T(r,n,{get:()=>e[n],enumerable:!(s=Q(e,n))||s.enumerable});return r};var y=(r,e,t)=>(t=r!=null?Z(ee(r)):{},F(e||!r||!r.__esModule?T(t,"default",{value:r,enumerable:!0}):t,r)),se=r=>F(T({},"__esModule",{value:!0}),r);var X={};z(X,{loadSecretsConfig:()=>N,resolveEnvInSecret:()=>E,resolveEnvInSecrets:()=>$,resolveEnvString:()=>p});function p(r){return r.replace(/\$\{([A-Z0-9_]+)\}|\$([A-Z0-9_]+)/g,(e,t,s)=>{let n=t||s,i=process.env[n];if(i===void 0)throw new Error(`Environment variable ${n} is not defined`);return i})}function E(r){let e={name:p(r.name),provider:p(r.provider),api_key:p(r.api_key)};return r.base_url&&(e.base_url=p(r.base_url)),r.auth_header&&(e.auth_header=p(r.auth_header)),r.auth_prefix&&(e.auth_prefix=p(r.auth_prefix)),e}function $(r){return r.map(E)}function N(r){let e=l("fs"),s=l("path").resolve(r);if(!e.existsSync(s))throw new Error(`Config file not found: ${s}`);let n=e.readFileSync(s,"utf-8"),i=JSON.parse(n);if(!i.version)throw new Error('Config file missing "version" field');if(!i.project)throw new Error('Config file missing "project" field');if(!Array.isArray(i.secrets))throw new Error('Config file missing "secrets" array');let a=$(i.secrets);return{version:i.version,project:i.project,secrets:a}}var J=re(()=>{"use strict"});var ce={};z(ce,{InvalidTokenError:()=>b,OriginMismatchError:()=>S,RateLimitError:()=>x,SecretsManagement:()=>j,SecretsSDK:()=>_,SecretsSDKError:()=>o,WorkerClient:()=>A,deleteSDKCredentials:()=>H,getMachineId:()=>O,hasSDKCredentials:()=>W,loadSecretsConfig:()=>N,readAllCredentials:()=>v,readSDKCredentials:()=>k,resolveEnvInSecret:()=>E,resolveEnvInSecrets:()=>$,resolveEnvString:()=>p,writeAllCredentials:()=>R,writeSDKCredentials:()=>D});var o=class extends Error{constructor(t,s,n){super(t);this.status=s;this.response=n;this.name="SecretsSDKError"}},S=class extends o{constructor(e="Origin not allowed for this token"){super(e,403),this.name="OriginMismatchError"}},x=class extends o{constructor(e="Rate limit exceeded",t=60,s=0,n=100){super(e,429),this.name="RateLimitError",this.retryAfter=t,this.remaining=s,this.limit=n}},b=class extends o{constructor(e="Invalid or expired token"){super(e,401),this.name="InvalidTokenError"}};var _=class{constructor(e={}){this.rateLimitInfo=null;this.appId=e.appId||null,this.token=e.token||e.sessionToken||null,this.baseUrl=e.baseUrl||"https://cloudprototype.org",this.timeout=e.timeout||3e4,this.retryOn429=e.retryOn429!==void 0?e.retryOn429:!0,this.zeroConfigMode=!this.appId&&!this.token}getUsage(){return this.rateLimitInfo}parseRateLimitHeaders(e){let t=e.get("X-RateLimit-Limit"),s=e.get("X-RateLimit-Remaining"),n=e.get("X-RateLimit-Reset");t&&s&&n&&(this.rateLimitInfo={limit:parseInt(t),remaining:parseInt(s),reset:parseInt(n)})}sleep(e){return new Promise(t=>setTimeout(t,e))}async fetchWithTimeout(e,t,s){let n=new AbortController,i=setTimeout(()=>n.abort(),s);try{return await fetch(e,{...t,signal:n.signal})}finally{clearTimeout(i)}}async call(e,t,s={}){let n=0,i=this.retryOn429?3:0;for(;;)try{let a=this.zeroConfigMode?`${this.baseUrl}/api/sdk/proxy`:`${this.baseUrl}/api/sdk/${this.appId}/proxy`,m={"Content-Type":"application/json"};!this.zeroConfigMode&&this.token&&(m.Authorization=`Bearer ${this.token}`);let c=await this.fetchWithTimeout(a,{method:"POST",headers:m,body:JSON.stringify({keyName:e,endpoint:t,method:s.method||"GET",body:s.body,headers:s.headers})},this.timeout);this.parseRateLimitHeaders(c.headers);let w=await c.json();if(!c.ok){let h=w.data?.message||w?.message||`Request failed with status ${c.status}`;if(c.status===403&&h.includes("Origin"))throw new S(h);if(c.status===401)throw new b(h);if(c.status===429){let I=this.rateLimitInfo?this.rateLimitInfo.reset-Math.floor(Date.now()/1e3):60,u=new x(h,I,this.rateLimitInfo?.remaining||0,this.rateLimitInfo?.limit||100);if(this.retryOn429&&n<i){n++;let f=Math.min(1e3*Math.pow(2,n-1),8e3);await this.sleep(f);continue}throw u}throw new o(h,c.status,w)}return w.data}catch(a){throw a instanceof o?a:a instanceof Error&&a.name==="AbortError"?new o("Request timeout",408):new o(a instanceof Error?a.message:"Unknown error occurred",500)}}async get(e,t,s){return this.call(e,t,{method:"GET",headers:s})}async post(e,t,s,n){return this.call(e,t,{method:"POST",body:s,headers:n})}async put(e,t,s,n){return this.call(e,t,{method:"PUT",body:s,headers:n})}async delete(e,t,s){return this.call(e,t,{method:"DELETE",headers:s})}async patch(e,t,s,n){return this.call(e,t,{method:"PATCH",body:s,headers:n})}setAppId(e){this.appId=e}async secretsRequest(e,t){if(!this.appId)throw new o("App ID required for secrets management. Call setAppId() first.",400);let s=await this.fetchWithTimeout(`${this.baseUrl}/api/projects/${this.appId}/secrets/manage?action=${e}`,{method:"POST",headers:{"Content-Type":"application/json"},body:t?JSON.stringify(t):void 0},this.timeout);if(!s.ok){let n=await s.json().catch(()=>({}));throw new o(n.message||`Secrets management failed: ${s.statusText}`,s.status,n)}return s.json()}async listSecrets(){return this.secretsRequest("list")}async createSecret(e){return this.secretsRequest("create",e)}async updateSecret(e){return this.secretsRequest("update",e)}async deleteSecret(e){return this.secretsRequest("delete",{name:e})}async syncEnv(e,t){return this.secretsRequest("sync",{secrets:e,provider:t})}};var d=y(l("fs")),P=y(l("path")),L=y(l("os"));function U(){let r=L.homedir(),e=P.join(r,".learn");return P.join(e,"credentials.json")}function ne(){let r=L.homedir(),e=P.join(r,".learn");d.existsSync(e)||d.mkdirSync(e,{recursive:!0,mode:448})}function v(){try{let r=U();if(!d.existsSync(r))return null;let e=d.readFileSync(r,"utf-8"),t=JSON.parse(e);return t.access_token&&!t["learn-secrets-sdk"]&&!t["learn-auth-sdk"]?{"learn-secrets-sdk":t}:t}catch{return null}}function R(r){ne();let e=U(),t=JSON.stringify(r,null,2);d.writeFileSync(e,t,{mode:384})}function k(r){let e=v();return e&&e[r]||null}function D(r,e){let t=v()||{};t[r]=e,R(t)}function H(r){let e=v();e&&(delete e[r],Object.keys(e).length===0?K():R(e))}function M(){return k("learn-secrets-sdk")}function K(){try{let r=U();d.existsSync(r)&&d.unlinkSync(r)}catch{}}function W(r){let e=k(r);return e!==null&&!!e.access_token}function G(){let r=M();return r?r.expires_at?new Date(r.expires_at)>new Date:!0:!1}var B=l("child_process"),V=l("util"),q=l("crypto"),g=y(l("os")),C=(0,V.promisify)(B.exec);async function O(){let r=process.platform;try{let e;if(r==="darwin")e=await ie();else if(r==="linux")e=await oe();else if(r==="win32")e=await ae();else throw new Error(`Unsupported platform: ${r}`);let t=(0,q.createHash)("sha256").update(e).digest("hex").substring(0,32),s=JSON.stringify({machineId:t,cpus:g.cpus().length,arch:g.arch(),platform:g.platform(),homedir:g.homedir()}),n=(0,q.createHash)("sha256").update(s).digest("hex");return{machineId:t,fingerprint:n}}catch(e){throw new Error(`Failed to get machine ID: ${e.message}`)}}async function ie(){try{let{stdout:r}=await C(`ioreg -l | grep IOPlatformSerialNumber | awk '{print $4}' | sed 's/"//g'`),{stdout:e}=await C("hostname"),t=r.trim(),s=e.trim();if(t&&s)return`${t}-${s}`;let{stdout:n}=await C(`ioreg -rd1 -c IOPlatformExpertDevice | grep IOPlatformUUID | awk '{print $3}' | sed 's/"//g'`);return n.trim()}catch{throw new Error("Failed to get macOS machine ID")}}async function oe(){try{let{stdout:r}=await C("cat /etc/machine-id 2>/dev/null || cat /var/lib/dbus/machine-id 2>/dev/null"),e=r.trim();if(e)return e;throw new Error("Machine ID file not found")}catch{throw new Error("Failed to get Linux machine ID")}}async function ae(){try{let{stdout:r}=await C("wmic csproduct get uuid"),t=r.split(`
2
+ `).map(s=>s.trim()).find(s=>s&&s!=="UUID");if(t)return t;throw new Error("Failed to parse machine UUID from WMIC")}catch{throw new Error("Failed to get Windows machine ID")}}var j=class{constructor(e){this.appId=e.appId,this.baseUrl=e.baseUrl||"https://cloudprototype.org",this.timeout=e.timeout||3e4}async openBrowser(e){let{exec:t}=await import("child_process"),{promisify:s}=await import("util"),n=s(t),i=process.platform;try{i==="darwin"?await n(`open "${e}"`):i==="win32"?await n(`start "${e}"`):await n(`xdg-open "${e}"`)}catch(a){throw console.error("Failed to open browser:",a),new o("Failed to open browser",500)}}sleep(e){return new Promise(t=>setTimeout(t,e))}async login(){let e="learn-secrets-sdk";try{console.log(`Authenticating with Learn (${e})...
3
+ `);let t=await O(),s=k(e);if(s&&s.access_token){let u=await fetch(`${this.baseUrl}/api/auth/cli/check-authorization`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({machine_id:t.machineId,sdk_name:e})});if(u.ok){let f=await u.json();if(f.authorized)return console.log("This machine is already authorized."),console.log(`Logged in as: ${f.user_email||f.user_id}`),{success:!0,user:f.user_email||f.user_id}}}let n=await fetch(`${this.baseUrl}/api/auth/cli/device`,{method:"POST",headers:{"Content-Type":"application/json"}});if(!n.ok)throw new o("Failed to generate device code",n.status);let i=await n.json();console.log(`
4
4
  Log in on ${this.baseUrl}`),console.log("Login at:"),console.log(i.verification_uri_complete),console.log(`
5
5
  Press ENTER to open in the browser...`);let m=(await import("readline")).createInterface({input:process.stdin,output:process.stdout});await new Promise(u=>{m.question("",()=>{m.close(),u()})}),console.log(`Opening browser...
6
- `),await this.openBrowser(i.verification_uri_complete);let c=null,w=i.interval*1e3,h=Math.ceil(i.expires_in/i.interval),k=0;for(;k<h;){await this.sleep(w),k++;let u=await fetch(`${this.baseUrl}/api/auth/cli/token`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({device_code:i.device_code})});if(u.status===202){process.stdout.write(".");continue}if(u.ok){c=await u.json();break}}if(!c)throw new o("Device authorization timeout",408);return console.log(`
6
+ `),await this.openBrowser(i.verification_uri_complete);let c=null,w=i.interval*1e3,h=Math.ceil(i.expires_in/i.interval),I=0;for(;I<h;){await this.sleep(w),I++;let u=await fetch(`${this.baseUrl}/api/auth/cli/token`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({device_code:i.device_code})});if(u.status===202){process.stdout.write(".");continue}if(u.ok){c=await u.json();break}}if(!c)throw new o("Device authorization timeout",408);return console.log(`
7
7
 
8
- Device authorized! Retrieving access token...`),P(e,{access_token:c.access_token,refresh_token:null,expires_at:null,user_id:c.user_id,machine_id:r.machineId,sdk_name:e,authorized_at:new Date().toISOString()}),console.log(`
8
+ Device authorized! Retrieving access token...`),D(e,{access_token:c.access_token,refresh_token:null,expires_at:null,user_id:c.user_id,machine_id:t.machineId,sdk_name:e,authorized_at:new Date().toISOString()}),console.log(`
9
9
  Authentication successful!`),console.log(`Logged in as: ${c.user_email||c.user_id}`),console.log(`This machine is now permanently authorized.
10
- `),{success:!0,user:c.user_email||c.user_id}}catch(r){throw r instanceof o?r:new o(r.message||"Login failed",r.status||500)}}async isAuthenticated(){return W()}logout(){K(),console.log("Logged out successfully")}getAccessToken(){let e=j();if(!e)throw new o("Not authenticated. Run login() first.",401);if(e.expires_at&&new Date(e.expires_at)<=new Date)throw new o("Token expired. Run login() again.",401);return e.access_token}async request(e,r,s){let n=this.getAccessToken(),i={method:e,headers:{"Content-Type":"application/json",Authorization:`Bearer ${n}`}};s&&(i.body=JSON.stringify(s));let a=await fetch(`${this.baseUrl}${r}`,i);if(!a.ok){let m=await a.json().catch(()=>({}));throw new o(m.message||`Request failed: ${a.statusText}`,a.status,m)}return a.json()}async list(){return(await this.request("GET",`/api/projects/${this.appId}/secrets`)).secrets}async sync(e,r){return await this.request("POST",`/api/projects/${this.appId}/secrets/sync`,{secrets:e,options:{deleteMissing:r?.deleteMissing??!1,dryRun:r?.dryRun??!1}})}async importSecrets(e){return this.request("POST",`/api/projects/${this.appId}/import-secrets`,{version:e.version||"1.0",origins:e.origins,secrets:e.secrets})}async importFromFile(e){let{loadSecretsConfig:r}=await Promise.resolve().then(()=>(N(),V)),s=r(e);return this.importSecrets(s)}};N();return re(ae);})();
10
+ `),{success:!0,user:c.user_email||c.user_id}}catch(t){throw t instanceof o?t:new o(t.message||"Login failed",t.status||500)}}async isAuthenticated(){return G()}logout(){K(),console.log("Logged out successfully")}getAccessToken(){let e=M();if(!e)throw new o("Not authenticated. Run login() first.",401);if(e.expires_at&&new Date(e.expires_at)<=new Date)throw new o("Token expired. Run login() again.",401);return e.access_token}async request(e,t,s){let n=this.getAccessToken(),i={method:e,headers:{"Content-Type":"application/json",Authorization:`Bearer ${n}`}};s&&(i.body=JSON.stringify(s));let a=await fetch(`${this.baseUrl}${t}`,i);if(!a.ok){let m=await a.json().catch(()=>({}));throw new o(m.message||`Request failed: ${a.statusText}`,a.status,m)}return a.json()}async list(){return(await this.request("GET",`/api/projects/${this.appId}/secrets`)).secrets}async sync(e,t){return await this.request("POST",`/api/projects/${this.appId}/secrets/sync`,{secrets:e,options:{deleteMissing:t?.deleteMissing??!1,dryRun:t?.dryRun??!1}})}async importSecrets(e){return this.request("POST",`/api/projects/${this.appId}/import-secrets`,{version:e.version||"1.0",origins:e.origins,secrets:e.secrets})}async importFromFile(e){let{loadSecretsConfig:t}=await Promise.resolve().then(()=>(J(),X)),s=t(e);return this.importSecrets(s)}};var A=class{constructor(e){this.workerUrl=e.workerUrl.replace(/\/$/,"")}async getConfig(){let e=await fetch(`${this.workerUrl}/config`,{method:"GET",credentials:"include"});if(!e.ok)throw new Error(`Failed to get config: ${e.status} ${e.statusText}`);return(await e.json()).public||{}}async proxy(e){let t=await fetch(`${this.workerUrl}/proxy`,{method:"POST",headers:{"Content-Type":"application/json"},credentials:"include",body:JSON.stringify(e)});if(!t.ok){let s=await t.json().catch(()=>({error:"Unknown error"}));throw new Error(s.error||`Proxy request failed: ${t.status}`)}return t.json()}async generateAppleJwt(e){let t=await fetch(`${this.workerUrl}/auth/apple-jwt`,{method:"POST",headers:{"Content-Type":"application/json"},credentials:"include",body:JSON.stringify(e)});if(!t.ok){let n=await t.json().catch(()=>({error:"Unknown error"}));throw new Error(n.error||`Apple JWT generation failed: ${t.status}`)}return(await t.json()).token}async exchangeOAuthCode(e){let t=await fetch(`${this.workerUrl}/auth/oauth`,{method:"POST",headers:{"Content-Type":"application/json"},credentials:"include",body:JSON.stringify(e)});if(!t.ok){let s=await t.json().catch(()=>({error:"Unknown error"}));throw new Error(s.error||`OAuth exchange failed: ${t.status}`)}return t.json()}async health(){let e=await fetch(`${this.workerUrl}/health`,{method:"GET"});if(!e.ok)throw new Error(`Health check failed: ${e.status}`);return e.json()}};J();return se(ce);})();
package/dist/index.mjs CHANGED
@@ -1,10 +1,10 @@
1
- import{a as K,b as $,c as A,d as U}from"./chunk-AS6G7JYX.mjs";var i=class extends Error{constructor(t,r,n){super(t);this.status=r;this.response=n;this.name="SecretsSDKError"}},f=class extends i{constructor(e="Origin not allowed for this token"){super(e,403),this.name="OriginMismatchError"}},y=class extends i{constructor(e="Rate limit exceeded",t=60,r=0,n=100){super(e,429),this.name="RateLimitError",this.retryAfter=t,this.remaining=r,this.limit=n}},w=class extends i{constructor(e="Invalid or expired token"){super(e,401),this.name="InvalidTokenError"}};var v=class{constructor(e={}){this.rateLimitInfo=null;this.appId=e.appId||null,this.token=e.token||e.sessionToken||null,this.baseUrl=e.baseUrl||"https://cloudprototype.org",this.timeout=e.timeout||3e4,this.retryOn429=e.retryOn429!==void 0?e.retryOn429:!0,this.zeroConfigMode=!this.appId&&!this.token}getUsage(){return this.rateLimitInfo}parseRateLimitHeaders(e){let t=e.get("X-RateLimit-Limit"),r=e.get("X-RateLimit-Remaining"),n=e.get("X-RateLimit-Reset");t&&r&&n&&(this.rateLimitInfo={limit:parseInt(t),remaining:parseInt(r),reset:parseInt(n)})}sleep(e){return new Promise(t=>setTimeout(t,e))}async fetchWithTimeout(e,t,r){let n=new AbortController,c=setTimeout(()=>n.abort(),r);try{return await fetch(e,{...t,signal:n.signal})}finally{clearTimeout(c)}}async call(e,t,r={}){let n=0,c=this.retryOn429?3:0;for(;;)try{let o=this.zeroConfigMode?`${this.baseUrl}/api/sdk/proxy`:`${this.baseUrl}/api/sdk/${this.appId}/proxy`,u={"Content-Type":"application/json"};!this.zeroConfigMode&&this.token&&(u.Authorization=`Bearer ${this.token}`);let a=await this.fetchWithTimeout(o,{method:"POST",headers:u,body:JSON.stringify({keyName:e,endpoint:t,method:r.method||"GET",body:r.body,headers:r.headers})},this.timeout);this.parseRateLimitHeaders(a.headers);let h=await a.json();if(!a.ok){let m=h.data?.message||h?.message||`Request failed with status ${a.status}`;if(a.status===403&&m.includes("Origin"))throw new f(m);if(a.status===401)throw new w(m);if(a.status===429){let x=this.rateLimitInfo?this.rateLimitInfo.reset-Math.floor(Date.now()/1e3):60,l=new y(m,x,this.rateLimitInfo?.remaining||0,this.rateLimitInfo?.limit||100);if(this.retryOn429&&n<c){n++;let g=Math.min(1e3*Math.pow(2,n-1),8e3);await this.sleep(g);continue}throw l}throw new i(m,a.status,h)}return h.data}catch(o){throw o instanceof i?o:o instanceof Error&&o.name==="AbortError"?new i("Request timeout",408):new i(o instanceof Error?o.message:"Unknown error occurred",500)}}async get(e,t,r){return this.call(e,t,{method:"GET",headers:r})}async post(e,t,r,n){return this.call(e,t,{method:"POST",body:r,headers:n})}async put(e,t,r,n){return this.call(e,t,{method:"PUT",body:r,headers:n})}async delete(e,t,r){return this.call(e,t,{method:"DELETE",headers:r})}async patch(e,t,r,n){return this.call(e,t,{method:"PATCH",body:r,headers:n})}setAppId(e){this.appId=e}async secretsRequest(e,t){if(!this.appId)throw new i("App ID required for secrets management. Call setAppId() first.",400);let r=await this.fetchWithTimeout(`${this.baseUrl}/api/projects/${this.appId}/secrets/manage?action=${e}`,{method:"POST",headers:{"Content-Type":"application/json"},body:t?JSON.stringify(t):void 0},this.timeout);if(!r.ok){let n=await r.json().catch(()=>({}));throw new i(n.message||`Secrets management failed: ${r.statusText}`,r.status,n)}return r.json()}async listSecrets(){return this.secretsRequest("list")}async createSecret(e){return this.secretsRequest("create",e)}async updateSecret(e){return this.secretsRequest("update",e)}async deleteSecret(e){return this.secretsRequest("delete",{name:e})}async syncEnv(e,t){return this.secretsRequest("sync",{secrets:e,provider:t})}};import*as d from"fs";import*as I from"path";import*as C from"os";function T(){let s=C.homedir(),e=I.join(s,".learn");return I.join(e,"credentials.json")}function j(){let s=C.homedir(),e=I.join(s,".learn");d.existsSync(e)||d.mkdirSync(e,{recursive:!0,mode:448})}function k(){try{let s=T();if(!d.existsSync(s))return null;let e=d.readFileSync(s,"utf-8"),t=JSON.parse(e);return t.access_token&&!t["learn-secrets-sdk"]&&!t["learn-auth-sdk"]?{"learn-secrets-sdk":t}:t}catch{return null}}function _(s){j();let e=T(),t=JSON.stringify(s,null,2);d.writeFileSync(e,t,{mode:384})}function S(s){let e=k();return e&&e[s]||null}function D(s,e){let t=k()||{};t[s]=e,_(t)}function q(s){let e=k();e&&(delete e[s],Object.keys(e).length===0?P():_(e))}function R(){return S("learn-secrets-sdk")}function P(){try{let s=T();d.existsSync(s)&&d.unlinkSync(s)}catch{}}function N(s){let e=S(s);return e!==null&&!!e.access_token}function E(){let s=R();return s?s.expires_at?new Date(s.expires_at)>new Date:!0:!1}import{exec as z}from"child_process";import{promisify as F}from"util";import{createHash as M}from"crypto";import*as p from"os";var b=F(z);async function O(){let s=process.platform;try{let e;if(s==="darwin")e=await J();else if(s==="linux")e=await H();else if(s==="win32")e=await W();else throw new Error(`Unsupported platform: ${s}`);let t=M("sha256").update(e).digest("hex").substring(0,32),r=JSON.stringify({machineId:t,cpus:p.cpus().length,arch:p.arch(),platform:p.platform(),homedir:p.homedir()}),n=M("sha256").update(r).digest("hex");return{machineId:t,fingerprint:n}}catch(e){throw new Error(`Failed to get machine ID: ${e.message}`)}}async function J(){try{let{stdout:s}=await b(`ioreg -l | grep IOPlatformSerialNumber | awk '{print $4}' | sed 's/"//g'`),{stdout:e}=await b("hostname"),t=s.trim(),r=e.trim();if(t&&r)return`${t}-${r}`;let{stdout:n}=await b(`ioreg -rd1 -c IOPlatformExpertDevice | grep IOPlatformUUID | awk '{print $3}' | sed 's/"//g'`);return n.trim()}catch{throw new Error("Failed to get macOS machine ID")}}async function H(){try{let{stdout:s}=await b("cat /etc/machine-id 2>/dev/null || cat /var/lib/dbus/machine-id 2>/dev/null"),e=s.trim();if(e)return e;throw new Error("Machine ID file not found")}catch{throw new Error("Failed to get Linux machine ID")}}async function W(){try{let{stdout:s}=await b("wmic csproduct get uuid"),t=s.split(`
2
- `).map(r=>r.trim()).find(r=>r&&r!=="UUID");if(t)return t;throw new Error("Failed to parse machine UUID from WMIC")}catch{throw new Error("Failed to get Windows machine ID")}}var L=class{constructor(e){this.appId=e.appId,this.baseUrl=e.baseUrl||"https://cloudprototype.org",this.timeout=e.timeout||3e4}async openBrowser(e){let{exec:t}=await import("child_process"),{promisify:r}=await import("util"),n=r(t),c=process.platform;try{c==="darwin"?await n(`open "${e}"`):c==="win32"?await n(`start "${e}"`):await n(`xdg-open "${e}"`)}catch(o){throw console.error("Failed to open browser:",o),new i("Failed to open browser",500)}}sleep(e){return new Promise(t=>setTimeout(t,e))}async login(){let e="learn-secrets-sdk";try{console.log(`Authenticating with Learn (${e})...
1
+ import{a as j,b as A,c as M,d as K}from"./chunk-AS6G7JYX.mjs";var i=class extends Error{constructor(t,r,n){super(t);this.status=r;this.response=n;this.name="SecretsSDKError"}},f=class extends i{constructor(e="Origin not allowed for this token"){super(e,403),this.name="OriginMismatchError"}},y=class extends i{constructor(e="Rate limit exceeded",t=60,r=0,n=100){super(e,429),this.name="RateLimitError",this.retryAfter=t,this.remaining=r,this.limit=n}},w=class extends i{constructor(e="Invalid or expired token"){super(e,401),this.name="InvalidTokenError"}};var T=class{constructor(e={}){this.rateLimitInfo=null;this.appId=e.appId||null,this.token=e.token||e.sessionToken||null,this.baseUrl=e.baseUrl||"https://cloudprototype.org",this.timeout=e.timeout||3e4,this.retryOn429=e.retryOn429!==void 0?e.retryOn429:!0,this.zeroConfigMode=!this.appId&&!this.token}getUsage(){return this.rateLimitInfo}parseRateLimitHeaders(e){let t=e.get("X-RateLimit-Limit"),r=e.get("X-RateLimit-Remaining"),n=e.get("X-RateLimit-Reset");t&&r&&n&&(this.rateLimitInfo={limit:parseInt(t),remaining:parseInt(r),reset:parseInt(n)})}sleep(e){return new Promise(t=>setTimeout(t,e))}async fetchWithTimeout(e,t,r){let n=new AbortController,c=setTimeout(()=>n.abort(),r);try{return await fetch(e,{...t,signal:n.signal})}finally{clearTimeout(c)}}async call(e,t,r={}){let n=0,c=this.retryOn429?3:0;for(;;)try{let o=this.zeroConfigMode?`${this.baseUrl}/api/sdk/proxy`:`${this.baseUrl}/api/sdk/${this.appId}/proxy`,u={"Content-Type":"application/json"};!this.zeroConfigMode&&this.token&&(u.Authorization=`Bearer ${this.token}`);let a=await this.fetchWithTimeout(o,{method:"POST",headers:u,body:JSON.stringify({keyName:e,endpoint:t,method:r.method||"GET",body:r.body,headers:r.headers})},this.timeout);this.parseRateLimitHeaders(a.headers);let h=await a.json();if(!a.ok){let m=h.data?.message||h?.message||`Request failed with status ${a.status}`;if(a.status===403&&m.includes("Origin"))throw new f(m);if(a.status===401)throw new w(m);if(a.status===429){let b=this.rateLimitInfo?this.rateLimitInfo.reset-Math.floor(Date.now()/1e3):60,l=new y(m,b,this.rateLimitInfo?.remaining||0,this.rateLimitInfo?.limit||100);if(this.retryOn429&&n<c){n++;let g=Math.min(1e3*Math.pow(2,n-1),8e3);await this.sleep(g);continue}throw l}throw new i(m,a.status,h)}return h.data}catch(o){throw o instanceof i?o:o instanceof Error&&o.name==="AbortError"?new i("Request timeout",408):new i(o instanceof Error?o.message:"Unknown error occurred",500)}}async get(e,t,r){return this.call(e,t,{method:"GET",headers:r})}async post(e,t,r,n){return this.call(e,t,{method:"POST",body:r,headers:n})}async put(e,t,r,n){return this.call(e,t,{method:"PUT",body:r,headers:n})}async delete(e,t,r){return this.call(e,t,{method:"DELETE",headers:r})}async patch(e,t,r,n){return this.call(e,t,{method:"PATCH",body:r,headers:n})}setAppId(e){this.appId=e}async secretsRequest(e,t){if(!this.appId)throw new i("App ID required for secrets management. Call setAppId() first.",400);let r=await this.fetchWithTimeout(`${this.baseUrl}/api/projects/${this.appId}/secrets/manage?action=${e}`,{method:"POST",headers:{"Content-Type":"application/json"},body:t?JSON.stringify(t):void 0},this.timeout);if(!r.ok){let n=await r.json().catch(()=>({}));throw new i(n.message||`Secrets management failed: ${r.statusText}`,r.status,n)}return r.json()}async listSecrets(){return this.secretsRequest("list")}async createSecret(e){return this.secretsRequest("create",e)}async updateSecret(e){return this.secretsRequest("update",e)}async deleteSecret(e){return this.secretsRequest("delete",{name:e})}async syncEnv(e,t){return this.secretsRequest("sync",{secrets:e,provider:t})}};import*as d from"fs";import*as k from"path";import*as C from"os";function v(){let s=C.homedir(),e=k.join(s,".learn");return k.join(e,"credentials.json")}function q(){let s=C.homedir(),e=k.join(s,".learn");d.existsSync(e)||d.mkdirSync(e,{recursive:!0,mode:448})}function I(){try{let s=v();if(!d.existsSync(s))return null;let e=d.readFileSync(s,"utf-8"),t=JSON.parse(e);return t.access_token&&!t["learn-secrets-sdk"]&&!t["learn-auth-sdk"]?{"learn-secrets-sdk":t}:t}catch{return null}}function P(s){q();let e=v(),t=JSON.stringify(s,null,2);d.writeFileSync(e,t,{mode:384})}function S(s){let e=I();return e&&e[s]||null}function R(s,e){let t=I()||{};t[s]=e,P(t)}function N(s){let e=I();e&&(delete e[s],Object.keys(e).length===0?D():P(e))}function _(){return S("learn-secrets-sdk")}function D(){try{let s=v();d.existsSync(s)&&d.unlinkSync(s)}catch{}}function J(s){let e=S(s);return e!==null&&!!e.access_token}function L(){let s=_();return s?s.expires_at?new Date(s.expires_at)>new Date:!0:!1}import{exec as z}from"child_process";import{promisify as F}from"util";import{createHash as U}from"crypto";import*as p from"os";var x=F(z);async function O(){let s=process.platform;try{let e;if(s==="darwin")e=await H();else if(s==="linux")e=await W();else if(s==="win32")e=await G();else throw new Error(`Unsupported platform: ${s}`);let t=U("sha256").update(e).digest("hex").substring(0,32),r=JSON.stringify({machineId:t,cpus:p.cpus().length,arch:p.arch(),platform:p.platform(),homedir:p.homedir()}),n=U("sha256").update(r).digest("hex");return{machineId:t,fingerprint:n}}catch(e){throw new Error(`Failed to get machine ID: ${e.message}`)}}async function H(){try{let{stdout:s}=await x(`ioreg -l | grep IOPlatformSerialNumber | awk '{print $4}' | sed 's/"//g'`),{stdout:e}=await x("hostname"),t=s.trim(),r=e.trim();if(t&&r)return`${t}-${r}`;let{stdout:n}=await x(`ioreg -rd1 -c IOPlatformExpertDevice | grep IOPlatformUUID | awk '{print $3}' | sed 's/"//g'`);return n.trim()}catch{throw new Error("Failed to get macOS machine ID")}}async function W(){try{let{stdout:s}=await x("cat /etc/machine-id 2>/dev/null || cat /var/lib/dbus/machine-id 2>/dev/null"),e=s.trim();if(e)return e;throw new Error("Machine ID file not found")}catch{throw new Error("Failed to get Linux machine ID")}}async function G(){try{let{stdout:s}=await x("wmic csproduct get uuid"),t=s.split(`
2
+ `).map(r=>r.trim()).find(r=>r&&r!=="UUID");if(t)return t;throw new Error("Failed to parse machine UUID from WMIC")}catch{throw new Error("Failed to get Windows machine ID")}}var E=class{constructor(e){this.appId=e.appId,this.baseUrl=e.baseUrl||"https://cloudprototype.org",this.timeout=e.timeout||3e4}async openBrowser(e){let{exec:t}=await import("child_process"),{promisify:r}=await import("util"),n=r(t),c=process.platform;try{c==="darwin"?await n(`open "${e}"`):c==="win32"?await n(`start "${e}"`):await n(`xdg-open "${e}"`)}catch(o){throw console.error("Failed to open browser:",o),new i("Failed to open browser",500)}}sleep(e){return new Promise(t=>setTimeout(t,e))}async login(){let e="learn-secrets-sdk";try{console.log(`Authenticating with Learn (${e})...
3
3
  `);let t=await O(),r=S(e);if(r&&r.access_token){let l=await fetch(`${this.baseUrl}/api/auth/cli/check-authorization`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({machine_id:t.machineId,sdk_name:e})});if(l.ok){let g=await l.json();if(g.authorized)return console.log("This machine is already authorized."),console.log(`Logged in as: ${g.user_email||g.user_id}`),{success:!0,user:g.user_email||g.user_id}}}let n=await fetch(`${this.baseUrl}/api/auth/cli/device`,{method:"POST",headers:{"Content-Type":"application/json"}});if(!n.ok)throw new i("Failed to generate device code",n.status);let c=await n.json();console.log(`
4
4
  Log in on ${this.baseUrl}`),console.log("Login at:"),console.log(c.verification_uri_complete),console.log(`
5
5
  Press ENTER to open in the browser...`);let u=(await import("readline")).createInterface({input:process.stdin,output:process.stdout});await new Promise(l=>{u.question("",()=>{u.close(),l()})}),console.log(`Opening browser...
6
- `),await this.openBrowser(c.verification_uri_complete);let a=null,h=c.interval*1e3,m=Math.ceil(c.expires_in/c.interval),x=0;for(;x<m;){await this.sleep(h),x++;let l=await fetch(`${this.baseUrl}/api/auth/cli/token`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({device_code:c.device_code})});if(l.status===202){process.stdout.write(".");continue}if(l.ok){a=await l.json();break}}if(!a)throw new i("Device authorization timeout",408);return console.log(`
6
+ `),await this.openBrowser(c.verification_uri_complete);let a=null,h=c.interval*1e3,m=Math.ceil(c.expires_in/c.interval),b=0;for(;b<m;){await this.sleep(h),b++;let l=await fetch(`${this.baseUrl}/api/auth/cli/token`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({device_code:c.device_code})});if(l.status===202){process.stdout.write(".");continue}if(l.ok){a=await l.json();break}}if(!a)throw new i("Device authorization timeout",408);return console.log(`
7
7
 
8
- Device authorized! Retrieving access token...`),D(e,{access_token:a.access_token,refresh_token:null,expires_at:null,user_id:a.user_id,machine_id:t.machineId,sdk_name:e,authorized_at:new Date().toISOString()}),console.log(`
8
+ Device authorized! Retrieving access token...`),R(e,{access_token:a.access_token,refresh_token:null,expires_at:null,user_id:a.user_id,machine_id:t.machineId,sdk_name:e,authorized_at:new Date().toISOString()}),console.log(`
9
9
  Authentication successful!`),console.log(`Logged in as: ${a.user_email||a.user_id}`),console.log(`This machine is now permanently authorized.
10
- `),{success:!0,user:a.user_email||a.user_id}}catch(t){throw t instanceof i?t:new i(t.message||"Login failed",t.status||500)}}async isAuthenticated(){return E()}logout(){P(),console.log("Logged out successfully")}getAccessToken(){let e=R();if(!e)throw new i("Not authenticated. Run login() first.",401);if(e.expires_at&&new Date(e.expires_at)<=new Date)throw new i("Token expired. Run login() again.",401);return e.access_token}async request(e,t,r){let n=this.getAccessToken(),c={method:e,headers:{"Content-Type":"application/json",Authorization:`Bearer ${n}`}};r&&(c.body=JSON.stringify(r));let o=await fetch(`${this.baseUrl}${t}`,c);if(!o.ok){let u=await o.json().catch(()=>({}));throw new i(u.message||`Request failed: ${o.statusText}`,o.status,u)}return o.json()}async list(){return(await this.request("GET",`/api/projects/${this.appId}/secrets`)).secrets}async sync(e,t){return await this.request("POST",`/api/projects/${this.appId}/secrets/sync`,{secrets:e,options:{deleteMissing:t?.deleteMissing??!1,dryRun:t?.dryRun??!1}})}async importSecrets(e){return this.request("POST",`/api/projects/${this.appId}/import-secrets`,{version:e.version||"1.0",origins:e.origins,secrets:e.secrets})}async importFromFile(e){let{loadSecretsConfig:t}=await import("./env-resolver-Y4SFGOKB.mjs"),r=t(e);return this.importSecrets(r)}};export{w as InvalidTokenError,f as OriginMismatchError,y as RateLimitError,L as SecretsManagement,v as SecretsSDK,i as SecretsSDKError,q as deleteSDKCredentials,O as getMachineId,N as hasSDKCredentials,U as loadSecretsConfig,k as readAllCredentials,S as readSDKCredentials,$ as resolveEnvInSecret,A as resolveEnvInSecrets,K as resolveEnvString,_ as writeAllCredentials,D as writeSDKCredentials};
10
+ `),{success:!0,user:a.user_email||a.user_id}}catch(t){throw t instanceof i?t:new i(t.message||"Login failed",t.status||500)}}async isAuthenticated(){return L()}logout(){D(),console.log("Logged out successfully")}getAccessToken(){let e=_();if(!e)throw new i("Not authenticated. Run login() first.",401);if(e.expires_at&&new Date(e.expires_at)<=new Date)throw new i("Token expired. Run login() again.",401);return e.access_token}async request(e,t,r){let n=this.getAccessToken(),c={method:e,headers:{"Content-Type":"application/json",Authorization:`Bearer ${n}`}};r&&(c.body=JSON.stringify(r));let o=await fetch(`${this.baseUrl}${t}`,c);if(!o.ok){let u=await o.json().catch(()=>({}));throw new i(u.message||`Request failed: ${o.statusText}`,o.status,u)}return o.json()}async list(){return(await this.request("GET",`/api/projects/${this.appId}/secrets`)).secrets}async sync(e,t){return await this.request("POST",`/api/projects/${this.appId}/secrets/sync`,{secrets:e,options:{deleteMissing:t?.deleteMissing??!1,dryRun:t?.dryRun??!1}})}async importSecrets(e){return this.request("POST",`/api/projects/${this.appId}/import-secrets`,{version:e.version||"1.0",origins:e.origins,secrets:e.secrets})}async importFromFile(e){let{loadSecretsConfig:t}=await import("./env-resolver-Y4SFGOKB.mjs"),r=t(e);return this.importSecrets(r)}};var $=class{constructor(e){this.workerUrl=e.workerUrl.replace(/\/$/,"")}async getConfig(){let e=await fetch(`${this.workerUrl}/config`,{method:"GET",credentials:"include"});if(!e.ok)throw new Error(`Failed to get config: ${e.status} ${e.statusText}`);return(await e.json()).public||{}}async proxy(e){let t=await fetch(`${this.workerUrl}/proxy`,{method:"POST",headers:{"Content-Type":"application/json"},credentials:"include",body:JSON.stringify(e)});if(!t.ok){let r=await t.json().catch(()=>({error:"Unknown error"}));throw new Error(r.error||`Proxy request failed: ${t.status}`)}return t.json()}async generateAppleJwt(e){let t=await fetch(`${this.workerUrl}/auth/apple-jwt`,{method:"POST",headers:{"Content-Type":"application/json"},credentials:"include",body:JSON.stringify(e)});if(!t.ok){let n=await t.json().catch(()=>({error:"Unknown error"}));throw new Error(n.error||`Apple JWT generation failed: ${t.status}`)}return(await t.json()).token}async exchangeOAuthCode(e){let t=await fetch(`${this.workerUrl}/auth/oauth`,{method:"POST",headers:{"Content-Type":"application/json"},credentials:"include",body:JSON.stringify(e)});if(!t.ok){let r=await t.json().catch(()=>({error:"Unknown error"}));throw new Error(r.error||`OAuth exchange failed: ${t.status}`)}return t.json()}async health(){let e=await fetch(`${this.workerUrl}/health`,{method:"GET"});if(!e.ok)throw new Error(`Health check failed: ${e.status}`);return e.json()}};export{w as InvalidTokenError,f as OriginMismatchError,y as RateLimitError,E as SecretsManagement,T as SecretsSDK,i as SecretsSDKError,$ as WorkerClient,N as deleteSDKCredentials,O as getMachineId,J as hasSDKCredentials,K as loadSecretsConfig,I as readAllCredentials,S as readSDKCredentials,A as resolveEnvInSecret,M as resolveEnvInSecrets,j as resolveEnvString,P as writeAllCredentials,R as writeSDKCredentials};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "learn-secrets-sdk",
3
- "version": "1.6.9",
4
- "description": "Secure API proxy SDK for static sites with domain-scoped tokens",
3
+ "version": "1.9.0",
4
+ "description": "Secure API proxy SDK with Cloudflare Worker deployment for static sites",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.esm.js",
7
7
  "types": "dist/index.d.ts",