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.
- package/dist/cli/index.mjs +104 -32
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.global.js +2 -2
- package/dist/index.mjs +2 -2
- package/package.json +1 -1
package/dist/cli/index.mjs
CHANGED
|
@@ -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
|
|
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("\
|
|
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
|
|
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
|
-
|
|
631
|
-
|
|
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: ${
|
|
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:
|
|
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(
|
|
709
|
-
const
|
|
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:
|
|
783
|
+
project: projectId,
|
|
712
784
|
origins
|
|
713
785
|
};
|
|
714
|
-
|
|
786
|
+
fs3.writeFileSync(configFile, JSON.stringify(config, null, 2));
|
|
715
787
|
console.log(`
|
|
716
|
-
|
|
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://
|
|
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
|
|
925
|
-
|
|
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
|
|
1008
|
+
import fs4 from "fs";
|
|
937
1009
|
async function configCommand(options) {
|
|
938
1010
|
const configFile = "secrets-config.json";
|
|
939
|
-
if (!
|
|
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(
|
|
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
|
-
|
|
1020
|
+
fs4.writeFileSync(configFile, JSON.stringify(config, null, 2));
|
|
949
1021
|
}
|
|
950
1022
|
|
|
951
1023
|
// src/cli/commands/deploy.ts
|
|
952
|
-
import
|
|
1024
|
+
import fs5 from "fs";
|
|
953
1025
|
async function deployCommand() {
|
|
954
1026
|
const configFile = "secrets-config.json";
|
|
955
|
-
if (!
|
|
1027
|
+
if (!fs5.existsSync(configFile)) {
|
|
956
1028
|
console.error("No config found.");
|
|
957
1029
|
return;
|
|
958
1030
|
}
|
|
959
|
-
const config = JSON.parse(
|
|
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)").
|
|
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
package/dist/index.d.ts
CHANGED
package/dist/index.global.js
CHANGED
|
@@ -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:()=>
|
|
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),
|
|
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://
|
|
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,
|
|
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};
|