pplx-zero 1.1.5 → 1.1.7

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 (3) hide show
  1. package/README.md +79 -329
  2. package/dist/index.js +179 -160
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,404 +1,154 @@
1
- # PPLX-Zero
1
+ # PPLXZero
2
2
 
3
- > Fast Perplexity AI search CLI - minimal setup, maximal results
3
+ Use the Perplexity API straight from the terminal — sub‑1s search API, ideal for coding agents and automation.
4
+ Minimal setup, fast results, and practical flags for files, images, streaming, and batch workflows.
4
5
 
5
- <p align="center">
6
- <a href="https://badge.fury.io/js/pplx-zero"><img src="https://badge.fury.io/js/pplx-zero.svg" alt="npm version"></a>
7
- <a href="https://aur.archlinux.org/packages/pplx-zero"><img src="https://img.shields.io/aur/version/pplx-zero?style=flat-square" alt="AUR package"></a>
8
- <a href="https://www.typescriptlang.org/"><img src="https://img.shields.io/badge/TypeScript-007ACC?logo=typescript&logoColor=white" alt="TypeScript"></a>
9
- <a href="https://bun.sh"><img src="https://img.shields.io/badge/Bun-black?logo=bun&logoColor=white" alt="Bun"></a>
10
- <a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
11
- </p>
6
+ ### What you can do
7
+ - Ask, research, and stream answers instantly from the CLI with sensible defaults for rapid iteration.
8
+ - Summarize documents and analyze images using a single command with optional model control.
9
+ - Run batch jobs with concurrency and timeouts for agent pipelines and CI flows.
12
10
 
13
- A fast TypeScript CLI for Perplexity AI search with multimodal support. Built with Bun runtime for performance and reliability.
14
-
15
- ## Features
16
-
17
- - **⚡ Fast Search** - Concurrent queries with intelligent rate limiting
18
- - **🎯 Simple Setup** - Works with just an API key, no configuration required
19
- - **📦 Batch Processing** - Handle multiple searches simultaneously
20
- - **🔄 Real-time Updates** - JSONL streaming progress events
21
- - **🖼️ File Analysis** - Process documents and images with AI models
22
- - **🤖 AI Models** - Sonar, Sonar Pro, Sonar Deep Research, Sonar Reasoning
23
- - **🛡️ Type Safe** - Full Zod validation and TypeScript support
24
- - **🌍 Cross-Platform** - Native Bun runtime support
25
- - **🔄 Auto-Update** - Background update checking to stay current
26
-
27
- ## Quick Start
28
-
29
- ### 1️⃣ Install
30
-
31
- **📦 Package Manager Installation (Recommended)**
32
-
33
- <p align="center">
34
- <a href="https://badge.fury.io/js/pplx-zero"><img src="https://badge.fury.io/js/pplx-zero.svg" alt="npm version"></a>
35
- <a href="https://aur.archlinux.org/packages/pplx-zero"><img src="https://img.shields.io/aur/version/pplx-zero?style=flat-square" alt="AUR package"></a>
36
- </p>
11
+ ### Install
12
+ Choose one:
37
13
 
38
14
  ```bash
39
- # npm (Node.js package manager) - Global installation
15
+ # npm or bun (global)
40
16
  npm install -g pplx-zero
17
+ pplx --version
18
+ ```
41
19
 
42
- # AUR (Arch Linux) - Binary package
43
- yay -S pplx-zero
44
20
 
45
- # AUR (Arch Linux) - Manual build
46
- git clone https://aur.archlinux.org/pplx-zero.git
47
- cd pplx-zero
48
- makepkg -si
21
+ or
49
22
 
50
- # Verify installation
23
+ ```bash
24
+ # AUR (Arch Linux)
25
+ yay -S pplx-zero
51
26
  pplx --version
52
27
  ```
53
28
 
54
- **🔨 Manual Installation**
29
+
30
+ or
55
31
 
56
32
  ```bash
57
- # Clone and build locally
58
- git clone https://github.com/codewithkenzo/pplx-zero.git
59
- cd pplx-zero
33
+ # Build from source
34
+ # 1) clone the repository
35
+ # 2) enter the folder
36
+ # 3) build and link the CLI
60
37
  bun install && bun run build
61
-
62
- # Add to PATH
63
38
  sudo ln -s "$(pwd)/dist/cli.js" /usr/local/bin/pplx
64
-
65
- # Verify installation
66
39
  pplx --version
67
40
  ```
68
41
 
69
- ### 2️⃣ Setup API Key
70
42
 
71
- **Linux/macOS:**
43
+ ### Configure
44
+ Set your API key as an environment variable before running commands.
45
+
46
+ Linux/macOS:
72
47
  ```bash
73
48
  export PERPLEXITY_API_KEY="your-api-key"
74
49
  ```
75
50
 
76
- **Windows:**
51
+
52
+ Windows:
77
53
  ```cmd
78
54
  setx PERPLEXITY_API_KEY "your-api-key"
79
55
  ```
80
56
 
81
- **Get your API key:** https://www.perplexity.ai/account/api/keys
82
57
 
83
- ### 3️⃣ Start Searching
58
+ Get your key from your Perplexity account and keep it private to your machine or CI secrets manager.
84
59
 
60
+ ### Quick examples
61
+ Simple search (default model: sonar)
85
62
  ```bash
86
- # Simple search
87
- pplx "latest AI developments"
88
-
89
- # Choose model for detailed analysis
90
- pplx --model sonar-pro "Explain quantum computing"
91
-
92
- # Analyze document
93
- pplx --file report.pdf "Summarize this document"
94
-
95
- # Analyze image
96
- pplx --image screenshot.png "What does this interface do?"
97
-
98
- # Document + image analysis
99
- pplx --file data.csv --image chart.png "Analyze this data"
100
-
101
- # Advanced AI models
102
- pplx --model sonar-reasoning "Solve this math problem"
103
- pplx --model sonar-deep-research "History of artificial intelligence"
104
-
105
- # See basic help
106
- pplx --help
63
+ pplx "python type hints best practices"
107
64
  ```
108
65
 
109
- ## Auto-Update
110
-
111
- PPLX-Zero includes intelligent auto-update functionality that runs in the background to keep your CLI current.
112
-
113
- ### How It Works
114
-
115
- - **Background Checking**: Automatically checks for updates every 24 hours when you run searches
116
- - **Non-Blocking**: Never slows down your search queries - updates happen in the background
117
- - **Smart Caching**: Uses intelligent caching to avoid unnecessary network requests
118
- - **Silent by Default**: Runs quietly without interrupting your workflow
119
-
120
- ### Update Commands
121
66
 
67
+ Deep research or sonar-pro or reasoning when you need more steps or web context
122
68
  ```bash
123
- # Check for updates manually
124
- pplx update --check
125
-
126
- # Install available updates automatically
127
- pplx update --auto
128
-
129
- # Show version information
130
- pplx version
131
-
132
- # Check updates with version command
133
- pplx version --check-updates
69
+ pplx -m sonar-pro "React 19 Hooks"
70
+ pplx -m sonar-deep-research "best Rust web frameworks 2025"
71
+ pplx -m sonar-reasoning "prove this algorithm runs in O(n log n)"
134
72
  ```
135
73
 
136
- ### Update Methods
137
-
138
- The auto-update system tries multiple package managers in order of preference:
139
-
140
- 1. **npm global** (`npm update -g pplx-zero`)
141
- 2. **bun global** (`bun update -g pplx-zero`)
142
- 3. **yarn global** (`yarn global upgrade pplx-zero`)
143
- 4. **pnpm global** (`pnpm update -g pplx-zero`)
144
-
145
- If automatic updates fail, you'll get helpful instructions to update manually.
146
-
147
- ### Configuration
148
-
149
- Auto-update settings are stored in `~/.pplx-zero/update-cache.json`:
150
-
151
- - **Check Interval**: 24 hours by default
152
- - **Quiet Mode**: Silent operation to not interrupt workflow
153
- - **Auto Install**: Disabled by default for safety
154
-
155
- ### Privacy & Performance
156
74
 
157
- - **Minimal Network**: Only checks npm registry for version information
158
- - **No Data Collection**: Never sends search queries or personal data
159
- - **Fast Performance**: Cached results prevent repeated network requests
160
- - **Local Only**: All update logic runs locally on your machine
161
-
162
- ## Usage Guide
163
-
164
- ### Command Line Options
165
-
166
- **Quick Reference:**
75
+ Summarize a PDF report quickly
167
76
  ```bash
168
- # Basic search
169
- pplx "latest AI developments"
170
-
171
- # File analysis (simplified)
172
- pplx -f document.pdf "Summarize this"
77
+ pplx -f report.pdf "summarize key findings and risks"
78
+ ```
173
79
 
174
- # Advanced batch processing
175
- pplx --input queries.json -c 10 -t 60000
176
80
 
177
- # See all options
178
- pplx --help
81
+ Understand an interface from a screenshot
82
+ ```bash
83
+ pplx -i screenshot.png "what is this UI and what are the next steps?"
179
84
  ```
180
85
 
181
- **Essential Commands:**
182
- ```bash
183
- # Model selection
184
- pplx -m sonar-pro "Detailed analysis"
185
86
 
186
- # File + image
187
- pplx -f doc.md -i chart.png "Analyze both"
87
+ Combine a doc and an image in one prompt
88
+ ```bash
89
+ pplx -f data.csv -i chart.png "spot anomalies and explain the chart"
90
+ ```
188
91
 
189
- # Output format
190
- pplx -o jsonl "Get streaming output"
191
92
 
192
- # Async with webhook
193
- pplx --async --webhook https://api.example.com "Long task"
93
+ Stream newline‑delimited JSON for agents or UNIX pipes
94
+ ```bash
95
+ pplx -o jsonl "ai trends"
194
96
  ```
195
97
 
196
- ### Batch Processing
197
-
198
- Create `queries.json`:
199
98
 
99
+ Batch from a JSON file (concurrency and timeout shown)
200
100
  ```json
201
101
  {
202
102
  "version": "1.0.0",
203
103
  "requests": [
204
- {"op": "search", "args": {"query": "AI trends", "maxResults": 5}},
205
- {"op": "search", "args": {"query": "TypeScript patterns", "maxResults": 3}},
206
- {"op": "search", "args": {"query": "Bun performance", "maxResults": 3}}
104
+ { "op": "search", "args": { "query": "AI trends", "maxResults": 5 } },
105
+ { "op": "search", "args": { "query": "TypeScript patterns", "maxResults": 3 } }
207
106
  ],
208
- "options": {
209
- "concurrency": 5,
210
- "timeoutMs": 30000
211
- }
107
+ "options": { "concurrency": 5, "timeoutMs": 30000 }
212
108
  }
213
109
  ```
214
110
 
215
- Process with:
216
111
 
217
112
  ```bash
218
- pplx --input queries.json --format jsonl
113
+ pplx -I queries.json -o jsonl -c 5 -t 30000
219
114
  ```
220
115
 
221
- ### File Attachments
222
-
223
- Supported file formats for analysis:
224
116
 
225
- **Documents (max 50MB):**
226
- - PDF, DOC, DOCX, TXT, RTF, MD
227
-
228
- **Images (max 50MB):**
229
- - PNG, JPEG, WebP, HEIF, HEIC, GIF
230
-
231
- **Examples:**
117
+ Fire‑and‑forget async with a webhook callback (agent workflows)
232
118
  ```bash
233
- # Document analysis
234
- pplx --attach report.pdf "Summarize this document"
235
-
236
- # Image analysis
237
- pplx --attach-image screenshot.png "Analyze this interface"
238
-
239
- # Multiple files
240
- pplx --attach document.txt --attach-image chart.png "Analyze this data"
241
- ```
242
-
243
- ### Programmatic Usage
244
-
245
- ```typescript
119
+ pplx --async --webhook http://localhost:3000/callback "long research task"
120
+ ```
121
+
122
+
123
+ ### Flags
124
+ - -m, --model: sonar | sonar-pro | sonar-deep-research | sonar-reasoning (default: sonar)
125
+ - -f, --file: attach a document (PDF, DOC, DOCX, TXT, RTF, MD; up to ~50MB)
126
+ - -i, --image: attach an image (PNG, JPEG, WebP, HEIF/HEIC, GIF; up to ~50MB)
127
+ - -o, --format: json | jsonl (default: json)
128
+ - -I, --input: read batch requests from a JSON file
129
+ - -c, --concurrency: max parallel requests, e.g., 5 (default: 5)
130
+ - -t, --timeout: request timeout in ms, e.g., 30000 (default: 30000)
131
+ - --async: process requests asynchronously
132
+ - --webhook: URL receiving async notifications
133
+ - -h, --help: show help
134
+ - -v, --version: show version
135
+
136
+ ### Programmatic use (optional)
137
+ Use the toolkit directly in TypeScript when embedding into agents or services.
138
+ ```ts
246
139
  import { PerplexitySearchTool } from 'pplx-zero';
247
140
 
248
141
  const tool = new PerplexitySearchTool();
249
142
 
250
143
  const result = await tool.runBatch({
251
144
  version: "1.0.0",
252
- requests: [{
253
- op: "search",
254
- args: { query: "TypeScript best practices", maxResults: 5 }
255
- }]
145
+ requests: [{ op: "search", args: { query: "TypeScript best practices", maxResults: 5 } }]
256
146
  });
257
147
 
258
148
  console.log(result);
259
149
  ```
260
150
 
261
- ## Configuration
262
-
263
- ### Simplified Options (Everyday Usage)
264
-
265
- | Option | Short | Type | Default | Description |
266
- |--------|-------|------|---------|-------------|
267
- | `--file` | `-f` | string | - | Attach document (PDF, DOC, DOCX, TXT, RTF, MD) |
268
- | `--image` | `-i` | string | - | Attach image (PNG, JPEG, WebP, HEIF, HEIC, GIF) |
269
- | `--format` | `-o` | string | json | Output format: json|jsonl |
270
- | `--model` | `-m` | string | sonar | AI model: sonar, sonar-pro, sonar-deep-research, sonar-reasoning |
271
- | `--version` | `-v` | boolean | - | Show version |
272
- | `--help` | `-h` | boolean | - | Show basic help |
273
-
274
- ### Update Commands
275
-
276
- | Command | Options | Description |
277
- |---------|---------|-------------|
278
- | `pplx update` | `--check` | Check for available updates |
279
- | `pplx update` | `--auto` | Install available updates automatically |
280
- | `pplx version` | `--check-updates` | Show version and check for updates |
281
- | `pplx version` | `--verbose` | Show detailed version information |
282
-
283
- ### Advanced Options (Power Users)
284
-
285
- | Option | Short | Type | Default | Description |
286
- |--------|-------|------|---------|-------------|
287
- | `--input` | `-I` | string | - | Read batch requests from JSON file |
288
- | `--stdin` | `-s` | boolean | false | Read JSONL requests from stdin |
289
- | `--concurrency` | `-c` | number | 5 | Max concurrent requests (1-20) |
290
- | `--timeout` | `-t` | number | 30000 | Request timeout in ms (1000-300000) |
291
- | `--workspace` | `-w` | string | - | Workspace directory for sandboxing |
292
- | `--attach` | - | string[] | - | Attach document files (multiple) |
293
- | `--attach-image` | - | string[] | - | Attach image files (multiple) |
294
- | `--async` | - | boolean | false | Process requests asynchronously |
295
- | `--webhook` | - | string | - | Webhook URL for async notifications |
296
- |
297
-
298
- ### Quick Reference
299
-
300
- ```bash
301
- # Basic usage (simplified)
302
- pplx -f doc.pdf -m sonar-pro "analyze this"
303
-
304
- # Advanced usage (full control)
305
- pplx -I batch.json -c 10 -t 60000 --format jsonl "process all"
306
-
307
- # See all available options
308
- pplx --help
309
- ```
310
-
311
- ### AI Models
312
-
313
- - `sonar` - Fast, concise responses (default)
314
- - `sonar-pro` - Detailed, comprehensive responses
315
- - `sonar-deep-research` - In-depth research with web search
316
- - `sonar-reasoning` - Step-by-step logical reasoning
317
-
318
- ## Output Formats
319
-
320
- ### JSON (Default)
321
- ```json
322
- {
323
- "version": "1.0.0",
324
- "ok": true,
325
- "summary": {
326
- "total": 1,
327
- "successful": 1,
328
- "failed": 0,
329
- "totalDuration": 572
330
- },
331
- "results": [...]
332
- }
333
- ```
334
-
335
- ### JSONL (Streaming)
336
- ```bash
337
- pplx --format jsonl "AI trends"
338
- ```
339
- Each result printed as a separate JSON line for real-time processing.
340
-
341
- ## Development
342
-
343
- ```bash
344
- # Clone and build
345
- git clone https://github.com/codewithkenzo/pplx-zero.git
346
- cd pplx-zero
347
- bun install && bun run build
348
-
349
- # Development mode
350
- bun run dev
351
-
352
- # Type checking
353
- bun run typecheck
354
-
355
- # Build for production
356
- bun run build
357
-
358
- # Create binary
359
- bun run build:binary
360
- ```
361
-
362
- ## Architecture
363
-
364
- - **Bun Runtime** - Fast JavaScript runtime
365
- - **Zod Validation** - Type-safe schema validation
366
- - **Error Handling** - Resilient error recovery
367
- - **Concurrency Control** - Semaphore pattern for rate limiting
368
- - **Streaming Events** - Real-time progress updates
369
-
370
- ## Security Features
371
-
372
- - Environment variable API key management
373
- - Input validation and sanitization
374
- - Request timeout protection
375
- - Error information filtering
376
- - No external dependencies beyond core runtime
377
-
378
- ## Error Handling
379
-
380
- PPLX-Zero provides comprehensive error classification:
381
-
382
- ```typescript
383
- enum ErrorCode {
384
- VALIDATION_ERROR = "VALIDATION_ERROR",
385
- API_KEY_MISSING = "API_KEY_MISSING",
386
- API_ERROR = "API_ERROR",
387
- TIMEOUT_ERROR = "TIMEOUT_ERROR",
388
- NETWORK_ERROR = "NETWORK_ERROR",
389
- RATE_LIMIT_ERROR = "RATE_LIMIT_ERROR",
390
- UNEXPECTED_ERROR = "UNEXPECTED_ERROR"
391
- }
392
- ```
393
-
394
- ## Contributing
395
-
396
- Contributions are welcome! Please feel free to submit a Pull Request.
397
-
398
- ## License
399
-
400
- MIT License - see [LICENSE](LICENSE) file for details.
401
-
402
- ---
403
151
 
404
- **Built with [Bun](https://bun.sh) and [Perplexity AI](https://www.perplexity.ai)**
152
+ ### Notes
153
+ - Use pplx --help to see all available options and short flags without scanning long docs.
154
+ - Keep output in jsonl for streaming pipelines and agent frameworks that consume line‑by‑line events.*Built with [Bun](https://bun.sh) and [Perplexity AI](https://www.perplexity.ai)**
package/dist/index.js CHANGED
@@ -1883,7 +1883,7 @@ __export(exports_version, {
1883
1883
  getDescription: () => getDescription,
1884
1884
  getAuthor: () => getAuthor,
1885
1885
  formatVersionInfo: () => formatVersionInfo,
1886
- compareVersions: () => compareVersions
1886
+ compareVersions: () => compareVersions2
1887
1887
  });
1888
1888
  import { readFile as readFile2 } from "node:fs/promises";
1889
1889
  import { join as join6, dirname as dirname3 } from "node:path";
@@ -1972,7 +1972,7 @@ async function formatVersionInfo(verbose = false) {
1972
1972
  }
1973
1973
  return `${versionInfo.name} v${versionInfo.version}`;
1974
1974
  }
1975
- function compareVersions(version1, version2) {
1975
+ function compareVersions2(version1, version2) {
1976
1976
  const v1Parts = version1.split(".").map(Number);
1977
1977
  const v2Parts = version2.split(".").map(Number);
1978
1978
  const maxLength = Math.max(v1Parts.length, v2Parts.length);
@@ -1987,7 +1987,7 @@ function compareVersions(version1, version2) {
1987
1987
  return 0;
1988
1988
  }
1989
1989
  function isNewerVersion(current, latest) {
1990
- return compareVersions(latest, current) > 0;
1990
+ return compareVersions2(latest, current) > 0;
1991
1991
  }
1992
1992
  function parseVersion(version) {
1993
1993
  const mainVersion = version.split("-")[0];
@@ -9118,149 +9118,156 @@ class CliFormatter {
9118
9118
  import { promises as fs3 } from "node:fs";
9119
9119
  import { join as join3 } from "node:path";
9120
9120
  import { homedir as homedir3 } from "node:os";
9121
-
9122
- class AutoUpdateService {
9123
- static UPDATE_CACHE_FILE = join3(homedir3(), ".pplx-zero", "update-cache.json");
9124
- static DEFAULT_CONFIG = {
9125
- enabled: true,
9126
- checkInterval: 1440,
9127
- autoInstall: false,
9128
- quietMode: true
9129
- };
9130
- config;
9131
- updateCache = null;
9132
- constructor(config = {}) {
9133
- this.config = { ...AutoUpdateService.DEFAULT_CONFIG, ...config };
9134
- }
9135
- async checkForUpdates() {
9136
- if (!this.config.enabled) {
9137
- return null;
9138
- }
9139
- try {
9140
- await this.loadUpdateCache();
9141
- const now = new Date;
9142
- const timeSinceLastCheck = this.updateCache?.lastChecked ? now.getTime() - this.updateCache.lastChecked.getTime() : Infinity;
9143
- const checkIntervalMs = this.config.checkInterval * 60 * 1000;
9144
- if (this.updateCache && timeSinceLastCheck < checkIntervalMs) {
9145
- return this.updateCache;
9146
- }
9147
- const updateInfo = await this.fetchUpdateInfo();
9148
- await this.saveUpdateCache(updateInfo);
9149
- if (this.config.autoInstall && updateInfo.updateAvailable) {
9150
- this.performAutoUpdate(updateInfo).catch((error) => {
9151
- if (!this.config.quietMode) {
9152
- console.error("Auto-update failed:", error);
9153
- }
9154
- });
9155
- }
9156
- return updateInfo;
9157
- } catch (error) {
9158
- if (!this.config.quietMode) {
9159
- console.error("Update check failed:", error);
9160
- }
9161
- return null;
9162
- }
9163
- }
9164
- async fetchUpdateInfo() {
9165
- const currentVersion = await this.getCurrentVersion();
9166
- try {
9167
- const npmResponse = await fetch("https://registry.npmjs.org/pplx-zero");
9168
- const npmData = await npmResponse.json();
9169
- const latestVersion = npmData["dist-tags"]?.latest || currentVersion;
9170
- const updateAvailable = this.compareVersions(latestVersion, currentVersion) > 0;
9171
- return {
9172
- currentVersion,
9173
- latestVersion,
9174
- updateAvailable,
9175
- lastChecked: new Date,
9176
- updateUrl: npmData.versions?.[latestVersion]?.dist?.tarball,
9177
- releaseNotes: npmData.versions?.[latestVersion]?.description
9178
- };
9179
- } catch (error) {
9180
- return {
9181
- currentVersion,
9182
- latestVersion: currentVersion,
9183
- updateAvailable: false,
9184
- lastChecked: new Date
9185
- };
9186
- }
9121
+ var UPDATE_CACHE_FILE = join3(homedir3(), ".pplx-zero", "update-cache.json");
9122
+ var CONFIG_FILE = join3(homedir3(), ".pplx-zero", "config.json");
9123
+ var DEFAULT_CONFIG = {
9124
+ enabled: true,
9125
+ checkInterval: 1440,
9126
+ autoInstall: false,
9127
+ quietMode: true
9128
+ };
9129
+ async function loadAutoUpdateConfig() {
9130
+ try {
9131
+ const configContent = await fs3.readFile(CONFIG_FILE, "utf-8");
9132
+ const config = JSON.parse(configContent);
9133
+ return { ...DEFAULT_CONFIG, ...config.autoUpdate };
9134
+ } catch {
9135
+ return DEFAULT_CONFIG;
9187
9136
  }
9188
- async getCurrentVersion() {
9189
- try {
9190
- const packageJsonPath = join3(process.cwd(), "package.json");
9191
- const packageJson = JSON.parse(await fs3.readFile(packageJsonPath, "utf-8"));
9192
- return packageJson.version || "1.1.4";
9193
- } catch {
9194
- return "1.1.4";
9137
+ }
9138
+ async function saveAutoUpdateConfig(config) {
9139
+ try {
9140
+ const configDir = join3(homedir3(), ".pplx-zero");
9141
+ await fs3.mkdir(configDir, { recursive: true });
9142
+ const existingConfig = await loadAutoUpdateConfig();
9143
+ const updatedConfig = { ...existingConfig, ...config };
9144
+ await fs3.writeFile(CONFIG_FILE, JSON.stringify({ autoUpdate: updatedConfig }, null, 2), "utf-8");
9145
+ } catch (error) {
9146
+ if (process.env.PPLX_DEBUG) {
9147
+ console.error("Failed to save auto-update config:", error);
9195
9148
  }
9196
9149
  }
9197
- compareVersions(version1, version2) {
9198
- const v1Parts = version1.split(".").map(Number);
9199
- const v2Parts = version2.split(".").map(Number);
9200
- const maxLength = Math.max(v1Parts.length, v2Parts.length);
9201
- for (let i = 0;i < maxLength; i++) {
9202
- const v1Part = v1Parts[i] || 0;
9203
- const v2Part = v2Parts[i] || 0;
9204
- if (v1Part > v2Part)
9205
- return 1;
9206
- if (v1Part < v2Part)
9207
- return -1;
9150
+ }
9151
+ async function checkForUpdatesCached() {
9152
+ try {
9153
+ const config = await loadAutoUpdateConfig();
9154
+ if (!config.enabled) {
9155
+ return null;
9208
9156
  }
9209
- return 0;
9210
- }
9211
- async loadUpdateCache() {
9157
+ let updateCache = null;
9212
9158
  try {
9213
- const cacheData = await fs3.readFile(AutoUpdateService.UPDATE_CACHE_FILE, "utf-8");
9214
- this.updateCache = JSON.parse(cacheData);
9159
+ const cacheData = await fs3.readFile(UPDATE_CACHE_FILE, "utf-8");
9160
+ updateCache = JSON.parse(cacheData);
9215
9161
  } catch {
9216
- this.updateCache = null;
9162
+ updateCache = null;
9217
9163
  }
9218
- }
9219
- async saveUpdateCache(updateInfo) {
9220
- try {
9221
- await fs3.mkdir(join3(homedir3(), ".pplx-zero"), { recursive: true });
9222
- await fs3.writeFile(AutoUpdateService.UPDATE_CACHE_FILE, JSON.stringify(updateInfo, null, 2), "utf-8");
9223
- this.updateCache = updateInfo;
9224
- } catch (error) {
9225
- if (!this.config.quietMode) {
9226
- console.error("Failed to save update cache:", error);
9227
- }
9164
+ const now = new Date;
9165
+ const timeSinceLastCheck = updateCache?.lastChecked ? now.getTime() - new Date(updateCache.lastChecked).getTime() : Infinity;
9166
+ const checkIntervalMs = config.checkInterval * 60 * 1000;
9167
+ if (updateCache && timeSinceLastCheck < checkIntervalMs) {
9168
+ return updateCache;
9169
+ }
9170
+ const updateInfo = await fetchUpdateInfo();
9171
+ await saveUpdateCache(updateInfo);
9172
+ return updateInfo;
9173
+ } catch (error) {
9174
+ if (process.env.PPLX_DEBUG) {
9175
+ console.error("Update check failed:", error);
9228
9176
  }
9177
+ return null;
9229
9178
  }
9230
- async performAutoUpdate(updateInfo) {
9231
- if (!updateInfo.updateAvailable || !updateInfo.updateUrl) {
9232
- return;
9233
- }
9234
- try {
9235
- if (!this.config.quietMode) {
9236
- console.log(`\uD83D\uDD04 Update available: ${updateInfo.currentVersion} → ${updateInfo.latestVersion}`);
9237
- console.log(`\uD83D\uDCA1 Run 'pplx update --auto' to install`);
9238
- }
9239
- } catch (error) {
9240
- if (!this.config.quietMode) {
9241
- console.error("Auto-update failed:", error);
9242
- }
9243
- }
9179
+ }
9180
+ async function forceUpdateCheck() {
9181
+ try {
9182
+ await fs3.unlink(UPDATE_CACHE_FILE);
9183
+ } catch {}
9184
+ return fetchUpdateInfo();
9185
+ }
9186
+ async function toggleAutoUpdate(enabled, autoInstall = false) {
9187
+ const config = await loadAutoUpdateConfig();
9188
+ const updatedConfig = {
9189
+ ...config,
9190
+ enabled,
9191
+ autoInstall
9192
+ };
9193
+ await saveAutoUpdateConfig(updatedConfig);
9194
+ }
9195
+ async function getAutoUpdateStatus() {
9196
+ const config = await loadAutoUpdateConfig();
9197
+ let updateCache = null;
9198
+ try {
9199
+ const cacheData = await fs3.readFile(UPDATE_CACHE_FILE, "utf-8");
9200
+ updateCache = JSON.parse(cacheData);
9201
+ } catch {
9202
+ updateCache = null;
9244
9203
  }
9245
- async getUpdateStatus() {
9246
- await this.loadUpdateCache();
9204
+ return {
9205
+ enabled: config.enabled,
9206
+ autoInstall: config.autoInstall,
9207
+ lastChecked: updateCache?.lastChecked ? new Date(updateCache.lastChecked) : undefined,
9208
+ updateAvailable: updateCache?.updateAvailable,
9209
+ currentVersion: updateCache?.currentVersion,
9210
+ latestVersion: updateCache?.latestVersion
9211
+ };
9212
+ }
9213
+ async function fetchUpdateInfo() {
9214
+ const currentVersion = await getCurrentVersion();
9215
+ try {
9216
+ const npmResponse = await fetch("https://registry.npmjs.org/pplx-zero");
9217
+ const npmData = await npmResponse.json();
9218
+ const latestVersion = npmData["dist-tags"]?.latest || currentVersion;
9219
+ const updateAvailable = compareVersions(latestVersion, currentVersion) > 0;
9247
9220
  return {
9248
- enabled: this.config.enabled,
9249
- lastChecked: this.updateCache?.lastChecked,
9250
- updateAvailable: this.updateCache?.updateAvailable,
9251
- currentVersion: this.updateCache?.currentVersion,
9252
- latestVersion: this.updateCache?.latestVersion
9221
+ currentVersion,
9222
+ latestVersion,
9223
+ updateAvailable,
9224
+ lastChecked: new Date,
9225
+ updateUrl: npmData.versions?.[latestVersion]?.dist?.tarball,
9226
+ releaseNotes: npmData.versions?.[latestVersion]?.description
9227
+ };
9228
+ } catch (error) {
9229
+ return {
9230
+ currentVersion,
9231
+ latestVersion: currentVersion,
9232
+ updateAvailable: false,
9233
+ lastChecked: new Date
9253
9234
  };
9254
9235
  }
9255
- async forceUpdateCheck() {
9256
- this.updateCache = null;
9257
- try {
9258
- await fs3.unlink(AutoUpdateService.UPDATE_CACHE_FILE);
9259
- } catch {}
9260
- return this.checkForUpdates();
9236
+ }
9237
+ async function getCurrentVersion() {
9238
+ try {
9239
+ const packageJsonPath = join3(process.cwd(), "package.json");
9240
+ const packageJson = JSON.parse(await fs3.readFile(packageJsonPath, "utf-8"));
9241
+ return packageJson.version || "1.1.7";
9242
+ } catch {
9243
+ return "1.1.7";
9244
+ }
9245
+ }
9246
+ function compareVersions(version1, version2) {
9247
+ const v1Parts = version1.split(".").map(Number);
9248
+ const v2Parts = version2.split(".").map(Number);
9249
+ const maxLength = Math.max(v1Parts.length, v2Parts.length);
9250
+ for (let i = 0;i < maxLength; i++) {
9251
+ const v1Part = v1Parts[i] || 0;
9252
+ const v2Part = v2Parts[i] || 0;
9253
+ if (v1Part > v2Part)
9254
+ return 1;
9255
+ if (v1Part < v2Part)
9256
+ return -1;
9257
+ }
9258
+ return 0;
9259
+ }
9260
+ async function saveUpdateCache(updateInfo) {
9261
+ try {
9262
+ const configDir = join3(homedir3(), ".pplx-zero");
9263
+ await fs3.mkdir(configDir, { recursive: true });
9264
+ await fs3.writeFile(UPDATE_CACHE_FILE, JSON.stringify(updateInfo, null, 2), "utf-8");
9265
+ } catch (error) {
9266
+ if (process.env.PPLX_DEBUG) {
9267
+ console.error("Failed to save update cache:", error);
9268
+ }
9261
9269
  }
9262
9270
  }
9263
- var service_default = AutoUpdateService;
9264
9271
 
9265
9272
  // src/cli/commands/search.ts
9266
9273
  function parseNumber(value, defaultValue, min, max, name) {
@@ -9563,13 +9570,7 @@ async function logToHistory(queries, results, metadata, exportPath) {
9563
9570
  async function handleSearchCommand(options) {
9564
9571
  const startTime = Date.now();
9565
9572
  const sessionId = randomUUID3();
9566
- const autoUpdateService = new service_default({
9567
- enabled: true,
9568
- checkInterval: 1440,
9569
- autoInstall: false,
9570
- quietMode: true
9571
- });
9572
- autoUpdateService.checkForUpdates().catch((error) => {
9573
+ checkForUpdatesCached().catch((error) => {
9573
9574
  if (process.env.PPLX_DEBUG) {
9574
9575
  console.error("Background update check failed:", error);
9575
9576
  }
@@ -9877,7 +9878,9 @@ import { join as join5 } from "node:path";
9877
9878
  import { homedir as homedir4 } from "node:os";
9878
9879
  async function handleUpdateCommand(options) {
9879
9880
  try {
9880
- if (options.check) {
9881
+ if (options.check && options.auto) {
9882
+ return await handleToggleAutoUpdate();
9883
+ } else if (options.check) {
9881
9884
  return await handleUpdateCheck(options.silent);
9882
9885
  } else if (options.auto) {
9883
9886
  return await handleAutoUpdate();
@@ -9895,14 +9898,8 @@ async function handleUpdateCommand(options) {
9895
9898
  }
9896
9899
  }
9897
9900
  async function handleUpdateCheck(silent = false) {
9898
- const autoUpdateService = new service_default({
9899
- enabled: true,
9900
- checkInterval: 0,
9901
- autoInstall: false,
9902
- quietMode: silent
9903
- });
9904
9901
  try {
9905
- const updateInfo = await autoUpdateService.forceUpdateCheck();
9902
+ const updateInfo = await forceUpdateCheck();
9906
9903
  if (!updateInfo) {
9907
9904
  const errorMessage = "Update check failed";
9908
9905
  if (!silent) {
@@ -9921,7 +9918,16 @@ async function handleUpdateCheck(silent = false) {
9921
9918
  if (updateInfo.updateAvailable && !silent) {
9922
9919
  const updateMessage = CliFormatter.formatUpdateNotification(updateInfo.currentVersion, updateInfo.latestVersion);
9923
9920
  console.log(CliFormatter.supportsColors() ? updateMessage : CliFormatter.formatPlainText(updateMessage));
9924
- console.log('\uD83D\uDCA1 Run "pplx update --auto" to install automatically');
9921
+ const status = await getAutoUpdateStatus();
9922
+ if (status.enabled) {
9923
+ console.log("\uD83D\uDCA1 Auto-updates are enabled");
9924
+ if (status.autoInstall) {
9925
+ console.log("\uD83D\uDD04 Auto-install is enabled - updates will install automatically");
9926
+ }
9927
+ } else {
9928
+ console.log('\uD83D\uDCA1 Run "pplx update --auto --check" to enable auto-updates');
9929
+ console.log(' or run "pplx update --auto" to install manually');
9930
+ }
9925
9931
  }
9926
9932
  return {
9927
9933
  exitCode: 0,
@@ -9958,13 +9964,7 @@ async function handleAutoUpdate() {
9958
9964
  } catch {}
9959
9965
  await writeFile(updateLockFile, currentPid.toString());
9960
9966
  try {
9961
- const autoUpdateService = new service_default({
9962
- enabled: true,
9963
- checkInterval: 0,
9964
- autoInstall: false,
9965
- quietMode: false
9966
- });
9967
- const updateInfo = await autoUpdateService.forceUpdateCheck();
9967
+ const updateInfo = await forceUpdateCheck();
9968
9968
  if (!updateInfo || !updateInfo.updateAvailable) {
9969
9969
  console.log("ℹ️ No updates available. You are running the latest version.");
9970
9970
  return {
@@ -10051,17 +10051,36 @@ async function executeCommand(command, args) {
10051
10051
  });
10052
10052
  });
10053
10053
  }
10054
- function validateUpdateOptions(options) {
10055
- if (!options.check && !options.auto) {
10054
+ async function handleToggleAutoUpdate() {
10055
+ try {
10056
+ const status = await getAutoUpdateStatus();
10057
+ if (status.enabled) {
10058
+ await toggleAutoUpdate(false);
10059
+ return {
10060
+ exitCode: 0,
10061
+ output: "Auto-updates disabled"
10062
+ };
10063
+ } else {
10064
+ await toggleAutoUpdate(true, false);
10065
+ return {
10066
+ exitCode: 0,
10067
+ output: "Auto-updates enabled"
10068
+ };
10069
+ }
10070
+ } catch (error) {
10071
+ const errorMessage = error instanceof Error ? error.message : String(error);
10072
+ console.error(`❌ Failed to toggle auto-update: ${errorMessage}`);
10056
10073
  return {
10057
- valid: false,
10058
- error: "Either --check or --auto must be specified for update command"
10074
+ exitCode: 1,
10075
+ error: errorMessage
10059
10076
  };
10060
10077
  }
10061
- if (options.check && options.auto) {
10078
+ }
10079
+ function validateUpdateOptions(options) {
10080
+ if (!options.check && !options.auto) {
10062
10081
  return {
10063
10082
  valid: false,
10064
- error: "Cannot specify both --check and --auto options"
10083
+ error: "Either --check or --auto must be specified for update command"
10065
10084
  };
10066
10085
  }
10067
10086
  return { valid: true };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pplx-zero",
3
- "version": "1.1.5",
3
+ "version": "1.1.7",
4
4
  "description": "Fast Perplexity AI search CLI with multimodal support - minimal setup, maximal results",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",