learn-secrets-sdk 1.6.4 → 1.6.6
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 +97 -40
- package/dist/index.global.js +8 -8
- package/dist/index.mjs +9 -9
- 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) {
|
|
@@ -270,7 +273,7 @@ Log in on ${this.baseUrl}`);
|
|
|
270
273
|
});
|
|
271
274
|
console.log("Opening browser...\n");
|
|
272
275
|
await this.openBrowser(deviceData.verification_uri_complete);
|
|
273
|
-
let
|
|
276
|
+
let tokenData = null;
|
|
274
277
|
const interval = deviceData.interval * 1e3;
|
|
275
278
|
const maxAttempts = Math.ceil(deviceData.expires_in / deviceData.interval);
|
|
276
279
|
let attempts = 0;
|
|
@@ -291,30 +294,14 @@ Log in on ${this.baseUrl}`);
|
|
|
291
294
|
continue;
|
|
292
295
|
}
|
|
293
296
|
if (tokenResponse.ok) {
|
|
294
|
-
|
|
297
|
+
tokenData = await tokenResponse.json();
|
|
295
298
|
break;
|
|
296
299
|
}
|
|
297
300
|
}
|
|
298
|
-
if (!
|
|
301
|
+
if (!tokenData) {
|
|
299
302
|
throw new SecretsSDKError("Device authorization timeout", 408);
|
|
300
303
|
}
|
|
301
304
|
console.log("\n\nDevice authorized! Retrieving access token...");
|
|
302
|
-
const finalTokenResponse = await fetch(`${this.baseUrl}/api/auth/cli/token`, {
|
|
303
|
-
method: "POST",
|
|
304
|
-
headers: {
|
|
305
|
-
"Content-Type": "application/json"
|
|
306
|
-
},
|
|
307
|
-
body: JSON.stringify({
|
|
308
|
-
device_code: deviceData.device_code
|
|
309
|
-
})
|
|
310
|
-
});
|
|
311
|
-
if (!finalTokenResponse.ok) {
|
|
312
|
-
throw new SecretsSDKError(
|
|
313
|
-
"Failed to retrieve access token",
|
|
314
|
-
finalTokenResponse.status
|
|
315
|
-
);
|
|
316
|
-
}
|
|
317
|
-
const tokenData = await finalTokenResponse.json();
|
|
318
305
|
writeSDKCredentials(SDK_NAME, {
|
|
319
306
|
access_token: tokenData.access_token,
|
|
320
307
|
refresh_token: null,
|
|
@@ -457,17 +444,76 @@ Log in on ${this.baseUrl}`);
|
|
|
457
444
|
// src/cli/commands/login.ts
|
|
458
445
|
async function loginCommand(options = {}) {
|
|
459
446
|
console.log("Authenticating with Learn Secrets...\n");
|
|
447
|
+
const baseUrl = options.baseUrl || "https://cloudprototype.org";
|
|
460
448
|
const mgmt = new SecretsManagement({
|
|
461
449
|
appId: "temp",
|
|
462
450
|
// Will be set after login
|
|
463
|
-
baseUrl
|
|
451
|
+
baseUrl
|
|
464
452
|
});
|
|
465
453
|
try {
|
|
466
454
|
const result = await mgmt.login();
|
|
467
455
|
console.log(`
|
|
468
456
|
Logged in as: ${result.user}`);
|
|
469
457
|
console.log("Credentials saved to ~/.learn/credentials.json");
|
|
470
|
-
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");
|
|
471
517
|
} catch (error) {
|
|
472
518
|
console.error("\nLogin failed:", error.message);
|
|
473
519
|
process.exit(1);
|
|
@@ -475,7 +521,7 @@ Logged in as: ${result.user}`);
|
|
|
475
521
|
}
|
|
476
522
|
|
|
477
523
|
// src/cli/commands/init.ts
|
|
478
|
-
import
|
|
524
|
+
import fs3 from "fs";
|
|
479
525
|
|
|
480
526
|
// src/cli/utils/env-parser.ts
|
|
481
527
|
import { readFileSync as readFileSync2, existsSync as existsSync2 } from "fs";
|
|
@@ -643,8 +689,20 @@ async function initCommand(options) {
|
|
|
643
689
|
console.error("Example: learn-secrets init --origins mysite.com,localhost");
|
|
644
690
|
process.exit(1);
|
|
645
691
|
}
|
|
646
|
-
|
|
647
|
-
|
|
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)');
|
|
648
706
|
console.error("Example: learn-secrets init --project my-app-id --origins mysite.com");
|
|
649
707
|
process.exit(1);
|
|
650
708
|
}
|
|
@@ -654,7 +712,7 @@ async function initCommand(options) {
|
|
|
654
712
|
process.exit(1);
|
|
655
713
|
}
|
|
656
714
|
console.log(`
|
|
657
|
-
Onboarding site for project: ${
|
|
715
|
+
Onboarding site for project: ${projectId}`);
|
|
658
716
|
console.log(`Origins: ${origins.join(", ")}
|
|
659
717
|
`);
|
|
660
718
|
const envFile = options.env || ".env";
|
|
@@ -693,7 +751,7 @@ Onboarding site for project: ${options.project}`);
|
|
|
693
751
|
}
|
|
694
752
|
console.log("\nUploading secrets...");
|
|
695
753
|
const mgmt = new SecretsManagement({
|
|
696
|
-
appId:
|
|
754
|
+
appId: projectId,
|
|
697
755
|
baseUrl: options.baseUrl
|
|
698
756
|
});
|
|
699
757
|
try {
|
|
@@ -722,14 +780,13 @@ Onboarding site for project: ${options.project}`);
|
|
|
722
780
|
}
|
|
723
781
|
console.log("\nYour site is ready!");
|
|
724
782
|
console.log(`Visit: https://ctklearn.carsontkempf.workers.dev to manage secrets`);
|
|
725
|
-
const configFile = "secrets-config.json";
|
|
726
783
|
const config = {
|
|
727
|
-
project:
|
|
784
|
+
project: projectId,
|
|
728
785
|
origins
|
|
729
786
|
};
|
|
730
|
-
|
|
787
|
+
fs3.writeFileSync(configFile, JSON.stringify(config, null, 2));
|
|
731
788
|
console.log(`
|
|
732
|
-
|
|
789
|
+
Updated local config: ${configFile}`);
|
|
733
790
|
} catch (error) {
|
|
734
791
|
console.error("\nUpload failed:", error.message);
|
|
735
792
|
if (error.status === 401) {
|
|
@@ -937,8 +994,8 @@ LEARN_ORIGIN=${options.origin}
|
|
|
937
994
|
# Token prefix: ${sdkToken.slice(-4)}
|
|
938
995
|
`;
|
|
939
996
|
try {
|
|
940
|
-
const
|
|
941
|
-
|
|
997
|
+
const fs6 = await import("fs");
|
|
998
|
+
fs6.writeFileSync(configPath, configContent, "utf-8");
|
|
942
999
|
console.log(`
|
|
943
1000
|
\u{1F4BE} Configuration saved to: ${configPath}`);
|
|
944
1001
|
} catch (error) {
|
|
@@ -949,30 +1006,30 @@ LEARN_ORIGIN=${options.origin}
|
|
|
949
1006
|
}
|
|
950
1007
|
|
|
951
1008
|
// src/cli/commands/config.ts
|
|
952
|
-
import
|
|
1009
|
+
import fs4 from "fs";
|
|
953
1010
|
async function configCommand(options) {
|
|
954
1011
|
const configFile = "secrets-config.json";
|
|
955
|
-
if (!
|
|
1012
|
+
if (!fs4.existsSync(configFile)) {
|
|
956
1013
|
console.error("Run learn-secrets init first to generate config.");
|
|
957
1014
|
return;
|
|
958
1015
|
}
|
|
959
|
-
const config = JSON.parse(
|
|
1016
|
+
const config = JSON.parse(fs4.readFileSync(configFile, "utf8"));
|
|
960
1017
|
if (options.origins) {
|
|
961
1018
|
config.origins = options.origins.split(",").map((o) => o.trim());
|
|
962
1019
|
console.log("Updated origins:", config.origins);
|
|
963
1020
|
}
|
|
964
|
-
|
|
1021
|
+
fs4.writeFileSync(configFile, JSON.stringify(config, null, 2));
|
|
965
1022
|
}
|
|
966
1023
|
|
|
967
1024
|
// src/cli/commands/deploy.ts
|
|
968
|
-
import
|
|
1025
|
+
import fs5 from "fs";
|
|
969
1026
|
async function deployCommand() {
|
|
970
1027
|
const configFile = "secrets-config.json";
|
|
971
|
-
if (!
|
|
1028
|
+
if (!fs5.existsSync(configFile)) {
|
|
972
1029
|
console.error("No config found.");
|
|
973
1030
|
return;
|
|
974
1031
|
}
|
|
975
|
-
const config = JSON.parse(
|
|
1032
|
+
const config = JSON.parse(fs5.readFileSync(configFile, "utf8"));
|
|
976
1033
|
console.log("Deploying secrets config to backend:", config);
|
|
977
1034
|
}
|
|
978
1035
|
|
|
@@ -985,7 +1042,7 @@ program.command("setup").description("Complete setup: create project, generate t
|
|
|
985
1042
|
program.command("login").description("Authenticate with Learn Secrets via browser").option("--base-url <url>", "Custom base URL for API").action(async (options) => {
|
|
986
1043
|
await loginCommand(options);
|
|
987
1044
|
});
|
|
988
|
-
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)").
|
|
1045
|
+
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) => {
|
|
989
1046
|
await initCommand(options);
|
|
990
1047
|
});
|
|
991
1048
|
program.command("config").description("Configure allowed origins").option("--origins <origins>", "Comma separated origins").action(async (options) => {
|
package/dist/index.global.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
"use strict";var SecretsSDK=(()=>{var
|
|
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
|
|
3
|
-
`);let r=await
|
|
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(`
|
|
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(`
|
|
4
4
|
Log in on ${this.baseUrl}`),console.log("Login at:"),console.log(i.verification_uri_complete),console.log(`
|
|
5
|
-
Press ENTER to open in the browser...`);let
|
|
6
|
-
`),await this.openBrowser(i.verification_uri_complete);let
|
|
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(`
|
|
7
7
|
|
|
8
|
-
Device authorized! Retrieving access token...`)
|
|
9
|
-
Authentication successful!`),console.log(`Logged in as: ${
|
|
10
|
-
`),{success:!0,user:
|
|
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
|
+
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);})();
|
package/dist/index.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import{a as K,b as
|
|
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
|
|
3
|
-
`);let t=await
|
|
4
|
-
Log in on ${this.baseUrl}`),console.log("Login at:"),console.log(
|
|
5
|
-
Press ENTER to open in the browser...`);let
|
|
6
|
-
`),await this.openBrowser(
|
|
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(`
|
|
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
|
+
`);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
|
+
Log in on ${this.baseUrl}`),console.log("Login at:"),console.log(c.verification_uri_complete),console.log(`
|
|
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(`
|
|
7
7
|
|
|
8
|
-
Device authorized! Retrieving access token...`)
|
|
9
|
-
Authentication successful!`),console.log(`Logged in as: ${
|
|
10
|
-
`),{success:!0,user:
|
|
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
|
+
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};
|