blok0 0.1.0 → 0.1.2

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.
@@ -0,0 +1,155 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TIMEOUT_HTML = exports.ERROR_HTML = exports.SUCCESS_HTML = exports.CALLBACK_PATH = exports.PORT_RANGE = exports.DEFAULT_TIMEOUT = exports.AUTHORIZE_ENDPOINT = exports.AUTH_BASE_URL = void 0;
4
+ exports.AUTH_BASE_URL = 'https://www.blok0.xyz';
5
+ exports.AUTHORIZE_ENDPOINT = '/api/authorize/cli';
6
+ exports.DEFAULT_TIMEOUT = 5 * 60 * 1000; // 5 minutes
7
+ exports.PORT_RANGE = { min: 3000, max: 4000 };
8
+ exports.CALLBACK_PATH = '/callback';
9
+ exports.SUCCESS_HTML = `
10
+ <!DOCTYPE html>
11
+ <html>
12
+ <head>
13
+ <title>Authentication Successful</title>
14
+ <style>
15
+ body {
16
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
17
+ background: #f5f5f5;
18
+ margin: 0;
19
+ padding: 0;
20
+ display: flex;
21
+ align-items: center;
22
+ justify-content: center;
23
+ height: 100vh;
24
+ }
25
+ .container {
26
+ background: white;
27
+ padding: 2rem;
28
+ border-radius: 8px;
29
+ box-shadow: 0 2px 10px rgba(0,0,0,0.1);
30
+ text-align: center;
31
+ max-width: 400px;
32
+ }
33
+ .success-icon {
34
+ color: #10b981;
35
+ font-size: 3rem;
36
+ margin-bottom: 1rem;
37
+ }
38
+ h1 {
39
+ color: #1f2937;
40
+ margin: 0 0 0.5rem 0;
41
+ font-size: 1.5rem;
42
+ }
43
+ p {
44
+ color: #6b7280;
45
+ margin: 0;
46
+ }
47
+ </style>
48
+ </head>
49
+ <body>
50
+ <div class="container">
51
+ <div class="success-icon">✓</div>
52
+ <h1>Authentication Successful!</h1>
53
+ <p>You can now close this window and return to your terminal.</p>
54
+ </div>
55
+ </body>
56
+ </html>
57
+ `;
58
+ exports.ERROR_HTML = `
59
+ <!DOCTYPE html>
60
+ <html>
61
+ <head>
62
+ <title>Authentication Failed</title>
63
+ <style>
64
+ body {
65
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
66
+ background: #f5f5f5;
67
+ margin: 0;
68
+ padding: 0;
69
+ display: flex;
70
+ align-items: center;
71
+ justify-content: center;
72
+ height: 100vh;
73
+ }
74
+ .container {
75
+ background: white;
76
+ padding: 2rem;
77
+ border-radius: 8px;
78
+ box-shadow: 0 2px 10px rgba(0,0,0,0.1);
79
+ text-align: center;
80
+ max-width: 400px;
81
+ }
82
+ .error-icon {
83
+ color: #ef4444;
84
+ font-size: 3rem;
85
+ margin-bottom: 1rem;
86
+ }
87
+ h1 {
88
+ color: #1f2937;
89
+ margin: 0 0 0.5rem 0;
90
+ font-size: 1.5rem;
91
+ }
92
+ p {
93
+ color: #6b7280;
94
+ margin: 0;
95
+ }
96
+ </style>
97
+ </head>
98
+ <body>
99
+ <div class="container">
100
+ <div class="error-icon">✗</div>
101
+ <h1>Authentication Failed</h1>
102
+ <p>Please try again or contact support if the problem persists.</p>
103
+ </div>
104
+ </body>
105
+ </html>
106
+ `;
107
+ exports.TIMEOUT_HTML = `
108
+ <!DOCTYPE html>
109
+ <html>
110
+ <head>
111
+ <title>Authentication Timeout</title>
112
+ <style>
113
+ body {
114
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
115
+ background: #f5f5f5;
116
+ margin: 0;
117
+ padding: 0;
118
+ display: flex;
119
+ align-items: center;
120
+ justify-content: center;
121
+ height: 100vh;
122
+ }
123
+ .container {
124
+ background: white;
125
+ padding: 2rem;
126
+ border-radius: 8px;
127
+ box-shadow: 0 2px 10px rgba(0,0,0,0.1);
128
+ text-align: center;
129
+ max-width: 400px;
130
+ }
131
+ .timeout-icon {
132
+ color: #f59e0b;
133
+ font-size: 3rem;
134
+ margin-bottom: 1rem;
135
+ }
136
+ h1 {
137
+ color: #1f2937;
138
+ margin: 0 0 0.5rem 0;
139
+ font-size: 1.5rem;
140
+ }
141
+ p {
142
+ color: #6b7280;
143
+ margin: 0;
144
+ }
145
+ </style>
146
+ </head>
147
+ <body>
148
+ <div class="container">
149
+ <div class="timeout-icon">⏱</div>
150
+ <h1>Authentication Timeout</h1>
151
+ <p>The authentication request has timed out. Please try again.</p>
152
+ </div>
153
+ </body>
154
+ </html>
155
+ `;
@@ -0,0 +1,61 @@
1
+ export interface AuthConfig {
2
+ apiEndpoint?: string;
3
+ refreshToken?: string;
4
+ tokenExpiry?: number;
5
+ }
6
+ export interface AuthCallback {
7
+ token: string;
8
+ expires_in?: number;
9
+ refresh_token?: string;
10
+ }
11
+ export interface AuthServerOptions {
12
+ port?: number;
13
+ timeout?: number;
14
+ state?: string;
15
+ }
16
+ export interface TokenResponse {
17
+ access_token: string;
18
+ refresh_token?: string;
19
+ expires_in?: number;
20
+ token_type?: string;
21
+ }
22
+ /**
23
+ * Load auth configuration from file
24
+ */
25
+ export declare function loadAuthConfig(): AuthConfig;
26
+ /**
27
+ * Save auth configuration to file
28
+ */
29
+ export declare function saveAuthConfig(config: AuthConfig): void;
30
+ /**
31
+ * Store access token securely
32
+ */
33
+ export declare function storeAccessToken(token: string): Promise<void>;
34
+ /**
35
+ * Get stored access token
36
+ */
37
+ export declare function getAccessToken(): Promise<string | null>;
38
+ /**
39
+ * Store refresh token securely
40
+ */
41
+ export declare function storeRefreshToken(token: string): Promise<void>;
42
+ /**
43
+ * Get stored refresh token
44
+ */
45
+ export declare function getRefreshToken(): Promise<string | null>;
46
+ /**
47
+ * Clear all stored credentials
48
+ */
49
+ export declare function clearCredentials(): Promise<void>;
50
+ /**
51
+ * Check if user is authenticated
52
+ */
53
+ export declare function isAuthenticated(): Promise<boolean>;
54
+ /**
55
+ * Validate token expiry (basic check)
56
+ */
57
+ export declare function isTokenExpired(expiry?: number): boolean;
58
+ /**
59
+ * Get authorization header for API requests
60
+ */
61
+ export declare function getAuthHeader(): Promise<string | null>;
@@ -0,0 +1,168 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.loadAuthConfig = loadAuthConfig;
37
+ exports.saveAuthConfig = saveAuthConfig;
38
+ exports.storeAccessToken = storeAccessToken;
39
+ exports.getAccessToken = getAccessToken;
40
+ exports.storeRefreshToken = storeRefreshToken;
41
+ exports.getRefreshToken = getRefreshToken;
42
+ exports.clearCredentials = clearCredentials;
43
+ exports.isAuthenticated = isAuthenticated;
44
+ exports.isTokenExpired = isTokenExpired;
45
+ exports.getAuthHeader = getAuthHeader;
46
+ const keytar = __importStar(require("keytar"));
47
+ const os = __importStar(require("os"));
48
+ const path = __importStar(require("path"));
49
+ const fs = __importStar(require("fs"));
50
+ const SERVICE_NAME = 'blok0';
51
+ const CONFIG_DIR = path.join(os.homedir(), '.blok0');
52
+ const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
53
+ /**
54
+ * Ensure config directory exists
55
+ */
56
+ function ensureConfigDir() {
57
+ if (!fs.existsSync(CONFIG_DIR)) {
58
+ fs.mkdirSync(CONFIG_DIR, { recursive: true });
59
+ }
60
+ }
61
+ /**
62
+ * Load auth configuration from file
63
+ */
64
+ function loadAuthConfig() {
65
+ try {
66
+ if (fs.existsSync(CONFIG_FILE)) {
67
+ const data = fs.readFileSync(CONFIG_FILE, 'utf-8');
68
+ return JSON.parse(data);
69
+ }
70
+ }
71
+ catch (error) {
72
+ console.warn('Failed to load auth config:', error);
73
+ }
74
+ return {};
75
+ }
76
+ /**
77
+ * Save auth configuration to file
78
+ */
79
+ function saveAuthConfig(config) {
80
+ ensureConfigDir();
81
+ fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
82
+ }
83
+ /**
84
+ * Store access token securely
85
+ */
86
+ async function storeAccessToken(token) {
87
+ try {
88
+ await keytar.setPassword(SERVICE_NAME, 'access_token', token);
89
+ }
90
+ catch (error) {
91
+ console.error('Failed to store access token:', error);
92
+ throw new Error('Unable to securely store access token');
93
+ }
94
+ }
95
+ /**
96
+ * Get stored access token
97
+ */
98
+ async function getAccessToken() {
99
+ try {
100
+ return await keytar.getPassword(SERVICE_NAME, 'access_token');
101
+ }
102
+ catch (error) {
103
+ console.error('Failed to retrieve access token:', error);
104
+ return null;
105
+ }
106
+ }
107
+ /**
108
+ * Store refresh token securely
109
+ */
110
+ async function storeRefreshToken(token) {
111
+ try {
112
+ await keytar.setPassword(SERVICE_NAME, 'refresh_token', token);
113
+ }
114
+ catch (error) {
115
+ console.error('Failed to store refresh token:', error);
116
+ throw new Error('Unable to securely store refresh token');
117
+ }
118
+ }
119
+ /**
120
+ * Get stored refresh token
121
+ */
122
+ async function getRefreshToken() {
123
+ try {
124
+ return await keytar.getPassword(SERVICE_NAME, 'refresh_token');
125
+ }
126
+ catch (error) {
127
+ console.error('Failed to retrieve refresh token:', error);
128
+ return null;
129
+ }
130
+ }
131
+ /**
132
+ * Clear all stored credentials
133
+ */
134
+ async function clearCredentials() {
135
+ try {
136
+ await keytar.deletePassword(SERVICE_NAME, 'access_token');
137
+ await keytar.deletePassword(SERVICE_NAME, 'refresh_token');
138
+ if (fs.existsSync(CONFIG_FILE)) {
139
+ fs.unlinkSync(CONFIG_FILE);
140
+ }
141
+ }
142
+ catch (error) {
143
+ console.error('Failed to clear credentials:', error);
144
+ throw new Error('Unable to clear stored credentials');
145
+ }
146
+ }
147
+ /**
148
+ * Check if user is authenticated
149
+ */
150
+ async function isAuthenticated() {
151
+ const token = await getAccessToken();
152
+ return token !== null;
153
+ }
154
+ /**
155
+ * Validate token expiry (basic check)
156
+ */
157
+ function isTokenExpired(expiry) {
158
+ if (!expiry)
159
+ return false;
160
+ return Date.now() >= expiry;
161
+ }
162
+ /**
163
+ * Get authorization header for API requests
164
+ */
165
+ async function getAuthHeader() {
166
+ const token = await getAccessToken();
167
+ return token ? `Bearer ${token}` : null;
168
+ }
@@ -0,0 +1,55 @@
1
+ import { EventEmitter } from 'events';
2
+ import { AuthCallback, AuthServerOptions } from './index';
3
+ export declare class AuthServer extends EventEmitter {
4
+ private server?;
5
+ private port?;
6
+ private state;
7
+ private timeoutId?;
8
+ private resolveCallback?;
9
+ private rejectCallback?;
10
+ constructor(options?: AuthServerOptions);
11
+ /**
12
+ * Generate a random state parameter for CSRF protection
13
+ */
14
+ private generateState;
15
+ /**
16
+ * Find an available port in the configured range
17
+ */
18
+ private findAvailablePort;
19
+ /**
20
+ * Check if a port is available
21
+ */
22
+ private isPortAvailable;
23
+ /**
24
+ * Handle incoming HTTP requests
25
+ */
26
+ private handleRequest;
27
+ /**
28
+ * Handle the OAuth callback
29
+ */
30
+ private handleCallback;
31
+ /**
32
+ * Initialize the server by finding an available port
33
+ */
34
+ initialize(): Promise<void>;
35
+ /**
36
+ * Start the authentication server
37
+ */
38
+ start(): Promise<AuthCallback>;
39
+ /**
40
+ * Handle authentication timeout
41
+ */
42
+ private handleTimeout;
43
+ /**
44
+ * Get the authorization URL to open in browser
45
+ */
46
+ getAuthorizationUrl(): string;
47
+ /**
48
+ * Clean up server resources
49
+ */
50
+ private cleanup;
51
+ /**
52
+ * Stop the server
53
+ */
54
+ stop(): void;
55
+ }
@@ -0,0 +1,236 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.AuthServer = void 0;
37
+ const http = __importStar(require("http"));
38
+ const events_1 = require("events");
39
+ const crypto_1 = require("crypto");
40
+ const constants_1 = require("./constants");
41
+ class AuthServer extends events_1.EventEmitter {
42
+ server;
43
+ port;
44
+ state;
45
+ timeoutId;
46
+ resolveCallback;
47
+ rejectCallback;
48
+ constructor(options = {}) {
49
+ super();
50
+ this.state = options.state || this.generateState();
51
+ }
52
+ /**
53
+ * Generate a random state parameter for CSRF protection
54
+ */
55
+ generateState() {
56
+ return (0, crypto_1.randomBytes)(32).toString('hex');
57
+ }
58
+ /**
59
+ * Find an available port in the configured range
60
+ */
61
+ async findAvailablePort() {
62
+ const { min, max } = constants_1.PORT_RANGE;
63
+ for (let port = min; port <= max; port++) {
64
+ if (await this.isPortAvailable(port)) {
65
+ return port;
66
+ }
67
+ }
68
+ throw new Error('No available ports found in range');
69
+ }
70
+ /**
71
+ * Check if a port is available
72
+ */
73
+ async isPortAvailable(port) {
74
+ return new Promise((resolve) => {
75
+ const testServer = http.createServer();
76
+ testServer.listen(port, '127.0.0.1', () => {
77
+ testServer.close(() => resolve(true));
78
+ });
79
+ testServer.on('error', () => resolve(false));
80
+ });
81
+ }
82
+ /**
83
+ * Handle incoming HTTP requests
84
+ */
85
+ handleRequest = (req, res) => {
86
+ try {
87
+ const fullUrl = `http://localhost:${this.port}${req.url}`;
88
+ const parsedUrl = new URL(fullUrl);
89
+ const pathname = parsedUrl.pathname;
90
+ // Handle callback endpoint
91
+ if (pathname === constants_1.CALLBACK_PATH && req.method === 'GET') {
92
+ this.handleCallback(parsedUrl, res);
93
+ return;
94
+ }
95
+ // Handle any other request with a 404
96
+ res.writeHead(404, { 'Content-Type': 'text/plain' });
97
+ res.end('Not found');
98
+ }
99
+ catch (error) {
100
+ console.error('Error parsing request URL:', error);
101
+ res.writeHead(400, { 'Content-Type': 'text/plain' });
102
+ res.end('Bad request');
103
+ }
104
+ };
105
+ /**
106
+ * Handle the OAuth callback
107
+ */
108
+ handleCallback(parsedUrl, res) {
109
+ const searchParams = parsedUrl.searchParams;
110
+ const state = searchParams.get('state');
111
+ const token = searchParams.get('token');
112
+ const error = searchParams.get('error');
113
+ // Validate state parameter
114
+ if (!state || state !== this.state) {
115
+ console.error('State parameter mismatch - possible CSRF attack');
116
+ console.error(`Expected: ${this.state}, Received: ${state}`);
117
+ res.writeHead(400, { 'Content-Type': 'text/html' });
118
+ res.end(constants_1.ERROR_HTML);
119
+ this.emit('error', new Error('Invalid state parameter'));
120
+ return;
121
+ }
122
+ // Handle error from authorization server
123
+ if (error) {
124
+ console.error('Authorization error:', error);
125
+ res.writeHead(400, { 'Content-Type': 'text/html' });
126
+ res.end(constants_1.ERROR_HTML);
127
+ this.emit('error', new Error(`Authorization failed: ${error}`));
128
+ return;
129
+ }
130
+ // Handle successful authorization
131
+ if (token) {
132
+ const authCallback = { token };
133
+ res.writeHead(200, { 'Content-Type': 'text/html' });
134
+ res.end(constants_1.SUCCESS_HTML);
135
+ this.emit('success', authCallback);
136
+ return;
137
+ }
138
+ // Handle missing token
139
+ console.error('No token received in callback');
140
+ res.writeHead(400, { 'Content-Type': 'text/html' });
141
+ res.end(constants_1.ERROR_HTML);
142
+ this.emit('error', new Error('No token received'));
143
+ }
144
+ /**
145
+ * Initialize the server by finding an available port
146
+ */
147
+ async initialize() {
148
+ if (!this.port) {
149
+ this.port = await this.findAvailablePort();
150
+ }
151
+ }
152
+ /**
153
+ * Start the authentication server
154
+ */
155
+ async start() {
156
+ return new Promise(async (resolve, reject) => {
157
+ this.resolveCallback = resolve;
158
+ this.rejectCallback = reject;
159
+ try {
160
+ // Initialize (find port) if not already done
161
+ await this.initialize();
162
+ // Create server
163
+ this.server = http.createServer(this.handleRequest);
164
+ // Set up event listeners
165
+ this.on('success', (callback) => {
166
+ this.cleanup();
167
+ this.resolveCallback(callback);
168
+ });
169
+ this.on('error', (error) => {
170
+ this.cleanup();
171
+ this.rejectCallback(error);
172
+ });
173
+ // Set timeout
174
+ this.timeoutId = setTimeout(() => {
175
+ this.handleTimeout();
176
+ }, constants_1.DEFAULT_TIMEOUT);
177
+ // Start server
178
+ await new Promise((resolveServer, rejectServer) => {
179
+ this.server.listen(this.port, '127.0.0.1', () => {
180
+ console.log(`🔐 Authentication server started on http://localhost:${this.port}`);
181
+ resolveServer();
182
+ });
183
+ this.server.on('error', rejectServer);
184
+ });
185
+ }
186
+ catch (error) {
187
+ this.cleanup();
188
+ reject(error);
189
+ }
190
+ });
191
+ }
192
+ /**
193
+ * Handle authentication timeout
194
+ */
195
+ handleTimeout() {
196
+ console.log('⏱️ Authentication timed out');
197
+ // Send timeout page to any open browser windows
198
+ if (this.server) {
199
+ // Note: In a real implementation, we'd track active connections
200
+ // For now, we'll just emit the error
201
+ }
202
+ this.emit('error', new Error('Authentication timed out'));
203
+ }
204
+ /**
205
+ * Get the authorization URL to open in browser
206
+ */
207
+ getAuthorizationUrl() {
208
+ const redirectUri = `http://localhost:${this.port}${constants_1.CALLBACK_PATH}`;
209
+ const params = new URLSearchParams({
210
+ redirect_uri: redirectUri,
211
+ state: this.state,
212
+ });
213
+ return `${constants_1.AUTH_BASE_URL}${constants_1.AUTHORIZE_ENDPOINT}?${params.toString()}`;
214
+ }
215
+ /**
216
+ * Clean up server resources
217
+ */
218
+ cleanup() {
219
+ if (this.timeoutId) {
220
+ clearTimeout(this.timeoutId);
221
+ this.timeoutId = undefined;
222
+ }
223
+ if (this.server) {
224
+ this.server.close();
225
+ this.server = undefined;
226
+ }
227
+ this.removeAllListeners();
228
+ }
229
+ /**
230
+ * Stop the server
231
+ */
232
+ stop() {
233
+ this.cleanup();
234
+ }
235
+ }
236
+ exports.AuthServer = AuthServer;