learn-secrets-sdk 1.4.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +192 -72
- package/bin/learn-secrets.js +5 -2
- package/dist/chunk-AS6G7JYX.mjs +1 -0
- package/dist/cli/chunk-Y6FXYEAI.mjs +10 -0
- package/dist/cli/env-resolver-4ASC2IMR.mjs +66 -0
- package/dist/cli/{index.d.ts → index.d.mts} +0 -1
- package/dist/cli/index.mjs +1020 -0
- package/dist/env-resolver-Y4SFGOKB.mjs +1 -0
- package/dist/index.d.mts +432 -0
- package/dist/index.d.ts +432 -5
- package/dist/index.global.js +11 -0
- package/dist/index.mjs +11 -0
- package/package.json +12 -5
- package/dist/cli/commands/init.d.ts +0 -11
- package/dist/cli/commands/init.js +0 -113
- package/dist/cli/commands/login.d.ts +0 -7
- package/dist/cli/commands/login.js +0 -22
- package/dist/cli/index.js +0 -35
- package/dist/cli/utils/env-parser.d.ts +0 -19
- package/dist/cli/utils/env-parser.js +0 -47
- package/dist/cli/utils/key-detector.d.ts +0 -33
- package/dist/cli/utils/key-detector.js +0 -153
- package/dist/client.d.ts +0 -65
- package/dist/client.js +0 -172
- package/dist/credentials.d.ts +0 -24
- package/dist/credentials.js +0 -86
- package/dist/env-resolver.d.ts +0 -23
- package/dist/env-resolver.js +0 -73
- package/dist/index.js +0 -4
- package/dist/management.d.ts +0 -98
- package/dist/management.js +0 -214
- package/dist/types.d.ts +0 -123
- package/dist/types.js +0 -29
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,432 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
interface SecretsSDKOptions {
|
|
2
|
+
/**
|
|
3
|
+
* App ID from dashboard (optional in zero-config mode)
|
|
4
|
+
* If omitted, SDK uses origin-based authentication
|
|
5
|
+
*/
|
|
6
|
+
appId?: string;
|
|
7
|
+
/**
|
|
8
|
+
* SDK token (optional in zero-config mode)
|
|
9
|
+
* If omitted, SDK uses origin-based authentication
|
|
10
|
+
*/
|
|
11
|
+
token?: string;
|
|
12
|
+
sessionToken?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Base URL of the proxy server
|
|
15
|
+
* Default: https://ctklearn.carsontkempf.workers.dev
|
|
16
|
+
*/
|
|
17
|
+
baseUrl?: string;
|
|
18
|
+
timeout?: number;
|
|
19
|
+
retryOn429?: boolean;
|
|
20
|
+
}
|
|
21
|
+
interface ProxyRequest {
|
|
22
|
+
keyName: string;
|
|
23
|
+
endpoint: string;
|
|
24
|
+
method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
|
|
25
|
+
body?: any;
|
|
26
|
+
headers?: Record<string, string>;
|
|
27
|
+
}
|
|
28
|
+
interface ProxyResponse<T = any> {
|
|
29
|
+
success: boolean;
|
|
30
|
+
status: number;
|
|
31
|
+
data: T;
|
|
32
|
+
}
|
|
33
|
+
interface RateLimitInfo {
|
|
34
|
+
limit: number;
|
|
35
|
+
remaining: number;
|
|
36
|
+
reset: number;
|
|
37
|
+
}
|
|
38
|
+
declare class SecretsSDKError extends Error {
|
|
39
|
+
status: number;
|
|
40
|
+
response?: any | undefined;
|
|
41
|
+
constructor(message: string, status: number, response?: any | undefined);
|
|
42
|
+
}
|
|
43
|
+
declare class OriginMismatchError extends SecretsSDKError {
|
|
44
|
+
constructor(message?: string);
|
|
45
|
+
}
|
|
46
|
+
declare class RateLimitError extends SecretsSDKError {
|
|
47
|
+
retryAfter: number;
|
|
48
|
+
remaining: number;
|
|
49
|
+
limit: number;
|
|
50
|
+
constructor(message?: string, retryAfter?: number, remaining?: number, limit?: number);
|
|
51
|
+
}
|
|
52
|
+
declare class InvalidTokenError extends SecretsSDKError {
|
|
53
|
+
constructor(message?: string);
|
|
54
|
+
}
|
|
55
|
+
interface SecretsManagementOptions {
|
|
56
|
+
appId: string;
|
|
57
|
+
baseUrl?: string;
|
|
58
|
+
timeout?: number;
|
|
59
|
+
}
|
|
60
|
+
interface SecretConfig {
|
|
61
|
+
name: string;
|
|
62
|
+
provider: string;
|
|
63
|
+
api_key: string;
|
|
64
|
+
base_url?: string;
|
|
65
|
+
auth_header?: string;
|
|
66
|
+
auth_prefix?: string;
|
|
67
|
+
}
|
|
68
|
+
interface MaskedSecret {
|
|
69
|
+
id: string;
|
|
70
|
+
name: string;
|
|
71
|
+
provider: string;
|
|
72
|
+
api_key: string;
|
|
73
|
+
base_url?: string;
|
|
74
|
+
auth_header?: string;
|
|
75
|
+
auth_prefix?: string;
|
|
76
|
+
created?: string;
|
|
77
|
+
updated?: string;
|
|
78
|
+
}
|
|
79
|
+
interface SyncOptions {
|
|
80
|
+
deleteMissing?: boolean;
|
|
81
|
+
dryRun?: boolean;
|
|
82
|
+
}
|
|
83
|
+
interface SyncDiffItem {
|
|
84
|
+
name: string;
|
|
85
|
+
provider?: string;
|
|
86
|
+
changes?: string[];
|
|
87
|
+
}
|
|
88
|
+
interface SyncResult {
|
|
89
|
+
success: boolean;
|
|
90
|
+
diff: {
|
|
91
|
+
created: SyncDiffItem[];
|
|
92
|
+
updated: SyncDiffItem[];
|
|
93
|
+
deleted: SyncDiffItem[];
|
|
94
|
+
unchanged: SyncDiffItem[];
|
|
95
|
+
};
|
|
96
|
+
summary: {
|
|
97
|
+
created: number;
|
|
98
|
+
updated: number;
|
|
99
|
+
deleted: number;
|
|
100
|
+
unchanged: number;
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
interface CLICredentials {
|
|
104
|
+
access_token: string;
|
|
105
|
+
refresh_token: string | null;
|
|
106
|
+
expires_at: string | null;
|
|
107
|
+
user_id: string;
|
|
108
|
+
machine_id?: string;
|
|
109
|
+
sdk_name?: string;
|
|
110
|
+
authorized_at?: string;
|
|
111
|
+
}
|
|
112
|
+
interface SDKCredentialsStore {
|
|
113
|
+
'learn-secrets-sdk'?: CLICredentials;
|
|
114
|
+
'learn-auth-sdk'?: CLICredentials;
|
|
115
|
+
}
|
|
116
|
+
type SDKName = 'learn-secrets-sdk' | 'learn-auth-sdk';
|
|
117
|
+
|
|
118
|
+
declare class SecretsSDK {
|
|
119
|
+
private appId;
|
|
120
|
+
private token;
|
|
121
|
+
private baseUrl;
|
|
122
|
+
private timeout;
|
|
123
|
+
private retryOn429;
|
|
124
|
+
private rateLimitInfo;
|
|
125
|
+
private zeroConfigMode;
|
|
126
|
+
/**
|
|
127
|
+
* Create a new SecretsSDK instance
|
|
128
|
+
*
|
|
129
|
+
* Zero-config mode (recommended for static sites):
|
|
130
|
+
* const sdk = new SecretsSDK();
|
|
131
|
+
* // Origin header is used for authentication
|
|
132
|
+
*
|
|
133
|
+
* Token mode (for backward compatibility):
|
|
134
|
+
* const sdk = new SecretsSDK({ appId: '...', token: 'sk_live_...' });
|
|
135
|
+
*/
|
|
136
|
+
constructor(options?: SecretsSDKOptions);
|
|
137
|
+
/**
|
|
138
|
+
* Get current rate limit information
|
|
139
|
+
*/
|
|
140
|
+
getUsage(): RateLimitInfo | null;
|
|
141
|
+
/**
|
|
142
|
+
* Parse rate limit headers from response
|
|
143
|
+
*/
|
|
144
|
+
private parseRateLimitHeaders;
|
|
145
|
+
/**
|
|
146
|
+
* Sleep utility for retry backoff
|
|
147
|
+
*/
|
|
148
|
+
private sleep;
|
|
149
|
+
/**
|
|
150
|
+
* Make fetch request with timeout
|
|
151
|
+
*/
|
|
152
|
+
private fetchWithTimeout;
|
|
153
|
+
/**
|
|
154
|
+
* Call an external API securely through the proxy
|
|
155
|
+
* @param keyName - Name of the API key to use
|
|
156
|
+
* @param endpoint - API endpoint path (e.g., '/v1/chat/completions')
|
|
157
|
+
* @param options - Request options (method, body, headers)
|
|
158
|
+
* @returns Promise with the API response
|
|
159
|
+
*/
|
|
160
|
+
call<T = any>(keyName: string, endpoint: string, options?: Omit<ProxyRequest, 'keyName' | 'endpoint'>): Promise<T>;
|
|
161
|
+
/**
|
|
162
|
+
* Make a GET request
|
|
163
|
+
*/
|
|
164
|
+
get<T = any>(keyName: string, endpoint: string, headers?: Record<string, string>): Promise<T>;
|
|
165
|
+
/**
|
|
166
|
+
* Make a POST request
|
|
167
|
+
*/
|
|
168
|
+
post<T = any>(keyName: string, endpoint: string, body: any, headers?: Record<string, string>): Promise<T>;
|
|
169
|
+
/**
|
|
170
|
+
* Make a PUT request
|
|
171
|
+
*/
|
|
172
|
+
put<T = any>(keyName: string, endpoint: string, body: any, headers?: Record<string, string>): Promise<T>;
|
|
173
|
+
/**
|
|
174
|
+
* Make a DELETE request
|
|
175
|
+
*/
|
|
176
|
+
delete<T = any>(keyName: string, endpoint: string, headers?: Record<string, string>): Promise<T>;
|
|
177
|
+
/**
|
|
178
|
+
* Make a PATCH request
|
|
179
|
+
*/
|
|
180
|
+
patch<T = any>(keyName: string, endpoint: string, body: any, headers?: Record<string, string>): Promise<T>;
|
|
181
|
+
/**
|
|
182
|
+
* Secrets Management API (zero-config mode)
|
|
183
|
+
* These methods allow programmatic CRUD operations on project secrets
|
|
184
|
+
* Requires appId to be set (can be set in constructor or via setAppId)
|
|
185
|
+
*/
|
|
186
|
+
/**
|
|
187
|
+
* Set App ID for secrets management
|
|
188
|
+
* Required before calling secrets management methods
|
|
189
|
+
*/
|
|
190
|
+
setAppId(appId: string): void;
|
|
191
|
+
/**
|
|
192
|
+
* Make authenticated request to secrets management API
|
|
193
|
+
*/
|
|
194
|
+
private secretsRequest;
|
|
195
|
+
/**
|
|
196
|
+
* List all secrets for the project
|
|
197
|
+
* Returns secret metadata (no API keys)
|
|
198
|
+
*/
|
|
199
|
+
listSecrets(): Promise<{
|
|
200
|
+
success: boolean;
|
|
201
|
+
secrets: Array<{
|
|
202
|
+
name: string;
|
|
203
|
+
provider: string;
|
|
204
|
+
base_url: string;
|
|
205
|
+
auth_header: string;
|
|
206
|
+
auth_prefix: string;
|
|
207
|
+
created: string;
|
|
208
|
+
updated: string;
|
|
209
|
+
}>;
|
|
210
|
+
count: number;
|
|
211
|
+
}>;
|
|
212
|
+
/**
|
|
213
|
+
* Create a new secret
|
|
214
|
+
*/
|
|
215
|
+
createSecret(secret: {
|
|
216
|
+
name: string;
|
|
217
|
+
provider: string;
|
|
218
|
+
api_key: string;
|
|
219
|
+
base_url?: string;
|
|
220
|
+
auth_header?: string;
|
|
221
|
+
auth_prefix?: string;
|
|
222
|
+
}): Promise<{
|
|
223
|
+
success: boolean;
|
|
224
|
+
message: string;
|
|
225
|
+
secret: any;
|
|
226
|
+
}>;
|
|
227
|
+
/**
|
|
228
|
+
* Update an existing secret
|
|
229
|
+
*/
|
|
230
|
+
updateSecret(update: {
|
|
231
|
+
name: string;
|
|
232
|
+
api_key?: string;
|
|
233
|
+
provider?: string;
|
|
234
|
+
base_url?: string;
|
|
235
|
+
auth_header?: string;
|
|
236
|
+
auth_prefix?: string;
|
|
237
|
+
}): Promise<{
|
|
238
|
+
success: boolean;
|
|
239
|
+
message: string;
|
|
240
|
+
secret: any;
|
|
241
|
+
}>;
|
|
242
|
+
/**
|
|
243
|
+
* Delete a secret
|
|
244
|
+
*/
|
|
245
|
+
deleteSecret(name: string): Promise<{
|
|
246
|
+
success: boolean;
|
|
247
|
+
message: string;
|
|
248
|
+
}>;
|
|
249
|
+
/**
|
|
250
|
+
* Sync secrets from .env file format
|
|
251
|
+
* Bulk upload/update secrets
|
|
252
|
+
*/
|
|
253
|
+
syncEnv(secrets: Record<string, string>, provider?: string): Promise<{
|
|
254
|
+
success: boolean;
|
|
255
|
+
message: string;
|
|
256
|
+
results: {
|
|
257
|
+
created: number;
|
|
258
|
+
updated: number;
|
|
259
|
+
failed: number;
|
|
260
|
+
details: {
|
|
261
|
+
created: string[];
|
|
262
|
+
updated: string[];
|
|
263
|
+
failed: Array<{
|
|
264
|
+
name: string;
|
|
265
|
+
error: string;
|
|
266
|
+
}>;
|
|
267
|
+
};
|
|
268
|
+
};
|
|
269
|
+
}>;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* SecretsManagement class for CLI authentication and secrets management
|
|
274
|
+
*/
|
|
275
|
+
declare class SecretsManagement {
|
|
276
|
+
private appId;
|
|
277
|
+
private baseUrl;
|
|
278
|
+
private timeout;
|
|
279
|
+
constructor(options: SecretsManagementOptions);
|
|
280
|
+
/**
|
|
281
|
+
* Open a URL in the default browser
|
|
282
|
+
*/
|
|
283
|
+
private openBrowser;
|
|
284
|
+
/**
|
|
285
|
+
* Sleep for specified milliseconds
|
|
286
|
+
*/
|
|
287
|
+
private sleep;
|
|
288
|
+
/**
|
|
289
|
+
* Browser OAuth login flow with 2FA
|
|
290
|
+
* Opens browser for user to authorize, then prompts for 2FA code
|
|
291
|
+
*/
|
|
292
|
+
login(): Promise<{
|
|
293
|
+
success: boolean;
|
|
294
|
+
user: string;
|
|
295
|
+
}>;
|
|
296
|
+
/**
|
|
297
|
+
* Check if user is authenticated
|
|
298
|
+
*/
|
|
299
|
+
isAuthenticated(): Promise<boolean>;
|
|
300
|
+
/**
|
|
301
|
+
* Logout - clear stored credentials
|
|
302
|
+
*/
|
|
303
|
+
logout(): void;
|
|
304
|
+
/**
|
|
305
|
+
* Get access token from stored credentials
|
|
306
|
+
* Throws error if not authenticated
|
|
307
|
+
*/
|
|
308
|
+
private getAccessToken;
|
|
309
|
+
/**
|
|
310
|
+
* Make authenticated request
|
|
311
|
+
*/
|
|
312
|
+
private request;
|
|
313
|
+
/**
|
|
314
|
+
* List all secrets (with masked API keys)
|
|
315
|
+
*/
|
|
316
|
+
list(): Promise<MaskedSecret[]>;
|
|
317
|
+
/**
|
|
318
|
+
* Sync secrets from config
|
|
319
|
+
*/
|
|
320
|
+
sync(secrets: SecretConfig[], options?: SyncOptions): Promise<SyncResult>;
|
|
321
|
+
/**
|
|
322
|
+
* Import secrets from a config object (bulk import)
|
|
323
|
+
* Also creates SDK token if origins are provided
|
|
324
|
+
*
|
|
325
|
+
* @param config - The secrets configuration
|
|
326
|
+
* @returns Import results including created/updated counts and optional SDK token
|
|
327
|
+
*/
|
|
328
|
+
importSecrets(config: {
|
|
329
|
+
version?: string;
|
|
330
|
+
origins?: string[];
|
|
331
|
+
secrets: SecretConfig[];
|
|
332
|
+
}): Promise<{
|
|
333
|
+
success: boolean;
|
|
334
|
+
results: {
|
|
335
|
+
created: number;
|
|
336
|
+
updated: number;
|
|
337
|
+
failed: number;
|
|
338
|
+
details: {
|
|
339
|
+
created: string[];
|
|
340
|
+
updated: string[];
|
|
341
|
+
failed: {
|
|
342
|
+
name: string;
|
|
343
|
+
error: string;
|
|
344
|
+
}[];
|
|
345
|
+
};
|
|
346
|
+
};
|
|
347
|
+
sdkToken?: {
|
|
348
|
+
token: string;
|
|
349
|
+
message: string;
|
|
350
|
+
};
|
|
351
|
+
}>;
|
|
352
|
+
/**
|
|
353
|
+
* Import secrets from a JSON file path
|
|
354
|
+
* Resolves environment variables in the config
|
|
355
|
+
*/
|
|
356
|
+
importFromFile(configPath: string): Promise<{
|
|
357
|
+
success: boolean;
|
|
358
|
+
results: {
|
|
359
|
+
created: number;
|
|
360
|
+
updated: number;
|
|
361
|
+
failed: number;
|
|
362
|
+
};
|
|
363
|
+
sdkToken?: {
|
|
364
|
+
token: string;
|
|
365
|
+
message: string;
|
|
366
|
+
};
|
|
367
|
+
}>;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Resolve environment variable references in a string
|
|
372
|
+
* Supports ${VAR} and $VAR syntax
|
|
373
|
+
*/
|
|
374
|
+
declare function resolveEnvString(value: string): string;
|
|
375
|
+
/**
|
|
376
|
+
* Resolve environment variables in a single secret config
|
|
377
|
+
*/
|
|
378
|
+
declare function resolveEnvInSecret(secret: SecretConfig): SecretConfig;
|
|
379
|
+
/**
|
|
380
|
+
* Resolve environment variables in an array of secrets
|
|
381
|
+
*/
|
|
382
|
+
declare function resolveEnvInSecrets(secrets: SecretConfig[]): SecretConfig[];
|
|
383
|
+
/**
|
|
384
|
+
* Load and parse a secrets config file
|
|
385
|
+
* Resolves environment variables in the config
|
|
386
|
+
*/
|
|
387
|
+
declare function loadSecretsConfig(configPath: string): {
|
|
388
|
+
version: string;
|
|
389
|
+
project: string;
|
|
390
|
+
secrets: SecretConfig[];
|
|
391
|
+
};
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Machine ID Generation
|
|
395
|
+
* Cross-platform machine identification for CLI authentication
|
|
396
|
+
*/
|
|
397
|
+
interface MachineInfo {
|
|
398
|
+
machineId: string;
|
|
399
|
+
fingerprint: string;
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* Get a unique machine identifier
|
|
403
|
+
* This ID persists across reboots and SDK reinstalls
|
|
404
|
+
*/
|
|
405
|
+
declare function getMachineId(): Promise<MachineInfo>;
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* Read all credentials from file (SDK-keyed format)
|
|
409
|
+
*/
|
|
410
|
+
declare function readAllCredentials(): SDKCredentialsStore | null;
|
|
411
|
+
/**
|
|
412
|
+
* Write all credentials to file (SDK-keyed format)
|
|
413
|
+
*/
|
|
414
|
+
declare function writeAllCredentials(store: SDKCredentialsStore): void;
|
|
415
|
+
/**
|
|
416
|
+
* Read credentials for a specific SDK
|
|
417
|
+
*/
|
|
418
|
+
declare function readSDKCredentials(sdkName: SDKName): CLICredentials | null;
|
|
419
|
+
/**
|
|
420
|
+
* Write credentials for a specific SDK
|
|
421
|
+
*/
|
|
422
|
+
declare function writeSDKCredentials(sdkName: SDKName, creds: CLICredentials): void;
|
|
423
|
+
/**
|
|
424
|
+
* Delete credentials for a specific SDK
|
|
425
|
+
*/
|
|
426
|
+
declare function deleteSDKCredentials(sdkName: SDKName): void;
|
|
427
|
+
/**
|
|
428
|
+
* Check if credentials exist for a specific SDK
|
|
429
|
+
*/
|
|
430
|
+
declare function hasSDKCredentials(sdkName: SDKName): boolean;
|
|
431
|
+
|
|
432
|
+
export { type CLICredentials, InvalidTokenError, type MachineInfo, type MaskedSecret, OriginMismatchError, type ProxyRequest, type ProxyResponse, RateLimitError, type RateLimitInfo, type SDKCredentialsStore, type SDKName, type SecretConfig, SecretsManagement, type SecretsManagementOptions, SecretsSDK, SecretsSDKError, type SecretsSDKOptions, type SyncOptions, type SyncResult, deleteSDKCredentials, getMachineId, hasSDKCredentials, loadSecretsConfig, readAllCredentials, readSDKCredentials, resolveEnvInSecret, resolveEnvInSecrets, resolveEnvString, writeAllCredentials, writeSDKCredentials };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";var SecretsSDK=(()=>{var ee=Object.create;var D=Object.defineProperty;var te=Object.getOwnPropertyDescriptor;var re=Object.getOwnPropertyNames;var se=Object.getPrototypeOf,ne=Object.prototype.hasOwnProperty;var c=(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 ie=(t,e)=>()=>(t&&(e=t(t=0)),e);var W=(t,e)=>{for(var r in e)D(t,r,{get:e[r],enumerable:!0})},B=(t,e,r,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of re(e))!ne.call(t,n)&&n!==r&&D(t,n,{get:()=>e[n],enumerable:!(s=te(e,n))||s.enumerable});return t};var w=(t,e,r)=>(r=t!=null?ee(se(t)):{},B(e||!t||!t.__esModule?D(r,"default",{value:t,enumerable:!0}):r,t)),oe=t=>B(D({},"__esModule",{value:!0}),t);var Y={};W(Y,{loadSecretsConfig:()=>z,resolveEnvInSecret:()=>L,resolveEnvInSecrets:()=>$,resolveEnvString:()=>m});function m(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 L(t){let e={name:m(t.name),provider:m(t.provider),api_key:m(t.api_key)};return t.base_url&&(e.base_url=m(t.base_url)),t.auth_header&&(e.auth_header=m(t.auth_header)),t.auth_prefix&&(e.auth_prefix=m(t.auth_prefix)),e}function $(t){return t.map(L)}function z(t){let e=c("fs"),s=c("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=$(i.secrets);return{version:i.version,project:i.project,secrets:a}}var J=ie(()=>{"use strict"});var ue={};W(ue,{InvalidTokenError:()=>b,OriginMismatchError:()=>v,RateLimitError:()=>x,SecretsManagement:()=>j,SecretsSDK:()=>R,SecretsSDKError:()=>o,deleteSDKCredentials:()=>G,getMachineId:()=>A,hasSDKCredentials:()=>V,loadSecretsConfig:()=>z,readAllCredentials:()=>C,readSDKCredentials:()=>k,resolveEnvInSecret:()=>L,resolveEnvInSecrets:()=>$,resolveEnvString:()=>m,writeAllCredentials:()=>O,writeSDKCredentials:()=>E});var o=class extends Error{constructor(r,s,n){super(r);this.status=s;this.response=n;this.name="SecretsSDKError"}},v=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 R=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`,h={"Content-Type":"application/json"};!this.zeroConfigMode&&this.token&&(h.Authorization=`Bearer ${this.token}`);let u=await this.fetchWithTimeout(a,{method:"POST",headers:h,body:JSON.stringify({keyName:e,endpoint:r,method:s.method||"GET",body:s.body,headers:s.headers})},this.timeout);this.parseRateLimitHeaders(u.headers);let f=await u.json();if(!u.ok){let p=f.data?.message||f?.message||`Request failed with status ${u.status}`;if(u.status===403&&p.includes("Origin"))throw new v(p);if(u.status===401)throw new b(p);if(u.status===429){let H=this.rateLimitInfo?this.rateLimitInfo.reset-Math.floor(Date.now()/1e3):60,I=new x(p,H,this.rateLimitInfo?.remaining||0,this.rateLimitInfo?.limit||100);if(this.retryOn429&&n<i){n++;let M=Math.min(1e3*Math.pow(2,n-1),8e3);await this.sleep(M);continue}throw I}throw new o(p,u.status,f)}return f.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 d=w(c("fs")),P=w(c("path")),K=w(c("os"));function U(){let t=K.homedir(),e=P.join(t,".learn");return P.join(e,"credentials.json")}function ae(){let t=K.homedir(),e=P.join(t,".learn");d.existsSync(e)||d.mkdirSync(e,{recursive:!0,mode:448})}function C(){try{let t=U();if(!d.existsSync(t))return null;let e=d.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 O(t){ae();let e=U(),r=JSON.stringify(t,null,2);d.writeFileSync(e,r,{mode:384})}function k(t){let e=C();return e&&e[t]||null}function E(t,e){let r=C()||{};r[t]=e,O(r)}function G(t){let e=C();e&&(delete e[t],Object.keys(e).length===0?N():O(e))}function q(){return k("learn-secrets-sdk")}function N(){try{let t=U();d.existsSync(t)&&d.unlinkSync(t)}catch{}}function V(t){let e=k(t);return e!==null&&!!e.access_token}function X(){let t=q();return t?t.expires_at?new Date(t.expires_at)>new Date:!0:!1}var Z=c("child_process"),Q=c("util"),F=c("crypto"),g=w(c("os")),_=(0,Q.promisify)(Z.exec);async function A(){let t=process.platform;try{let e;if(t==="darwin")e=await ce();else if(t==="linux")e=await de();else if(t==="win32")e=await le();else throw new Error(`Unsupported platform: ${t}`);let r=(0,F.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,F.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 ce(){try{let{stdout:t}=await _(`ioreg -l | grep IOPlatformSerialNumber | awk '{print $4}' | sed 's/"//g'`),{stdout:e}=await _("hostname"),r=t.trim(),s=e.trim();if(r&&s)return`${r}-${s}`;let{stdout:n}=await _(`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 de(){try{let{stdout:t}=await _("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 le(){try{let{stdout:t}=await _("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 j=class{constructor(e){this.appId=e.appId,this.baseUrl=e.baseUrl||"https://ctklearn.carsontkempf.workers.dev",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 A(),s=k(e);if(s&&s.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:r.machineId,sdk_name:e})});if(l.ok){let y=await l.json();if(y.authorized)return console.log("This machine is already authorized."),console.log(`Logged in as: ${y.user_email||y.user_id}`),{success:!0,user:y.user_email||y.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
|
+
To authorize this device:`),console.log(`1. Visit: ${i.verification_uri}`),console.log(`2. Enter code: ${i.user_code}`),console.log("3. Complete 2FA verification"),console.log(`
|
|
5
|
+
Opening browser...
|
|
6
|
+
`),await this.openBrowser(i.verification_uri_complete);let a=!1,h=i.interval*1e3,u=Math.ceil(i.expires_in/i.interval),f=0;for(;f<u;){await this.sleep(h),f++;let l=await fetch(`${this.baseUrl}/api/auth/cli/token`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({device_code:i.device_code})});if(l.status===202){process.stdout.write(".");continue}if(l.ok){a=!0;break}}if(!a)throw new o("Device authorization timeout",408);console.log(`
|
|
7
|
+
|
|
8
|
+
Device authorized! Sending 2FA code...`);let p=await fetch(`${this.baseUrl}/api/auth/2fa/send`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({device_code:i.device_code,sdk_name:e,machine_id:r.machineId})});if(!p.ok){let l=await p.json().catch(()=>({}));throw new o(l.message||"Failed to send 2FA code",p.status)}console.log(`2FA code sent to your email.
|
|
9
|
+
`);let I=(await import("readline")).createInterface({input:process.stdin,output:process.stdout}),M=await new Promise(l=>{I.question("Enter 2FA code from email: ",y=>{I.close(),l(y)})}),T=await fetch(`${this.baseUrl}/api/auth/2fa/verify`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({device_code:i.device_code,code:M.trim()})});if(!T.ok){let l=await T.json().catch(()=>({}));throw new o(l.message||"2FA verification failed",T.status)}let S=await T.json();return E(e,{access_token:S.access_token,refresh_token:null,expires_at:null,user_id:S.user_id,machine_id:r.machineId,sdk_name:e,authorized_at:new Date().toISOString()}),console.log(`
|
|
10
|
+
Authentication successful!`),console.log(`Logged in as: ${S.user_email||S.user_id}`),console.log(`This machine is now permanently authorized.
|
|
11
|
+
`),{success:!0,user:S.user_email||S.user_id}}catch(r){throw r instanceof o?r:new o(r.message||"Login failed",r.status||500)}}async isAuthenticated(){return X()}logout(){N(),console.log("Logged out successfully")}getAccessToken(){let e=q();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 h=await a.json().catch(()=>({}));throw new o(h.message||`Request failed: ${a.statusText}`,a.status,h)}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(()=>(J(),Y)),s=r(e);return this.importSecrets(s)}};J();return oe(ue);})();
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import{a as U,b as q,c as N,d as F}from"./chunk-AS6G7JYX.mjs";var i=class extends Error{constructor(t,r,n){super(t);this.status=r;this.response=n;this.name="SecretsSDKError"}},y=class extends i{constructor(e="Origin not allowed for this token"){super(e,403),this.name="OriginMismatchError"}},w=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}},S=class extends i{constructor(e="Invalid or expired token"){super(e,401),this.name="InvalidTokenError"}};var _=class{constructor(e={}){this.rateLimitInfo=null;this.appId=e.appId||null,this.token=e.token||e.sessionToken||null,this.baseUrl=e.baseUrl||"https://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,a=setTimeout(()=>n.abort(),r);try{return await fetch(e,{...t,signal:n.signal})}finally{clearTimeout(a)}}async call(e,t,r={}){let n=0,a=this.retryOn429?3:0;for(;;)try{let o=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 l=await this.fetchWithTimeout(o,{method:"POST",headers:m,body:JSON.stringify({keyName:e,endpoint:t,method:r.method||"GET",body:r.body,headers:r.headers})},this.timeout);this.parseRateLimitHeaders(l.headers);let h=await l.json();if(!l.ok){let u=h.data?.message||h?.message||`Request failed with status ${l.status}`;if(l.status===403&&u.includes("Origin"))throw new y(u);if(l.status===401)throw new S(u);if(l.status===429){let M=this.rateLimitInfo?this.rateLimitInfo.reset-Math.floor(Date.now()/1e3):60,k=new w(u,M,this.rateLimitInfo?.remaining||0,this.rateLimitInfo?.limit||100);if(this.retryOn429&&n<a){n++;let T=Math.min(1e3*Math.pow(2,n-1),8e3);await this.sleep(T);continue}throw k}throw new i(u,l.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 c from"fs";import*as v from"path";import*as D from"os";function R(){let s=D.homedir(),e=v.join(s,".learn");return v.join(e,"credentials.json")}function z(){let s=D.homedir(),e=v.join(s,".learn");c.existsSync(e)||c.mkdirSync(e,{recursive:!0,mode:448})}function C(){try{let s=R();if(!c.existsSync(s))return null;let e=c.readFileSync(s,"utf-8"),t=JSON.parse(e);return t.access_token&&!t["learn-secrets-sdk"]&&!t["learn-auth-sdk"]?{"learn-secrets-sdk":t}:t}catch{return null}}function P(s){z();let e=R(),t=JSON.stringify(s,null,2);c.writeFileSync(e,t,{mode:384})}function b(s){let e=C();return e&&e[s]||null}function O(s,e){let t=C()||{};t[s]=e,P(t)}function J(s){let e=C();e&&(delete e[s],Object.keys(e).length===0?A():P(e))}function L(){return b("learn-secrets-sdk")}function A(){try{let s=R();c.existsSync(s)&&c.unlinkSync(s)}catch{}}function H(s){let e=b(s);return e!==null&&!!e.access_token}function K(){let s=L();return s?s.expires_at?new Date(s.expires_at)>new Date:!0:!1}import{exec as W}from"child_process";import{promisify as B}from"util";import{createHash as j}from"crypto";import*as p from"os";var x=B(W);async function E(){let s=process.platform;try{let e;if(s==="darwin")e=await G();else if(s==="linux")e=await V();else if(s==="win32")e=await X();else throw new Error(`Unsupported platform: ${s}`);let t=j("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=j("sha256").update(r).digest("hex");return{machineId:t,fingerprint:n}}catch(e){throw new Error(`Failed to get machine ID: ${e.message}`)}}async function G(){try{let{stdout:s}=await x(`ioreg -l | grep IOPlatformSerialNumber | awk '{print $4}' | sed 's/"//g'`),{stdout:e}=await x("hostname"),t=s.trim(),r=e.trim();if(t&&r)return`${t}-${r}`;let{stdout:n}=await x(`ioreg -rd1 -c IOPlatformExpertDevice | grep IOPlatformUUID | awk '{print $3}' | sed 's/"//g'`);return n.trim()}catch{throw new Error("Failed to get macOS machine ID")}}async function V(){try{let{stdout:s}=await x("cat /etc/machine-id 2>/dev/null || cat /var/lib/dbus/machine-id 2>/dev/null"),e=s.trim();if(e)return e;throw new Error("Machine ID file not found")}catch{throw new Error("Failed to get Linux machine ID")}}async function X(){try{let{stdout:s}=await x("wmic csproduct get uuid"),t=s.split(`
|
|
2
|
+
`).map(r=>r.trim()).find(r=>r&&r!=="UUID");if(t)return t;throw new Error("Failed to parse machine UUID from WMIC")}catch{throw new Error("Failed to get Windows machine ID")}}var $=class{constructor(e){this.appId=e.appId,this.baseUrl=e.baseUrl||"https://ctklearn.carsontkempf.workers.dev",this.timeout=e.timeout||3e4}async openBrowser(e){let{exec:t}=await import("child_process"),{promisify:r}=await import("util"),n=r(t),a=process.platform;try{a==="darwin"?await n(`open "${e}"`):a==="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 E(),r=b(e);if(r&&r.access_token){let d=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(d.ok){let g=await d.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 a=await n.json();console.log(`
|
|
4
|
+
To authorize this device:`),console.log(`1. Visit: ${a.verification_uri}`),console.log(`2. Enter code: ${a.user_code}`),console.log("3. Complete 2FA verification"),console.log(`
|
|
5
|
+
Opening browser...
|
|
6
|
+
`),await this.openBrowser(a.verification_uri_complete);let o=!1,m=a.interval*1e3,l=Math.ceil(a.expires_in/a.interval),h=0;for(;h<l;){await this.sleep(m),h++;let d=await fetch(`${this.baseUrl}/api/auth/cli/token`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({device_code:a.device_code})});if(d.status===202){process.stdout.write(".");continue}if(d.ok){o=!0;break}}if(!o)throw new i("Device authorization timeout",408);console.log(`
|
|
7
|
+
|
|
8
|
+
Device authorized! Sending 2FA code...`);let u=await fetch(`${this.baseUrl}/api/auth/2fa/send`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({device_code:a.device_code,sdk_name:e,machine_id:t.machineId})});if(!u.ok){let d=await u.json().catch(()=>({}));throw new i(d.message||"Failed to send 2FA code",u.status)}console.log(`2FA code sent to your email.
|
|
9
|
+
`);let k=(await import("readline")).createInterface({input:process.stdin,output:process.stdout}),T=await new Promise(d=>{k.question("Enter 2FA code from email: ",g=>{k.close(),d(g)})}),I=await fetch(`${this.baseUrl}/api/auth/2fa/verify`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({device_code:a.device_code,code:T.trim()})});if(!I.ok){let d=await I.json().catch(()=>({}));throw new i(d.message||"2FA verification failed",I.status)}let f=await I.json();return O(e,{access_token:f.access_token,refresh_token:null,expires_at:null,user_id:f.user_id,machine_id:t.machineId,sdk_name:e,authorized_at:new Date().toISOString()}),console.log(`
|
|
10
|
+
Authentication successful!`),console.log(`Logged in as: ${f.user_email||f.user_id}`),console.log(`This machine is now permanently authorized.
|
|
11
|
+
`),{success:!0,user:f.user_email||f.user_id}}catch(t){throw t instanceof i?t:new i(t.message||"Login failed",t.status||500)}}async isAuthenticated(){return K()}logout(){A(),console.log("Logged out successfully")}getAccessToken(){let e=L();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(),a={method:e,headers:{"Content-Type":"application/json",Authorization:`Bearer ${n}`}};r&&(a.body=JSON.stringify(r));let o=await fetch(`${this.baseUrl}${t}`,a);if(!o.ok){let m=await o.json().catch(()=>({}));throw new i(m.message||`Request failed: ${o.statusText}`,o.status,m)}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{S as InvalidTokenError,y as OriginMismatchError,w as RateLimitError,$ as SecretsManagement,_ as SecretsSDK,i as SecretsSDKError,J as deleteSDKCredentials,E as getMachineId,H as hasSDKCredentials,F as loadSecretsConfig,C as readAllCredentials,b as readSDKCredentials,q as resolveEnvInSecret,N as resolveEnvInSecrets,U as resolveEnvString,P as writeAllCredentials,O as writeSDKCredentials};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "learn-secrets-sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
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",
|
|
@@ -13,10 +13,13 @@
|
|
|
13
13
|
"bin",
|
|
14
14
|
"secrets.template.json"
|
|
15
15
|
],
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/carsontkempf/learn.git",
|
|
19
|
+
"directory": "packages/secrets-sdk"
|
|
19
20
|
},
|
|
21
|
+
"homepage": "https://github.com/carsontkempf/learn/tree/main/packages/secrets-sdk",
|
|
22
|
+
"author": "Carson Kempf",
|
|
20
23
|
"keywords": [
|
|
21
24
|
"api",
|
|
22
25
|
"proxy",
|
|
@@ -26,13 +29,17 @@
|
|
|
26
29
|
"cli",
|
|
27
30
|
"secrets-management"
|
|
28
31
|
],
|
|
29
|
-
"author": "",
|
|
30
32
|
"license": "MIT",
|
|
31
33
|
"dependencies": {
|
|
32
34
|
"commander": "^12.0.0"
|
|
33
35
|
},
|
|
34
36
|
"devDependencies": {
|
|
35
37
|
"@types/node": "^20.0.0",
|
|
38
|
+
"tsup": "^8.5.1",
|
|
36
39
|
"typescript": "^5.0.0"
|
|
40
|
+
},
|
|
41
|
+
"scripts": {
|
|
42
|
+
"build": "tsup src/index.ts --format esm,iife --dts --minify --global-name SecretsSDK && tsup src/cli/index.ts --format esm --dts --out-dir dist/cli --external commander",
|
|
43
|
+
"prepublishOnly": "npm run build"
|
|
37
44
|
}
|
|
38
45
|
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
export interface InitOptions {
|
|
2
|
-
origins: string;
|
|
3
|
-
env?: string;
|
|
4
|
-
project?: string;
|
|
5
|
-
baseUrl?: string;
|
|
6
|
-
yes?: boolean;
|
|
7
|
-
}
|
|
8
|
-
/**
|
|
9
|
-
* Init command - onboard a new site by uploading .env secrets
|
|
10
|
-
*/
|
|
11
|
-
export declare function initCommand(options: InitOptions): Promise<void>;
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
import { SecretsManagement } from '../../management.js';
|
|
2
|
-
import { parseEnvFile } from '../utils/env-parser.js';
|
|
3
|
-
import { detectApiKeys, summarizeDetectedSecrets } from '../utils/key-detector.js';
|
|
4
|
-
import { hasValidCredentials } from '../../credentials.js';
|
|
5
|
-
/**
|
|
6
|
-
* Init command - onboard a new site by uploading .env secrets
|
|
7
|
-
*/
|
|
8
|
-
export async function initCommand(options) {
|
|
9
|
-
// Validate authentication
|
|
10
|
-
if (!hasValidCredentials()) {
|
|
11
|
-
console.error('Error: Not authenticated. Run "learn-secrets login" first.');
|
|
12
|
-
process.exit(1);
|
|
13
|
-
}
|
|
14
|
-
// Validate required options
|
|
15
|
-
if (!options.origins) {
|
|
16
|
-
console.error('Error: --origins is required');
|
|
17
|
-
console.error('Example: learn-secrets init --origins mysite.com,localhost');
|
|
18
|
-
process.exit(1);
|
|
19
|
-
}
|
|
20
|
-
if (!options.project) {
|
|
21
|
-
console.error('Error: --project is required');
|
|
22
|
-
console.error('Example: learn-secrets init --project my-app-id --origins mysite.com');
|
|
23
|
-
process.exit(1);
|
|
24
|
-
}
|
|
25
|
-
// Parse origins
|
|
26
|
-
const origins = options.origins.split(',').map(o => o.trim()).filter(Boolean);
|
|
27
|
-
if (origins.length === 0) {
|
|
28
|
-
console.error('Error: No valid origins provided');
|
|
29
|
-
process.exit(1);
|
|
30
|
-
}
|
|
31
|
-
console.log(`\nOnboarding site for project: ${options.project}`);
|
|
32
|
-
console.log(`Origins: ${origins.join(', ')}\n`);
|
|
33
|
-
// Load and detect secrets from .env
|
|
34
|
-
const envFile = options.env || '.env';
|
|
35
|
-
let secrets;
|
|
36
|
-
try {
|
|
37
|
-
console.log(`Reading ${envFile}...`);
|
|
38
|
-
const envVars = parseEnvFile(envFile);
|
|
39
|
-
secrets = detectApiKeys(envVars);
|
|
40
|
-
if (secrets.length === 0) {
|
|
41
|
-
console.log('\nNo API keys detected in .env file.');
|
|
42
|
-
console.log('Make sure your .env contains variables like:');
|
|
43
|
-
console.log(' OPENAI_API_KEY=sk-...');
|
|
44
|
-
console.log(' ANTHROPIC_API_KEY=sk-ant-...');
|
|
45
|
-
process.exit(0);
|
|
46
|
-
}
|
|
47
|
-
console.log('\n' + summarizeDetectedSecrets(secrets));
|
|
48
|
-
console.log('');
|
|
49
|
-
}
|
|
50
|
-
catch (error) {
|
|
51
|
-
console.error(`Error reading ${envFile}:`, error.message);
|
|
52
|
-
process.exit(1);
|
|
53
|
-
}
|
|
54
|
-
// Ask for confirmation unless --yes flag
|
|
55
|
-
if (!options.yes) {
|
|
56
|
-
const readline = await import('readline');
|
|
57
|
-
const rl = readline.createInterface({
|
|
58
|
-
input: process.stdin,
|
|
59
|
-
output: process.stdout
|
|
60
|
-
});
|
|
61
|
-
const answer = await new Promise((resolve) => {
|
|
62
|
-
rl.question('Upload these secrets? (y/N): ', resolve);
|
|
63
|
-
});
|
|
64
|
-
rl.close();
|
|
65
|
-
if (answer.toLowerCase() !== 'y' && answer.toLowerCase() !== 'yes') {
|
|
66
|
-
console.log('Cancelled.');
|
|
67
|
-
process.exit(0);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
// Upload secrets
|
|
71
|
-
console.log('\nUploading secrets...');
|
|
72
|
-
const mgmt = new SecretsManagement({
|
|
73
|
-
appId: options.project,
|
|
74
|
-
baseUrl: options.baseUrl
|
|
75
|
-
});
|
|
76
|
-
try {
|
|
77
|
-
const result = await mgmt.importSecrets({
|
|
78
|
-
version: '1.0',
|
|
79
|
-
origins,
|
|
80
|
-
secrets
|
|
81
|
-
});
|
|
82
|
-
console.log('\nSuccess!');
|
|
83
|
-
console.log(` Created: ${result.results.created} secrets`);
|
|
84
|
-
console.log(` Updated: ${result.results.updated} secrets`);
|
|
85
|
-
if (result.results.failed > 0) {
|
|
86
|
-
console.log(` Failed: ${result.results.failed} secrets`);
|
|
87
|
-
for (const failure of result.results.details.failed) {
|
|
88
|
-
console.log(` - ${failure.name}: ${failure.error}`);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
if (result.sdkToken) {
|
|
92
|
-
console.log('\nSDK Token created:');
|
|
93
|
-
console.log(` ${result.sdkToken.token}`);
|
|
94
|
-
console.log('\n IMPORTANT: Save this token - it will not be shown again.');
|
|
95
|
-
console.log(' Add it to your static site code:');
|
|
96
|
-
console.log('');
|
|
97
|
-
console.log(' const sdk = new SecretsSDK();');
|
|
98
|
-
console.log(' // No appId or token needed - uses origin-based auth!');
|
|
99
|
-
}
|
|
100
|
-
console.log('\nYour site is ready!');
|
|
101
|
-
console.log(`Visit: https://ctklearn.carsontkempf.workers.dev to manage secrets`);
|
|
102
|
-
}
|
|
103
|
-
catch (error) {
|
|
104
|
-
console.error('\nUpload failed:', error.message);
|
|
105
|
-
if (error.status === 401) {
|
|
106
|
-
console.error('Your session may have expired. Try running "learn-secrets login" again.');
|
|
107
|
-
}
|
|
108
|
-
if (error.status === 404) {
|
|
109
|
-
console.error(`Project "${options.project}" not found. Check your project ID.`);
|
|
110
|
-
}
|
|
111
|
-
process.exit(1);
|
|
112
|
-
}
|
|
113
|
-
}
|