@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.
- package/README.md +89 -0
- package/dist/api/client.d.ts +111 -0
- package/dist/api/client.d.ts.map +1 -0
- package/dist/api/client.js +186 -0
- package/dist/api/client.js.map +1 -0
- package/dist/cli/commands/init.d.ts +7 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +104 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/login.d.ts +7 -0
- package/dist/cli/commands/login.d.ts.map +1 -0
- package/dist/cli/commands/login.js +154 -0
- package/dist/cli/commands/login.js.map +1 -0
- package/dist/cli/commands/record.d.ts +14 -0
- package/dist/cli/commands/record.d.ts.map +1 -0
- package/dist/cli/commands/record.js +244 -0
- package/dist/cli/commands/record.js.map +1 -0
- package/dist/core/chunk-uploader.d.ts +52 -0
- package/dist/core/chunk-uploader.d.ts.map +1 -0
- package/dist/core/chunk-uploader.js +180 -0
- package/dist/core/chunk-uploader.js.map +1 -0
- package/dist/core/pty-manager.d.ts +72 -0
- package/dist/core/pty-manager.d.ts.map +1 -0
- package/dist/core/pty-manager.js +205 -0
- package/dist/core/pty-manager.js.map +1 -0
- package/dist/core/recorder.d.ts +104 -0
- package/dist/core/recorder.d.ts.map +1 -0
- package/dist/core/recorder.js +330 -0
- package/dist/core/recorder.js.map +1 -0
- package/dist/core/stream-client.d.ts +62 -0
- package/dist/core/stream-client.d.ts.map +1 -0
- package/dist/core/stream-client.js +185 -0
- package/dist/core/stream-client.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +75 -0
- package/dist/index.js.map +1 -0
- package/dist/storage/auth-store.d.ts +37 -0
- package/dist/storage/auth-store.d.ts.map +1 -0
- package/dist/storage/auth-store.js +168 -0
- package/dist/storage/auth-store.js.map +1 -0
- package/dist/storage/config-store.d.ts +74 -0
- package/dist/storage/config-store.d.ts.map +1 -0
- package/dist/storage/config-store.js +164 -0
- package/dist/storage/config-store.js.map +1 -0
- 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":";AAAA;;;;GAIG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AASH,oCA+DC;AAtED,mDAAqC;AACrC,kDAA0B;AAC1B,8CAAsB;AACtB,yDAAqD;AACrD,6DAAyD;AACzD,6CAA6C;AAEtC,KAAK,UAAU,YAAY;IAChC,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC,CAAC;IAEtD,MAAM,SAAS,GAAG,IAAI,sBAAS,EAAE,CAAC;IAClC,MAAM,WAAW,GAAG,IAAI,0BAAW,EAAE,CAAC;IAEtC,6BAA6B;IAC7B,MAAM,cAAc,GAAG,MAAM,SAAS,CAAC,SAAS,EAAE,CAAC;IACnD,IAAI,cAAc,EAAE,CAAC;QACnB,MAAM,EAAE,GAAG,QAAQ,CAAC,eAAe,CAAC;YAClC,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,MAAM,EAAE,OAAO,CAAC,MAAM;SACvB,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,MAAM,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,EAAE;YACnD,EAAE,CAAC,QAAQ,CACT,eAAK,CAAC,MAAM,CAAC,gEAAgE,CAAC,EAC9E,OAAO,CACR,CAAC;QACJ,CAAC,CAAC,CAAC;QACH,EAAE,CAAC,KAAK,EAAE,CAAC;QAEX,IAAI,MAAM,CAAC,WAAW,EAAE,KAAK,GAAG,EAAE,CAAC;YACjC,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC,CAAC;YAC5C,OAAO;QACT,CAAC;IACH,CAAC;IAED,qBAAqB;IACrB,MAAM,MAAM,GAAG,MAAM,eAAe,EAAE,CAAC;IAEvC,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1C,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,GAAG,CAAC,6BAA6B,CAAC,CAAC,CAAC;QACtD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,mBAAmB;IACnB,MAAM,OAAO,GAAG,IAAA,aAAG,EAAC,uBAAuB,CAAC,CAAC,KAAK,EAAE,CAAC;IAErD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,WAAW,CAAC,SAAS,EAAE,CAAC;QACvC,MAAM,MAAM,GAAG,IAAI,kBAAS,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;QAE1D,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,cAAc,EAAE,CAAC;QAE9C,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,CAAC,IAAI,CAAC,eAAK,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC,CAAC;YAC3C,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,4DAA4D,CAAC,CAAC,CAAC;YACtF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,eAAe;QACf,MAAM,SAAS,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QAElC,OAAO,CAAC,OAAO,CAAC,eAAK,CAAC,KAAK,CAAC,6BAA6B,CAAC,CAAC,CAAC;QAC5D,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,yCAAyC,CAAC,CAAC,CAAC;QACnE,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,oBAAoB,eAAK,CAAC,KAAK,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC;IAC9E,CAAC;IAAC,OAAO,KAAU,EAAE,CAAC;QACpB,OAAO,CAAC,IAAI,CAAC,eAAK,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC,CAAC;QACjD,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,GAAG,CAAC,OAAO,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAC/C,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,4DAA4D,CAAC,CAAC,CAAC;QACtF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAS,eAAe;IACtB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,MAAM,EAAE,GAAG,QAAQ,CAAC,eAAe,CAAC;YAClC,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,MAAM,EAAE,OAAO,CAAC,MAAM;SACvB,CAAC,CAAC;QAEH,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,4DAA4D,CAAC,CAAC,CAAC;QAEtF,wCAAwC;QACxC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;QAC5B,MAAM,eAAe,GAAG,KAAK,CAAC,KAAK,CAAC;QAEpC,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;YACrB,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QACzB,CAAC;QAED,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC;QAE7C,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,SAAS,OAAO,CAAC,IAAI;YACpC,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YAElC,QAAQ,GAAG,EAAE,CAAC;gBACZ,KAAK,IAAI,CAAC;gBACV,KAAK,IAAI,CAAC;gBACV,KAAK,QAAQ,EAAE,SAAS;oBACtB,gBAAgB;oBAChB,KAAK,CAAC,cAAc,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;oBACtC,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;wBACrB,KAAK,CAAC,UAAU,CAAC,eAAe,IAAI,KAAK,CAAC,CAAC;oBAC7C,CAAC;oBACD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;oBAC3B,EAAE,CAAC,KAAK,EAAE,CAAC;oBACX,OAAO,CAAC,MAAM,CAAC,CAAC;oBAChB,MAAM;gBACR,KAAK,QAAQ,EAAE,SAAS;oBACtB,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC,CAAC;oBAC/C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;oBAChB,MAAM;gBACR,KAAK,MAAM,CAAC,CAAC,YAAY;gBACzB,KAAK,IAAI;oBACP,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;wBACtB,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;wBAC7B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;oBAChC,CAAC;oBACD,MAAM;gBACR;oBACE,kCAAkC;oBAClC,MAAM,IAAI,GAAG,CAAC;oBACd,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;oBAC1B,MAAM;YACV,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -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
|