dsh-office-kit 0.2.0 → 0.3.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/CAPABILITY.md +147 -68
- package/README.md +3 -1
- package/assets/skills/excel-live-control/SKILL.md +234 -0
- package/assets/skills/excel-live-control/container_tools/live_excel.py +1589 -0
- package/assets/skills/excel-live-control/domain_guidance/financial_models.md +86 -0
- package/assets/skills/excel-live-control/domain_guidance/healthcare.md +58 -0
- package/assets/skills/excel-live-control/domain_guidance/marketing_advertising.md +53 -0
- package/assets/skills/excel-live-control/domain_guidance/scientific_research.md +53 -0
- package/assets/skills/excel-live-control/references/office-automation.md +207 -0
- package/assets/skills/excel-live-control/references/read_only_qna.md +59 -0
- package/assets/skills/excel-live-control/style_guidelines.md +87 -0
- package/assets/skills/excel-live-control/workflows/create_workflows.md +78 -0
- package/assets/skills/excel-live-control/workflows/edit_workflows.md +72 -0
- package/assets/skills/office-documents/SKILL.md +90 -77
- package/assets/skills/office-documents/examples/end_to_end_smoke_test.md +86 -0
- package/assets/skills/office-documents/ooxml/comments.md +40 -0
- package/assets/skills/office-documents/ooxml/hyperlinks_and_fields.md +66 -0
- package/assets/skills/office-documents/ooxml/rels_and_content_types.md +62 -0
- package/assets/skills/office-documents/ooxml/tracked_changes.md +53 -0
- package/assets/skills/office-documents/references/docx-structure.md +83 -0
- package/assets/skills/office-documents/references/template-elicitation.md +49 -0
- package/assets/skills/office-documents/references/writing-quality.md +49 -0
- package/assets/skills/office-documents/render_docx.py +92 -0
- package/assets/skills/office-documents/scripts/a11y_audit.py +194 -0
- package/assets/skills/office-documents/scripts/accept_tracked_changes.py +151 -0
- package/assets/skills/office-documents/scripts/add_tracked_replacements.py +148 -0
- package/assets/skills/office-documents/scripts/append_blocks.py +108 -0
- package/assets/skills/office-documents/scripts/apply_template_styles.py +104 -0
- package/assets/skills/office-documents/scripts/captions_and_crossrefs.py +102 -0
- package/assets/skills/office-documents/scripts/comments_add.py +136 -0
- package/assets/skills/office-documents/scripts/comments_apply_patch.py +114 -0
- package/assets/skills/office-documents/scripts/comments_extract.py +85 -0
- package/assets/skills/office-documents/scripts/comments_strip.py +74 -0
- package/assets/skills/office-documents/scripts/content_controls.py +192 -0
- package/assets/skills/office-documents/scripts/core_properties.py +124 -0
- package/assets/skills/office-documents/scripts/docx_kit.py +587 -0
- package/assets/skills/office-documents/scripts/docx_ooxml_patch.py +225 -0
- package/assets/skills/office-documents/scripts/docx_table_to_csv.py +99 -0
- package/assets/skills/office-documents/scripts/fields_materialize.py +66 -0
- package/assets/skills/office-documents/scripts/fields_report.py +59 -0
- package/assets/skills/office-documents/scripts/flatten_ref_fields.py +62 -0
- package/assets/skills/office-documents/scripts/footnotes_report.py +77 -0
- package/assets/skills/office-documents/scripts/google_docs_title_sanitize.py +96 -0
- package/assets/skills/office-documents/scripts/heading_audit.py +81 -0
- package/assets/skills/office-documents/scripts/images_audit.py +80 -0
- package/assets/skills/office-documents/scripts/insert_note.py +126 -0
- package/assets/skills/office-documents/scripts/insert_ref_fields.py +90 -0
- package/assets/skills/office-documents/scripts/insert_toc.py +84 -0
- package/assets/skills/office-documents/scripts/internal_nav.py +146 -0
- package/assets/skills/office-documents/scripts/make_fixtures.py +169 -0
- package/assets/skills/office-documents/scripts/merge_docx_append.py +239 -0
- package/assets/skills/office-documents/scripts/privacy_scrub.py +131 -0
- package/assets/skills/office-documents/scripts/redact_docx.py +113 -0
- package/assets/skills/office-documents/scripts/render_and_diff.py +101 -0
- package/assets/skills/office-documents/scripts/replace_text.py +154 -0
- package/assets/skills/office-documents/scripts/section_audit.py +91 -0
- package/assets/skills/office-documents/scripts/set_protection.py +92 -0
- package/assets/skills/office-documents/scripts/style_lint.py +111 -0
- package/assets/skills/office-documents/scripts/style_normalize.py +103 -0
- package/assets/skills/office-documents/scripts/table_geometry.py +167 -0
- package/assets/skills/office-documents/scripts/watermark_add.py +48 -0
- package/assets/skills/office-documents/scripts/watermark_audit_remove.py +67 -0
- package/assets/skills/office-documents/scripts/xlsx_to_docx_table.py +117 -0
- package/assets/skills/office-documents/tasks/accessibility_a11y.md +45 -0
- package/assets/skills/office-documents/tasks/captions_crossrefs.md +49 -0
- package/assets/skills/office-documents/tasks/clean_tracked_changes.md +49 -0
- package/assets/skills/office-documents/tasks/comments_manage.md +59 -0
- package/assets/skills/office-documents/tasks/compare_diff.md +40 -0
- package/assets/skills/office-documents/tasks/create_edit.md +107 -0
- package/assets/skills/office-documents/tasks/equations.md +59 -0
- package/assets/skills/office-documents/tasks/fields_update.md +44 -0
- package/assets/skills/office-documents/tasks/fixtures_edge_cases.md +50 -0
- package/assets/skills/office-documents/tasks/footnotes_endnotes.md +38 -0
- package/assets/skills/office-documents/tasks/forms_content_controls.md +48 -0
- package/assets/skills/office-documents/tasks/headings_numbering.md +55 -0
- package/assets/skills/office-documents/tasks/images_figures.md +63 -0
- package/assets/skills/office-documents/tasks/multi_doc_merge.md +43 -0
- package/assets/skills/office-documents/tasks/navigation_internal_links.md +38 -0
- package/assets/skills/office-documents/tasks/privacy_scrub_metadata.md +45 -0
- package/assets/skills/office-documents/tasks/protection_restrict_editing.md +41 -0
- package/assets/skills/office-documents/tasks/read_review.md +50 -0
- package/assets/skills/office-documents/tasks/redaction_anonymization.md +42 -0
- package/assets/skills/office-documents/tasks/sections_layout.md +54 -0
- package/assets/skills/office-documents/tasks/style_lint_normalize.md +44 -0
- package/assets/skills/office-documents/tasks/tables_spreadsheets.md +64 -0
- package/assets/skills/office-documents/tasks/templates_style_packs.md +53 -0
- package/assets/skills/office-documents/tasks/toc_workflow.md +39 -0
- package/assets/skills/office-documents/tasks/verify_render.md +50 -0
- package/assets/skills/office-documents/tasks/watermarks_background.md +35 -0
- package/assets/skills/office-documents/troubleshooting/libreoffice_headless.md +47 -0
- package/assets/skills/office-documents/troubleshooting/run_splitting.md +36 -0
- package/assets/skills/office-pdf/SKILL.md +73 -56
- package/assets/skills/office-pdf/references/form-fields.md +56 -0
- package/assets/skills/office-pdf/references/geometry.md +44 -0
- package/assets/skills/office-pdf/references/layout-quality.md +41 -0
- package/assets/skills/office-pdf/references/limits.md +58 -0
- package/assets/skills/office-pdf/scripts/extract_text.py +172 -0
- package/assets/skills/office-pdf/scripts/forms.py +556 -0
- package/assets/skills/office-pdf/scripts/metadata.py +161 -0
- package/assets/skills/office-pdf/scripts/page_ops.py +198 -0
- package/assets/skills/office-pdf/scripts/redact_region.py +347 -0
- package/assets/skills/office-pdf/scripts/render_pages.py +207 -0
- package/assets/skills/office-pdf/scripts/split_pages.py +155 -0
- package/assets/skills/office-pdf/scripts/stamp_overlay.py +199 -0
- package/assets/skills/office-pdf/tasks/annotations.md +98 -0
- package/assets/skills/office-pdf/tasks/create.md +75 -0
- package/assets/skills/office-pdf/tasks/edit-pages.md +71 -0
- package/assets/skills/office-pdf/tasks/forms.md +67 -0
- package/assets/skills/office-pdf/tasks/read.md +62 -0
- package/assets/skills/office-pdf/tasks/redact.md +72 -0
- package/assets/skills/office-pdf/tasks/stamp.md +60 -0
- package/assets/skills/office-presentations/SKILL.md +141 -87
- package/assets/skills/office-presentations/container_tools/add_slides.py +136 -0
- package/assets/skills/office-presentations/container_tools/core_properties.py +120 -0
- package/assets/skills/office-presentations/container_tools/create_montage.py +126 -0
- package/assets/skills/office-presentations/container_tools/inspect_presentation_layout_geometry.py +285 -0
- package/assets/skills/office-presentations/container_tools/inspect_presentation_package_integrity.py +157 -0
- package/assets/skills/office-presentations/container_tools/native_chart_title_gate.py +144 -0
- package/assets/skills/office-presentations/container_tools/native_quantitative_chart_gate.py +199 -0
- package/assets/skills/office-presentations/container_tools/native_table_arithmetic_gate.py +212 -0
- package/assets/skills/office-presentations/container_tools/ooxml_helpers.py +345 -0
- package/assets/skills/office-presentations/container_tools/preview_render.py +375 -0
- package/assets/skills/office-presentations/container_tools/render_slides.py +139 -0
- package/assets/skills/office-presentations/container_tools/replace_text.py +168 -0
- package/assets/skills/office-presentations/container_tools/template_source_fidelity_gate.py +229 -0
- package/assets/skills/office-presentations/domain_guidance/finance.md +66 -0
- package/assets/skills/office-presentations/recipes/README.md +71 -0
- package/assets/skills/office-presentations/recipes/autolayout.md +88 -0
- package/assets/skills/office-presentations/recipes/connectors.md +83 -0
- package/assets/skills/office-presentations/recipes/images.md +80 -0
- package/assets/skills/office-presentations/recipes/master-and-layouts.md +127 -0
- package/assets/skills/office-presentations/recipes/native-charts.md +87 -0
- package/assets/skills/office-presentations/recipes/rich-text-runs.md +86 -0
- package/assets/skills/office-presentations/recipes/speaker-notes.md +65 -0
- package/assets/skills/office-presentations/recipes/tables.md +88 -0
- package/assets/skills/office-presentations/recipes/textbox-at-coordinate.md +71 -0
- package/assets/skills/office-presentations/recipes/theme-tokens.md +87 -0
- package/assets/skills/office-presentations/references/authoring-workflow.md +65 -0
- package/assets/skills/office-presentations/references/finalization.md +63 -0
- package/assets/skills/office-presentations/references/native-bullets.md +78 -0
- package/assets/skills/office-presentations/references/native-evidence.md +67 -0
- package/assets/skills/office-presentations/references/style-guidelines.md +66 -0
- package/assets/skills/office-presentations/references/templates.md +76 -0
- package/assets/skills/office-presentations/routing/google_slides.md +45 -0
- package/assets/skills/office-presentations/tasks/apply-annotations.md +83 -0
- package/assets/skills/office-presentations/tasks/charts-tables-notes.md +79 -0
- package/assets/skills/office-presentations/tasks/create-deck.md +106 -0
- package/assets/skills/office-presentations/tasks/edit-deck.md +93 -0
- package/assets/skills/office-presentations/tasks/inspect-and-verify.md +107 -0
- package/assets/skills/office-presentations/tasks/template-following.md +132 -0
- package/assets/skills/office-presentations/template_following_scripts/check_template_fidelity.py +176 -0
- package/assets/skills/office-presentations/template_following_scripts/inspect_template_deck.py +154 -0
- package/assets/skills/office-presentations/template_following_scripts/make_contact_sheet.py +111 -0
- package/assets/skills/office-presentations/template_following_scripts/prepare_template_starter_deck.py +239 -0
- package/assets/skills/office-presentations/template_following_scripts/validate_template_plan.py +185 -0
- package/assets/skills/office-spreadsheets/SKILL.md +105 -101
- package/assets/skills/office-spreadsheets/container_tools/add_chart.py +236 -0
- package/assets/skills/office-spreadsheets/container_tools/add_image.py +107 -0
- package/assets/skills/office-spreadsheets/container_tools/append_rows.py +122 -0
- package/assets/skills/office-spreadsheets/container_tools/diff_workbooks.py +306 -0
- package/assets/skills/office-spreadsheets/container_tools/inspect_workbook.py +398 -0
- package/assets/skills/office-spreadsheets/container_tools/recalc_gate.py +275 -0
- package/assets/skills/office-spreadsheets/container_tools/set_cells.py +135 -0
- package/assets/skills/office-spreadsheets/container_tools/sheet_ops.py +134 -0
- package/assets/skills/office-spreadsheets/container_tools/style_cells.py +133 -0
- package/assets/skills/office-spreadsheets/recipes/charts.md +66 -0
- package/assets/skills/office-spreadsheets/recipes/conditional-formats.md +69 -0
- package/assets/skills/office-spreadsheets/recipes/data-tables.md +58 -0
- package/assets/skills/office-spreadsheets/recipes/named-ranges.md +41 -0
- package/assets/skills/office-spreadsheets/recipes/number-formats.md +72 -0
- package/assets/skills/office-spreadsheets/recipes/sparklines.md +43 -0
- package/assets/skills/office-spreadsheets/references/charts.md +63 -0
- package/assets/skills/office-spreadsheets/references/domain/financial-models.md +40 -0
- package/assets/skills/office-spreadsheets/references/domain/healthcare.md +22 -0
- package/assets/skills/office-spreadsheets/references/domain/marketing.md +28 -0
- package/assets/skills/office-spreadsheets/references/domain/research.md +22 -0
- package/assets/skills/office-spreadsheets/references/formula-patterns.md +78 -0
- package/assets/skills/office-spreadsheets/references/style-guidelines.md +69 -0
- package/assets/skills/office-spreadsheets/references/workbook-structure.md +60 -0
- package/assets/skills/office-spreadsheets/routing/google-sheets.md +36 -0
- package/assets/skills/office-spreadsheets/tasks/analyze-workbook.md +37 -0
- package/assets/skills/office-spreadsheets/tasks/annotations.md +51 -0
- package/assets/skills/office-spreadsheets/tasks/create-workbook.md +66 -0
- package/assets/skills/office-spreadsheets/tasks/diff-workbooks.md +38 -0
- package/assets/skills/office-spreadsheets/tasks/edit-workbook.md +69 -0
- package/assets/skills/office-spreadsheets/tasks/inspect-workbook.md +38 -0
- package/assets/skills/office-spreadsheets/tasks/recalc-and-check.md +57 -0
- package/assets/skills/shared/annotation-recipes.md +252 -0
- package/assets/skills/shared/check_runtime.py +253 -0
- package/assets/skills/shared/common-workflow.md +139 -0
- package/assets/skills/shared/scripts/ensure_raster_image.py +187 -0
- package/dist/client.js +4 -4
- package/dist/client.js.map +1 -1
- package/dist/index.js +239 -89
- package/package.json +1 -1
- package/postinstall.mjs +92 -82
- package/python/office_cli.py +171 -246
package/postinstall.mjs
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/* eslint-disable no-console */
|
|
3
3
|
/**
|
|
4
|
-
* dsh-office-kit
|
|
4
|
+
* dsh-office-kit runtime bootstrap.
|
|
5
5
|
*
|
|
6
6
|
* Ensures a compatible Python (3.10–3.13) with the Office helper libraries is
|
|
7
|
-
* available.
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* available. The virtual environment lives in `~/.dsh/office/runtime/venv` on
|
|
8
|
+
* every platform. On Windows a missing Python is downloaded from python.org,
|
|
9
|
+
* signature-checked and installed silently into the installer's own per-user
|
|
10
|
+
* location (AppendPath=1, no TargetDir, no manual registry edits).
|
|
10
11
|
*
|
|
11
|
-
* The
|
|
12
|
-
*
|
|
13
|
-
*
|
|
12
|
+
* The host half runs this file itself when the runtime is missing, so pnpm's
|
|
13
|
+
* lifecycle-script policy can never leave the plugin with a bare interpreter.
|
|
14
|
+
* The script is idempotent: a venv that already imports every dependency
|
|
15
|
+
* returns immediately. PATH is maintained by the Python installer, never here.
|
|
14
16
|
*/
|
|
15
17
|
|
|
16
18
|
import { spawnSync } from 'node:child_process'
|
|
@@ -23,11 +25,17 @@ import { fileURLToPath } from 'node:url'
|
|
|
23
25
|
const PACKAGE_ROOT = dirname(fileURLToPath(import.meta.url))
|
|
24
26
|
const IS_WINDOWS = process.platform === 'win32'
|
|
25
27
|
const PYTHON_VERSION = '3.12.10'
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
const CONFIG_DIR = join(process.env.APPDATA || join(homedir(), 'AppData', 'Roaming'), 'dsh-office-kit')
|
|
29
|
-
const VENV_DIR = join(DATA_DIR, 'venv')
|
|
28
|
+
const RUNTIME_DIR = join(homedir(), '.dsh', 'office', 'runtime')
|
|
29
|
+
const VENV_DIR = join(RUNTIME_DIR, 'venv')
|
|
30
30
|
const VENV_PYTHON = join(VENV_DIR, IS_WINDOWS ? 'Scripts/python.exe' : 'bin/python')
|
|
31
|
+
const LEGACY_DATA_DIR = IS_WINDOWS
|
|
32
|
+
? join(process.env.LOCALAPPDATA || join(homedir(), 'AppData', 'Local'), 'dsh-office-kit')
|
|
33
|
+
: process.platform === 'darwin'
|
|
34
|
+
? join(homedir(), 'Library', 'Application Support', 'dsh-office-kit')
|
|
35
|
+
: join(process.env.XDG_DATA_HOME || join(homedir(), '.local', 'share'), 'dsh-office-kit')
|
|
36
|
+
const LEGACY_VENV_DIR = join(LEGACY_DATA_DIR, 'venv')
|
|
37
|
+
const LEGACY_VENV_PYTHON = join(LEGACY_VENV_DIR, IS_WINDOWS ? 'Scripts/python.exe' : 'bin/python')
|
|
38
|
+
const CONFIG_DIR = join(process.env.APPDATA || join(homedir(), 'AppData', 'Roaming'), 'dsh-office-kit')
|
|
31
39
|
const CONFIG_FILE = join(CONFIG_DIR, 'config.json')
|
|
32
40
|
const REQUIREMENTS = join(PACKAGE_ROOT, 'python', 'requirements.txt')
|
|
33
41
|
const LOG_PREFIX = '[dsh-office-kit]'
|
|
@@ -118,10 +126,26 @@ function findCompatiblePython() {
|
|
|
118
126
|
}
|
|
119
127
|
|
|
120
128
|
// A ready virtual environment still works even if its base interpreter was removed.
|
|
121
|
-
|
|
122
|
-
|
|
129
|
+
for (const venvPython of [VENV_PYTHON, LEGACY_VENV_PYTHON]) {
|
|
130
|
+
if (existsSync(venvPython)) {
|
|
131
|
+
const info = pythonInfo(venvPython)
|
|
132
|
+
if (info) {
|
|
133
|
+
return { executable: venvPython, args: [], info }
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return null
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** The first existing virtual environment that still runs, new layout first. */
|
|
141
|
+
function workingVenv() {
|
|
142
|
+
for (const [python, directory] of [[VENV_PYTHON, VENV_DIR], [LEGACY_VENV_PYTHON, LEGACY_VENV_DIR]]) {
|
|
143
|
+
if (!existsSync(python)) {
|
|
144
|
+
continue
|
|
145
|
+
}
|
|
146
|
+
const info = pythonInfo(python)
|
|
123
147
|
if (info) {
|
|
124
|
-
return {
|
|
148
|
+
return { python, directory, info }
|
|
125
149
|
}
|
|
126
150
|
}
|
|
127
151
|
return null
|
|
@@ -159,20 +183,19 @@ function verifySignature(file) {
|
|
|
159
183
|
|
|
160
184
|
function installPython() {
|
|
161
185
|
if (!IS_WINDOWS) {
|
|
162
|
-
return
|
|
186
|
+
return false
|
|
163
187
|
}
|
|
164
188
|
// Unique per process: a stale installer from an interrupted run must not lock this download.
|
|
165
189
|
const installer = join(tmpdir(), `python-${PYTHON_VERSION}-amd64-${process.pid}-${Date.now()}.exe`)
|
|
166
190
|
const url = `https://www.python.org/ftp/python/${PYTHON_VERSION}/python-${PYTHON_VERSION}-amd64.exe`
|
|
167
191
|
download(url, installer)
|
|
168
192
|
verifySignature(installer)
|
|
169
|
-
log(`installing Python ${PYTHON_VERSION} into
|
|
193
|
+
log(`installing Python ${PYTHON_VERSION} into the installer default per-user location`)
|
|
170
194
|
const args = [
|
|
171
195
|
'/quiet',
|
|
172
196
|
'InstallAllUsers=0',
|
|
173
|
-
|
|
174
|
-
'
|
|
175
|
-
'Include_launcher=0',
|
|
197
|
+
'AppendPath=1',
|
|
198
|
+
'CompileAll=0',
|
|
176
199
|
'Include_test=0',
|
|
177
200
|
'Include_doc=0',
|
|
178
201
|
'Include_pip=1',
|
|
@@ -183,17 +206,16 @@ function installPython() {
|
|
|
183
206
|
if (result.status !== 0) {
|
|
184
207
|
throw new Error(`Python installer exited with status ${result.status}`)
|
|
185
208
|
}
|
|
186
|
-
|
|
187
|
-
if (!existsSync(target)) {
|
|
188
|
-
throw new Error(`Python installer finished but ${target} is missing`)
|
|
189
|
-
}
|
|
190
|
-
return target
|
|
209
|
+
return true
|
|
191
210
|
}
|
|
192
211
|
|
|
193
212
|
function ensureVenv(base) {
|
|
194
|
-
const existing =
|
|
213
|
+
const existing = workingVenv()
|
|
195
214
|
if (existing) {
|
|
196
|
-
|
|
215
|
+
if (existing.directory === LEGACY_VENV_DIR) {
|
|
216
|
+
log(`using the existing virtual environment at ${LEGACY_VENV_DIR} (legacy layout)`)
|
|
217
|
+
}
|
|
218
|
+
return existing.python
|
|
197
219
|
}
|
|
198
220
|
if (existsSync(VENV_DIR)) {
|
|
199
221
|
warn(`replacing incomplete virtual environment at ${VENV_DIR}`)
|
|
@@ -209,15 +231,15 @@ function ensureVenv(base) {
|
|
|
209
231
|
if (!info) {
|
|
210
232
|
throw new Error('virtual environment interpreter is not executable')
|
|
211
233
|
}
|
|
212
|
-
return
|
|
234
|
+
return VENV_PYTHON
|
|
213
235
|
}
|
|
214
236
|
|
|
215
|
-
function installRequirements() {
|
|
237
|
+
function installRequirements(python) {
|
|
216
238
|
if (!existsSync(REQUIREMENTS)) {
|
|
217
239
|
throw new Error(`requirements file missing: ${REQUIREMENTS}`)
|
|
218
240
|
}
|
|
219
241
|
log('installing Python packages (this may take a few minutes)')
|
|
220
|
-
const result = spawnSync(
|
|
242
|
+
const result = spawnSync(python, ['-m', 'pip', 'install', '--disable-pip-version-check', '--no-input', '--upgrade', '-r', REQUIREMENTS], {
|
|
221
243
|
stdio: 'inherit',
|
|
222
244
|
timeout: 30 * 60 * 1000,
|
|
223
245
|
})
|
|
@@ -226,10 +248,10 @@ function installRequirements() {
|
|
|
226
248
|
}
|
|
227
249
|
}
|
|
228
250
|
|
|
229
|
-
function verifyImports() {
|
|
251
|
+
function verifyImports(python) {
|
|
230
252
|
const modules = ['docx', 'openpyxl', 'pptx', 'pypdf', 'reportlab', 'pdfplumber', 'pymupdf', 'PIL', 'latex2mathml', 'mathml2omml']
|
|
231
253
|
const script = `import importlib, json\nmissing = []\nfor name in ${JSON.stringify(modules)}:\n try:\n importlib.import_module(name)\n except Exception as exc:\n missing.append({"module": name, "error": f"{type(exc).__name__}: {exc}"})\nprint(json.dumps({"ok": not missing, "missing": missing}))`
|
|
232
|
-
const result = spawnSync(
|
|
254
|
+
const result = spawnSync(python, ['-c', script], { encoding: 'utf8', timeout: 120000 })
|
|
233
255
|
if (result.status !== 0) {
|
|
234
256
|
throw new Error(`import verification failed: ${(result.stderr || '').trim()}`)
|
|
235
257
|
}
|
|
@@ -257,31 +279,17 @@ function writeConfig(config) {
|
|
|
257
279
|
writeFileSync(CONFIG_FILE, `${JSON.stringify(config, null, 2)}\n`, 'utf8')
|
|
258
280
|
}
|
|
259
281
|
|
|
260
|
-
function
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
'
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
'foreach ($entry in ($env:DSH_PATH_ENTRIES -split ";")) { if ($entry -and ($parts -notcontains $entry)) { $parts += $entry; $changed = $true } }',
|
|
271
|
-
'if ($changed) { try { $item.SetValue("Path", ($parts -join ";"), "ExpandString"); Write-Output "updated" } catch { Write-Output "failed: $($_.Exception.Message)"; exit 3 } } else { Write-Output "unchanged" }',
|
|
272
|
-
].join('; ')
|
|
273
|
-
const result = spawnSync('powershell', ['-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-Command', script], {
|
|
274
|
-
encoding: 'utf8',
|
|
275
|
-
env: { ...process.env, DSH_PATH_ENTRIES: entries.join(';') },
|
|
276
|
-
timeout: 60000,
|
|
282
|
+
function writeReadyConfig(existingConfig, python, basePython, report) {
|
|
283
|
+
const info = pythonInfo(python)
|
|
284
|
+
writeConfig({
|
|
285
|
+
...existingConfig,
|
|
286
|
+
python,
|
|
287
|
+
...(basePython ? { basePython } : {}),
|
|
288
|
+
...(info ? { pythonVersion: info.version.join('.') } : {}),
|
|
289
|
+
pluginVersion: readPackageVersion(),
|
|
290
|
+
updatedAt: new Date().toISOString(),
|
|
291
|
+
imports: report,
|
|
277
292
|
})
|
|
278
|
-
if (result.status !== 0) {
|
|
279
|
-
warn(`could not update the user PATH: ${(result.stdout || result.stderr || '').trim()}`)
|
|
280
|
-
warn('The plugin still resolves the verified interpreter by absolute path; reopen the terminal only after a successful PATH update.')
|
|
281
|
-
}
|
|
282
|
-
else {
|
|
283
|
-
log(`user PATH ${(result.stdout || '').trim()}`)
|
|
284
|
-
}
|
|
285
293
|
}
|
|
286
294
|
|
|
287
295
|
function main() {
|
|
@@ -295,17 +303,31 @@ function main() {
|
|
|
295
303
|
}
|
|
296
304
|
|
|
297
305
|
const existingConfig = readConfig()
|
|
298
|
-
let base = findCompatiblePython()
|
|
299
306
|
|
|
307
|
+
// Fast path: an installed venv that already imports everything needs no network and no pip.
|
|
308
|
+
const ready = workingVenv()
|
|
309
|
+
if (ready) {
|
|
310
|
+
try {
|
|
311
|
+
const report = verifyImports(ready.python)
|
|
312
|
+
writeReadyConfig(existingConfig, ready.python, existingConfig.basePython, report)
|
|
313
|
+
log('Python environment is ready.')
|
|
314
|
+
log(`interpreter: ${ready.python}`)
|
|
315
|
+
return
|
|
316
|
+
}
|
|
317
|
+
catch (error) {
|
|
318
|
+
warn(`existing virtual environment needs repair: ${error.message}`)
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
let base = findCompatiblePython()
|
|
300
323
|
if (!base) {
|
|
301
324
|
if (!IS_WINDOWS) {
|
|
302
325
|
warn('no compatible Python 3.10–3.13 was found. Install one and rerun `node postinstall.mjs`.')
|
|
326
|
+
process.exitCode = 1
|
|
303
327
|
return
|
|
304
328
|
}
|
|
305
329
|
try {
|
|
306
|
-
|
|
307
|
-
const info = installed ? pythonInfo(installed) : null
|
|
308
|
-
base = installed && info ? { executable: installed, args: [], info } : null
|
|
330
|
+
installPython()
|
|
309
331
|
}
|
|
310
332
|
catch (error) {
|
|
311
333
|
warn(`automatic Python installation failed: ${error.message}`)
|
|
@@ -313,34 +335,22 @@ function main() {
|
|
|
313
335
|
process.exitCode = 1
|
|
314
336
|
return
|
|
315
337
|
}
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
338
|
+
base = findCompatiblePython()
|
|
339
|
+
if (!base) {
|
|
340
|
+
warn('the Python installer finished but no compatible interpreter was found in the default per-user location.')
|
|
341
|
+
process.exitCode = 1
|
|
342
|
+
return
|
|
343
|
+
}
|
|
322
344
|
}
|
|
323
345
|
|
|
324
346
|
log(`using Python ${base.info.version.join('.')} at ${base.info.executable}`)
|
|
325
347
|
try {
|
|
326
|
-
ensureVenv(base)
|
|
327
|
-
installRequirements()
|
|
328
|
-
const report = verifyImports()
|
|
329
|
-
|
|
330
|
-
...existingConfig,
|
|
331
|
-
python: VENV_PYTHON,
|
|
332
|
-
basePython: base.info.executable,
|
|
333
|
-
pythonVersion: base.info.version.join('.'),
|
|
334
|
-
pluginVersion: readPackageVersion(),
|
|
335
|
-
updatedAt: new Date().toISOString(),
|
|
336
|
-
imports: report,
|
|
337
|
-
}
|
|
338
|
-
writeConfig(config)
|
|
339
|
-
const pathEntries = IS_WINDOWS ? [dirname(base.info.executable), join(dirname(base.info.executable), 'Scripts')] : []
|
|
340
|
-
addToUserPath(pathEntries)
|
|
348
|
+
const python = ensureVenv(base)
|
|
349
|
+
installRequirements(python)
|
|
350
|
+
const report = verifyImports(python)
|
|
351
|
+
writeReadyConfig(existingConfig, python, base.info.executable, report)
|
|
341
352
|
log('Python environment is ready.')
|
|
342
|
-
log(`interpreter: ${
|
|
343
|
-
log('Open a new terminal for PATH changes to apply to new shells.')
|
|
353
|
+
log(`interpreter: ${python}`)
|
|
344
354
|
}
|
|
345
355
|
catch (error) {
|
|
346
356
|
warn(`Python environment setup failed: ${error.message}`)
|