learn-secrets-sdk 1.6.5 → 1.6.7

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.
@@ -4,6 +4,9 @@ import "./chunk-Y6FXYEAI.mjs";
4
4
  // src/cli/index.ts
5
5
  import { Command } from "commander";
6
6
 
7
+ // src/cli/commands/login.ts
8
+ import fs2 from "fs";
9
+
7
10
  // src/types.ts
8
11
  var SecretsSDKError = class extends Error {
9
12
  constructor(message, status, response) {
@@ -441,17 +444,76 @@ Log in on ${this.baseUrl}`);
441
444
  // src/cli/commands/login.ts
442
445
  async function loginCommand(options = {}) {
443
446
  console.log("Authenticating with Learn Secrets...\n");
447
+ const baseUrl = options.baseUrl || "https://cloudprototype.org";
444
448
  const mgmt = new SecretsManagement({
445
449
  appId: "temp",
446
450
  // Will be set after login
447
- baseUrl: options.baseUrl
451
+ baseUrl
448
452
  });
449
453
  try {
450
454
  const result = await mgmt.login();
451
455
  console.log(`
452
456
  Logged in as: ${result.user}`);
453
457
  console.log("Credentials saved to ~/.learn/credentials.json");
454
- console.log("\nYou can now run: learn-secrets init --origins yourdomain.com");
458
+ console.log("\nFetching your projects...");
459
+ const creds = readCredentials();
460
+ if (!creds) {
461
+ throw new Error("Failed to read credentials");
462
+ }
463
+ const projectsResponse = await fetch(`${baseUrl}/api/projects`, {
464
+ headers: {
465
+ Authorization: `Bearer ${creds.access_token}`
466
+ }
467
+ });
468
+ if (!projectsResponse.ok) {
469
+ throw new Error("Failed to fetch projects");
470
+ }
471
+ const { projects } = await projectsResponse.json();
472
+ if (projects.length === 0) {
473
+ console.log("\nNo projects found.");
474
+ console.log("Create a project at: https://cloudprototype.org/dashboard");
475
+ console.log("\nAfter creating a project, run: learn-secrets login");
476
+ return;
477
+ }
478
+ let selectedProject;
479
+ if (projects.length === 1) {
480
+ selectedProject = projects[0];
481
+ console.log(`
482
+ Auto-selected project: ${selectedProject.name} (${selectedProject.appid})`);
483
+ } else {
484
+ console.log("\nYou have multiple projects:");
485
+ projects.forEach((p, idx) => {
486
+ console.log(` ${idx + 1}. ${p.name} (${p.appid})`);
487
+ });
488
+ const readline = await import("readline");
489
+ const rl = readline.createInterface({
490
+ input: process.stdin,
491
+ output: process.stdout
492
+ });
493
+ const answer = await new Promise((resolve2) => {
494
+ rl.question("\nSelect a project (1-" + projects.length + "): ", resolve2);
495
+ });
496
+ rl.close();
497
+ const selection = parseInt(answer.trim());
498
+ if (isNaN(selection) || selection < 1 || selection > projects.length) {
499
+ console.error("Invalid selection");
500
+ process.exit(1);
501
+ }
502
+ selectedProject = projects[selection - 1];
503
+ console.log(`
504
+ Selected: ${selectedProject.name}`);
505
+ }
506
+ const configFile = "secrets-config.json";
507
+ const config = {
508
+ project: selectedProject.appid,
509
+ origins: []
510
+ };
511
+ fs2.writeFileSync(configFile, JSON.stringify(config, null, 2));
512
+ console.log(`
513
+ Project configuration saved to: ${configFile}`);
514
+ console.log("\nNext steps:");
515
+ console.log(" 1. Run: learn-secrets init --origins yourdomain.com --env .netlify_env");
516
+ console.log(" 2. This will upload your secrets and generate an SDK token");
455
517
  } catch (error) {
456
518
  console.error("\nLogin failed:", error.message);
457
519
  process.exit(1);
@@ -459,7 +521,7 @@ Logged in as: ${result.user}`);
459
521
  }
460
522
 
461
523
  // src/cli/commands/init.ts
462
- import fs2 from "fs";
524
+ import fs3 from "fs";
463
525
 
464
526
  // src/cli/utils/env-parser.ts
465
527
  import { readFileSync as readFileSync2, existsSync as existsSync2 } from "fs";
@@ -627,8 +689,20 @@ async function initCommand(options) {
627
689
  console.error("Example: learn-secrets init --origins mysite.com,localhost");
628
690
  process.exit(1);
629
691
  }
630
- if (!options.project) {
631
- console.error("Error: --project is required");
692
+ let projectId = options.project;
693
+ const configFile = "secrets-config.json";
694
+ if (!projectId && fs3.existsSync(configFile)) {
695
+ try {
696
+ const config = JSON.parse(fs3.readFileSync(configFile, "utf8"));
697
+ projectId = config.project;
698
+ if (projectId) {
699
+ console.log(`Using project from config: ${projectId}`);
700
+ }
701
+ } catch (err) {
702
+ }
703
+ }
704
+ if (!projectId) {
705
+ console.error('Error: --project is required (or run "learn-secrets login" first)');
632
706
  console.error("Example: learn-secrets init --project my-app-id --origins mysite.com");
633
707
  process.exit(1);
634
708
  }
@@ -638,7 +712,7 @@ async function initCommand(options) {
638
712
  process.exit(1);
639
713
  }
640
714
  console.log(`
641
- Onboarding site for project: ${options.project}`);
715
+ Onboarding site for project: ${projectId}`);
642
716
  console.log(`Origins: ${origins.join(", ")}
643
717
  `);
644
718
  const envFile = options.env || ".env";
@@ -677,7 +751,7 @@ Onboarding site for project: ${options.project}`);
677
751
  }
678
752
  console.log("\nUploading secrets...");
679
753
  const mgmt = new SecretsManagement({
680
- appId: options.project,
754
+ appId: projectId,
681
755
  baseUrl: options.baseUrl
682
756
  });
683
757
  try {
@@ -695,25 +769,23 @@ Onboarding site for project: ${options.project}`);
695
769
  console.log(` - ${failure.name}: ${failure.error}`);
696
770
  }
697
771
  }
698
- if (result.sdkToken) {
699
- console.log("\nSDK Token created:");
700
- console.log(` ${result.sdkToken.token}`);
701
- console.log("\n IMPORTANT: Save this token - it will not be shown again.");
702
- console.log(" Add it to your static site code:");
703
- console.log("");
704
- console.log(" const sdk = new SecretsSDK();");
705
- console.log(" // No appId or token needed - uses origin-based auth!");
706
- }
707
772
  console.log("\nYour site is ready!");
708
- console.log(`Visit: https://ctklearn.carsontkempf.workers.dev to manage secrets`);
709
- const configFile = "secrets-config.json";
773
+ console.log("\nAdd to your static site code:");
774
+ console.log(" const sdk = new SecretsSDK();");
775
+ console.log(" // Uses origin-based auth - no token needed!");
776
+ const dashboardUrl = options.baseUrl || "https://cloudprototype.org";
777
+ console.log(`
778
+ Next steps:`);
779
+ console.log(` 1. Run: learn-secrets deploy`);
780
+ console.log(` (Push these secrets to dashboard at ${dashboardUrl}/dashboard/secrets/api_keys)`);
781
+ console.log(` 2. Visit: ${dashboardUrl}/dashboard to manage secrets`);
710
782
  const config = {
711
- project: options.project,
783
+ project: projectId,
712
784
  origins
713
785
  };
714
- fs2.writeFileSync(configFile, JSON.stringify(config, null, 2));
786
+ fs3.writeFileSync(configFile, JSON.stringify(config, null, 2));
715
787
  console.log(`
716
- Created local config: ${configFile}`);
788
+ Updated local config: ${configFile}`);
717
789
  } catch (error) {
718
790
  console.error("\nUpload failed:", error.message);
719
791
  if (error.status === 401) {
@@ -728,7 +800,7 @@ Created local config: ${configFile}`);
728
800
 
729
801
  // src/cli/commands/setup.ts
730
802
  async function setupCommand(options) {
731
- const baseUrl = options.baseUrl || "https://ctklearn.carsontkempf.workers.dev";
803
+ const baseUrl = options.baseUrl || "https://cloudprototype.org";
732
804
  const envFile = options.env || ".env";
733
805
  const projectName = options.projectName || `Project - ${options.origin}`;
734
806
  console.log("\n\u{1F680} Learn Secrets Setup\n");
@@ -921,8 +993,8 @@ LEARN_ORIGIN=${options.origin}
921
993
  # Token prefix: ${sdkToken.slice(-4)}
922
994
  `;
923
995
  try {
924
- const fs5 = await import("fs");
925
- fs5.writeFileSync(configPath, configContent, "utf-8");
996
+ const fs6 = await import("fs");
997
+ fs6.writeFileSync(configPath, configContent, "utf-8");
926
998
  console.log(`
927
999
  \u{1F4BE} Configuration saved to: ${configPath}`);
928
1000
  } catch (error) {
@@ -933,30 +1005,30 @@ LEARN_ORIGIN=${options.origin}
933
1005
  }
934
1006
 
935
1007
  // src/cli/commands/config.ts
936
- import fs3 from "fs";
1008
+ import fs4 from "fs";
937
1009
  async function configCommand(options) {
938
1010
  const configFile = "secrets-config.json";
939
- if (!fs3.existsSync(configFile)) {
1011
+ if (!fs4.existsSync(configFile)) {
940
1012
  console.error("Run learn-secrets init first to generate config.");
941
1013
  return;
942
1014
  }
943
- const config = JSON.parse(fs3.readFileSync(configFile, "utf8"));
1015
+ const config = JSON.parse(fs4.readFileSync(configFile, "utf8"));
944
1016
  if (options.origins) {
945
1017
  config.origins = options.origins.split(",").map((o) => o.trim());
946
1018
  console.log("Updated origins:", config.origins);
947
1019
  }
948
- fs3.writeFileSync(configFile, JSON.stringify(config, null, 2));
1020
+ fs4.writeFileSync(configFile, JSON.stringify(config, null, 2));
949
1021
  }
950
1022
 
951
1023
  // src/cli/commands/deploy.ts
952
- import fs4 from "fs";
1024
+ import fs5 from "fs";
953
1025
  async function deployCommand() {
954
1026
  const configFile = "secrets-config.json";
955
- if (!fs4.existsSync(configFile)) {
1027
+ if (!fs5.existsSync(configFile)) {
956
1028
  console.error("No config found.");
957
1029
  return;
958
1030
  }
959
- const config = JSON.parse(fs4.readFileSync(configFile, "utf8"));
1031
+ const config = JSON.parse(fs5.readFileSync(configFile, "utf8"));
960
1032
  console.log("Deploying secrets config to backend:", config);
961
1033
  }
962
1034
 
@@ -969,7 +1041,7 @@ program.command("setup").description("Complete setup: create project, generate t
969
1041
  program.command("login").description("Authenticate with Learn Secrets via browser").option("--base-url <url>", "Custom base URL for API").action(async (options) => {
970
1042
  await loginCommand(options);
971
1043
  });
972
- 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)").requiredOption("-p, --project <appid>", "Project app ID from 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) => {
1044
+ 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) => {
973
1045
  await initCommand(options);
974
1046
  });
975
1047
  program.command("config").description("Configure allowed origins").option("--origins <origins>", "Comma separated origins").action(async (options) => {
package/dist/index.d.mts CHANGED
@@ -12,7 +12,7 @@ interface SecretsSDKOptions {
12
12
  sessionToken?: string;
13
13
  /**
14
14
  * Base URL of the proxy server
15
- * Default: https://ctklearn.carsontkempf.workers.dev
15
+ * Default: https://cloudprototype.org
16
16
  */
17
17
  baseUrl?: string;
18
18
  timeout?: number;
package/dist/index.d.ts CHANGED
@@ -12,7 +12,7 @@ interface SecretsSDKOptions {
12
12
  sessionToken?: string;
13
13
  /**
14
14
  * Base URL of the proxy server
15
- * Default: https://ctklearn.carsontkempf.workers.dev
15
+ * Default: https://cloudprototype.org
16
16
  */
17
17
  baseUrl?: string;
18
18
  timeout?: number;
@@ -1,9 +1,9 @@
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:()=>k,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://ctklearn.carsontkempf.workers.dev",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 C=this.rateLimitInfo?this.rateLimitInfo.reset-Math.floor(Date.now()/1e3):60,u=new x(h,C,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 k(){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=k();return e&&e[t]||null}function P(t,e){let r=k()||{};r[t]=e,R(r)}function J(t){let e=k();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")),I=(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 I(`ioreg -l | grep IOPlatformSerialNumber | awk '{print $4}' | sed 's/"//g'`),{stdout:e}=await I("hostname"),r=t.trim(),s=e.trim();if(r&&s)return`${r}-${s}`;let{stdout:n}=await I(`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 I("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 I("wmic csproduct get uuid"),r=t.split(`
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
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
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(`
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),C=0;for(;C<h;){await this.sleep(w),C++;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),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(`
7
7
 
8
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(`
9
9
  Authentication successful!`),console.log(`Logged in as: ${c.user_email||c.user_id}`),console.log(`This machine is now permanently authorized.
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
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://ctklearn.carsontkempf.workers.dev",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 k from"path";import*as C from"os";function T(){let s=C.homedir(),e=k.join(s,".learn");return k.join(e,"credentials.json")}function j(){let s=C.homedir(),e=k.join(s,".learn");d.existsSync(e)||d.mkdirSync(e,{recursive:!0,mode:448})}function I(){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=I();return e&&e[s]||null}function D(s,e){let t=I()||{};t[s]=e,_(t)}function q(s){let e=I();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(`
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
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})...
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(`
@@ -7,4 +7,4 @@ Press ENTER to open in the browser...`);let u=(await import("readline")).createI
7
7
 
8
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(`
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,I 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 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};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "learn-secrets-sdk",
3
- "version": "1.6.5",
3
+ "version": "1.6.7",
4
4
  "description": "Secure API proxy SDK for static sites with domain-scoped tokens",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.esm.js",