pi-docparser 1.0.0 → 1.1.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/CHANGELOG.md +17 -0
- package/README.md +6 -4
- package/extensions/docparser/constants.ts +1 -10
- package/extensions/docparser/deps.ts +140 -78
- package/extensions/docparser/doctor.ts +92 -30
- package/extensions/docparser/types.ts +0 -1
- package/package.json +5 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,23 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project follows semantic versioning.
|
|
6
6
|
|
|
7
|
+
## [1.1.0] - 2026-03-20
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- renamed the dependency diagnostic command from `/docparser-doctor` to `/docparser:doctor`
|
|
12
|
+
- improved `/docparser:doctor` so missing dependencies are reported as normal diagnostics instead of error-style command failures
|
|
13
|
+
- added a dedicated in-progress loader while automatic dependency installation commands are running
|
|
14
|
+
- unified doctor install guidance and auto-install command generation, including correct Homebrew cask usage for LibreOffice on macOS
|
|
15
|
+
|
|
16
|
+
## [1.0.1] - 2026-03-20
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- added Pi package gallery preview image metadata
|
|
21
|
+
- added concrete `document_parse` usage examples to the README
|
|
22
|
+
- added GitHub repository metadata to improve the npm package listing
|
|
23
|
+
|
|
7
24
|
## [1.0.0] - 2026-03-20
|
|
8
25
|
|
|
9
26
|
Initial public release.
|
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ A standalone [pi](https://shittycodingagent.ai/) package that adds a `document_p
|
|
|
4
4
|
|
|
5
5
|
It wraps [`@llamaindex/liteparse`](https://github.com/run-llama/liteparse) so pi can parse PDFs, Office documents, spreadsheets, CSV files, and common image formats through a dedicated tool instead of ad-hoc shell commands. It can also perform OCR.
|
|
6
6
|
|
|
7
|
-
When required host tools such as LibreOffice, ImageMagick, or Ghostscript are missing, the tool surfaces actionable install guidance instead of generic conversion failures and points users to `/docparser
|
|
7
|
+
When required host tools such as LibreOffice, ImageMagick, or Ghostscript are missing, the tool surfaces actionable install guidance instead of generic conversion failures and points users to `/docparser:doctor` for guided setup.
|
|
8
8
|
|
|
9
9
|
## What this package provides
|
|
10
10
|
|
|
@@ -169,7 +169,7 @@ Some image or vector conversion paths may also require Ghostscript.
|
|
|
169
169
|
If parsing fails because a host dependency is missing, the extension points users to:
|
|
170
170
|
|
|
171
171
|
```text
|
|
172
|
-
/docparser
|
|
172
|
+
/docparser:doctor
|
|
173
173
|
```
|
|
174
174
|
|
|
175
175
|
Run it inside pi to:
|
|
@@ -178,13 +178,15 @@ Run it inside pi to:
|
|
|
178
178
|
- check whether LibreOffice, ImageMagick, and Ghostscript are available
|
|
179
179
|
- optionally focus the check on a specific file path
|
|
180
180
|
- suggest the most appropriate install commands for the current machine
|
|
181
|
+
- report missing packages as doctor findings instead of making the command look like it failed
|
|
182
|
+
- keep the UI visibly busy while automatic install commands are running
|
|
181
183
|
- optionally attempt those install commands after user confirmation when that looks safe to automate
|
|
182
184
|
|
|
183
185
|
Examples:
|
|
184
186
|
|
|
185
187
|
```text
|
|
186
|
-
/docparser
|
|
187
|
-
/docparser
|
|
188
|
+
/docparser:doctor
|
|
189
|
+
/docparser:doctor @./slides.pptx
|
|
188
190
|
```
|
|
189
191
|
|
|
190
192
|
## Known limitations
|
|
@@ -58,14 +58,5 @@ export const IMAGE_EXTENSIONS = new Set([
|
|
|
58
58
|
|
|
59
59
|
export const GHOSTSCRIPT_REQUIRED_EXTENSIONS = new Set([".svg", ".eps", ".ps", ".ai"]);
|
|
60
60
|
|
|
61
|
-
export const
|
|
62
|
-
"LibreOffice is not installed. Please install LibreOffice to convert office documents. On macOS: brew install --cask libreoffice, On Ubuntu: apt-get install libreoffice, On Windows: choco install libreoffice-fresh";
|
|
63
|
-
|
|
64
|
-
export const IMAGEMAGICK_MISSING_MESSAGE =
|
|
65
|
-
"ImageMagick is not installed. Please install ImageMagick to convert images. On macOS: brew install imagemagick, On Ubuntu: apt-get install imagemagick, On Windows: choco install imagemagick.app";
|
|
66
|
-
|
|
67
|
-
export const GHOSTSCRIPT_MISSING_MESSAGE =
|
|
68
|
-
"Ghostscript is required to convert %s files but is not installed. On macOS: brew install ghostscript, On Ubuntu: apt-get install ghostscript, On Windows: choco install ghostscript";
|
|
69
|
-
|
|
70
|
-
export const DOCTOR_COMMAND_NAME = "docparser-doctor";
|
|
61
|
+
export const DOCTOR_COMMAND_NAME = "docparser:doctor";
|
|
71
62
|
export const DOCTOR_COMMAND = `/${DOCTOR_COMMAND_NAME}`;
|
|
@@ -5,11 +5,8 @@ import { access } from "node:fs/promises";
|
|
|
5
5
|
|
|
6
6
|
import {
|
|
7
7
|
DOCTOR_COMMAND,
|
|
8
|
-
GHOSTSCRIPT_MISSING_MESSAGE,
|
|
9
8
|
GHOSTSCRIPT_REQUIRED_EXTENSIONS,
|
|
10
|
-
IMAGEMAGICK_MISSING_MESSAGE,
|
|
11
9
|
INSTALL_COMMAND_TIMEOUT_MS,
|
|
12
|
-
LIBREOFFICE_MISSING_MESSAGE,
|
|
13
10
|
} from "./constants.ts";
|
|
14
11
|
import type {
|
|
15
12
|
DependencyDiagnosis,
|
|
@@ -82,6 +79,7 @@ const PACKAGE_NAMES: Record<PackageManagerId, Record<DependencyName, string>> =
|
|
|
82
79
|
ghostscript: "ghostscript",
|
|
83
80
|
},
|
|
84
81
|
};
|
|
82
|
+
const BREW_CASK_DEPENDENCIES = new Set<DependencyName>(["libreoffice"]);
|
|
85
83
|
const LINUX_MANAGERS: Array<{ id: PackageManagerId; label: string }> = [
|
|
86
84
|
{ id: "apt-get", label: "APT" },
|
|
87
85
|
{ id: "dnf", label: "DNF" },
|
|
@@ -140,77 +138,10 @@ async function findFirstAvailableCommand(
|
|
|
140
138
|
return undefined;
|
|
141
139
|
}
|
|
142
140
|
|
|
143
|
-
function formatGhostscriptMissingMessage(extension: string): string {
|
|
144
|
-
const fileTypeLabel = (extension || "vector").replace(/^\./, "").toUpperCase();
|
|
145
|
-
return GHOSTSCRIPT_MISSING_MESSAGE.replace("%s", fileTypeLabel);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
const DEPENDENCY_METADATA: Record<
|
|
149
|
-
DependencyName,
|
|
150
|
-
{
|
|
151
|
-
label: string;
|
|
152
|
-
summary: string;
|
|
153
|
-
findCommand: () => Promise<string | undefined>;
|
|
154
|
-
getMissingMessage: (inspection?: InputInspection) => string;
|
|
155
|
-
}
|
|
156
|
-
> = {
|
|
157
|
-
libreoffice: {
|
|
158
|
-
label: "LibreOffice",
|
|
159
|
-
summary:
|
|
160
|
-
"Needed for Office documents and spreadsheets such as DOCX, PPTX, XLSX, CSV, and similar formats.",
|
|
161
|
-
findCommand: () =>
|
|
162
|
-
findFirstAvailableCommand(
|
|
163
|
-
["libreoffice", "soffice"],
|
|
164
|
-
process.platform === "darwin"
|
|
165
|
-
? [
|
|
166
|
-
"/Applications/LibreOffice.app/Contents/MacOS/soffice",
|
|
167
|
-
"/Applications/LibreOffice.app/Contents/MacOS/libreoffice",
|
|
168
|
-
]
|
|
169
|
-
: process.platform === "win32"
|
|
170
|
-
? [
|
|
171
|
-
"C:\\Program Files\\LibreOffice\\program\\soffice.exe",
|
|
172
|
-
"C:\\Program Files\\LibreOffice\\program\\libreoffice.exe",
|
|
173
|
-
"C:\\Program Files (x86)\\LibreOffice\\program\\soffice.exe",
|
|
174
|
-
"C:\\Program Files (x86)\\LibreOffice\\program\\libreoffice.exe",
|
|
175
|
-
]
|
|
176
|
-
: [],
|
|
177
|
-
),
|
|
178
|
-
getMissingMessage: () => LIBREOFFICE_MISSING_MESSAGE,
|
|
179
|
-
},
|
|
180
|
-
imagemagick: {
|
|
181
|
-
label: "ImageMagick",
|
|
182
|
-
summary:
|
|
183
|
-
"Needed for image inputs such as PNG, JPG, TIFF, WebP, SVG, and similar formats that must be converted before parsing.",
|
|
184
|
-
findCommand: () =>
|
|
185
|
-
findFirstAvailableCommand(process.platform === "win32" ? ["magick"] : ["magick", "convert"]),
|
|
186
|
-
getMissingMessage: () => IMAGEMAGICK_MISSING_MESSAGE,
|
|
187
|
-
},
|
|
188
|
-
ghostscript: {
|
|
189
|
-
label: "Ghostscript",
|
|
190
|
-
summary:
|
|
191
|
-
"Needed for vector image conversion paths such as SVG, EPS, PS, and AI when ImageMagick delegates rendering.",
|
|
192
|
-
findCommand: () => findFirstAvailableCommand(["gs", "gswin64c", "gswin32c"]),
|
|
193
|
-
getMissingMessage: (inspection) =>
|
|
194
|
-
formatGhostscriptMissingMessage(inspection?.extension || ".svg"),
|
|
195
|
-
},
|
|
196
|
-
};
|
|
197
|
-
|
|
198
141
|
function getPackageNames(manager: PackageManagerId, dependencyNames: DependencyName[]): string[] {
|
|
199
142
|
return dependencyNames.map((dependencyName) => PACKAGE_NAMES[manager][dependencyName]);
|
|
200
143
|
}
|
|
201
144
|
|
|
202
|
-
function getLinuxInstallArgs(managerId: PackageManagerId, packageNames: string[]): string[] {
|
|
203
|
-
if (managerId === "pacman") {
|
|
204
|
-
return ["-Sy", "--noconfirm", ...packageNames];
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
if (managerId === "apk") {
|
|
208
|
-
return ["add", ...packageNames];
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
return ["install", "-y", ...packageNames];
|
|
212
|
-
}
|
|
213
|
-
|
|
214
145
|
function buildCommandDisplay(command: string, args: string[], displayPrefix = ""): string {
|
|
215
146
|
return `${displayPrefix}${command}${args.length > 0 ? ` ${args.join(" ")}` : ""}`;
|
|
216
147
|
}
|
|
@@ -243,6 +174,52 @@ function createCommandSpec(
|
|
|
243
174
|
};
|
|
244
175
|
}
|
|
245
176
|
|
|
177
|
+
function createDisplayCommand(manager: PackageManagerId, dependencyName: DependencyName): string {
|
|
178
|
+
if (manager === "brew" && BREW_CASK_DEPENDENCIES.has(dependencyName)) {
|
|
179
|
+
return buildCommandDisplay("brew", ["install", "--cask", PACKAGE_NAMES.brew[dependencyName]]);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (manager === "choco") {
|
|
183
|
+
return buildCommandDisplay("choco", ["install", PACKAGE_NAMES.choco[dependencyName]]);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return buildCommandDisplay(manager, ["install", PACKAGE_NAMES[manager][dependencyName]]);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function buildGuidedInstallMessage(
|
|
190
|
+
dependencyName: DependencyName,
|
|
191
|
+
summary: string,
|
|
192
|
+
options: { requiredForFileType?: string } = {},
|
|
193
|
+
): string {
|
|
194
|
+
const macCommand = createDisplayCommand("brew", dependencyName);
|
|
195
|
+
const ubuntuCommand = createDisplayCommand("apt-get", dependencyName);
|
|
196
|
+
const windowsCommand = createDisplayCommand("choco", dependencyName);
|
|
197
|
+
const requirement = options.requiredForFileType
|
|
198
|
+
? ` to convert ${options.requiredForFileType} files`
|
|
199
|
+
: "";
|
|
200
|
+
|
|
201
|
+
return `${summary}${requirement}. On macOS: ${macCommand}, On Ubuntu: ${ubuntuCommand}, On Windows: ${windowsCommand}`;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function formatGhostscriptMissingMessage(extension: string): string {
|
|
205
|
+
const fileTypeLabel = (extension || "vector").replace(/^\./, "").toUpperCase();
|
|
206
|
+
return buildGuidedInstallMessage("ghostscript", `Ghostscript is required but is not installed`, {
|
|
207
|
+
requiredForFileType: fileTypeLabel,
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function getLinuxInstallArgs(managerId: PackageManagerId, packageNames: string[]): string[] {
|
|
212
|
+
if (managerId === "pacman") {
|
|
213
|
+
return ["-Sy", "--noconfirm", ...packageNames];
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (managerId === "apk") {
|
|
217
|
+
return ["add", ...packageNames];
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return ["install", "-y", ...packageNames];
|
|
221
|
+
}
|
|
222
|
+
|
|
246
223
|
async function getUnixPrivilegeContext(): Promise<UnixPrivilegeContext> {
|
|
247
224
|
if (typeof process.getuid === "function" && process.getuid() === 0) {
|
|
248
225
|
return {
|
|
@@ -302,6 +279,40 @@ function buildLinuxInstallCommands(
|
|
|
302
279
|
return commands;
|
|
303
280
|
}
|
|
304
281
|
|
|
282
|
+
function buildBrewInstallCommands(dependencyNames: DependencyName[]): InstallCommandSpec[] {
|
|
283
|
+
const formulaDependencies = dependencyNames.filter(
|
|
284
|
+
(dependencyName) => !BREW_CASK_DEPENDENCIES.has(dependencyName),
|
|
285
|
+
);
|
|
286
|
+
const caskDependencies = dependencyNames.filter((dependencyName) =>
|
|
287
|
+
BREW_CASK_DEPENDENCIES.has(dependencyName),
|
|
288
|
+
);
|
|
289
|
+
const commands: InstallCommandSpec[] = [];
|
|
290
|
+
|
|
291
|
+
if (formulaDependencies.length > 0) {
|
|
292
|
+
commands.push(
|
|
293
|
+
createCommandSpec(
|
|
294
|
+
"Install missing document parser dependencies via Homebrew",
|
|
295
|
+
"brew",
|
|
296
|
+
["install", ...getPackageNames("brew", formulaDependencies)],
|
|
297
|
+
{ timeoutMs: INSTALL_COMMAND_TIMEOUT_MS },
|
|
298
|
+
),
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if (caskDependencies.length > 0) {
|
|
303
|
+
commands.push(
|
|
304
|
+
createCommandSpec(
|
|
305
|
+
"Install missing document parser dependencies via Homebrew Cask",
|
|
306
|
+
"brew",
|
|
307
|
+
["install", "--cask", ...getPackageNames("brew", caskDependencies)],
|
|
308
|
+
{ timeoutMs: INSTALL_COMMAND_TIMEOUT_MS },
|
|
309
|
+
),
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
return commands;
|
|
314
|
+
}
|
|
315
|
+
|
|
305
316
|
function buildWingetCommands(dependencyNames: DependencyName[]): InstallCommandSpec[] {
|
|
306
317
|
return dependencyNames.map((dependencyName) =>
|
|
307
318
|
createCommandSpec(
|
|
@@ -320,6 +331,64 @@ function buildWingetCommands(dependencyNames: DependencyName[]): InstallCommandS
|
|
|
320
331
|
);
|
|
321
332
|
}
|
|
322
333
|
|
|
334
|
+
const DEPENDENCY_METADATA: Record<
|
|
335
|
+
DependencyName,
|
|
336
|
+
{
|
|
337
|
+
label: string;
|
|
338
|
+
summary: string;
|
|
339
|
+
findCommand: () => Promise<string | undefined>;
|
|
340
|
+
getMissingMessage: (inspection?: InputInspection) => string;
|
|
341
|
+
}
|
|
342
|
+
> = {
|
|
343
|
+
libreoffice: {
|
|
344
|
+
label: "LibreOffice",
|
|
345
|
+
summary:
|
|
346
|
+
"Needed for Office documents and spreadsheets such as DOCX, PPTX, XLSX, CSV, and similar formats.",
|
|
347
|
+
findCommand: () =>
|
|
348
|
+
findFirstAvailableCommand(
|
|
349
|
+
["libreoffice", "soffice"],
|
|
350
|
+
process.platform === "darwin"
|
|
351
|
+
? [
|
|
352
|
+
"/Applications/LibreOffice.app/Contents/MacOS/soffice",
|
|
353
|
+
"/Applications/LibreOffice.app/Contents/MacOS/libreoffice",
|
|
354
|
+
]
|
|
355
|
+
: process.platform === "win32"
|
|
356
|
+
? [
|
|
357
|
+
"C:\\Program Files\\LibreOffice\\program\\soffice.exe",
|
|
358
|
+
"C:\\Program Files\\LibreOffice\\program\\libreoffice.exe",
|
|
359
|
+
"C:\\Program Files (x86)\\LibreOffice\\program\\soffice.exe",
|
|
360
|
+
"C:\\Program Files (x86)\\LibreOffice\\program\\libreoffice.exe",
|
|
361
|
+
]
|
|
362
|
+
: [],
|
|
363
|
+
),
|
|
364
|
+
getMissingMessage: () =>
|
|
365
|
+
buildGuidedInstallMessage(
|
|
366
|
+
"libreoffice",
|
|
367
|
+
"LibreOffice is not installed. Please install LibreOffice to convert office documents",
|
|
368
|
+
),
|
|
369
|
+
},
|
|
370
|
+
imagemagick: {
|
|
371
|
+
label: "ImageMagick",
|
|
372
|
+
summary:
|
|
373
|
+
"Needed for image inputs such as PNG, JPG, TIFF, WebP, SVG, and similar formats that must be converted before parsing.",
|
|
374
|
+
findCommand: () =>
|
|
375
|
+
findFirstAvailableCommand(process.platform === "win32" ? ["magick"] : ["magick", "convert"]),
|
|
376
|
+
getMissingMessage: () =>
|
|
377
|
+
buildGuidedInstallMessage(
|
|
378
|
+
"imagemagick",
|
|
379
|
+
"ImageMagick is not installed. Please install ImageMagick to convert images",
|
|
380
|
+
),
|
|
381
|
+
},
|
|
382
|
+
ghostscript: {
|
|
383
|
+
label: "Ghostscript",
|
|
384
|
+
summary:
|
|
385
|
+
"Needed for vector image conversion paths such as SVG, EPS, PS, and AI when ImageMagick delegates rendering.",
|
|
386
|
+
findCommand: () => findFirstAvailableCommand(["gs", "gswin64c", "gswin32c"]),
|
|
387
|
+
getMissingMessage: (inspection) =>
|
|
388
|
+
formatGhostscriptMissingMessage(inspection?.extension || ".svg"),
|
|
389
|
+
},
|
|
390
|
+
};
|
|
391
|
+
|
|
323
392
|
export function getRelevantDependencyNames(inspection?: InputInspection): Set<DependencyName> {
|
|
324
393
|
if (!inspection) {
|
|
325
394
|
return new Set(DEPENDENCY_NAMES);
|
|
@@ -409,14 +478,7 @@ export async function buildInstallStrategies(
|
|
|
409
478
|
label: "Homebrew",
|
|
410
479
|
autoRunnable: brewAvailable,
|
|
411
480
|
autoRunBlockedReason: brewAvailable ? undefined : "Homebrew was not detected on PATH.",
|
|
412
|
-
commands:
|
|
413
|
-
createCommandSpec(
|
|
414
|
-
"Install missing document parser dependencies via Homebrew",
|
|
415
|
-
"brew",
|
|
416
|
-
["install", ...getPackageNames("brew", missingNames)],
|
|
417
|
-
{ timeoutMs: INSTALL_COMMAND_TIMEOUT_MS },
|
|
418
|
-
),
|
|
419
|
-
],
|
|
481
|
+
commands: buildBrewInstallCommands(missingNames),
|
|
420
482
|
},
|
|
421
483
|
];
|
|
422
484
|
}
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {
|
|
2
|
+
BorderedLoader,
|
|
3
|
+
type ExtensionAPI,
|
|
4
|
+
type ExtensionCommandContext,
|
|
5
|
+
} from "@mariozechner/pi-coding-agent";
|
|
2
6
|
|
|
3
7
|
import { DOCTOR_COMMAND, DOCTOR_COMMAND_NAME, INSTALL_COMMAND_TIMEOUT_MS } from "./constants.ts";
|
|
4
8
|
import {
|
|
@@ -14,8 +18,8 @@ import { resolveDocumentTarget } from "./input.ts";
|
|
|
14
18
|
import type {
|
|
15
19
|
DependencyDiagnosis,
|
|
16
20
|
InputInspection,
|
|
21
|
+
InstallCommandSpec,
|
|
17
22
|
InstallStrategy,
|
|
18
|
-
UiMessageLevel,
|
|
19
23
|
} from "./types.ts";
|
|
20
24
|
|
|
21
25
|
function normalizeDoctorArgument(input: string): string | undefined {
|
|
@@ -38,11 +42,9 @@ function formatDoctorReport(options: {
|
|
|
38
42
|
diagnoses: DependencyDiagnosis[];
|
|
39
43
|
strategies: InstallStrategy[];
|
|
40
44
|
installSummary?: string[];
|
|
41
|
-
}):
|
|
45
|
+
}): string {
|
|
42
46
|
const missing = options.diagnoses.filter((diagnosis) => !diagnosis.installed);
|
|
43
47
|
const relevantMissing = missing.filter((diagnosis) => diagnosis.relevant);
|
|
44
|
-
const level: UiMessageLevel =
|
|
45
|
-
relevantMissing.length > 0 ? "error" : missing.length > 0 ? "warning" : "info";
|
|
46
48
|
const lines = ["docparser doctor", `Platform: ${getPlatformLabel()}`];
|
|
47
49
|
|
|
48
50
|
if (options.sourcePath) {
|
|
@@ -64,6 +66,14 @@ function formatDoctorReport(options: {
|
|
|
64
66
|
lines.push(
|
|
65
67
|
"This input type does not require extra host conversion packages for normal parsing.",
|
|
66
68
|
);
|
|
69
|
+
} else if (relevantMissing.length > 0) {
|
|
70
|
+
lines.push("Action needed: install the missing packages listed below.");
|
|
71
|
+
} else if (missing.length > 0) {
|
|
72
|
+
lines.push(
|
|
73
|
+
"Optional host packages are missing. Install them if you plan to parse inputs that need them.",
|
|
74
|
+
);
|
|
75
|
+
} else {
|
|
76
|
+
lines.push("All relevant host dependencies are installed.");
|
|
67
77
|
}
|
|
68
78
|
|
|
69
79
|
lines.push("Dependency status:");
|
|
@@ -119,10 +129,7 @@ function formatDoctorReport(options: {
|
|
|
119
129
|
}
|
|
120
130
|
}
|
|
121
131
|
|
|
122
|
-
return
|
|
123
|
-
text: lines.join("\n"),
|
|
124
|
-
level,
|
|
125
|
-
};
|
|
132
|
+
return lines.join("\n");
|
|
126
133
|
}
|
|
127
134
|
|
|
128
135
|
async function collectDoctorState(inspection?: InputInspection): Promise<{
|
|
@@ -163,6 +170,77 @@ async function selectInstallStrategy(
|
|
|
163
170
|
return strategies.find((strategy) => strategy.label === selectedLabel);
|
|
164
171
|
}
|
|
165
172
|
|
|
173
|
+
async function runInstallCommands(
|
|
174
|
+
pi: ExtensionAPI,
|
|
175
|
+
ctx: ExtensionCommandContext,
|
|
176
|
+
commands: InstallCommandSpec[],
|
|
177
|
+
): Promise<string[]> {
|
|
178
|
+
const runInstallLoop = async () => {
|
|
179
|
+
const installSummary: string[] = [];
|
|
180
|
+
|
|
181
|
+
for (const command of commands) {
|
|
182
|
+
const result = await pi.exec(command.command, command.args, {
|
|
183
|
+
timeout: command.timeoutMs ?? INSTALL_COMMAND_TIMEOUT_MS,
|
|
184
|
+
});
|
|
185
|
+
const success = result.code === 0 && !result.killed;
|
|
186
|
+
|
|
187
|
+
installSummary.push(
|
|
188
|
+
`${command.description}: ${success ? "ok" : `failed (exit ${result.code}${result.killed ? ", killed" : ""})`}`,
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
if (!success) {
|
|
192
|
+
const outputSummary = summarizeInstallOutput(result.stdout, result.stderr);
|
|
193
|
+
if (outputSummary) {
|
|
194
|
+
installSummary.push(`Command output:\n${outputSummary}`);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return installSummary;
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
let ranCustomUi = false;
|
|
203
|
+
let installSummary: string[] | undefined;
|
|
204
|
+
let installError: unknown;
|
|
205
|
+
|
|
206
|
+
await ctx.ui.custom<boolean | undefined>((tui, theme, _kb, done) => {
|
|
207
|
+
ranCustomUi = true;
|
|
208
|
+
|
|
209
|
+
const loader = new BorderedLoader(
|
|
210
|
+
tui,
|
|
211
|
+
theme,
|
|
212
|
+
"Installing missing packages. Please do not quit pi until this finishes.",
|
|
213
|
+
{ cancellable: false },
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
runInstallLoop()
|
|
217
|
+
.then((summary) => {
|
|
218
|
+
installSummary = summary;
|
|
219
|
+
done(true);
|
|
220
|
+
})
|
|
221
|
+
.catch((error) => {
|
|
222
|
+
installError = error;
|
|
223
|
+
done(false);
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
return loader;
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
if (!ranCustomUi) {
|
|
230
|
+
ctx.ui.notify(
|
|
231
|
+
"Installing missing packages. This can take a few minutes. Please wait for the final doctor report.",
|
|
232
|
+
"info",
|
|
233
|
+
);
|
|
234
|
+
return runInstallLoop();
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (installError) {
|
|
238
|
+
throw installError;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return installSummary ?? [];
|
|
242
|
+
}
|
|
243
|
+
|
|
166
244
|
export function registerDoctorCommand(pi: ExtensionAPI) {
|
|
167
245
|
pi.registerCommand(DOCTOR_COMMAND_NAME, {
|
|
168
246
|
description:
|
|
@@ -188,6 +266,8 @@ export function registerDoctorCommand(pi: ExtensionAPI) {
|
|
|
188
266
|
return;
|
|
189
267
|
}
|
|
190
268
|
|
|
269
|
+
await ctx.waitForIdle();
|
|
270
|
+
|
|
191
271
|
try {
|
|
192
272
|
let target: Awaited<ReturnType<typeof resolveDocumentTarget>> | undefined;
|
|
193
273
|
try {
|
|
@@ -207,7 +287,7 @@ export function registerDoctorCommand(pi: ExtensionAPI) {
|
|
|
207
287
|
diagnoses: initialState.diagnoses,
|
|
208
288
|
strategies: initialState.strategies,
|
|
209
289
|
});
|
|
210
|
-
ctx.ui.notify(initialReport
|
|
290
|
+
ctx.ui.notify(initialReport, "info");
|
|
211
291
|
|
|
212
292
|
if (
|
|
213
293
|
initialState.missingDependencies.length === 0 ||
|
|
@@ -250,25 +330,7 @@ export function registerDoctorCommand(pi: ExtensionAPI) {
|
|
|
250
330
|
return;
|
|
251
331
|
}
|
|
252
332
|
|
|
253
|
-
const installSummary
|
|
254
|
-
for (const command of selectedStrategy.commands) {
|
|
255
|
-
ctx.ui.notify(`Running: ${command.display}`, "info");
|
|
256
|
-
const result = await pi.exec(command.command, command.args, {
|
|
257
|
-
timeout: command.timeoutMs ?? INSTALL_COMMAND_TIMEOUT_MS,
|
|
258
|
-
});
|
|
259
|
-
const success = result.code === 0 && !result.killed;
|
|
260
|
-
|
|
261
|
-
installSummary.push(
|
|
262
|
-
`${command.description}: ${success ? "ok" : `failed (exit ${result.code}${result.killed ? ", killed" : ""})`}`,
|
|
263
|
-
);
|
|
264
|
-
|
|
265
|
-
if (!success) {
|
|
266
|
-
const outputSummary = summarizeInstallOutput(result.stdout, result.stderr);
|
|
267
|
-
if (outputSummary) {
|
|
268
|
-
installSummary.push(`Command output:\n${outputSummary}`);
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
}
|
|
333
|
+
const installSummary = await runInstallCommands(pi, ctx, selectedStrategy.commands);
|
|
272
334
|
|
|
273
335
|
const finalState = await collectDoctorState(target?.inspection);
|
|
274
336
|
const finalReport = formatDoctorReport({
|
|
@@ -279,7 +341,7 @@ export function registerDoctorCommand(pi: ExtensionAPI) {
|
|
|
279
341
|
strategies: finalState.strategies,
|
|
280
342
|
installSummary,
|
|
281
343
|
});
|
|
282
|
-
ctx.ui.notify(finalReport
|
|
344
|
+
ctx.ui.notify(finalReport, "info");
|
|
283
345
|
} catch (error) {
|
|
284
346
|
ctx.ui.notify(
|
|
285
347
|
`docparser doctor failed: ${error instanceof Error ? error.message : String(error)}`,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-docparser",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Pi package that adds a document_parse tool and companion skill for parsing PDFs, Office documents, spreadsheets, and images with LiteParse.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"document-parse",
|
|
@@ -13,6 +13,10 @@
|
|
|
13
13
|
],
|
|
14
14
|
"homepage": "https://maximilian-schwarzmueller.com",
|
|
15
15
|
"license": "MIT",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/maxedapps/pi-docparser.git"
|
|
19
|
+
},
|
|
16
20
|
"files": [
|
|
17
21
|
"assets",
|
|
18
22
|
"extensions",
|