recker 1.0.34 → 1.0.35

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.
@@ -271,11 +271,15 @@ export class HlsPromise {
271
271
  }
272
272
  }
273
273
  async info() {
274
- const content = await this.client.get(this.manifestUrl).text();
274
+ const content = await this.client.get(this.manifestUrl, {
275
+ signal: this.abortController.signal,
276
+ }).text();
275
277
  if (isMasterPlaylist(content)) {
276
278
  const master = parseMasterPlaylist(content, this.manifestUrl);
277
279
  const selectedVariant = selectVariant(master.variants, this.options.quality);
278
- const playlistContent = await this.client.get(selectedVariant.url).text();
280
+ const playlistContent = await this.client.get(selectedVariant.url, {
281
+ signal: this.abortController.signal,
282
+ }).text();
279
283
  const playlist = parseMediaPlaylist(playlistContent, selectedVariant.url);
280
284
  const totalDuration = playlist.endList
281
285
  ? playlist.segments.reduce((sum, s) => sum + s.duration, 0)
@@ -299,7 +303,9 @@ export class HlsPromise {
299
303
  };
300
304
  }
301
305
  async resolveMediaPlaylist() {
302
- const content = await this.client.get(this.manifestUrl).text();
306
+ const content = await this.client.get(this.manifestUrl, {
307
+ signal: this.abortController.signal,
308
+ }).text();
303
309
  if (!isMasterPlaylist(content)) {
304
310
  return this.manifestUrl;
305
311
  }
@@ -308,7 +314,9 @@ export class HlsPromise {
308
314
  return variant.url;
309
315
  }
310
316
  async fetchMediaPlaylist(url) {
311
- const content = await this.client.get(url).text();
317
+ const content = await this.client.get(url, {
318
+ signal: this.abortController.signal,
319
+ }).text();
312
320
  return parseMediaPlaylist(content, url);
313
321
  }
314
322
  async downloadSegment(segment) {
@@ -1 +1,5 @@
1
- export declare function resolvePreset(name: string): any;
1
+ export interface ResolvePresetOptions {
2
+ throwOnError?: boolean;
3
+ silent?: boolean;
4
+ }
5
+ export declare function resolvePreset(name: string, options?: ResolvePresetOptions): any;
@@ -10,38 +10,62 @@ const ENV_MAPPING = {
10
10
  slack: ['SLACK_TOKEN'],
11
11
  vercel: ['VERCEL_TOKEN'],
12
12
  supabase: ['SUPABASE_URL', 'SUPABASE_KEY'],
13
+ groq: ['GROQ_API_KEY'],
14
+ google: ['GOOGLE_API_KEY'],
15
+ xai: ['XAI_API_KEY'],
16
+ mistral: ['MISTRAL_API_KEY'],
17
+ cohere: ['COHERE_API_KEY'],
18
+ deepseek: ['DEEPSEEK_API_KEY'],
19
+ fireworks: ['FIREWORKS_API_KEY'],
20
+ together: ['TOGETHER_API_KEY'],
21
+ perplexity: ['PERPLEXITY_API_KEY'],
13
22
  };
14
- export function resolvePreset(name) {
23
+ export function resolvePreset(name, options = {}) {
24
+ const { throwOnError = false, silent = false } = options;
15
25
  const presetFn = presets[name];
16
26
  if (!presetFn) {
17
- console.error(colors.red(`Error: Unknown preset '@${name}'`));
18
- process.exit(1);
27
+ const msg = `Unknown preset '@${name}'`;
28
+ if (throwOnError) {
29
+ throw new Error(msg);
30
+ }
31
+ if (!silent) {
32
+ console.error(colors.red(`Error: ${msg}`));
33
+ }
34
+ return null;
19
35
  }
20
36
  const requiredEnvs = ENV_MAPPING[name];
21
- const options = {};
37
+ const presetOptions = {};
22
38
  if (requiredEnvs) {
23
39
  let missing = false;
24
40
  for (const envVar of requiredEnvs) {
25
41
  const value = process.env[envVar];
26
42
  if (!value) {
27
- console.error(colors.yellow(`Warning: Missing env variable ${envVar} for preset @${name}`));
43
+ if (!silent) {
44
+ console.error(colors.yellow(`Warning: Missing env variable ${envVar} for preset @${name}`));
45
+ }
28
46
  missing = true;
29
47
  }
30
48
  else {
31
49
  const key = mapEnvToOption(name, envVar);
32
- options[key] = value;
50
+ presetOptions[key] = value;
33
51
  }
34
52
  }
35
- if (missing) {
53
+ if (missing && !silent) {
36
54
  console.log(colors.gray(`Tip: export ${requiredEnvs.join('=... ')}=...`));
37
55
  }
38
56
  }
39
57
  try {
40
- return presetFn(options);
58
+ return presetFn(presetOptions);
41
59
  }
42
60
  catch (error) {
43
- console.error(colors.red(`Error initializing preset @${name}: ${error.message}`));
44
- process.exit(1);
61
+ const msg = `Error initializing preset @${name}: ${error.message}`;
62
+ if (throwOnError) {
63
+ throw new Error(msg);
64
+ }
65
+ if (!silent) {
66
+ console.error(colors.red(msg));
67
+ }
68
+ return null;
45
69
  }
46
70
  }
47
71
  function mapEnvToOption(preset, env) {
@@ -1,2 +1,7 @@
1
1
  import readline from 'node:readline';
2
- export declare function startAIChat(rl: readline.Interface, provider?: string, apiKey?: string, model?: string): Promise<void>;
2
+ import type { Client } from '../../core/client.js';
3
+ export interface AIMode {
4
+ aiClients: Map<string, Client>;
5
+ variables?: Record<string, any>;
6
+ }
7
+ export declare function startAIChat(rl: readline.Interface, provider: string | undefined, context: AIMode): Promise<void>;
@@ -1,100 +1,304 @@
1
1
  import readline from 'node:readline';
2
2
  import colors from '../../utils/colors.js';
3
- import { createAI } from '../../ai/client.js';
4
- export async function startAIChat(rl, provider = 'openai', apiKey, model) {
5
- console.clear();
6
- console.log(colors.bold(colors.magenta(`🤖 Rek AI Chat (${provider})`)));
7
- console.log(colors.gray('Type your message. Ctrl+C to exit.'));
8
- const envKey = provider === 'openai' ? 'OPENAI_API_KEY' : 'ANTHROPIC_API_KEY';
9
- const key = apiKey || process.env[envKey];
10
- if (!key) {
11
- console.log(colors.yellow(`
12
- Warning: No API Key found for ${provider}.`));
13
- console.log(`Please set it via environment variable ${colors.bold(envKey)} or passing it to the command.`);
14
- console.log(`Example: set ${envKey}=sk-... inside the shell.`);
3
+ import { createClient } from '../../core/client.js';
4
+ import { resolvePreset } from '../presets.js';
5
+ const AI_PRESETS = [
6
+ 'openai', 'anthropic', 'groq', 'google', 'xai',
7
+ 'mistral', 'cohere', 'deepseek', 'fireworks', 'together', 'perplexity'
8
+ ];
9
+ const ENV_VAR_MAP = {
10
+ openai: 'OPENAI_API_KEY',
11
+ anthropic: 'ANTHROPIC_API_KEY',
12
+ google: 'GOOGLE_API_KEY',
13
+ groq: 'GROQ_API_KEY',
14
+ xai: 'XAI_API_KEY',
15
+ mistral: 'MISTRAL_API_KEY',
16
+ cohere: 'COHERE_API_KEY',
17
+ deepseek: 'DEEPSEEK_API_KEY',
18
+ fireworks: 'FIREWORKS_API_KEY',
19
+ together: 'TOGETHER_API_KEY',
20
+ perplexity: 'PERPLEXITY_API_KEY',
21
+ };
22
+ export async function startAIChat(rl, provider = 'openai', context) {
23
+ let currentProvider = provider.toLowerCase();
24
+ if (!AI_PRESETS.includes(currentProvider)) {
25
+ console.log(colors.red(`Unknown AI provider: ${currentProvider}`));
26
+ console.log(colors.gray(`Available: ${AI_PRESETS.join(', ')}`));
15
27
  return;
16
28
  }
17
- const client = createAI({
18
- defaultProvider: provider,
19
- providers: {
20
- [provider]: { apiKey: key }
21
- },
22
- observability: false
23
- });
24
- const history = [
25
- { role: 'system', content: 'You are Recker AI, a helpful and concise assistant in a terminal environment.' }
26
- ];
27
- rl.setPrompt(colors.magenta('You › '));
29
+ console.clear();
30
+ printHeader(currentProvider);
31
+ let client = await getOrCreateClient(currentProvider, context);
32
+ if (!client)
33
+ return;
34
+ const getPrompt = () => colors.magenta(`${currentProvider} › `);
35
+ rl.setPrompt(getPrompt());
28
36
  rl.prompt();
37
+ let isGenerating = false;
38
+ let abortController = null;
29
39
  return new Promise((resolve) => {
40
+ const cleanup = () => {
41
+ rl.off('line', onLine);
42
+ rl.off('SIGINT', onSigInt);
43
+ if (process.stdin.isTTY) {
44
+ process.stdin.off('keypress', onKeypress);
45
+ }
46
+ console.log('');
47
+ console.log(colors.gray('Exiting AI mode...'));
48
+ };
30
49
  const onLine = async (line) => {
31
50
  const input = line.trim();
32
51
  if (!input) {
33
52
  rl.prompt();
34
53
  return;
35
54
  }
36
- if (input.toLowerCase() === '/clear') {
37
- history.length = 1;
38
- console.clear();
39
- console.log(colors.gray('Context cleared.'));
40
- rl.prompt();
41
- return;
55
+ if (input.startsWith('/')) {
56
+ const handled = await handleCommand(input, currentProvider, client, context, rl, () => {
57
+ cleanup();
58
+ resolve();
59
+ }, async (newProvider) => {
60
+ currentProvider = newProvider;
61
+ client = await getOrCreateClient(currentProvider, context);
62
+ if (client) {
63
+ rl.setPrompt(getPrompt());
64
+ console.log(colors.green(`Switched to ${currentProvider}`));
65
+ const memory = client.ai.getMemory();
66
+ if (memory.length > 0) {
67
+ console.log(colors.gray(`Memory: ${Math.floor(memory.length / 2)} pairs`));
68
+ }
69
+ }
70
+ rl.prompt();
71
+ });
72
+ if (handled)
73
+ return;
42
74
  }
43
- if (input.toLowerCase() === '/exit') {
44
- cleanup();
45
- resolve();
75
+ if (!client || !client.hasAI) {
76
+ console.log(colors.red('AI client not available'));
77
+ rl.prompt();
46
78
  return;
47
79
  }
48
- history.push({ role: 'user', content: input });
49
80
  rl.pause();
50
- process.stdout.write(colors.cyan('AI › '));
51
- let fullResponse = '';
81
+ isGenerating = true;
82
+ abortController = new AbortController();
83
+ const model = client._aiConfig?.model || currentProvider;
84
+ process.stdout.write(colors.gray(`${model} › `));
52
85
  try {
53
- const stream = await client.stream({
54
- provider: provider,
55
- model: model,
56
- messages: history
57
- });
58
- for await (const chunk of stream) {
59
- const content = typeof chunk === 'string' ? chunk : chunk.content || chunk.delta?.content || '';
60
- if (content) {
61
- process.stdout.write(content);
62
- fullResponse += content;
86
+ const stream = await client.ai.chatStream(input);
87
+ let hasContent = false;
88
+ for await (const event of stream) {
89
+ if (abortController?.signal.aborted) {
90
+ console.log(colors.yellow('\n[Interrupted]'));
91
+ break;
92
+ }
93
+ if (event.type === 'text') {
94
+ if (!hasContent) {
95
+ process.stdout.write('\n' + colors.orange(event.content));
96
+ hasContent = true;
97
+ }
98
+ else {
99
+ process.stdout.write(colors.orange(event.content));
100
+ }
101
+ }
102
+ else if (event.type === 'error') {
103
+ console.log(colors.red(`\nError: ${event.error}`));
63
104
  }
64
105
  }
65
- process.stdout.write('\n');
66
- history.push({ role: 'assistant', content: fullResponse });
106
+ console.log(colors.reset(''));
107
+ const memory = client.ai.getMemory();
108
+ const pairs = Math.floor(memory.length / 2);
109
+ console.log(colors.gray(`Memory: ${pairs}/12 pairs`));
67
110
  }
68
111
  catch (error) {
69
- console.log(colors.red(`
70
- Error: ${error.message}`));
71
- if (error.cause)
72
- console.log(colors.gray(error.cause));
112
+ handleError(error, currentProvider);
73
113
  }
74
114
  finally {
115
+ isGenerating = false;
116
+ abortController = null;
75
117
  rl.resume();
76
118
  rl.prompt();
77
119
  }
78
120
  };
79
- const cleanup = () => {
80
- rl.off('line', onLine);
81
- rl.off('SIGINT', onSigInt);
82
- process.stdin.off('keypress', onKeypress);
83
- };
84
- if (process.stdin.isTTY)
85
- readline.emitKeypressEvents(process.stdin);
86
121
  const onKeypress = (_str, key) => {
87
122
  if (key && key.name === 'escape') {
88
- cleanup();
89
- resolve();
123
+ if (isGenerating && abortController) {
124
+ abortController.abort();
125
+ }
126
+ else {
127
+ cleanup();
128
+ resolve();
129
+ }
90
130
  }
91
131
  };
92
- process.stdin.on('keypress', onKeypress);
132
+ if (process.stdin.isTTY) {
133
+ readline.emitKeypressEvents(process.stdin);
134
+ process.stdin.on('keypress', onKeypress);
135
+ }
93
136
  rl.on('line', onLine);
94
137
  const onSigInt = () => {
95
- cleanup();
96
- resolve();
138
+ if (isGenerating && abortController) {
139
+ abortController.abort();
140
+ }
141
+ else {
142
+ cleanup();
143
+ resolve();
144
+ }
97
145
  };
98
146
  rl.once('SIGINT', onSigInt);
99
147
  });
100
148
  }
149
+ async function getOrCreateClient(provider, context) {
150
+ let client = context.aiClients.get(provider);
151
+ if (client) {
152
+ return client;
153
+ }
154
+ try {
155
+ const presetConfig = resolvePreset(provider);
156
+ if (!presetConfig) {
157
+ console.log(colors.red(`Unknown preset: ${provider}`));
158
+ return null;
159
+ }
160
+ if (!presetConfig._aiConfig) {
161
+ console.log(colors.red(`Preset ${provider} does not support AI features.`));
162
+ return null;
163
+ }
164
+ client = createClient(presetConfig);
165
+ context.aiClients.set(provider, client);
166
+ return client;
167
+ }
168
+ catch (error) {
169
+ const envVar = ENV_VAR_MAP[provider] || `${provider.toUpperCase()}_API_KEY`;
170
+ console.log(colors.red(`Failed to initialize ${provider}`));
171
+ console.log(colors.gray(`Make sure ${envVar} is set.`));
172
+ return null;
173
+ }
174
+ }
175
+ async function handleCommand(input, currentProvider, client, context, rl, onExit, onSwitch) {
176
+ const parts = input.slice(1).split(/\s+/);
177
+ const cmd = parts[0].toLowerCase();
178
+ const args = parts.slice(1);
179
+ switch (cmd) {
180
+ case 'exit':
181
+ case 'quit':
182
+ case 'q':
183
+ onExit();
184
+ return true;
185
+ case 'clear':
186
+ if (client?.hasAI) {
187
+ client.ai.clearMemory();
188
+ console.log(colors.green(`Memory cleared for ${currentProvider}`));
189
+ }
190
+ rl.prompt();
191
+ return true;
192
+ case 'switch':
193
+ case 's':
194
+ const newProvider = args[0]?.toLowerCase();
195
+ if (!newProvider) {
196
+ console.log(colors.yellow('Usage: /switch <provider>'));
197
+ console.log(colors.gray(`Available: ${AI_PRESETS.join(', ')}`));
198
+ rl.prompt();
199
+ return true;
200
+ }
201
+ if (!AI_PRESETS.includes(newProvider)) {
202
+ console.log(colors.red(`Unknown provider: ${newProvider}`));
203
+ console.log(colors.gray(`Available: ${AI_PRESETS.join(', ')}`));
204
+ rl.prompt();
205
+ return true;
206
+ }
207
+ await onSwitch(newProvider);
208
+ return true;
209
+ case 'model':
210
+ case 'm':
211
+ const model = client?._aiConfig?.model || 'default';
212
+ console.log(colors.cyan(`Current model: ${model}`));
213
+ console.log(colors.gray('Note: Model is set by the preset configuration.'));
214
+ rl.prompt();
215
+ return true;
216
+ case 'memory':
217
+ case 'mem':
218
+ if (client?.hasAI) {
219
+ const memory = client.ai.getMemory();
220
+ const pairs = Math.floor(memory.length / 2);
221
+ console.log(colors.cyan(`Memory: ${pairs}/12 pairs (${memory.length} messages)`));
222
+ if (pairs > 0) {
223
+ console.log(colors.gray('Last exchange:'));
224
+ const lastTwo = memory.slice(-2);
225
+ for (const msg of lastTwo) {
226
+ const role = msg.role === 'user' ? colors.magenta('You') : colors.orange('AI');
227
+ const preview = msg.content.slice(0, 100) + (msg.content.length > 100 ? '...' : '');
228
+ console.log(` ${role}: ${colors.gray(preview)}`);
229
+ }
230
+ }
231
+ }
232
+ rl.prompt();
233
+ return true;
234
+ case 'providers':
235
+ case 'list':
236
+ console.log(colors.cyan('Available AI providers:'));
237
+ for (const p of AI_PRESETS) {
238
+ const isActive = p === currentProvider ? colors.green(' (active)') : '';
239
+ const hasClient = context.aiClients.has(p) ? colors.gray(' [loaded]') : '';
240
+ console.log(` ${p}${isActive}${hasClient}`);
241
+ }
242
+ rl.prompt();
243
+ return true;
244
+ case 'help':
245
+ case 'h':
246
+ case '?':
247
+ printHelp();
248
+ rl.prompt();
249
+ return true;
250
+ default:
251
+ console.log(colors.yellow(`Unknown command: /${cmd}`));
252
+ console.log(colors.gray('Type /help for available commands'));
253
+ rl.prompt();
254
+ return true;
255
+ }
256
+ }
257
+ function handleError(error, provider) {
258
+ if (error.message?.includes('API key') || error.message?.includes('401') || error.message?.includes('Unauthorized')) {
259
+ const envVar = ENV_VAR_MAP[provider] || `${provider.toUpperCase()}_API_KEY`;
260
+ console.log(colors.red(`\nAuthentication error for ${provider}`));
261
+ console.log(colors.gray(`Set ${envVar} environment variable.`));
262
+ }
263
+ else if (error.message?.includes('429') || error.message?.includes('rate limit')) {
264
+ console.log(colors.yellow(`\nRate limited by ${provider}. Wait a moment and try again.`));
265
+ }
266
+ else {
267
+ console.log(colors.red(`\nError: ${error.message || error}`));
268
+ }
269
+ }
270
+ function printHeader(provider) {
271
+ console.log(colors.bold(colors.cyan('Rek AI Mode')));
272
+ console.log('');
273
+ console.log(` ${colors.gray('Provider:')} ${colors.white(provider)}`);
274
+ console.log('');
275
+ console.log(` ${colors.green('/switch')} ${colors.gray('<provider>')} ${colors.gray('Change AI provider')}`);
276
+ console.log(` ${colors.green('/clear')} ${colors.gray('Clear conversation memory')}`);
277
+ console.log(` ${colors.green('/memory')} ${colors.gray('Show memory status')}`);
278
+ console.log(` ${colors.green('/help')} ${colors.gray('Show all commands')}`);
279
+ console.log('');
280
+ console.log(` ${colors.yellow('ESC')} ${colors.gray('or')} ${colors.yellow('/exit')} ${colors.gray('Exit AI mode')}`);
281
+ console.log('');
282
+ console.log(colors.gray('─'.repeat(50)));
283
+ console.log('');
284
+ }
285
+ function printHelp() {
286
+ console.log(`
287
+ ${colors.bold(colors.cyan('AI Mode Commands'))}
288
+
289
+ ${colors.green('/switch <provider>')} Switch to another AI provider
290
+ ${colors.green('/clear')} Clear conversation memory
291
+ ${colors.green('/memory')} Show memory status
292
+ ${colors.green('/model')} Show current model
293
+ ${colors.green('/providers')} List available providers
294
+ ${colors.green('/help')} Show this help
295
+ ${colors.green('/exit')} Exit AI mode
296
+
297
+ ${colors.bold('Shortcuts')}
298
+ ${colors.gray('ESC')} Exit AI mode (or abort generation)
299
+ ${colors.gray('Ctrl+C')} Exit AI mode
300
+
301
+ ${colors.bold('Available Providers')}
302
+ ${AI_PRESETS.join(', ')}
303
+ `);
304
+ }
@@ -623,12 +623,12 @@ export class RekShell {
623
623
  }
624
624
  async runAIChat(args) {
625
625
  const provider = args[0] || 'openai';
626
- const model = args[1];
627
- const envKeyName = provider === 'openai' ? 'OPENAI_API_KEY' : 'ANTHROPIC_API_KEY';
628
- const apiKey = this.variables[envKeyName] || process.env[envKeyName];
629
626
  const { startAIChat } = await import('./ai-chat.js');
630
627
  await this.runInteractiveMode(async (rl) => {
631
- await startAIChat(rl, provider, apiKey, model);
628
+ await startAIChat(rl, provider, {
629
+ aiClients: this.aiClients,
630
+ variables: this.variables
631
+ });
632
632
  });
633
633
  }
634
634
  async runAIPresetChat(presetName, message) {
@@ -4474,21 +4474,22 @@ ${colors.bold('Network:')}
4474
4474
  ${colors.white('mode=realistic')} ${colors.gray('realistic | throughput | stress')}
4475
4475
  ${colors.white('http2=false')} ${colors.gray('Force HTTP/2')}
4476
4476
 
4477
- ${colors.green('chat <provider>')} Start AI Chat.
4478
- ${colors.gray('Providers:')} ${colors.white('openai')}, ${colors.white('anthropic')}
4479
- ${colors.gray('Arg:')} ${colors.white('model=...')} (optional)
4480
-
4481
4477
  ${colors.green('ws <url>')} Start interactive WebSocket session.
4482
4478
  ${colors.green('udp <url>')} Send UDP packet.
4483
4479
 
4484
4480
  ${colors.bold('AI Chat:')}
4485
- ${colors.green('@openai <message>')} Chat with OpenAI (GPT) with memory.
4486
- ${colors.green('@anthropic <msg>')} Chat with Anthropic (Claude) with memory.
4487
- ${colors.green('@groq <message>')} Chat with Groq (fast inference).
4488
- ${colors.green('@google <message>')} Chat with Google (Gemini).
4489
- ${colors.green('@xai <message>')} Chat with xAI (Grok).
4490
- ${colors.green('@mistral <message>')} Chat with Mistral AI.
4491
- ${colors.gray('Memory:')} ${colors.white('12 pairs (24 messages)')} preserved per preset.
4481
+ ${colors.green('ai [provider]')} Enter AI mode (interactive conversation).
4482
+ ${colors.gray('Default: openai. Use /switch to change provider.')}
4483
+ ${colors.gray('Exit: ESC, Ctrl+C, or /exit')}
4484
+ ${colors.gray('Commands: /switch, /clear, /memory, /help')}
4485
+
4486
+ ${colors.green('@<provider> <msg>')} Quick AI message (inline, no mode switch).
4487
+ ${colors.gray('Examples: @openai Hello!, @anthropic Explain this')}
4488
+ ${colors.gray('Providers: openai, anthropic, groq, google, xai,')}
4489
+ ${colors.gray(' mistral, cohere, deepseek, fireworks,')}
4490
+ ${colors.gray(' together, perplexity')}
4491
+
4492
+ ${colors.gray('Memory:')} ${colors.white('12 pairs (24 messages)')} per provider.
4492
4493
  ${colors.gray('Env:')} Set ${colors.white('OPENAI_API_KEY')}, ${colors.white('ANTHROPIC_API_KEY')}, etc.
4493
4494
  ${colors.green('ai:clear [preset]')} Clear AI memory (all or specific preset).
4494
4495
 
@@ -271,11 +271,15 @@ export class HlsPromise {
271
271
  }
272
272
  }
273
273
  async info() {
274
- const content = await this.client.get(this.manifestUrl).text();
274
+ const content = await this.client.get(this.manifestUrl, {
275
+ signal: this.abortController.signal,
276
+ }).text();
275
277
  if (isMasterPlaylist(content)) {
276
278
  const master = parseMasterPlaylist(content, this.manifestUrl);
277
279
  const selectedVariant = selectVariant(master.variants, this.options.quality);
278
- const playlistContent = await this.client.get(selectedVariant.url).text();
280
+ const playlistContent = await this.client.get(selectedVariant.url, {
281
+ signal: this.abortController.signal,
282
+ }).text();
279
283
  const playlist = parseMediaPlaylist(playlistContent, selectedVariant.url);
280
284
  const totalDuration = playlist.endList
281
285
  ? playlist.segments.reduce((sum, s) => sum + s.duration, 0)
@@ -299,7 +303,9 @@ export class HlsPromise {
299
303
  };
300
304
  }
301
305
  async resolveMediaPlaylist() {
302
- const content = await this.client.get(this.manifestUrl).text();
306
+ const content = await this.client.get(this.manifestUrl, {
307
+ signal: this.abortController.signal,
308
+ }).text();
303
309
  if (!isMasterPlaylist(content)) {
304
310
  return this.manifestUrl;
305
311
  }
@@ -308,7 +314,9 @@ export class HlsPromise {
308
314
  return variant.url;
309
315
  }
310
316
  async fetchMediaPlaylist(url) {
311
- const content = await this.client.get(url).text();
317
+ const content = await this.client.get(url, {
318
+ signal: this.abortController.signal,
319
+ }).text();
312
320
  return parseMediaPlaylist(content, url);
313
321
  }
314
322
  async downloadSegment(segment) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "recker",
3
- "version": "1.0.34",
3
+ "version": "1.0.35",
4
4
  "description": "AI & DevX focused HTTP client for Node.js 18+",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",