learn-secrets-sdk 1.6.8 → 1.8.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(
@@ -587,43 +587,6 @@ var PROVIDER_PATTERNS = [
587
587
  authPrefix: "Bearer "
588
588
  }
589
589
  ];
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
590
  function detectProvider(key) {
628
591
  for (const pattern of PROVIDER_PATTERNS) {
629
592
  if (pattern.pattern.test(key)) {
@@ -633,12 +596,12 @@ function detectProvider(key) {
633
596
  return null;
634
597
  }
635
598
  function envKeyToSecretName(key) {
636
- return key.toLowerCase().replace(/_api_key$/i, "").replace(/_secret$/i, "").replace(/_token$/i, "").replace(/_key$/i, "").replace(/_/g, "-");
599
+ return key.toLowerCase().replace(/_/g, "-");
637
600
  }
638
- function detectApiKeys(envVars) {
601
+ function convertAllEnvVarsToSecrets(envVars) {
639
602
  const secrets = [];
640
603
  for (const { key, value } of envVars) {
641
- if (!isLikelyApiKey(key, value)) {
604
+ if (!value || value.trim() === "") {
642
605
  continue;
643
606
  }
644
607
  const providerInfo = detectProvider(key);
@@ -658,7 +621,6 @@ function detectApiKeys(envVars) {
658
621
  provider: "custom",
659
622
  api_key: value,
660
623
  base_url: "",
661
- // User must configure
662
624
  auth_header: "Authorization",
663
625
  auth_prefix: "Bearer "
664
626
  });
@@ -668,16 +630,117 @@ function detectApiKeys(envVars) {
668
630
  }
669
631
  function summarizeDetectedSecrets(secrets) {
670
632
  if (secrets.length === 0) {
671
- return "No API keys detected in .env file";
633
+ return "No secrets found in .env file";
672
634
  }
673
- const lines = ["Detected API keys:"];
635
+ const lines = ["Detected secrets:"];
674
636
  for (const secret of secrets) {
675
- const masked = secret.api_key.substring(0, 8) + "...";
637
+ const masked = secret.api_key.length > 8 ? secret.api_key.substring(0, 8) + "..." : "***";
676
638
  lines.push(` - ${secret.name} (${secret.provider}): ${masked}`);
677
639
  }
678
640
  return lines.join("\n");
679
641
  }
680
642
 
643
+ // src/cli/commands/connect-cloudflare.ts
644
+ function getApiUrl(baseUrl) {
645
+ return baseUrl || "https://cloudprototype.org";
646
+ }
647
+ async function connectCloudflare(apiToken, baseUrl) {
648
+ if (!apiToken.match(/^[A-Za-z0-9_-]{40,}$/)) {
649
+ return {
650
+ success: false,
651
+ error: "Invalid API token format. Token should be 40+ alphanumeric characters"
652
+ };
653
+ }
654
+ const validation = await validateCloudflareToken(apiToken);
655
+ if (!validation.valid) {
656
+ return {
657
+ success: false,
658
+ error: validation.error || "Token validation failed"
659
+ };
660
+ }
661
+ try {
662
+ const apiUrl = getApiUrl(baseUrl);
663
+ const response = await fetch(`${apiUrl}/api/cloudflare/connect`, {
664
+ method: "POST",
665
+ headers: {
666
+ "Content-Type": "application/json"
667
+ },
668
+ credentials: "include",
669
+ body: JSON.stringify({
670
+ apiToken,
671
+ accountId: validation.accountId,
672
+ accountName: validation.accountName
673
+ })
674
+ });
675
+ if (!response.ok) {
676
+ const error = await response.json();
677
+ return {
678
+ success: false,
679
+ error: error.message || "Failed to store token"
680
+ };
681
+ }
682
+ return {
683
+ success: true,
684
+ accountId: validation.accountId,
685
+ accountName: validation.accountName
686
+ };
687
+ } catch (error) {
688
+ return {
689
+ success: false,
690
+ error: error.message || "Failed to connect Cloudflare account"
691
+ };
692
+ }
693
+ }
694
+ async function validateCloudflareToken(token) {
695
+ try {
696
+ const response = await fetch("https://api.cloudflare.com/client/v4/user", {
697
+ headers: {
698
+ Authorization: `Bearer ${token}`,
699
+ "Content-Type": "application/json"
700
+ }
701
+ });
702
+ if (!response.ok) {
703
+ const error = await response.json();
704
+ return {
705
+ valid: false,
706
+ error: error.errors?.[0]?.message || "Invalid token"
707
+ };
708
+ }
709
+ const data = await response.json();
710
+ const accountsResponse = await fetch("https://api.cloudflare.com/client/v4/accounts", {
711
+ headers: {
712
+ Authorization: `Bearer ${token}`,
713
+ "Content-Type": "application/json"
714
+ }
715
+ });
716
+ if (!accountsResponse.ok) {
717
+ return {
718
+ valid: false,
719
+ error: "Token does not have access to accounts"
720
+ };
721
+ }
722
+ const accountsData = await accountsResponse.json();
723
+ const accounts = accountsData.result || [];
724
+ if (accounts.length === 0) {
725
+ return {
726
+ valid: false,
727
+ error: "No accounts found for this token"
728
+ };
729
+ }
730
+ const account = accounts[0];
731
+ return {
732
+ valid: true,
733
+ accountId: account.id,
734
+ accountName: account.name
735
+ };
736
+ } catch (error) {
737
+ return {
738
+ valid: false,
739
+ error: error.message || "Failed to validate token"
740
+ };
741
+ }
742
+ }
743
+
681
744
  // src/cli/commands/init.ts
682
745
  async function initCommand(options) {
683
746
  if (!hasValidCredentials()) {
@@ -720,12 +783,10 @@ Onboarding site for project: ${projectId}`);
720
783
  try {
721
784
  console.log(`Reading ${envFile}...`);
722
785
  const envVars = parseEnvFile(envFile);
723
- secrets = detectApiKeys(envVars);
786
+ secrets = convertAllEnvVarsToSecrets(envVars);
724
787
  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-...");
788
+ console.log("\nNo environment variables found in .env file.");
789
+ console.log("Make sure your .env file is not empty.");
729
790
  process.exit(0);
730
791
  }
731
792
  console.log("\n" + summarizeDetectedSecrets(secrets));
@@ -773,11 +834,62 @@ Onboarding site for project: ${projectId}`);
773
834
  console.log("\nAdd to your static site code:");
774
835
  console.log(" const sdk = new SecretsSDK();");
775
836
  console.log(" // Uses origin-based auth - no token needed!");
837
+ console.log("\n--- Cloudflare Worker Setup ---");
776
838
  const dashboardUrl = options.baseUrl || "https://cloudprototype.org";
839
+ let cloudflareConnected = false;
840
+ try {
841
+ const statusResponse = await fetch(`${dashboardUrl}/api/cloudflare/status`, {
842
+ credentials: "include"
843
+ });
844
+ cloudflareConnected = statusResponse.ok;
845
+ } catch {
846
+ cloudflareConnected = false;
847
+ }
848
+ if (cloudflareConnected) {
849
+ console.log("Cloudflare account is already connected.");
850
+ } else {
851
+ console.log("To deploy secrets via Cloudflare Worker, we need your Cloudflare API token.");
852
+ console.log("\nRequired permissions:");
853
+ console.log(" - Workers Scripts: Edit");
854
+ console.log(" - Workers Routes: Edit");
855
+ console.log(" - Account Settings: Read");
856
+ console.log("\nCreate token at: https://dash.cloudflare.com/profile/api-tokens");
857
+ const readline = await import("readline");
858
+ const rl = readline.createInterface({
859
+ input: process.stdin,
860
+ output: process.stdout
861
+ });
862
+ const apiToken = await new Promise((resolve2) => {
863
+ rl.question("\nCloudflare API Token (or press Enter to skip): ", (answer) => {
864
+ rl.close();
865
+ resolve2(answer.trim());
866
+ });
867
+ });
868
+ if (apiToken) {
869
+ console.log("\nValidating token...");
870
+ const result2 = await connectCloudflare(apiToken, options.baseUrl);
871
+ if (result2.success) {
872
+ console.log("Cloudflare account connected");
873
+ console.log(` Account: ${result2.accountName}`);
874
+ cloudflareConnected = true;
875
+ } else {
876
+ console.log(`
877
+ Warning: Failed to connect Cloudflare: ${result2.error}`);
878
+ console.log("You can connect later by running: learn-secrets deploy");
879
+ }
880
+ } else {
881
+ console.log("\nSkipped Cloudflare connection.");
882
+ console.log("You can connect later when you run: learn-secrets deploy");
883
+ }
884
+ }
777
885
  console.log(`
778
886
  Next steps:`);
779
887
  console.log(` 1. Run: learn-secrets deploy`);
780
- console.log(` (Push these secrets to dashboard at ${dashboardUrl}/dashboard/secrets/api_keys)`);
888
+ if (cloudflareConnected) {
889
+ console.log(` (Deploy Worker to Cloudflare with your secrets)`);
890
+ } else {
891
+ console.log(` (Connect Cloudflare and deploy Worker with your secrets)`);
892
+ }
781
893
  console.log(` 2. Visit: ${dashboardUrl}/dashboard to manage secrets`);
782
894
  const config = {
783
895
  project: projectId,
@@ -798,212 +910,6 @@ Updated local config: ${configFile}`);
798
910
  }
799
911
  }
800
912
 
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
913
  // src/cli/commands/config.ts
1008
914
  import fs4 from "fs";
1009
915
  async function configCommand(options) {
@@ -1045,22 +951,270 @@ Configuration saved to: ${configFile}`);
1045
951
 
1046
952
  // src/cli/commands/deploy.ts
1047
953
  import fs5 from "fs";
1048
- async function deployCommand() {
954
+
955
+ // src/cli/commands/provision-worker.ts
956
+ import path2 from "path";
957
+ import { fileURLToPath } from "url";
958
+ var __filename = fileURLToPath(import.meta.url);
959
+ var __dirname = path2.dirname(__filename);
960
+ function getApiUrl2(baseUrl) {
961
+ return baseUrl || "https://cloudprototype.org";
962
+ }
963
+ async function provisionWorker(workerName, origins, envVars, baseUrl) {
964
+ const publicVars = [];
965
+ const secretVars = [];
966
+ for (const { key, value } of envVars) {
967
+ if (isPublicConfig(key)) {
968
+ publicVars.push({ key, value });
969
+ } else {
970
+ secretVars.push({ key, value });
971
+ }
972
+ }
973
+ try {
974
+ const apiUrl = getApiUrl2(baseUrl);
975
+ const deployResponse = await fetch(`${apiUrl}/api/cloudflare/provision-worker`, {
976
+ method: "POST",
977
+ headers: {
978
+ "Content-Type": "application/json"
979
+ },
980
+ credentials: "include",
981
+ body: JSON.stringify({
982
+ workerName,
983
+ allowedOrigins: origins,
984
+ publicVars: publicVars.map((v) => ({
985
+ key: `PUBLIC_${v.key}`,
986
+ value: v.value
987
+ })),
988
+ secrets: secretVars.map((v) => ({
989
+ key: `SECRET_${v.key}`,
990
+ value: v.value
991
+ }))
992
+ })
993
+ });
994
+ if (!deployResponse.ok) {
995
+ const error = await deployResponse.json();
996
+ return {
997
+ success: false,
998
+ error: error.message || "Failed to deploy Worker"
999
+ };
1000
+ }
1001
+ const result = await deployResponse.json();
1002
+ return {
1003
+ success: true,
1004
+ workerUrl: result.workerUrl,
1005
+ workerName: result.workerName
1006
+ };
1007
+ } catch (error) {
1008
+ return {
1009
+ success: false,
1010
+ error: error.message || "Failed to provision Worker"
1011
+ };
1012
+ }
1013
+ }
1014
+ function isPublicConfig(key) {
1015
+ const publicPatterns = [
1016
+ /_DOMAIN$/,
1017
+ /_CLIENT_ID$/,
1018
+ /_AUDIENCE$/,
1019
+ /_CALLBACK_URL$/,
1020
+ /_ISSUER$/,
1021
+ /_TEAM_ID$/,
1022
+ /_KEY_ID$/,
1023
+ /^API_BASE_URL$/,
1024
+ /^PUBLIC_/
1025
+ ];
1026
+ for (const pattern of publicPatterns) {
1027
+ if (pattern.test(key)) {
1028
+ return true;
1029
+ }
1030
+ }
1031
+ return false;
1032
+ }
1033
+
1034
+ // src/cli/commands/deploy.ts
1035
+ async function deployCommand(options = {}) {
1036
+ if (!hasValidCredentials()) {
1037
+ console.error('Error: Not authenticated. Run "learn-secrets login" first.');
1038
+ process.exit(1);
1039
+ }
1049
1040
  const configFile = "secrets-config.json";
1050
1041
  if (!fs5.existsSync(configFile)) {
1051
- console.error("No config found.");
1052
- return;
1042
+ console.error("Error: Configuration file not found.");
1043
+ console.error('Run "learn-secrets init" first to create config.');
1044
+ process.exit(1);
1053
1045
  }
1054
1046
  const config = JSON.parse(fs5.readFileSync(configFile, "utf8"));
1055
- console.log("Deploying secrets config to backend:", config);
1047
+ if (!config.project) {
1048
+ console.error("Error: No project ID in config.");
1049
+ console.error('Run "learn-secrets config --project <appid>" to set project.');
1050
+ process.exit(1);
1051
+ }
1052
+ if (!config.origins || config.origins.length === 0) {
1053
+ console.error("Error: No origins in config.");
1054
+ console.error('Run "learn-secrets config --origins domain1.com,domain2.com" to set origins.');
1055
+ process.exit(1);
1056
+ }
1057
+ console.log(`
1058
+ Deploying secrets to project: ${config.project}`);
1059
+ console.log(`Origins: ${config.origins.join(", ")}
1060
+ `);
1061
+ const envFile = options.env || ".env";
1062
+ let secrets;
1063
+ let envVars = [];
1064
+ try {
1065
+ console.log(`Reading ${envFile}...`);
1066
+ envVars = parseEnvFile(envFile);
1067
+ secrets = convertAllEnvVarsToSecrets(envVars);
1068
+ if (secrets.length === 0) {
1069
+ console.log("\nNo environment variables found in .env file.");
1070
+ console.log("Make sure your .env file is not empty.");
1071
+ process.exit(0);
1072
+ }
1073
+ console.log("\n" + summarizeDetectedSecrets(secrets));
1074
+ console.log("");
1075
+ } catch (error) {
1076
+ console.error(`Error reading ${envFile}:`, error.message);
1077
+ process.exit(1);
1078
+ }
1079
+ if (!options.yes) {
1080
+ const readline = await import("readline");
1081
+ const rl = readline.createInterface({
1082
+ input: process.stdin,
1083
+ output: process.stdout
1084
+ });
1085
+ const answer = await new Promise((resolve2) => {
1086
+ rl.question("Deploy these secrets? (y/N): ", resolve2);
1087
+ });
1088
+ rl.close();
1089
+ if (answer.toLowerCase() !== "y" && answer.toLowerCase() !== "yes") {
1090
+ console.log("Cancelled.");
1091
+ process.exit(0);
1092
+ }
1093
+ }
1094
+ console.log("\nUploading secrets...");
1095
+ const mgmt = new SecretsManagement({
1096
+ appId: config.project,
1097
+ baseUrl: options.baseUrl
1098
+ });
1099
+ try {
1100
+ const result = await mgmt.importSecrets({
1101
+ version: "1.0",
1102
+ origins: config.origins,
1103
+ secrets
1104
+ });
1105
+ console.log("\nSuccess!");
1106
+ console.log(` Created: ${result.results.created} secrets`);
1107
+ console.log(` Updated: ${result.results.updated} secrets`);
1108
+ if (result.results.failed > 0) {
1109
+ console.log(` Failed: ${result.results.failed} secrets`);
1110
+ for (const failure of result.results.details.failed) {
1111
+ console.log(` - ${failure.name}: ${failure.error}`);
1112
+ }
1113
+ }
1114
+ const dashboardUrl = options.baseUrl || "https://cloudprototype.org";
1115
+ console.log(`
1116
+ Secrets deployed! View them at: ${dashboardUrl}/dashboard/secrets/api_keys`);
1117
+ console.log("\n--- Cloudflare Worker Deployment ---");
1118
+ let cloudflareConnected = false;
1119
+ try {
1120
+ const statusResponse = await fetch(`${dashboardUrl}/api/cloudflare/status`, {
1121
+ credentials: "include"
1122
+ });
1123
+ cloudflareConnected = statusResponse.ok;
1124
+ } catch {
1125
+ cloudflareConnected = false;
1126
+ }
1127
+ if (!cloudflareConnected) {
1128
+ console.log("Cloudflare account not connected.");
1129
+ console.log("\nTo deploy Worker, we need your Cloudflare API token.");
1130
+ console.log("Required permissions:");
1131
+ console.log(" - Workers Scripts: Edit");
1132
+ console.log(" - Workers Routes: Edit");
1133
+ console.log(" - Account Settings: Read");
1134
+ console.log("\nCreate token at: https://dash.cloudflare.com/profile/api-tokens");
1135
+ const readline = await import("readline");
1136
+ const rl = readline.createInterface({
1137
+ input: process.stdin,
1138
+ output: process.stdout
1139
+ });
1140
+ const apiToken = await new Promise((resolve2) => {
1141
+ rl.question("\nCloudflare API Token (or press Enter to skip): ", (answer) => {
1142
+ rl.close();
1143
+ resolve2(answer.trim());
1144
+ });
1145
+ });
1146
+ if (apiToken) {
1147
+ console.log("\nValidating token...");
1148
+ const result2 = await connectCloudflare(apiToken, options.baseUrl);
1149
+ if (result2.success) {
1150
+ console.log("Cloudflare account connected");
1151
+ console.log(` Account: ${result2.accountName}`);
1152
+ cloudflareConnected = true;
1153
+ } else {
1154
+ console.log(`
1155
+ Error: Failed to connect Cloudflare: ${result2.error}`);
1156
+ console.log("Worker deployment skipped.");
1157
+ }
1158
+ } else {
1159
+ console.log("\nSkipped Cloudflare connection. Worker deployment skipped.");
1160
+ }
1161
+ } else {
1162
+ console.log("Cloudflare account is connected.");
1163
+ }
1164
+ if (cloudflareConnected) {
1165
+ const readline = await import("readline");
1166
+ const rl = readline.createInterface({
1167
+ input: process.stdin,
1168
+ output: process.stdout
1169
+ });
1170
+ const shouldProvision = await new Promise((resolve2) => {
1171
+ rl.question("\nDeploy/update Worker to Cloudflare? (y/N): ", (answer) => {
1172
+ rl.close();
1173
+ resolve2(answer.trim());
1174
+ });
1175
+ });
1176
+ if (shouldProvision.toLowerCase() === "y" || shouldProvision.toLowerCase() === "yes") {
1177
+ console.log("\nDeploying Worker to Cloudflare...");
1178
+ const workerName = `learn-secrets-${config.project.toLowerCase()}`;
1179
+ const provisionResult = await provisionWorker(
1180
+ workerName,
1181
+ config.origins,
1182
+ envVars,
1183
+ options.baseUrl
1184
+ );
1185
+ if (provisionResult.success) {
1186
+ console.log("\nWorker deployed successfully");
1187
+ console.log(` Name: ${provisionResult.workerName}`);
1188
+ console.log(` URL: ${provisionResult.workerUrl}`);
1189
+ config.workerUrl = provisionResult.workerUrl;
1190
+ fs5.writeFileSync(configFile, JSON.stringify(config, null, 2));
1191
+ console.log("\nNext steps:");
1192
+ console.log(` 1. Test Worker: curl ${provisionResult.workerUrl}/health`);
1193
+ console.log(` 2. Update your frontend to use Worker URL`);
1194
+ console.log(` 3. Test origin validation from your site`);
1195
+ } else {
1196
+ console.log(`
1197
+ Error: Worker deployment failed: ${provisionResult.error}`);
1198
+ }
1199
+ } else {
1200
+ console.log("\nSkipped Worker deployment.");
1201
+ }
1202
+ }
1203
+ } catch (error) {
1204
+ console.error("\nDeploy failed:", error.message);
1205
+ if (error.status === 401) {
1206
+ console.error('Your session may have expired. Try running "learn-secrets login" again.');
1207
+ }
1208
+ if (error.status === 404) {
1209
+ console.error(`Project "${config.project}" not found. Check your project ID.`);
1210
+ }
1211
+ process.exit(1);
1212
+ }
1056
1213
  }
1057
1214
 
1058
1215
  // src/cli/index.ts
1059
1216
  var program = new Command();
1060
- program.name("learn-secrets").description("CLI tool for managing API secrets in static sites").version("1.6.0");
1061
- 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) => {
1062
- await setupCommand(options);
1063
- });
1217
+ program.name("learn-secrets").description("CLI tool for managing API secrets with Cloudflare Worker deployment").version("1.8.0");
1064
1218
  program.command("login").description("Authenticate with Learn Secrets via browser").option("--base-url <url>", "Custom base URL for API").action(async (options) => {
1065
1219
  await loginCommand(options);
1066
1220
  });
@@ -1070,8 +1224,8 @@ program.command("init").description("Initialize secrets for a new site").require
1070
1224
  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) => {
1071
1225
  await configCommand(options);
1072
1226
  });
1073
- program.command("deploy").description("Deploy secrets config live").action(async () => {
1074
- await deployCommand();
1227
+ 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) => {
1228
+ await deployCommand(options);
1075
1229
  });
1076
1230
  program.parse(process.argv);
1077
1231
  if (!process.argv.slice(2).length) {
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.8",
4
- "description": "Secure API proxy SDK for static sites with domain-scoped tokens",
3
+ "version": "1.8.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",