learn-secrets-sdk 1.4.0 → 1.5.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.
@@ -1,73 +0,0 @@
1
- /**
2
- * Resolve environment variable references in a string
3
- * Supports ${VAR} and $VAR syntax
4
- */
5
- export function resolveEnvString(value) {
6
- // Replace ${VAR} and $VAR references
7
- return value.replace(/\$\{([A-Z0-9_]+)\}|\$([A-Z0-9_]+)/g, (match, var1, var2) => {
8
- const varName = var1 || var2;
9
- const envValue = process.env[varName];
10
- if (envValue === undefined) {
11
- throw new Error(`Environment variable ${varName} is not defined`);
12
- }
13
- return envValue;
14
- });
15
- }
16
- /**
17
- * Resolve environment variables in a single secret config
18
- */
19
- export function resolveEnvInSecret(secret) {
20
- const resolved = {
21
- name: resolveEnvString(secret.name),
22
- provider: resolveEnvString(secret.provider),
23
- api_key: resolveEnvString(secret.api_key)
24
- };
25
- if (secret.base_url) {
26
- resolved.base_url = resolveEnvString(secret.base_url);
27
- }
28
- if (secret.auth_header) {
29
- resolved.auth_header = resolveEnvString(secret.auth_header);
30
- }
31
- if (secret.auth_prefix) {
32
- resolved.auth_prefix = resolveEnvString(secret.auth_prefix);
33
- }
34
- return resolved;
35
- }
36
- /**
37
- * Resolve environment variables in an array of secrets
38
- */
39
- export function resolveEnvInSecrets(secrets) {
40
- return secrets.map(resolveEnvInSecret);
41
- }
42
- /**
43
- * Load and parse a secrets config file
44
- * Resolves environment variables in the config
45
- */
46
- export function loadSecretsConfig(configPath) {
47
- const fs = require('fs');
48
- const path = require('path');
49
- // Read config file
50
- const fullPath = path.resolve(configPath);
51
- if (!fs.existsSync(fullPath)) {
52
- throw new Error(`Config file not found: ${fullPath}`);
53
- }
54
- const configData = fs.readFileSync(fullPath, 'utf-8');
55
- const config = JSON.parse(configData);
56
- // Validate config
57
- if (!config.version) {
58
- throw new Error('Config file missing "version" field');
59
- }
60
- if (!config.project) {
61
- throw new Error('Config file missing "project" field');
62
- }
63
- if (!Array.isArray(config.secrets)) {
64
- throw new Error('Config file missing "secrets" array');
65
- }
66
- // Resolve environment variables
67
- const resolvedSecrets = resolveEnvInSecrets(config.secrets);
68
- return {
69
- version: config.version,
70
- project: config.project,
71
- secrets: resolvedSecrets
72
- };
73
- }
package/dist/index.js DELETED
@@ -1,4 +0,0 @@
1
- export { SecretsSDK } from './client.js';
2
- export { SecretsManagement } from './management.js';
3
- export { resolveEnvString, resolveEnvInSecret, resolveEnvInSecrets, loadSecretsConfig } from './env-resolver.js';
4
- export { SecretsSDKError, OriginMismatchError, RateLimitError, InvalidTokenError } from './types.js';
@@ -1,98 +0,0 @@
1
- import { type SecretsManagementOptions, type SecretConfig, type MaskedSecret, type SyncOptions, type SyncResult } from './types.js';
2
- /**
3
- * SecretsManagement class for CLI authentication and secrets management
4
- */
5
- export declare class SecretsManagement {
6
- private appId;
7
- private baseUrl;
8
- private timeout;
9
- constructor(options: SecretsManagementOptions);
10
- /**
11
- * Open a URL in the default browser
12
- */
13
- private openBrowser;
14
- /**
15
- * Sleep for specified milliseconds
16
- */
17
- private sleep;
18
- /**
19
- * Browser OAuth login flow
20
- * Opens browser for user to authorize, then stores credentials locally
21
- */
22
- login(): Promise<{
23
- success: boolean;
24
- user: string;
25
- }>;
26
- /**
27
- * Check if user is authenticated
28
- */
29
- isAuthenticated(): Promise<boolean>;
30
- /**
31
- * Logout - clear stored credentials
32
- */
33
- logout(): void;
34
- /**
35
- * Get access token from stored credentials
36
- * Throws error if not authenticated
37
- */
38
- private getAccessToken;
39
- /**
40
- * Make authenticated request
41
- */
42
- private request;
43
- /**
44
- * List all secrets (with masked API keys)
45
- */
46
- list(): Promise<MaskedSecret[]>;
47
- /**
48
- * Sync secrets from config
49
- */
50
- sync(secrets: SecretConfig[], options?: SyncOptions): Promise<SyncResult>;
51
- /**
52
- * Import secrets from a config object (bulk import)
53
- * Also creates SDK token if origins are provided
54
- *
55
- * @param config - The secrets configuration
56
- * @returns Import results including created/updated counts and optional SDK token
57
- */
58
- importSecrets(config: {
59
- version?: string;
60
- origins?: string[];
61
- secrets: SecretConfig[];
62
- }): Promise<{
63
- success: boolean;
64
- results: {
65
- created: number;
66
- updated: number;
67
- failed: number;
68
- details: {
69
- created: string[];
70
- updated: string[];
71
- failed: {
72
- name: string;
73
- error: string;
74
- }[];
75
- };
76
- };
77
- sdkToken?: {
78
- token: string;
79
- message: string;
80
- };
81
- }>;
82
- /**
83
- * Import secrets from a JSON file path
84
- * Resolves environment variables in the config
85
- */
86
- importFromFile(configPath: string): Promise<{
87
- success: boolean;
88
- results: {
89
- created: number;
90
- updated: number;
91
- failed: number;
92
- };
93
- sdkToken?: {
94
- token: string;
95
- message: string;
96
- };
97
- }>;
98
- }
@@ -1,214 +0,0 @@
1
- import { SecretsSDKError } from './types.js';
2
- import { readCredentials, writeCredentials, deleteCredentials, hasValidCredentials } from './credentials.js';
3
- /**
4
- * SecretsManagement class for CLI authentication and secrets management
5
- */
6
- export class SecretsManagement {
7
- constructor(options) {
8
- this.appId = options.appId;
9
- this.baseUrl = options.baseUrl || 'https://ctklearn.carsontkempf.workers.dev';
10
- this.timeout = options.timeout || 30000;
11
- }
12
- /**
13
- * Open a URL in the default browser
14
- */
15
- async openBrowser(url) {
16
- const { exec } = await import('child_process');
17
- const { promisify } = await import('util');
18
- const execAsync = promisify(exec);
19
- const platform = process.platform;
20
- try {
21
- if (platform === 'darwin') {
22
- await execAsync(`open "${url}"`);
23
- }
24
- else if (platform === 'win32') {
25
- await execAsync(`start "${url}"`);
26
- }
27
- else {
28
- // Linux
29
- await execAsync(`xdg-open "${url}"`);
30
- }
31
- }
32
- catch (err) {
33
- console.error('Failed to open browser:', err);
34
- throw new SecretsSDKError('Failed to open browser', 500);
35
- }
36
- }
37
- /**
38
- * Sleep for specified milliseconds
39
- */
40
- sleep(ms) {
41
- return new Promise((resolve) => setTimeout(resolve, ms));
42
- }
43
- /**
44
- * Browser OAuth login flow
45
- * Opens browser for user to authorize, then stores credentials locally
46
- */
47
- async login() {
48
- try {
49
- // Step 1: Request device code
50
- const deviceResponse = await fetch(`${this.baseUrl}/api/auth/cli/device`, {
51
- method: 'POST',
52
- headers: {
53
- 'Content-Type': 'application/json'
54
- }
55
- });
56
- if (!deviceResponse.ok) {
57
- throw new SecretsSDKError('Failed to generate device code', deviceResponse.status);
58
- }
59
- const deviceData = await deviceResponse.json();
60
- // Step 2: Display user code and verification URI
61
- console.log('\nTo authorize this application, visit:');
62
- console.log(` ${deviceData.verification_uri}`);
63
- console.log('\nAnd enter the code:');
64
- console.log(` ${deviceData.user_code}`);
65
- console.log('\nOpening browser...\n');
66
- // Step 3: Open browser
67
- await this.openBrowser(deviceData.verification_uri_complete);
68
- // Step 4: Poll for token
69
- const interval = deviceData.interval * 1000; // Convert to ms
70
- const maxAttempts = Math.ceil(deviceData.expires_in / deviceData.interval);
71
- let attempts = 0;
72
- while (attempts < maxAttempts) {
73
- await this.sleep(interval);
74
- attempts++;
75
- const tokenResponse = await fetch(`${this.baseUrl}/api/auth/cli/token`, {
76
- method: 'POST',
77
- headers: {
78
- 'Content-Type': 'application/json'
79
- },
80
- body: JSON.stringify({
81
- device_code: deviceData.device_code
82
- })
83
- });
84
- if (tokenResponse.status === 202) {
85
- // Still pending
86
- process.stdout.write('.');
87
- continue;
88
- }
89
- if (!tokenResponse.ok) {
90
- const errorData = await tokenResponse.json().catch(() => ({}));
91
- throw new SecretsSDKError(errorData.message || 'Failed to exchange device code', tokenResponse.status);
92
- }
93
- // Success!
94
- const tokenData = await tokenResponse.json();
95
- // Step 5: Store credentials
96
- const expiresAt = new Date(Date.now() + tokenData.expires_in * 1000);
97
- writeCredentials({
98
- access_token: tokenData.access_token,
99
- refresh_token: tokenData.refresh_token,
100
- expires_at: expiresAt.toISOString(),
101
- user_id: tokenData.user_id
102
- });
103
- console.log('\n\nAuthentication successful!');
104
- return {
105
- success: true,
106
- user: tokenData.user_id
107
- };
108
- }
109
- // Timeout
110
- throw new SecretsSDKError('Authorization timeout', 408);
111
- }
112
- catch (err) {
113
- if (err instanceof SecretsSDKError) {
114
- throw err;
115
- }
116
- throw new SecretsSDKError(err.message || 'Login failed', err.status || 500);
117
- }
118
- }
119
- /**
120
- * Check if user is authenticated
121
- */
122
- async isAuthenticated() {
123
- return hasValidCredentials();
124
- }
125
- /**
126
- * Logout - clear stored credentials
127
- */
128
- logout() {
129
- deleteCredentials();
130
- console.log('Logged out successfully');
131
- }
132
- /**
133
- * Get access token from stored credentials
134
- * Throws error if not authenticated
135
- */
136
- getAccessToken() {
137
- const creds = readCredentials();
138
- if (!creds) {
139
- throw new SecretsSDKError('Not authenticated. Run login() first.', 401);
140
- }
141
- // Check if expired
142
- const expiresAt = new Date(creds.expires_at);
143
- const now = new Date();
144
- if (expiresAt <= now) {
145
- throw new SecretsSDKError('Token expired. Run login() again.', 401);
146
- }
147
- return creds.access_token;
148
- }
149
- /**
150
- * Make authenticated request
151
- */
152
- async request(method, path, body) {
153
- const token = this.getAccessToken();
154
- const options = {
155
- method,
156
- headers: {
157
- 'Content-Type': 'application/json',
158
- Authorization: `Bearer ${token}`
159
- }
160
- };
161
- if (body) {
162
- options.body = JSON.stringify(body);
163
- }
164
- const response = await fetch(`${this.baseUrl}${path}`, options);
165
- if (!response.ok) {
166
- const errorData = await response.json().catch(() => ({}));
167
- throw new SecretsSDKError(errorData.message || `Request failed: ${response.statusText}`, response.status, errorData);
168
- }
169
- return response.json();
170
- }
171
- /**
172
- * List all secrets (with masked API keys)
173
- */
174
- async list() {
175
- const response = await this.request('GET', `/api/projects/${this.appId}/secrets`);
176
- return response.secrets;
177
- }
178
- /**
179
- * Sync secrets from config
180
- */
181
- async sync(secrets, options) {
182
- const response = await this.request('POST', `/api/projects/${this.appId}/secrets/sync`, {
183
- secrets,
184
- options: {
185
- deleteMissing: options?.deleteMissing ?? false,
186
- dryRun: options?.dryRun ?? false
187
- }
188
- });
189
- return response;
190
- }
191
- /**
192
- * Import secrets from a config object (bulk import)
193
- * Also creates SDK token if origins are provided
194
- *
195
- * @param config - The secrets configuration
196
- * @returns Import results including created/updated counts and optional SDK token
197
- */
198
- async importSecrets(config) {
199
- return this.request('POST', `/api/projects/${this.appId}/import-secrets`, {
200
- version: config.version || '1.0',
201
- origins: config.origins,
202
- secrets: config.secrets
203
- });
204
- }
205
- /**
206
- * Import secrets from a JSON file path
207
- * Resolves environment variables in the config
208
- */
209
- async importFromFile(configPath) {
210
- const { loadSecretsConfig } = await import('./env-resolver');
211
- const config = loadSecretsConfig(configPath);
212
- return this.importSecrets(config);
213
- }
214
- }
package/dist/types.d.ts DELETED
@@ -1,123 +0,0 @@
1
- export 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
- export 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
- export interface ProxyResponse<T = any> {
29
- success: boolean;
30
- status: number;
31
- data: T;
32
- }
33
- export interface RateLimitInfo {
34
- limit: number;
35
- remaining: number;
36
- reset: number;
37
- }
38
- export declare class SecretsSDKError extends Error {
39
- status: number;
40
- response?: any | undefined;
41
- constructor(message: string, status: number, response?: any | undefined);
42
- }
43
- export declare class OriginMismatchError extends SecretsSDKError {
44
- constructor(message?: string);
45
- }
46
- export 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
- export declare class InvalidTokenError extends SecretsSDKError {
53
- constructor(message?: string);
54
- }
55
- export interface SecretsManagementOptions {
56
- appId: string;
57
- baseUrl?: string;
58
- timeout?: number;
59
- }
60
- export 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
- export 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
- export interface SyncOptions {
80
- deleteMissing?: boolean;
81
- dryRun?: boolean;
82
- }
83
- export interface SyncDiffItem {
84
- name: string;
85
- provider?: string;
86
- changes?: string[];
87
- }
88
- export 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
- export interface DeviceCodeResponse {
104
- device_code: string;
105
- user_code: string;
106
- verification_uri: string;
107
- verification_uri_complete: string;
108
- expires_in: number;
109
- interval: number;
110
- }
111
- export interface TokenResponse {
112
- access_token: string;
113
- refresh_token: string;
114
- token_type: string;
115
- expires_in: number;
116
- user_id: string;
117
- }
118
- export interface CLICredentials {
119
- access_token: string;
120
- refresh_token: string;
121
- expires_at: string;
122
- user_id: string;
123
- }
package/dist/types.js DELETED
@@ -1,29 +0,0 @@
1
- export class SecretsSDKError extends Error {
2
- constructor(message, status, response) {
3
- super(message);
4
- this.status = status;
5
- this.response = response;
6
- this.name = 'SecretsSDKError';
7
- }
8
- }
9
- export class OriginMismatchError extends SecretsSDKError {
10
- constructor(message = 'Origin not allowed for this token') {
11
- super(message, 403);
12
- this.name = 'OriginMismatchError';
13
- }
14
- }
15
- export class RateLimitError extends SecretsSDKError {
16
- constructor(message = 'Rate limit exceeded', retryAfter = 60, remaining = 0, limit = 100) {
17
- super(message, 429);
18
- this.name = 'RateLimitError';
19
- this.retryAfter = retryAfter;
20
- this.remaining = remaining;
21
- this.limit = limit;
22
- }
23
- }
24
- export class InvalidTokenError extends SecretsSDKError {
25
- constructor(message = 'Invalid or expired token') {
26
- super(message, 401);
27
- this.name = 'InvalidTokenError';
28
- }
29
- }