Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 28 additions & 1 deletion src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ import {
getBaseUrl,
setBaseUrl,
getSessionCookieName,
setSessionCookieName
setSessionCookieName,
getTimeout,
setTimeout
} from './config.js';

const program = new Command();
Expand All @@ -47,6 +49,7 @@ program
.version(VERSION)
.option('--base-url <url>', 'Overleaf instance base URL (overrides OVERLEAF_BASE_URL and config)')
.option('--cookie-name <name>', 'Session cookie name (default: overleaf_session2, use overleaf.sid for older instances)')
.option('--timeout <ms>', 'HTTP request timeout in milliseconds', parseInt)
.option('--verbose', 'Print every HTTP request, status, and error response body to stderr');

/**
Expand All @@ -65,6 +68,10 @@ async function getClient(cookieOpt?: string, baseUrlOpt?: string): Promise<Overl
const cookieName = (program.opts().cookieName as string | undefined) || getSessionCookieName();
const client = await OverleafClient.fromSessionCookie(cookie, baseUrl, cookieName);
if (program.opts().verbose) client.setVerbose(true);

const timeout = (program.opts().timeout as number | undefined) || getTimeout();
client.setGlobalTimeout(timeout);

return client;
}

Expand Down Expand Up @@ -1418,6 +1425,26 @@ configCmd
console.log(getSessionCookieName());
});

configCmd
.command('set-timeout <ms>')
.description('Set the default HTTP request timeout in milliseconds')
.action((ms: string) => {
const timeout = parseInt(ms, 10);
if (isNaN(timeout)) {
console.error(chalk.red('Invalid timeout value. Must be a number.'));
process.exit(1);
}
setTimeout(timeout);
console.log(chalk.green(`Default timeout set to: ${timeout}ms`));
});

configCmd
.command('get-timeout')
.description('Get the current default HTTP request timeout')
.action(() => {
console.log(`${getTimeout()}ms`);
});

program
.command('ignored [dir]')
.description('Show ignore patterns currently in effect for a project directory')
Expand Down
25 changes: 16 additions & 9 deletions src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ export class OverleafClient {
private csrf: string;
private baseUrl: string;
private verbose: boolean = false;
private timeoutMs: number = 10000;
// Cache per-project folder trees so repeated uploads in sync/upload calls
// don't re-fetch the tree via Socket.IO on every file.
private folderTreeCache: Map<string, Record<string, string>> = new Map();
Expand All @@ -148,6 +149,11 @@ export class OverleafClient {
this.verbose = v;
}

/** Set the global HTTP request timeout in milliseconds. */
setGlobalTimeout(ms: number): void {
this.timeoutMs = ms;
}

/**
* Resolve (and cache) the folder tree for a project. Falls back to a
* minimal tree containing only the root folder when the Socket.IO probe
Expand Down Expand Up @@ -312,7 +318,7 @@ export class OverleafClient {
expect?: 'text' | 'json' | 'buffer';
} = {}): Promise<{ status: number; ok: boolean; headers: Record<string, string | string[]>; body: string | Buffer | any }> {
const method = options.method || 'GET';
const timeoutMs = options.timeoutMs ?? 10000;
const timeoutMs = options.timeoutMs ?? this.timeoutMs;
const maxRedirects = options.maxRedirects ?? 5;
const expect = options.expect ?? 'text';

Expand Down Expand Up @@ -644,10 +650,11 @@ export class OverleafClient {
* characters in response headers (e.g. Content-Disposition with Unicode
* project names). See: https://github.com/aloth/olcli/issues/2
*/
private async downloadBuffer(url: string): Promise<Buffer> {
private async downloadBuffer(url: string, timeoutMs?: number): Promise<Buffer> {
const response = await this.httpRequest(url, {
headers: this.getHeaders(),
expect: 'buffer'
expect: 'buffer',
timeoutMs
});

if (!response.ok) {
Expand All @@ -665,8 +672,8 @@ export class OverleafClient {
* Uses downloadBuffer to avoid ByteString errors from non-Latin1
* Content-Disposition headers. See: https://github.com/aloth/olcli/issues/2
*/
async downloadProject(projectId: string): Promise<Buffer> {
return this.downloadBuffer(this.downloadUrl(projectId));
async downloadProject(projectId: string, timeoutMs?: number): Promise<Buffer> {
return this.downloadBuffer(this.downloadUrl(projectId), timeoutMs);
}

/**
Expand Down Expand Up @@ -718,9 +725,9 @@ export class OverleafClient {
/**
* Download compiled PDF
*/
async downloadPdf(projectId: string): Promise<Buffer> {
async downloadPdf(projectId: string, timeoutMs?: number): Promise<Buffer> {
const { pdfUrl } = await this.compileProject(projectId);
return this.downloadBuffer(pdfUrl);
return this.downloadBuffer(pdfUrl, timeoutMs);
}

/**
Expand Down Expand Up @@ -2085,7 +2092,7 @@ export class OverleafClient {
/**
* Download a compile output file (logs, bbl, aux, etc.)
*/
async downloadOutputFile(url: string): Promise<Buffer> {
return this.downloadBuffer(url);
async downloadOutputFile(url: string, timeoutMs?: number): Promise<Buffer> {
return this.downloadBuffer(url, timeoutMs);
}
}
12 changes: 11 additions & 1 deletion src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ interface OlcliConfig {
lastProject?: string;
baseUrl?: string;
sessionCookieName?: string;
timeout?: number;
}

const config = new Conf<OlcliConfig>({
Expand All @@ -22,7 +23,8 @@ const config = new Conf<OlcliConfig>({
csrf: { type: 'string' },
lastProject: { type: 'string' },
baseUrl: { type: 'string' },
sessionCookieName: { type: 'string' }
sessionCookieName: { type: 'string' },
timeout: { type: 'number' }
}
});

Expand All @@ -34,6 +36,14 @@ export function setBaseUrl(url: string): void {
config.set('baseUrl', url);
}

export function getTimeout(): number {
return Number.parseInt(process.env.OVERLEAF_TIMEOUT || '') || config.get('timeout') || 10000;
}

export function setTimeout(ms: number): void {
config.set('timeout', ms);
}

export function getSessionCookieName(): string {
return process.env.OVERLEAF_COOKIE_NAME || config.get('sessionCookieName') || 'overleaf_session2';
}
Expand Down