pplx-npx-search 0.2.1 → 0.2.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.
- package/CHANGELOG.md +10 -0
- package/README.md +3 -0
- package/package.json +2 -2
- package/src/cli.js +16 -1
- package/src/search.js +3 -0
- package/src/timeout.js +40 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,15 @@ All notable changes to pplx-cli will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.2.2] - 2026-05-21
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **Configurable stream timeout.** `search`, `reason`, and `research` now accept `--timeout-ms <duration>`, with support for raw milliseconds plus `s` and `m` suffixes.
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
- `pplx research` now defaults to a 10-minute stream timeout so Deep Research can finish instead of hitting the old 2-minute ceiling.
|
|
15
|
+
- `pplx --version` now reads from `package.json`, keeping CLI output aligned with npm releases.
|
|
16
|
+
|
|
8
17
|
## [0.2.1] - 2026-05-18
|
|
9
18
|
|
|
10
19
|
First public release worth telling people about. (v0.2.0 was unpublished before this release; do not use it.)
|
|
@@ -46,6 +55,7 @@ First public release worth telling people about. (v0.2.0 was unpublished before
|
|
|
46
55
|
- SSE streaming for real-time answers
|
|
47
56
|
- Optional Playwright and Chrome CDP transports
|
|
48
57
|
|
|
58
|
+
[0.2.2]: https://github.com/thatsrajan/pplx-cli/compare/v0.2.1...v0.2.2
|
|
49
59
|
[0.2.1]: https://github.com/thatsrajan/pplx-cli/compare/v0.1.1...v0.2.1
|
|
50
60
|
[0.1.1]: https://github.com/thatsrajan/pplx-cli/compare/v0.1.0...v0.1.1
|
|
51
61
|
[0.1.0]: https://github.com/thatsrajan/pplx-cli/releases/tag/v0.1.0
|
package/README.md
CHANGED
|
@@ -127,6 +127,8 @@ pplx search "query" | head -1
|
|
|
127
127
|
pplx search "query" --json || echo "failed"
|
|
128
128
|
```
|
|
129
129
|
|
|
130
|
+
Deep Research is slower than normal search. `pplx research` defaults to a 10-minute stream timeout; override it per run with `--timeout-ms 600000`, `--timeout-ms 120s`, or `--timeout-ms 10m`.
|
|
131
|
+
|
|
130
132
|
Recommended agent invocation:
|
|
131
133
|
|
|
132
134
|
```bash
|
|
@@ -160,6 +162,7 @@ pplx search "research this topic" --json --raw --mode pro
|
|
|
160
162
|
| `--chrome` | Use Chrome CDP bridge instead of HTTP |
|
|
161
163
|
| `--playwright` | Use Playwright headless Chromium |
|
|
162
164
|
| `--no-playwright` | Force HTTP transport even if config enables Playwright |
|
|
165
|
+
| `--timeout-ms 120000\|120s\|10m` | Overall stream timeout |
|
|
163
166
|
| `--curl` | Force curl-impersonate (auto-downloads if missing) |
|
|
164
167
|
| `--allow-anonymous` | Allow anonymous Perplexity responses when cookies are expired |
|
|
165
168
|
| `--incognito` | Do not save the query to Perplexity history |
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pplx-npx-search",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "CLI for Perplexity AI with cookie-based auth. Headless, agent-friendly, no API key required.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"pplx": "
|
|
7
|
+
"pplx": "bin/pplx.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
10
|
"start": "node bin/pplx.js",
|
package/src/cli.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
1
2
|
import { program } from 'commander';
|
|
2
3
|
import chalk from 'chalk';
|
|
3
4
|
import ora from 'ora';
|
|
@@ -10,6 +11,9 @@ import { formatSources } from './format.js';
|
|
|
10
11
|
import { LABS_MODELS, MODEL_MAP } from './constants.js';
|
|
11
12
|
import { setUseCurl } from './http.js';
|
|
12
13
|
import { loadConfig } from './config.js';
|
|
14
|
+
import { resolveTimeoutMs } from './timeout.js';
|
|
15
|
+
|
|
16
|
+
const pkg = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'));
|
|
13
17
|
|
|
14
18
|
// --- Output state ---
|
|
15
19
|
let rawMode = false;
|
|
@@ -84,7 +88,7 @@ async function extractAndValidateBrowser(browser, profile) {
|
|
|
84
88
|
program
|
|
85
89
|
.name('pplx')
|
|
86
90
|
.description('CLI for Perplexity AI')
|
|
87
|
-
.version(
|
|
91
|
+
.version(pkg.version);
|
|
88
92
|
|
|
89
93
|
program.option('--verbose', 'Enable verbose logging');
|
|
90
94
|
program.option('--proxy <url>', 'Set proxy URL (sets HTTPS_PROXY env var)');
|
|
@@ -274,6 +278,13 @@ async function doSearch(query, opts) {
|
|
|
274
278
|
}
|
|
275
279
|
|
|
276
280
|
const mode = opts.mode || 'pro';
|
|
281
|
+
let timeoutMs;
|
|
282
|
+
try {
|
|
283
|
+
timeoutMs = resolveTimeoutMs({ ...opts, mode });
|
|
284
|
+
} catch (e) {
|
|
285
|
+
console.error(chalk.red(e.message));
|
|
286
|
+
process.exit(1);
|
|
287
|
+
}
|
|
277
288
|
const sources = opts.sources ? opts.sources.split(',') : ['web'];
|
|
278
289
|
const lang = opts.lang || 'en-US';
|
|
279
290
|
|
|
@@ -291,6 +302,7 @@ async function doSearch(query, opts) {
|
|
|
291
302
|
chrome: opts.chrome,
|
|
292
303
|
playwright: opts.playwright,
|
|
293
304
|
curl: opts.curl,
|
|
305
|
+
timeoutMs,
|
|
294
306
|
})) {
|
|
295
307
|
lastData = data;
|
|
296
308
|
|
|
@@ -361,6 +373,7 @@ program
|
|
|
361
373
|
.option('--chrome', 'Use Chrome CDP bridge instead of HTTP')
|
|
362
374
|
.option('--playwright', 'Use Playwright headless Chromium instead of HTTP')
|
|
363
375
|
.option('--no-playwright', 'Disable Playwright even if config enables it')
|
|
376
|
+
.option('--timeout-ms <duration>', 'Overall stream timeout: milliseconds by default, or use 120s / 10m')
|
|
364
377
|
.option('--allow-anonymous', 'Allow anonymous Perplexity responses when cookies are expired')
|
|
365
378
|
.action(async (queryArg, opts) => {
|
|
366
379
|
if (opts.raw) { rawMode = true; chalk.level = 0; }
|
|
@@ -378,6 +391,7 @@ program
|
|
|
378
391
|
.option('--chrome', 'Use Chrome CDP bridge')
|
|
379
392
|
.option('--playwright', 'Use Playwright headless Chromium')
|
|
380
393
|
.option('--no-playwright', 'Disable Playwright even if config enables it')
|
|
394
|
+
.option('--timeout-ms <duration>', 'Overall stream timeout: milliseconds by default, or use 120s / 10m')
|
|
381
395
|
.option('--allow-anonymous', 'Allow anonymous Perplexity responses when cookies are expired')
|
|
382
396
|
.action(async (queryArg, opts) => {
|
|
383
397
|
const query = await resolveQuery(queryArg);
|
|
@@ -393,6 +407,7 @@ program
|
|
|
393
407
|
.option('--chrome', 'Use Chrome CDP bridge')
|
|
394
408
|
.option('--playwright', 'Use Playwright headless Chromium')
|
|
395
409
|
.option('--no-playwright', 'Disable Playwright even if config enables it')
|
|
410
|
+
.option('--timeout-ms <duration>', 'Overall stream timeout: milliseconds by default, or use 120s / 10m')
|
|
396
411
|
.option('--allow-anonymous', 'Allow anonymous Perplexity responses when cookies are expired')
|
|
397
412
|
.action(async (queryArg, opts) => {
|
|
398
413
|
const query = await resolveQuery(queryArg);
|
package/src/search.js
CHANGED
|
@@ -126,6 +126,7 @@ async function* searchWithChrome(query, cookies, opts) {
|
|
|
126
126
|
method: 'POST',
|
|
127
127
|
headers: { 'content-type': 'application/json' },
|
|
128
128
|
body,
|
|
129
|
+
timeout: opts.timeoutMs,
|
|
129
130
|
}
|
|
130
131
|
)) {
|
|
131
132
|
parser.feed(chunk);
|
|
@@ -177,6 +178,7 @@ async function* searchWithPlaywright(query, cookies, opts) {
|
|
|
177
178
|
method: 'POST',
|
|
178
179
|
headers: { 'content-type': 'application/json' },
|
|
179
180
|
body,
|
|
181
|
+
timeout: opts.timeoutMs,
|
|
180
182
|
}
|
|
181
183
|
)) {
|
|
182
184
|
parser.feed(chunk);
|
|
@@ -219,6 +221,7 @@ async function* searchWithHttp(query, cookies, opts) {
|
|
|
219
221
|
'cookie': cookieHeader(sessionCookies),
|
|
220
222
|
},
|
|
221
223
|
body,
|
|
224
|
+
timeout: opts.timeoutMs,
|
|
222
225
|
});
|
|
223
226
|
|
|
224
227
|
if (!resp.ok) {
|
package/src/timeout.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export const DEFAULT_SEARCH_TIMEOUT_MS = 120000;
|
|
2
|
+
export const DEFAULT_RESEARCH_TIMEOUT_MS = 600000;
|
|
3
|
+
|
|
4
|
+
export function parseTimeoutMs(value, label = 'timeout') {
|
|
5
|
+
if (value == null || value === '') return null;
|
|
6
|
+
|
|
7
|
+
if (typeof value === 'number') {
|
|
8
|
+
if (Number.isFinite(value) && value > 0) return Math.trunc(value);
|
|
9
|
+
throw new Error(`${label} must be a positive number of milliseconds`);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const text = String(value).trim().toLowerCase();
|
|
13
|
+
const match = text.match(/^(\d+(?:\.\d+)?)(ms|s|m)?$/);
|
|
14
|
+
if (!match) {
|
|
15
|
+
throw new Error(`${label} must be a positive duration like 120000, 120s, or 10m`);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const amount = Number(match[1]);
|
|
19
|
+
const unit = match[2] ?? 'ms';
|
|
20
|
+
const multipliers = { ms: 1, s: 1000, m: 60000 };
|
|
21
|
+
const timeoutMs = amount * multipliers[unit];
|
|
22
|
+
|
|
23
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
|
|
24
|
+
throw new Error(`${label} must be a positive duration`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return Math.trunc(timeoutMs);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function resolveTimeoutMs(opts = {}) {
|
|
31
|
+
const explicit = parseTimeoutMs(opts.timeoutMs, '--timeout-ms');
|
|
32
|
+
if (explicit != null) return explicit;
|
|
33
|
+
|
|
34
|
+
const configured = parseTimeoutMs(opts.timeout, 'config timeout');
|
|
35
|
+
if (configured != null) return configured;
|
|
36
|
+
|
|
37
|
+
return opts.mode === 'deep-research'
|
|
38
|
+
? DEFAULT_RESEARCH_TIMEOUT_MS
|
|
39
|
+
: DEFAULT_SEARCH_TIMEOUT_MS;
|
|
40
|
+
}
|