@b0xs/recorder 1.0.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.
Files changed (46) hide show
  1. package/README.md +89 -0
  2. package/dist/api/client.d.ts +111 -0
  3. package/dist/api/client.d.ts.map +1 -0
  4. package/dist/api/client.js +186 -0
  5. package/dist/api/client.js.map +1 -0
  6. package/dist/cli/commands/init.d.ts +7 -0
  7. package/dist/cli/commands/init.d.ts.map +1 -0
  8. package/dist/cli/commands/init.js +104 -0
  9. package/dist/cli/commands/init.js.map +1 -0
  10. package/dist/cli/commands/login.d.ts +7 -0
  11. package/dist/cli/commands/login.d.ts.map +1 -0
  12. package/dist/cli/commands/login.js +154 -0
  13. package/dist/cli/commands/login.js.map +1 -0
  14. package/dist/cli/commands/record.d.ts +14 -0
  15. package/dist/cli/commands/record.d.ts.map +1 -0
  16. package/dist/cli/commands/record.js +244 -0
  17. package/dist/cli/commands/record.js.map +1 -0
  18. package/dist/core/chunk-uploader.d.ts +52 -0
  19. package/dist/core/chunk-uploader.d.ts.map +1 -0
  20. package/dist/core/chunk-uploader.js +180 -0
  21. package/dist/core/chunk-uploader.js.map +1 -0
  22. package/dist/core/pty-manager.d.ts +72 -0
  23. package/dist/core/pty-manager.d.ts.map +1 -0
  24. package/dist/core/pty-manager.js +205 -0
  25. package/dist/core/pty-manager.js.map +1 -0
  26. package/dist/core/recorder.d.ts +104 -0
  27. package/dist/core/recorder.d.ts.map +1 -0
  28. package/dist/core/recorder.js +330 -0
  29. package/dist/core/recorder.js.map +1 -0
  30. package/dist/core/stream-client.d.ts +62 -0
  31. package/dist/core/stream-client.d.ts.map +1 -0
  32. package/dist/core/stream-client.js +185 -0
  33. package/dist/core/stream-client.js.map +1 -0
  34. package/dist/index.d.ts +8 -0
  35. package/dist/index.d.ts.map +1 -0
  36. package/dist/index.js +75 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/storage/auth-store.d.ts +37 -0
  39. package/dist/storage/auth-store.d.ts.map +1 -0
  40. package/dist/storage/auth-store.js +168 -0
  41. package/dist/storage/auth-store.js.map +1 -0
  42. package/dist/storage/config-store.d.ts +74 -0
  43. package/dist/storage/config-store.d.ts.map +1 -0
  44. package/dist/storage/config-store.js +164 -0
  45. package/dist/storage/config-store.js.map +1 -0
  46. package/package.json +54 -0
@@ -0,0 +1,154 @@
1
+ "use strict";
2
+ /**
3
+ * Login Command
4
+ *
5
+ * Authenticate with BOXS platform using API key
6
+ */
7
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
+ if (k2 === undefined) k2 = k;
9
+ var desc = Object.getOwnPropertyDescriptor(m, k);
10
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
+ desc = { enumerable: true, get: function() { return m[k]; } };
12
+ }
13
+ Object.defineProperty(o, k2, desc);
14
+ }) : (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ o[k2] = m[k];
17
+ }));
18
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
19
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
20
+ }) : function(o, v) {
21
+ o["default"] = v;
22
+ });
23
+ var __importStar = (this && this.__importStar) || (function () {
24
+ var ownKeys = function(o) {
25
+ ownKeys = Object.getOwnPropertyNames || function (o) {
26
+ var ar = [];
27
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
28
+ return ar;
29
+ };
30
+ return ownKeys(o);
31
+ };
32
+ return function (mod) {
33
+ if (mod && mod.__esModule) return mod;
34
+ var result = {};
35
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
36
+ __setModuleDefault(result, mod);
37
+ return result;
38
+ };
39
+ })();
40
+ var __importDefault = (this && this.__importDefault) || function (mod) {
41
+ return (mod && mod.__esModule) ? mod : { "default": mod };
42
+ };
43
+ Object.defineProperty(exports, "__esModule", { value: true });
44
+ exports.loginCommand = loginCommand;
45
+ const readline = __importStar(require("readline"));
46
+ const chalk_1 = __importDefault(require("chalk"));
47
+ const ora_1 = __importDefault(require("ora"));
48
+ const auth_store_1 = require("../../storage/auth-store");
49
+ const config_store_1 = require("../../storage/config-store");
50
+ const client_1 = require("../../api/client");
51
+ async function loginCommand() {
52
+ console.log(chalk_1.default.cyan('\n🔐 BOXS Recorder Login\n'));
53
+ const authStore = new auth_store_1.AuthStore();
54
+ const configStore = new config_store_1.ConfigStore();
55
+ // Check if already logged in
56
+ const existingApiKey = await authStore.getApiKey();
57
+ if (existingApiKey) {
58
+ const rl = readline.createInterface({
59
+ input: process.stdin,
60
+ output: process.stdout,
61
+ });
62
+ const answer = await new Promise((resolve) => {
63
+ rl.question(chalk_1.default.yellow('You are already logged in. Do you want to login again? (y/n): '), resolve);
64
+ });
65
+ rl.close();
66
+ if (answer.toLowerCase() !== 'y') {
67
+ console.log(chalk_1.default.gray('Login cancelled.'));
68
+ return;
69
+ }
70
+ }
71
+ // Prompt for API key
72
+ const apiKey = await promptForApiKey();
73
+ if (!apiKey || apiKey.trim().length === 0) {
74
+ console.log(chalk_1.default.red('\n✗ API key cannot be empty'));
75
+ process.exit(1);
76
+ }
77
+ // Validate API key
78
+ const spinner = (0, ora_1.default)('Validating API key...').start();
79
+ try {
80
+ const apiUrl = configStore.getApiUrl();
81
+ const client = new client_1.APIClient({ apiKey, baseUrl: apiUrl });
82
+ const isValid = await client.validateApiKey();
83
+ if (!isValid) {
84
+ spinner.fail(chalk_1.default.red('Invalid API key'));
85
+ console.log(chalk_1.default.gray('\nGet your API key from: https://boxs.sh/settings/api-keys'));
86
+ process.exit(1);
87
+ }
88
+ // Save API key
89
+ await authStore.setApiKey(apiKey);
90
+ spinner.succeed(chalk_1.default.green('Successfully authenticated!'));
91
+ console.log(chalk_1.default.gray(`\nYour API key has been saved securely.`));
92
+ console.log(chalk_1.default.gray(`You can now use: ${chalk_1.default.white('boxs record')}\n`));
93
+ }
94
+ catch (error) {
95
+ spinner.fail(chalk_1.default.red('Authentication failed'));
96
+ console.log(chalk_1.default.red(`\n✗ ${error.message}`));
97
+ console.log(chalk_1.default.gray('\nGet your API key from: https://boxs.sh/settings/api-keys'));
98
+ process.exit(1);
99
+ }
100
+ }
101
+ /**
102
+ * Prompt for API key with hidden input
103
+ */
104
+ function promptForApiKey() {
105
+ return new Promise((resolve) => {
106
+ const rl = readline.createInterface({
107
+ input: process.stdin,
108
+ output: process.stdout,
109
+ });
110
+ console.log(chalk_1.default.gray('Get your API key from: https://boxs.sh/settings/api-keys\n'));
111
+ // Hide input for password-like behavior
112
+ const stdin = process.stdin;
113
+ const originalRawMode = stdin.isRaw;
114
+ if (stdin.setRawMode) {
115
+ stdin.setRawMode(true);
116
+ }
117
+ let apiKey = '';
118
+ process.stdout.write('Enter your API key: ');
119
+ stdin.on('data', function handler(char) {
120
+ const str = char.toString('utf8');
121
+ switch (str) {
122
+ case '\n':
123
+ case '\r':
124
+ case '\u0004': // Ctrl+D
125
+ // Enter pressed
126
+ stdin.removeListener('data', handler);
127
+ if (stdin.setRawMode) {
128
+ stdin.setRawMode(originalRawMode || false);
129
+ }
130
+ process.stdout.write('\n');
131
+ rl.close();
132
+ resolve(apiKey);
133
+ break;
134
+ case '\u0003': // Ctrl+C
135
+ console.log(chalk_1.default.red('\n\nLogin cancelled.'));
136
+ process.exit(0);
137
+ break;
138
+ case '\x7f': // Backspace
139
+ case '\b':
140
+ if (apiKey.length > 0) {
141
+ apiKey = apiKey.slice(0, -1);
142
+ process.stdout.write('\b \b');
143
+ }
144
+ break;
145
+ default:
146
+ // Hide input by showing asterisks
147
+ apiKey += str;
148
+ process.stdout.write('*');
149
+ break;
150
+ }
151
+ });
152
+ });
153
+ }
154
+ //# sourceMappingURL=login.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../../../src/cli/commands/login.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Record Command
3
+ *
4
+ * Start recording a terminal session
5
+ */
6
+ export interface RecordOptions {
7
+ title?: string;
8
+ description?: string;
9
+ visibility?: 'public' | 'private' | 'unlisted' | 'team';
10
+ command?: string;
11
+ stream?: boolean;
12
+ }
13
+ export declare function recordCommand(options: RecordOptions): Promise<void>;
14
+ //# sourceMappingURL=record.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"record.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/record.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAUH,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,QAAQ,GAAG,SAAS,GAAG,UAAU,GAAG,MAAM,CAAC;IACxD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,wBAAsB,aAAa,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CA0IzE"}
@@ -0,0 +1,244 @@
1
+ "use strict";
2
+ /**
3
+ * Record Command
4
+ *
5
+ * Start recording a terminal session
6
+ */
7
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
+ if (k2 === undefined) k2 = k;
9
+ var desc = Object.getOwnPropertyDescriptor(m, k);
10
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
+ desc = { enumerable: true, get: function() { return m[k]; } };
12
+ }
13
+ Object.defineProperty(o, k2, desc);
14
+ }) : (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ o[k2] = m[k];
17
+ }));
18
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
19
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
20
+ }) : function(o, v) {
21
+ o["default"] = v;
22
+ });
23
+ var __importStar = (this && this.__importStar) || (function () {
24
+ var ownKeys = function(o) {
25
+ ownKeys = Object.getOwnPropertyNames || function (o) {
26
+ var ar = [];
27
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
28
+ return ar;
29
+ };
30
+ return ownKeys(o);
31
+ };
32
+ return function (mod) {
33
+ if (mod && mod.__esModule) return mod;
34
+ var result = {};
35
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
36
+ __setModuleDefault(result, mod);
37
+ return result;
38
+ };
39
+ })();
40
+ var __importDefault = (this && this.__importDefault) || function (mod) {
41
+ return (mod && mod.__esModule) ? mod : { "default": mod };
42
+ };
43
+ Object.defineProperty(exports, "__esModule", { value: true });
44
+ exports.recordCommand = recordCommand;
45
+ const readline = __importStar(require("readline"));
46
+ const chalk_1 = __importDefault(require("chalk"));
47
+ const ora_1 = __importDefault(require("ora"));
48
+ const auth_store_1 = require("../../storage/auth-store");
49
+ const config_store_1 = require("../../storage/config-store");
50
+ const client_1 = require("../../api/client");
51
+ const recorder_1 = require("../../core/recorder");
52
+ async function recordCommand(options) {
53
+ console.log(chalk_1.default.cyan('\n● BOXS Recorder\n'));
54
+ // Check authentication
55
+ const authStore = new auth_store_1.AuthStore();
56
+ const apiKey = await authStore.getApiKey();
57
+ if (!apiKey) {
58
+ console.log(chalk_1.default.red('✗ Not authenticated'));
59
+ console.log(chalk_1.default.gray('\nPlease run: ' + chalk_1.default.white('boxs login') + '\n'));
60
+ process.exit(1);
61
+ }
62
+ // Load config
63
+ const configStore = new config_store_1.ConfigStore();
64
+ const apiUrl = configStore.getApiUrl();
65
+ // Create API client
66
+ const apiClient = new client_1.APIClient({ apiKey, baseUrl: apiUrl });
67
+ // Get session metadata
68
+ const title = options.title || await promptWithDefault('Session title', `Recording ${new Date().toLocaleString()}`);
69
+ const description = options.description || await promptOptional('Description (optional)');
70
+ const visibility = options.visibility || configStore.getDefaultVisibility();
71
+ // Create session
72
+ const spinner = (0, ora_1.default)('Creating session...').start();
73
+ let session;
74
+ try {
75
+ session = await apiClient.createSession({
76
+ title,
77
+ description,
78
+ visibility,
79
+ });
80
+ spinner.succeed(chalk_1.default.green('Session created'));
81
+ console.log(chalk_1.default.gray(`Session ID: ${session.id}`));
82
+ }
83
+ catch (error) {
84
+ spinner.fail(chalk_1.default.red('Failed to create session'));
85
+ console.log(chalk_1.default.red(`\n✗ ${error.message}\n`));
86
+ process.exit(1);
87
+ }
88
+ // Get terminal size
89
+ const terminalSize = {
90
+ cols: process.stdout.columns || 80,
91
+ rows: process.stdout.rows || 24,
92
+ };
93
+ // Setup streaming if enabled
94
+ let streamingConfig;
95
+ if (options.stream) {
96
+ spinner.text = 'Starting stream...';
97
+ try {
98
+ const streamInfo = await apiClient.startStream(session.id);
99
+ streamingConfig = {
100
+ enabled: true,
101
+ wsUrl: streamInfo.wsUrl,
102
+ streamToken: streamInfo.streamToken,
103
+ };
104
+ spinner.succeed(chalk_1.default.green('Stream initialized'));
105
+ }
106
+ catch (error) {
107
+ spinner.fail(chalk_1.default.yellow('Failed to start stream (continuing without live streaming)'));
108
+ console.log(chalk_1.default.gray(` ${error.message}\n`));
109
+ // Continue without streaming
110
+ }
111
+ }
112
+ // Create recorder
113
+ const recorder = new recorder_1.Recorder({
114
+ sessionId: session.id,
115
+ userId: session.userId,
116
+ apiClient,
117
+ terminalSize,
118
+ chunkDuration: configStore.getChunkDuration(),
119
+ streaming: streamingConfig,
120
+ });
121
+ // Setup event handlers
122
+ recorder.on('started', () => {
123
+ console.log(chalk_1.default.green('\n● Recording started\n'));
124
+ if (options.stream) {
125
+ console.log(chalk_1.default.cyan('● Live streaming enabled\n'));
126
+ }
127
+ console.log(chalk_1.default.gray('Press Ctrl+C to stop recording'));
128
+ console.log(chalk_1.default.gray('Type ') + chalk_1.default.white('exit') + chalk_1.default.gray(' to end the session\n'));
129
+ });
130
+ recorder.on('stream-connected', () => {
131
+ console.log(chalk_1.default.cyan('● Connected to stream server\n'));
132
+ });
133
+ recorder.on('viewers', (count) => {
134
+ if (count > 0) {
135
+ process.stderr.write(chalk_1.default.gray(`\r👁 ${count} viewer${count !== 1 ? 's' : ''} watching`));
136
+ }
137
+ });
138
+ recorder.on('chunk-uploaded', (info) => {
139
+ // Subtle progress indicator
140
+ process.stderr.write(chalk_1.default.gray('.'));
141
+ });
142
+ recorder.on('command', (command) => {
143
+ // Commands are automatically detected and recorded
144
+ });
145
+ recorder.on('error', (error) => {
146
+ console.error(chalk_1.default.yellow(`\n⚠ Warning: ${error.message}`));
147
+ });
148
+ recorder.on('stopped', async (stats) => {
149
+ console.log(chalk_1.default.green('\n\n✓ Recording saved successfully'));
150
+ console.log(chalk_1.default.gray(`\nDuration: ${formatDuration(stats.duration)}`));
151
+ console.log(chalk_1.default.gray(`Chunks: ${stats.chunksUploaded}`));
152
+ console.log(chalk_1.default.gray(`Frames: ${stats.framesRecorded}`));
153
+ console.log(chalk_1.default.gray(`Commands: ${stats.eventsRecorded}`));
154
+ console.log(chalk_1.default.gray(`\nView at: ${chalk_1.default.white(recorder.getSessionUrl())}\n`));
155
+ });
156
+ // Setup signal handlers for graceful shutdown
157
+ setupSignalHandlers(recorder);
158
+ // Start recording
159
+ try {
160
+ await recorder.start({
161
+ command: options.command,
162
+ });
163
+ // Wait for recording to finish
164
+ await new Promise((resolve) => {
165
+ recorder.on('stopped', resolve);
166
+ });
167
+ }
168
+ catch (error) {
169
+ console.log(chalk_1.default.red(`\n✗ Error: ${error.message}\n`));
170
+ process.exit(1);
171
+ }
172
+ }
173
+ /**
174
+ * Setup signal handlers for graceful shutdown
175
+ */
176
+ function setupSignalHandlers(recorder) {
177
+ let isShuttingDown = false;
178
+ const shutdown = async () => {
179
+ if (isShuttingDown) {
180
+ // Force exit on second interrupt
181
+ console.log(chalk_1.default.red('\n\nForce exit. Recording may be incomplete.\n'));
182
+ process.exit(1);
183
+ }
184
+ isShuttingDown = true;
185
+ console.log(chalk_1.default.yellow('\n\n⏸ Stopping recording...'));
186
+ try {
187
+ await recorder.stop();
188
+ }
189
+ catch (error) {
190
+ console.error(chalk_1.default.red('Error during shutdown:'), error);
191
+ process.exit(1);
192
+ }
193
+ };
194
+ process.on('SIGINT', shutdown); // Ctrl+C
195
+ process.on('SIGTERM', shutdown); // Kill signal
196
+ }
197
+ /**
198
+ * Prompt with default value
199
+ */
200
+ function promptWithDefault(question, defaultValue) {
201
+ return new Promise((resolve) => {
202
+ const rl = readline.createInterface({
203
+ input: process.stdin,
204
+ output: process.stdout,
205
+ });
206
+ rl.question(`${question} ${chalk_1.default.gray(`(${defaultValue})`)}: `, (answer) => {
207
+ rl.close();
208
+ resolve(answer || defaultValue);
209
+ });
210
+ });
211
+ }
212
+ /**
213
+ * Prompt for optional value
214
+ */
215
+ function promptOptional(question) {
216
+ return new Promise((resolve) => {
217
+ const rl = readline.createInterface({
218
+ input: process.stdin,
219
+ output: process.stdout,
220
+ });
221
+ rl.question(`${question}: `, (answer) => {
222
+ rl.close();
223
+ resolve(answer || undefined);
224
+ });
225
+ });
226
+ }
227
+ /**
228
+ * Format duration in human-readable format
229
+ */
230
+ function formatDuration(ms) {
231
+ const seconds = Math.floor(ms / 1000);
232
+ const minutes = Math.floor(seconds / 60);
233
+ const hours = Math.floor(minutes / 60);
234
+ if (hours > 0) {
235
+ return `${hours}h ${minutes % 60}m ${seconds % 60}s`;
236
+ }
237
+ else if (minutes > 0) {
238
+ return `${minutes}m ${seconds % 60}s`;
239
+ }
240
+ else {
241
+ return `${seconds}s`;
242
+ }
243
+ }
244
+ //# sourceMappingURL=record.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"record.js","sourceRoot":"","sources":["../../../src/cli/commands/record.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Chunk Uploader
3
+ *
4
+ * Handles chunk upload with retry logic and offline buffering
5
+ */
6
+ import { APIClient } from '../api/client';
7
+ export interface ChunkUploadOptions {
8
+ sessionId: string;
9
+ apiClient: APIClient;
10
+ maxRetries?: number;
11
+ retryDelay?: number;
12
+ }
13
+ export declare class ChunkUploader {
14
+ private sessionId;
15
+ private apiClient;
16
+ private maxRetries;
17
+ private retryDelay;
18
+ private uploadQueue;
19
+ private retryAttempts;
20
+ private isOnline;
21
+ private bufferDir;
22
+ constructor(options: ChunkUploadOptions);
23
+ /**
24
+ * Upload a chunk
25
+ */
26
+ uploadChunk(chunkIndex: number, chunkData: Buffer): Promise<void>;
27
+ /**
28
+ * Handle upload failure with retry logic
29
+ */
30
+ private handleUploadFailure;
31
+ /**
32
+ * Buffer chunk to disk for later upload
33
+ */
34
+ private bufferChunkToDisk;
35
+ /**
36
+ * Recover buffered chunks from disk
37
+ */
38
+ private recoverBufferedChunks;
39
+ /**
40
+ * Clean up buffer directory
41
+ */
42
+ cleanup(): Promise<void>;
43
+ /**
44
+ * Check if uploader is online
45
+ */
46
+ getOnlineStatus(): boolean;
47
+ /**
48
+ * Get number of pending retries
49
+ */
50
+ getPendingRetries(): number;
51
+ }
52
+ //# sourceMappingURL=chunk-uploader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chunk-uploader.d.ts","sourceRoot":"","sources":["../../src/core/chunk-uploader.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE1C,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,SAAS,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,WAAW,CAA8C;IACjE,OAAO,CAAC,aAAa,CAAkC;IACvD,OAAO,CAAC,QAAQ,CAAiB;IACjC,OAAO,CAAC,SAAS,CAAS;gBAEd,OAAO,EAAE,kBAAkB;IAQvC;;OAEG;IACG,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAoBvE;;OAEG;YACW,mBAAmB;IAgCjC;;OAEG;YACW,iBAAiB;IAc/B;;OAEG;YACW,qBAAqB;IA0CnC;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ9B;;OAEG;IACH,eAAe,IAAI,OAAO;IAI1B;;OAEG;IACH,iBAAiB,IAAI,MAAM;CAG5B"}
@@ -0,0 +1,180 @@
1
+ "use strict";
2
+ /**
3
+ * Chunk Uploader
4
+ *
5
+ * Handles chunk upload with retry logic and offline buffering
6
+ */
7
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
+ if (k2 === undefined) k2 = k;
9
+ var desc = Object.getOwnPropertyDescriptor(m, k);
10
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
+ desc = { enumerable: true, get: function() { return m[k]; } };
12
+ }
13
+ Object.defineProperty(o, k2, desc);
14
+ }) : (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ o[k2] = m[k];
17
+ }));
18
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
19
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
20
+ }) : function(o, v) {
21
+ o["default"] = v;
22
+ });
23
+ var __importStar = (this && this.__importStar) || (function () {
24
+ var ownKeys = function(o) {
25
+ ownKeys = Object.getOwnPropertyNames || function (o) {
26
+ var ar = [];
27
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
28
+ return ar;
29
+ };
30
+ return ownKeys(o);
31
+ };
32
+ return function (mod) {
33
+ if (mod && mod.__esModule) return mod;
34
+ var result = {};
35
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
36
+ __setModuleDefault(result, mod);
37
+ return result;
38
+ };
39
+ })();
40
+ Object.defineProperty(exports, "__esModule", { value: true });
41
+ exports.ChunkUploader = void 0;
42
+ const fs = __importStar(require("fs/promises"));
43
+ const path = __importStar(require("path"));
44
+ const os = __importStar(require("os"));
45
+ class ChunkUploader {
46
+ constructor(options) {
47
+ this.uploadQueue = [];
48
+ this.retryAttempts = new Map();
49
+ this.isOnline = true;
50
+ this.sessionId = options.sessionId;
51
+ this.apiClient = options.apiClient;
52
+ this.maxRetries = options.maxRetries || 5;
53
+ this.retryDelay = options.retryDelay || 1000; // 1 second base delay
54
+ this.bufferDir = path.join(os.homedir(), '.boxs', 'buffer', this.sessionId);
55
+ }
56
+ /**
57
+ * Upload a chunk
58
+ */
59
+ async uploadChunk(chunkIndex, chunkData) {
60
+ try {
61
+ const response = await this.apiClient.uploadChunk(this.sessionId, chunkData);
62
+ // Upload successful
63
+ this.retryAttempts.delete(chunkIndex);
64
+ this.isOnline = true;
65
+ // Try to recover any buffered chunks
66
+ await this.recoverBufferedChunks();
67
+ return;
68
+ }
69
+ catch (error) {
70
+ console.error(`Failed to upload chunk ${chunkIndex}:`, error.message);
71
+ // Handle upload failure
72
+ await this.handleUploadFailure(chunkIndex, chunkData, error);
73
+ }
74
+ }
75
+ /**
76
+ * Handle upload failure with retry logic
77
+ */
78
+ async handleUploadFailure(chunkIndex, chunkData, error) {
79
+ const attempts = this.retryAttempts.get(chunkIndex) || 0;
80
+ // Check if this is a network error
81
+ if (error.name === 'NetworkError' || error.code === 'ECONNREFUSED') {
82
+ this.isOnline = false;
83
+ }
84
+ // Max retries reached - buffer to disk
85
+ if (attempts >= this.maxRetries) {
86
+ console.warn(`Max retries reached for chunk ${chunkIndex}. Buffering to disk...`);
87
+ await this.bufferChunkToDisk(chunkIndex, chunkData);
88
+ this.retryAttempts.delete(chunkIndex);
89
+ return;
90
+ }
91
+ // Calculate exponential backoff delay
92
+ const delay = this.retryDelay * Math.pow(2, attempts);
93
+ this.retryAttempts.set(chunkIndex, attempts + 1);
94
+ console.log(`Retrying chunk ${chunkIndex} in ${delay}ms (attempt ${attempts + 1}/${this.maxRetries})`);
95
+ // Retry after delay
96
+ setTimeout(() => {
97
+ this.uploadChunk(chunkIndex, chunkData);
98
+ }, delay);
99
+ }
100
+ /**
101
+ * Buffer chunk to disk for later upload
102
+ */
103
+ async bufferChunkToDisk(chunkIndex, chunkData) {
104
+ try {
105
+ // Ensure buffer directory exists
106
+ await fs.mkdir(this.bufferDir, { recursive: true });
107
+ const chunkPath = path.join(this.bufferDir, `${chunkIndex}.boxs`);
108
+ await fs.writeFile(chunkPath, chunkData);
109
+ console.log(`Chunk ${chunkIndex} buffered to: ${chunkPath}`);
110
+ }
111
+ catch (error) {
112
+ console.error(`Failed to buffer chunk ${chunkIndex}:`, error.message);
113
+ }
114
+ }
115
+ /**
116
+ * Recover buffered chunks from disk
117
+ */
118
+ async recoverBufferedChunks() {
119
+ if (!this.isOnline) {
120
+ return; // Skip if offline
121
+ }
122
+ try {
123
+ // Check if buffer directory exists
124
+ const files = await fs.readdir(this.bufferDir);
125
+ if (files.length === 0) {
126
+ return;
127
+ }
128
+ console.log(`Found ${files.length} buffered chunks. Uploading...`);
129
+ // Upload each buffered chunk
130
+ for (const file of files) {
131
+ if (file.endsWith('.boxs')) {
132
+ const chunkIndex = parseInt(file.replace('.boxs', ''));
133
+ const chunkPath = path.join(this.bufferDir, file);
134
+ try {
135
+ const chunkData = await fs.readFile(chunkPath);
136
+ await this.apiClient.uploadChunk(this.sessionId, chunkData);
137
+ // Delete after successful upload
138
+ await fs.unlink(chunkPath);
139
+ console.log(`Recovered chunk ${chunkIndex}`);
140
+ }
141
+ catch (error) {
142
+ console.error(`Failed to recover chunk ${chunkIndex}:`, error.message);
143
+ // Leave file for next attempt
144
+ }
145
+ }
146
+ }
147
+ }
148
+ catch (error) {
149
+ // Buffer directory doesn't exist or is empty - that's fine
150
+ if (error.code !== 'ENOENT') {
151
+ console.error('Error recovering buffered chunks:', error.message);
152
+ }
153
+ }
154
+ }
155
+ /**
156
+ * Clean up buffer directory
157
+ */
158
+ async cleanup() {
159
+ try {
160
+ await fs.rm(this.bufferDir, { recursive: true, force: true });
161
+ }
162
+ catch (error) {
163
+ // Ignore errors
164
+ }
165
+ }
166
+ /**
167
+ * Check if uploader is online
168
+ */
169
+ getOnlineStatus() {
170
+ return this.isOnline;
171
+ }
172
+ /**
173
+ * Get number of pending retries
174
+ */
175
+ getPendingRetries() {
176
+ return this.retryAttempts.size;
177
+ }
178
+ }
179
+ exports.ChunkUploader = ChunkUploader;
180
+ //# sourceMappingURL=chunk-uploader.js.map