@qvac/harness 0.8.0 → 0.8.6

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 (94) hide show
  1. package/CHANGELOG.md +97 -1
  2. package/README.md +49 -0
  3. package/dist-lib/index.d.ts +31 -2
  4. package/dist-lib/index.js +365 -53
  5. package/dist-lib/lib/audio-transcode-bare.d.ts +1 -1
  6. package/dist-lib/lib/audio-transcode-bare.js +22 -25
  7. package/dist-lib/lib/audio-transcode-node.d.ts +1 -1
  8. package/dist-lib/lib/audio-transcode-node.js +1 -3
  9. package/dist-lib/lib/capabilities/audio-resample.d.ts +1 -1
  10. package/dist-lib/lib/capabilities/audio-resample.js +7 -8
  11. package/dist-lib/lib/capabilities/completion.d.ts +5 -2
  12. package/dist-lib/lib/capabilities/completion.js +22 -4
  13. package/dist-lib/lib/capabilities/download.d.ts +3 -1
  14. package/dist-lib/lib/capabilities/download.js +1 -1
  15. package/dist-lib/lib/capabilities/preview-model-source.d.ts +1 -0
  16. package/dist-lib/lib/capabilities/preview-model-source.js +1 -0
  17. package/dist-lib/lib/capabilities/usage-breakdown.js +4 -1
  18. package/dist-lib/lib/capabilities/voice.d.ts +5 -1
  19. package/dist-lib/lib/capabilities/voice.js +94 -26
  20. package/dist-lib/lib/connect.d.ts +4 -1
  21. package/dist-lib/lib/connect.js +14 -1
  22. package/dist-lib/lib/contract-types.d.ts +10 -0
  23. package/dist-lib/lib/docx/index.d.ts +1 -1
  24. package/dist-lib/lib/docx/index.js +16 -24
  25. package/dist-lib/lib/models/download-gate.d.ts +20 -0
  26. package/dist-lib/lib/models/download-gate.js +73 -0
  27. package/dist-lib/lib/models/memory-cap.d.ts +13 -0
  28. package/dist-lib/lib/models/memory-cap.js +21 -0
  29. package/dist-lib/lib/models/model-catalog-constants.d.ts +3 -1
  30. package/dist-lib/lib/models/model-catalog-constants.js +9 -1
  31. package/dist-lib/lib/models/model-catalog.d.ts +6 -2
  32. package/dist-lib/lib/models/model-catalog.js +35 -3
  33. package/dist-lib/lib/models/model-handle.d.ts +8 -1
  34. package/dist-lib/lib/models/model-handle.js +20 -2
  35. package/dist-lib/lib/models/model-list.d.ts +8 -1
  36. package/dist-lib/lib/models/model-list.js +23 -0
  37. package/dist-lib/lib/models/model-pool.d.ts +33 -5
  38. package/dist-lib/lib/models/model-pool.js +97 -29
  39. package/dist-lib/lib/office/extract.d.ts +9 -0
  40. package/dist-lib/lib/office/extract.js +19 -0
  41. package/dist-lib/lib/office/index.d.ts +1 -1
  42. package/dist-lib/lib/office/index.js +74 -19
  43. package/dist-lib/lib/serve.js +18 -2
  44. package/dist-lib/lib/tools/exec/adapters/python/module-distributions.js +0 -3
  45. package/dist-lib/lib/tools/exec/adapters/python/python-adapter.d.ts +7 -50
  46. package/dist-lib/lib/tools/exec/adapters/python/python-adapter.js +38 -695
  47. package/dist-lib/lib/tools/exec/adapters/python/run-session.js +1 -85
  48. package/dist-lib/lib/tools/exec.js +9 -20
  49. package/dist-lib/lib/tools/generate-image.d.ts +4 -2
  50. package/dist-lib/lib/tools/generate-image.js +5 -2
  51. package/dist-lib/lib/tools/generate-music.d.ts +4 -2
  52. package/dist-lib/lib/tools/generate-music.js +5 -2
  53. package/dist-lib/lib/tools/office/ops.d.ts +13 -0
  54. package/dist-lib/lib/tools/office/ops.js +100 -0
  55. package/dist-lib/lib/tools/office.d.ts +12 -0
  56. package/dist-lib/lib/tools/office.js +486 -0
  57. package/dist-lib/lib/tools/translate.d.ts +3 -1
  58. package/dist-lib/lib/tools/translate.js +6 -1
  59. package/dist-lib/schema/harness.js +41 -3
  60. package/dist-lib/spec/bind.js +9 -0
  61. package/dist-lib/spec/calls.js +3 -0
  62. package/dist-lib/spec/hrpc/hrpc.json +39 -0
  63. package/dist-lib/spec/hrpc/index.js +39 -3
  64. package/dist-lib/spec/hrpc/messages.js +93 -10
  65. package/dist-lib/spec/hyperschema/index.js +93 -10
  66. package/dist-lib/spec/hyperschema/schema.json +57 -1
  67. package/dist-lib/spec/hyperschema/types.d.ts +19 -2
  68. package/package.json +13 -9
  69. package/wheels/manifest.json +1 -160
  70. package/worker/harness-sidecar.mjs +3 -1
  71. package/dist-lib/lib/office/pptx.d.ts +0 -1
  72. package/dist-lib/lib/office/pptx.js +0 -26
  73. package/dist-lib/lib/office/python.d.ts +0 -8
  74. package/dist-lib/lib/office/python.js +0 -32
  75. package/dist-lib/lib/office/xlsx.d.ts +0 -1
  76. package/dist-lib/lib/office/xlsx.js +0 -26
  77. package/dist-lib/lib/tools/exec/adapters/python/create-effect.d.ts +0 -7
  78. package/dist-lib/lib/tools/exec/adapters/python/create-effect.js +0 -318
  79. package/wheels/cffi-2.0.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl +0 -0
  80. package/wheels/cryptography-47.0.0-cp314-abi3-pyemscripten_2026_0_wasm32.whl +0 -0
  81. package/wheels/defusedxml-0.7.1-py2.py3-none-any.whl +0 -0
  82. package/wheels/et_xmlfile-2.0.0-py3-none-any.whl +0 -0
  83. package/wheels/fonttools-4.62.1-py3-none-any.whl +0 -0
  84. package/wheels/fpdf2-2.8.8-py3-none-any.whl +0 -0
  85. package/wheels/lxml-6.0.2-cp314-cp314-pyemscripten_2026_0_wasm32.whl +0 -0
  86. package/wheels/openpyxl-3.1.5-py2.py3-none-any.whl +0 -0
  87. package/wheels/pillow-12.2.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl +0 -0
  88. package/wheels/pycparser-3.0-py3-none-any.whl +0 -0
  89. package/wheels/pypdf-6.15.0-py3-none-any.whl +0 -0
  90. package/wheels/python_docx-1.2.0-py3-none-any.whl +0 -0
  91. package/wheels/python_pptx-1.0.2-py3-none-any.whl +0 -0
  92. package/wheels/six-1.17.0-py2.py3-none-any.whl +0 -0
  93. package/wheels/typing_extensions-4.15.0-py3-none-any.whl +0 -0
  94. package/wheels/xlsxwriter-3.2.9-py3-none-any.whl +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qvac/harness",
3
- "version": "0.8.0",
3
+ "version": "0.8.6",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "bare": ">=1.34.0"
@@ -210,10 +210,6 @@
210
210
  "types": "./dist-lib/lib/tools/exec/adapters/python/attachments.d.ts",
211
211
  "default": "./dist-lib/lib/tools/exec/adapters/python/attachments.js"
212
212
  },
213
- "./lib/tools/exec/adapters/python/create-effect.ts": {
214
- "types": "./dist-lib/lib/tools/exec/adapters/python/create-effect.d.ts",
215
- "default": "./dist-lib/lib/tools/exec/adapters/python/create-effect.js"
216
- },
217
213
  "./lib/tools/exec/adapters/python/deadline-state.ts": {
218
214
  "types": "./dist-lib/lib/tools/exec/adapters/python/deadline-state.d.ts",
219
215
  "default": "./dist-lib/lib/tools/exec/adapters/python/deadline-state.js"
@@ -454,6 +450,14 @@
454
450
  "types": "./dist-lib/lib/tools/model-args.d.ts",
455
451
  "default": "./dist-lib/lib/tools/model-args.js"
456
452
  },
453
+ "./lib/tools/office.ts": {
454
+ "types": "./dist-lib/lib/tools/office.d.ts",
455
+ "default": "./dist-lib/lib/tools/office.js"
456
+ },
457
+ "./lib/tools/office/ops.ts": {
458
+ "types": "./dist-lib/lib/tools/office/ops.d.ts",
459
+ "default": "./dist-lib/lib/tools/office/ops.js"
460
+ },
457
461
  "./lib/tools/operations/builders.ts": {
458
462
  "types": "./dist-lib/lib/tools/operations/builders.d.ts",
459
463
  "default": "./dist-lib/lib/tools/operations/builders.js"
@@ -740,10 +744,10 @@
740
744
  }
741
745
  },
742
746
  "dependencies": {
743
- "@qvac/inference": "0.20.0",
747
+ "@qvac/inference": "0.21.0",
744
748
  "@qvac/langdetect-text": "0.1.2",
745
- "@qvac/sdk": "0.20.0",
746
- "@qvac/skills": "0.1.9",
749
+ "@qvac/sdk": "0.21.0",
750
+ "@qvac/skills": "0.1.12",
747
751
  "@xmldom/xmldom": "^0.9.12",
748
752
  "asenion": "^0.1.0",
749
753
  "b4a": "^1.9.0",
@@ -817,7 +821,7 @@
817
821
  "localwatch@1.4.3": "patches/localwatch@1.4.3.patch",
818
822
  "hypercore-blob-server@1.15.0": "patches/hypercore-blob-server@1.15.0.patch",
819
823
  "bare-stow@0.1.5": "patches/bare-stow@0.1.5.patch",
820
- "@qvac/inference@0.20.0": "patches/@qvac%2Finference@0.20.0.patch"
824
+ "@qvac/inference@0.21.0": "patches/@qvac%2Finference@0.21.0.patch"
821
825
  },
822
826
  "repository": {
823
827
  "type": "git",
@@ -1,170 +1,11 @@
1
1
  {
2
2
  "pyodide": "314.0.2",
3
- "packages": {
4
- "fpdf2": {
5
- "version": "2.8.8",
6
- "imports": [
7
- "fpdf"
8
- ],
9
- "pypi": [
10
- "defusedxml-0.7.1-py2.py3-none-any.whl",
11
- "fpdf2-2.8.8-py3-none-any.whl"
12
- ],
13
- "pyodide": [
14
- "pillow",
15
- "fonttools"
16
- ]
17
- },
18
- "pypdf": {
19
- "version": "6.15.0",
20
- "imports": [
21
- "pypdf"
22
- ],
23
- "pypi": [
24
- "pypdf-6.15.0-py3-none-any.whl"
25
- ],
26
- "pyodide": []
27
- },
28
- "openpyxl": {
29
- "version": "3.1.5",
30
- "imports": [
31
- "openpyxl"
32
- ],
33
- "pypi": [
34
- "et_xmlfile-2.0.0-py3-none-any.whl",
35
- "openpyxl-3.1.5-py2.py3-none-any.whl"
36
- ],
37
- "pyodide": []
38
- },
39
- "python-docx": {
40
- "version": "1.2.0",
41
- "imports": [
42
- "docx"
43
- ],
44
- "pypi": [
45
- "python_docx-1.2.0-py3-none-any.whl"
46
- ],
47
- "pyodide": [
48
- "typing-extensions",
49
- "lxml"
50
- ]
51
- },
52
- "python-pptx": {
53
- "version": "1.0.2",
54
- "imports": [
55
- "pptx"
56
- ],
57
- "pypi": [
58
- "xlsxwriter-3.2.9-py3-none-any.whl",
59
- "python_pptx-1.0.2-py3-none-any.whl"
60
- ],
61
- "pyodide": [
62
- "typing-extensions",
63
- "pillow",
64
- "lxml"
65
- ]
66
- },
67
- "cryptography": {
68
- "pypi": [],
69
- "pyodide": [
70
- "six",
71
- "pycparser",
72
- "cffi",
73
- "cryptography"
74
- ]
75
- },
76
- "pillow": {
77
- "pypi": [],
78
- "pyodide": [
79
- "pillow"
80
- ]
81
- }
82
- },
3
+ "packages": {},
83
4
  "files": {
84
5
  "micropip-0.11.1-py3-none-any.whl": {
85
6
  "url": "https://cdn.jsdelivr.net/pyodide/v314.0.2/full/micropip-0.11.1-py3-none-any.whl",
86
7
  "sha256": "668cd1270f480ac9f93da4a2b4873a252743e4ad3da723d37a7e4c637c4f2314",
87
8
  "channel": "pyodide"
88
- },
89
- "defusedxml-0.7.1-py2.py3-none-any.whl": {
90
- "url": "https://files.pythonhosted.org/packages/07/6c/aa3f2f849e01cb6a001cd8554a88d4c77c5c1a31c95bdf1cf9301e6d9ef4/defusedxml-0.7.1-py2.py3-none-any.whl",
91
- "sha256": "a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61",
92
- "channel": "pypi"
93
- },
94
- "fpdf2-2.8.8-py3-none-any.whl": {
95
- "url": "https://files.pythonhosted.org/packages/f5/be/af012eda9507494f28b99b077423806c43a11573eb6225dd46f19ae2d263/fpdf2-2.8.8-py3-none-any.whl",
96
- "sha256": "3557a478fc577a929c94aace9666aed4dcc432b5ab6764232e6a59f1ccd75f17",
97
- "channel": "pypi"
98
- },
99
- "pillow-12.2.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl": {
100
- "url": "https://cdn.jsdelivr.net/pyodide/v314.0.2/full/pillow-12.2.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl",
101
- "sha256": "a50d83a8a76d646768e99e93ed5bf365d0b83563fd8b597e77562e75c8f575a9",
102
- "channel": "pyodide"
103
- },
104
- "fonttools-4.62.1-py3-none-any.whl": {
105
- "url": "https://cdn.jsdelivr.net/pyodide/v314.0.2/full/fonttools-4.62.1-py3-none-any.whl",
106
- "sha256": "6d8caa09c191f26c47403499d6cff62f3fb9685b978baa047bf791862bcb298a",
107
- "channel": "pyodide"
108
- },
109
- "pypdf-6.15.0-py3-none-any.whl": {
110
- "url": "https://files.pythonhosted.org/packages/af/72/ce3067ac31e214a66388159f8462ddb8c13dd00170f24d555a1f1ae8ee91/pypdf-6.15.0-py3-none-any.whl",
111
- "sha256": "14e001d6504822cb1ca9c7ed9a69bccb320f59b320730f55af804361abe4d5ee",
112
- "channel": "pypi"
113
- },
114
- "et_xmlfile-2.0.0-py3-none-any.whl": {
115
- "url": "https://files.pythonhosted.org/packages/c1/8b/5fe2cc11fee489817272089c4203e679c63b570a5aaeb18d852ae3cbba6a/et_xmlfile-2.0.0-py3-none-any.whl",
116
- "sha256": "7a91720bc756843502c3b7504c77b8fe44217c85c537d85037f0f536151b2caa",
117
- "channel": "pypi"
118
- },
119
- "openpyxl-3.1.5-py2.py3-none-any.whl": {
120
- "url": "https://files.pythonhosted.org/packages/c0/da/977ded879c29cbd04de313843e76868e6e13408a94ed6b987245dc7c8506/openpyxl-3.1.5-py2.py3-none-any.whl",
121
- "sha256": "5282c12b107bffeef825f4617dc029afaf41d0ea60823bbb665ef3079dc79de2",
122
- "channel": "pypi"
123
- },
124
- "python_docx-1.2.0-py3-none-any.whl": {
125
- "url": "https://files.pythonhosted.org/packages/d0/00/1e03a4989fa5795da308cd774f05b704ace555a70f9bf9d3be057b680bcf/python_docx-1.2.0-py3-none-any.whl",
126
- "sha256": "3fd478f3250fbbbfd3b94fe1e985955737c145627498896a8a6bf81f4baf66c7",
127
- "channel": "pypi"
128
- },
129
- "typing_extensions-4.15.0-py3-none-any.whl": {
130
- "url": "https://cdn.jsdelivr.net/pyodide/v314.0.2/full/typing_extensions-4.15.0-py3-none-any.whl",
131
- "sha256": "213fc049fb8bbea5f8369b06c7270cdddd634c2c000f721ce445e22473d66fd5",
132
- "channel": "pyodide"
133
- },
134
- "lxml-6.0.2-cp314-cp314-pyemscripten_2026_0_wasm32.whl": {
135
- "url": "https://cdn.jsdelivr.net/pyodide/v314.0.2/full/lxml-6.0.2-cp314-cp314-pyemscripten_2026_0_wasm32.whl",
136
- "sha256": "f37f82e09f6d7957aa167440db7ef683439cc3ce1befac1d2ca27e35bd7c95d4",
137
- "channel": "pyodide"
138
- },
139
- "xlsxwriter-3.2.9-py3-none-any.whl": {
140
- "url": "https://files.pythonhosted.org/packages/3a/0c/3662f4a66880196a590b202f0db82d919dd2f89e99a27fadef91c4a33d41/xlsxwriter-3.2.9-py3-none-any.whl",
141
- "sha256": "9a5db42bc5dff014806c58a20b9eae7322a134abb6fce3c92c181bfb275ec5b3",
142
- "channel": "pypi"
143
- },
144
- "python_pptx-1.0.2-py3-none-any.whl": {
145
- "url": "https://files.pythonhosted.org/packages/d9/4f/00be2196329ebbff56ce564aa94efb0fbc828d00de250b1980de1a34ab49/python_pptx-1.0.2-py3-none-any.whl",
146
- "sha256": "160838e0b8565a8b1f67947675886e9fea18aa5e795db7ae531606d68e785cba",
147
- "channel": "pypi"
148
- },
149
- "six-1.17.0-py2.py3-none-any.whl": {
150
- "url": "https://cdn.jsdelivr.net/pyodide/v314.0.2/full/six-1.17.0-py2.py3-none-any.whl",
151
- "sha256": "c6681057bd87fdec57e811b8e4007bad38deea284052817fa400d542c6423e2d",
152
- "channel": "pyodide"
153
- },
154
- "pycparser-3.0-py3-none-any.whl": {
155
- "url": "https://cdn.jsdelivr.net/pyodide/v314.0.2/full/pycparser-3.0-py3-none-any.whl",
156
- "sha256": "fab73d094251fa60eef1b744db416f4f1fae95b18741b207870b65aa2f3479ed",
157
- "channel": "pyodide"
158
- },
159
- "cffi-2.0.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl": {
160
- "url": "https://cdn.jsdelivr.net/pyodide/v314.0.2/full/cffi-2.0.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl",
161
- "sha256": "757845812fc8d2c94b0f0fb664d542e7d6e098263fbb5cda3b89dca5d7eff6d3",
162
- "channel": "pyodide"
163
- },
164
- "cryptography-47.0.0-cp314-abi3-pyemscripten_2026_0_wasm32.whl": {
165
- "url": "https://cdn.jsdelivr.net/pyodide/v314.0.2/full/cryptography-47.0.0-cp314-abi3-pyemscripten_2026_0_wasm32.whl",
166
- "sha256": "4e0e7e8e47613dd96110c640b15ee525db72a47946b8b06f8bcd0377c403fc5a",
167
- "channel": "pyodide"
168
9
  }
169
10
  }
170
11
  }
@@ -20,6 +20,7 @@ import { deniedRootsFor } from '../dist-lib/lib/tools/fs/scope.js'
20
20
  import { createGrepTool } from '../dist-lib/lib/tools/grep.js'
21
21
  import { createHttpRequestTool } from '../dist-lib/lib/tools/http-request.js'
22
22
  import { createMcpCallTool } from '../dist-lib/lib/tools/mcp-call.js'
23
+ import { createOfficeTools } from '../dist-lib/lib/tools/office.js'
23
24
  import { compileOperationTools } from '../dist-lib/lib/tools/operations/compile.js'
24
25
  import { createPdfTool } from '../dist-lib/lib/tools/pdf.js'
25
26
  import { createReadFilesTool } from '../dist-lib/lib/tools/read-files.js'
@@ -134,7 +135,8 @@ export default async function start(ipc, ready) {
134
135
  createGrepTool({ deniedRoots }),
135
136
  createCreateDocumentsTool({ deniedRoots }),
136
137
  createEditDocumentsTool({ deniedRoots }),
137
- createPdfTool()
138
+ createPdfTool(),
139
+ ...createOfficeTools()
138
140
  ]
139
141
  if (skillCatalog) {
140
142
  tools.push(...compileOperationTools(skillCatalog, { httpRequestTool, mcpCallTool }))
@@ -1 +0,0 @@
1
- export declare const PPTX_EXTRACT: string;
@@ -1,26 +0,0 @@
1
- export const PPTX_EXTRACT = `from pptx import Presentation
2
- from pptx.enum.shapes import MSO_SHAPE_TYPE
3
- from pathlib import Path
4
-
5
- def text(shapes):
6
- for shape in shapes:
7
- if shape.shape_type == MSO_SHAPE_TYPE.GROUP:
8
- yield from text(shape.shapes)
9
- elif shape.has_text_frame:
10
- if shape.text.strip():
11
- yield shape.text
12
- elif shape.has_table:
13
- for row in shape.table.rows:
14
- cells = ["" if cell.is_spanned else cell.text.replace("\\n", " ") for cell in row.cells]
15
- if any(cell.strip() for cell in cells):
16
- yield " | ".join(cells)
17
-
18
- with Path("extracted.txt").open("w", encoding="utf-8") as output:
19
- for number, slide in enumerate(Presentation("input.pptx").slides, 1):
20
- heading = False
21
- for block in text(slide.shapes):
22
- if not heading:
23
- output.write("Slide " + str(number) + "\\n")
24
- heading = True
25
- output.write(block + "\\n")
26
- `;
@@ -1,8 +0,0 @@
1
- import type { AbortSignal } from 'bare-abort-controller';
2
- export interface PythonExtractOptions {
3
- readonly format: string;
4
- readonly packages: readonly string[];
5
- readonly source: string;
6
- readonly signal?: AbortSignal | undefined;
7
- }
8
- export declare function extractWithPython(bytes: Uint8Array, fileName: string, { format, packages, source, signal }: PythonExtractOptions): Promise<string>;
@@ -1,32 +0,0 @@
1
- import b4a from 'b4a';
2
- import fs from 'fs';
3
- import { runPython } from '../tools/exec/adapters/python/pyodide.js';
4
- import { planVendoredInstall, vendoredManifest, wheelsDir } from '../tools/exec/adapters/python/wheels.js';
5
- const MAX_OFFICE_BYTES = 20 * 1024 * 1024;
6
- export async function extractWithPython(bytes, fileName, { format, packages, source, signal }) {
7
- if (bytes.byteLength > MAX_OFFICE_BYTES) {
8
- throw new Error(`document is too large to extract: ${bytes.byteLength} bytes, limit ${MAX_OFFICE_BYTES}`);
9
- }
10
- const dir = wheelsDir();
11
- const manifest = vendoredManifest(dir);
12
- const install = planVendoredInstall(packages, { dir, manifest });
13
- if (!manifest ||
14
- !install ||
15
- !['micropip', ...install.packages].every((name) => Object.keys(manifest.files).some((file) => file.replaceAll('_', '-').startsWith(`${name}-`) && fs.existsSync(`${dir}/${file}`)))) {
16
- throw new Error(`bundled ${format} extraction packages are unavailable`);
17
- }
18
- const result = await runPython(source, {
19
- packages,
20
- inputs: [{ path: `input.${format.toLowerCase()}`, bytes }],
21
- outputs: ['extracted.txt'],
22
- signal
23
- });
24
- if (result.exitCode !== 0) {
25
- throw new Error(`${fileName}: ${result.stderr.trim()}`);
26
- }
27
- const output = result.files?.get('extracted.txt');
28
- if (!output) {
29
- throw new Error(result.rejected?.[0]?.reason ?? `${format} extraction produced no text file`);
30
- }
31
- return b4a.toString(output, 'utf8');
32
- }
@@ -1 +0,0 @@
1
- export declare const XLSX_EXTRACT: string;
@@ -1,26 +0,0 @@
1
- export const XLSX_EXTRACT = `from openpyxl import load_workbook
2
- from pathlib import Path
3
-
4
- formulas = load_workbook("input.xlsx", read_only=True, data_only=False, keep_links=False)
5
- values = load_workbook("input.xlsx", read_only=True, data_only=True, keep_links=False)
6
- try:
7
- with Path("extracted.txt").open("w", encoding="utf-8") as output:
8
- for sheet in formulas.worksheets:
9
- heading = False
10
- cached = values[sheet.title]
11
- sheet.reset_dimensions()
12
- cached.reset_dimensions()
13
- for cells, results in zip(sheet.iter_rows(), cached.iter_rows()):
14
- row = [result.value if cell.data_type == "f" and result.value is not None else cell.value
15
- for cell, result in zip(cells, results)]
16
- if not any(value is not None and str(value).strip() for value in row):
17
- continue
18
- if not heading:
19
- output.write("Sheet: " + sheet.title + "\\n")
20
- heading = True
21
- output.write(" | ".join("" if value is None else str(value).replace("\\n", " ")
22
- for value in row) + "\\n")
23
- finally:
24
- formulas.close()
25
- values.close()
26
- `;
@@ -1,7 +0,0 @@
1
- interface SourceHalves {
2
- code: string;
3
- text: string;
4
- }
5
- export declare function splitPythonSource(source: string): SourceHalves | null;
6
- export declare function pythonCreateEffect(args: Record<string, unknown>): 'read' | 'write';
7
- export {};
@@ -1,318 +0,0 @@
1
- import { IMAGE_INPUT, OFFICE_READ, PDF_READ, isEscaped, parseInputs, parseOutputs, parsePackages, stripCommentsAndDocstrings, tripleQuoteAt, unwrapPythonSource } from './python-adapter.js';
2
- const ARTIFACT_OUTPUT = /\.(docx|xlsx|pptx|pdf)$/i;
3
- const CREATE_PACKAGES = new Map([
4
- ['python-docx', '1.2.0'],
5
- ['openpyxl', '3.1.5'],
6
- ['python-pptx', '1.0.2'],
7
- ['fpdf2', '2.8.8'],
8
- ['pillow', null]
9
- ]);
10
- const PACKAGE_SPEC = /^([A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?)(?:==([0-9A-Za-z.]+))?$/;
11
- const IMPORT_ROOTS = new Set(['docx', 'openpyxl', 'pptx', 'fpdf', 'datetime']);
12
- const BUILDER_ROOTS = new Set(['docx', 'openpyxl', 'pptx', 'fpdf']);
13
- const IMPORT_FROM = /^\s*from\s+([A-Za-z]\w*(?:\.[A-Za-z]\w*)*)\s+import\s+[A-Za-z]\w*(?:\s+as\s+[A-Za-z]\w*)?(?:\s*,\s*[A-Za-z]\w*(?:\s+as\s+[A-Za-z]\w*)?)*\s*$/;
14
- const ALLOWED_BUILTINS = new Set([
15
- 'True',
16
- 'False',
17
- 'None',
18
- 'Ellipsis',
19
- 'NotImplemented',
20
- 'Exception',
21
- 'ValueError',
22
- 'KeyError',
23
- 'IndexError',
24
- 'TypeError',
25
- 'RuntimeError',
26
- 'abs',
27
- 'all',
28
- 'any',
29
- 'bool',
30
- 'dict',
31
- 'divmod',
32
- 'enumerate',
33
- 'filter',
34
- 'float',
35
- 'int',
36
- 'len',
37
- 'list',
38
- 'map',
39
- 'max',
40
- 'min',
41
- 'pow',
42
- 'print',
43
- 'range',
44
- 'reversed',
45
- 'round',
46
- 'set',
47
- 'sorted',
48
- 'str',
49
- 'sum',
50
- 'tuple',
51
- 'zip'
52
- ]);
53
- const PYTHON_BUILTINS = new Set(`ArithmeticError AssertionError AttributeError BaseException BaseExceptionGroup
54
- BlockingIOError BrokenPipeError BufferError BytesWarning ChildProcessError
55
- ConnectionAbortedError ConnectionError ConnectionRefusedError ConnectionResetError
56
- DeprecationWarning EOFError Ellipsis EncodingWarning EnvironmentError Exception
57
- ExceptionGroup False FileExistsError FileNotFoundError FloatingPointError
58
- FutureWarning GeneratorExit IOError ImportError ImportWarning IndentationError
59
- IndexError InterruptedError IsADirectoryError KeyError KeyboardInterrupt
60
- LookupError MemoryError ModuleNotFoundError NameError None NotADirectoryError
61
- NotImplemented NotImplementedError OSError OverflowError PendingDeprecationWarning
62
- PermissionError ProcessLookupError PythonFinalizationError RecursionError
63
- ReferenceError ResourceWarning RuntimeError RuntimeWarning StopAsyncIteration
64
- StopIteration SyntaxError SyntaxWarning SystemError SystemExit TabError
65
- TimeoutError True TypeError UnboundLocalError UnicodeDecodeError
66
- UnicodeEncodeError UnicodeError UnicodeTranslateError UnicodeWarning UserWarning
67
- ValueError Warning ZeroDivisionError abs aiter all anext any ascii bin bool
68
- breakpoint bytearray bytes callable chr classmethod compile complex copyright
69
- credits delattr dict dir divmod enumerate eval exec execfile exit filter float
70
- format frozenset getattr globals hasattr hash help hex id input int isinstance
71
- issubclass iter len license list locals map max memoryview min next object oct
72
- open ord pow print property quit range reload repr reversed round set setattr
73
- slice sorted staticmethod str sum super tuple type unicode vars zip`.split(/\s+/));
74
- const DENIED_TOKENS = new Set([
75
- 'os',
76
- 'sys',
77
- 'js',
78
- 'pyodide',
79
- 'micropip',
80
- 'builtins',
81
- 'importlib',
82
- 'inspect',
83
- 'ctypes',
84
- 'subprocess',
85
- 'platform',
86
- 'shutil',
87
- 'pathlib',
88
- 'glob',
89
- 'tempfile',
90
- 'gc',
91
- 'socket',
92
- 'requests',
93
- 'urllib',
94
- 'http',
95
- 'ssl',
96
- 'ftplib',
97
- 'smtplib',
98
- 'webbrowser',
99
- 'pickle',
100
- 'marshal',
101
- 'base64',
102
- 'codecs',
103
- 'io',
104
- 'threading',
105
- 'asyncio',
106
- 'async',
107
- 'await',
108
- 'format_map',
109
- 'vformat'
110
- ]);
111
- const INTROSPECTION_TOKEN = /^(?:gi|cr|ag|f|tb|im|func)_/;
112
- function isFStringPrefix(code) {
113
- const prefix = /[A-Za-z]{1,3}$/.exec(code)?.[0] ?? '';
114
- return prefix.length <= 2 && /f/i.test(prefix);
115
- }
116
- export function splitPythonSource(source) {
117
- let code = '';
118
- let text = '';
119
- let i = 0;
120
- while (i < source.length) {
121
- const ch = source[i];
122
- if (ch === '#') {
123
- while (i < source.length && source[i] !== '\n') {
124
- text += source[i];
125
- i++;
126
- }
127
- continue;
128
- }
129
- const triple = tripleQuoteAt(source, i);
130
- const quote = triple ?? (ch === '"' || ch === "'" ? ch : null);
131
- if (quote) {
132
- const fstring = isFStringPrefix(code);
133
- const end = scanLiteral(source, i + quote.length, { quote, fstring });
134
- if (!end) {
135
- return null;
136
- }
137
- code += '""';
138
- text += end.text;
139
- code += end.code;
140
- i = end.index;
141
- continue;
142
- }
143
- code += ch;
144
- i++;
145
- }
146
- return { code, text };
147
- }
148
- function scanLiteral(source, start, { quote, fstring }) {
149
- let text = '';
150
- let code = '';
151
- let i = start;
152
- while (i < source.length) {
153
- if (source.startsWith(quote, i) && !isEscaped(source, i)) {
154
- return { index: i + quote.length, text, code };
155
- }
156
- const ch = source[i];
157
- if (fstring && (ch === '{' || ch === '}')) {
158
- if (source[i + 1] === ch) {
159
- text += ch + ch;
160
- i += 2;
161
- continue;
162
- }
163
- if (ch === '}') {
164
- return null;
165
- }
166
- const interior = scanInterpolation(source, i + 1);
167
- if (!interior) {
168
- return null;
169
- }
170
- code += ` ${interior.code} `;
171
- text += interior.text;
172
- i = interior.index;
173
- continue;
174
- }
175
- text += ch;
176
- i++;
177
- }
178
- return null;
179
- }
180
- function scanInterpolation(source, start) {
181
- let code = '';
182
- let text = '';
183
- let depth = 1;
184
- let nested = null;
185
- let i = start;
186
- while (i < source.length) {
187
- const ch = source[i];
188
- if (nested) {
189
- if (ch === nested && !isEscaped(source, i)) {
190
- nested = null;
191
- }
192
- else {
193
- text += ch;
194
- }
195
- i++;
196
- continue;
197
- }
198
- if (ch === '"' || ch === "'") {
199
- nested = ch;
200
- i++;
201
- continue;
202
- }
203
- if (ch === '{') {
204
- depth++;
205
- }
206
- if (ch === '}') {
207
- depth--;
208
- if (depth === 0) {
209
- return { index: i + 1, text, code };
210
- }
211
- }
212
- code += ch;
213
- i++;
214
- }
215
- return null;
216
- }
217
- function isKeywordArgument(code, start, length) {
218
- const before = code.slice(0, start).trimEnd();
219
- const last = before[before.length - 1];
220
- if (last !== '(' && last !== ',') {
221
- return false;
222
- }
223
- const after = code.slice(start + length).trimStart();
224
- return after.startsWith('=') && after[1] !== '=';
225
- }
226
- function lintCreateSource(source) {
227
- const halves = splitPythonSource(source);
228
- if (!halves) {
229
- return 'write';
230
- }
231
- if (halves.text.includes('__')) {
232
- return 'write';
233
- }
234
- if (/\\(?:x[0-9A-Fa-f]|u[0-9A-Fa-f]|U[0-9A-Fa-f]|N\{|[0-7])/.test(halves.text)) {
235
- return 'write';
236
- }
237
- if (/[^\x20-\x7e\t\r\n\f]/.test(halves.code)) {
238
- return 'write';
239
- }
240
- let builder = false;
241
- for (const line of halves.code.split('\n')) {
242
- for (const statement of line.split(';')) {
243
- if (!/\b(?:from|import)\b/.test(statement))
244
- continue;
245
- const match = IMPORT_FROM.exec(statement);
246
- if (!match) {
247
- return 'write';
248
- }
249
- const root = match[1].split('.')[0];
250
- if (!IMPORT_ROOTS.has(root)) {
251
- return 'write';
252
- }
253
- if (BUILDER_ROOTS.has(root)) {
254
- builder = true;
255
- }
256
- }
257
- }
258
- if (!builder) {
259
- return 'write';
260
- }
261
- for (const match of halves.code.matchAll(/[A-Za-z_]\w*/g)) {
262
- const token = match[0];
263
- if (token.includes('__') || DENIED_TOKENS.has(token) || INTROSPECTION_TOKEN.test(token)) {
264
- return 'write';
265
- }
266
- if (isKeywordArgument(halves.code, match.index, token.length)) {
267
- continue;
268
- }
269
- if (PYTHON_BUILTINS.has(token) && !ALLOWED_BUILTINS.has(token)) {
270
- return 'write';
271
- }
272
- }
273
- const openers = stripCommentsAndDocstrings(source);
274
- const reads = [new RegExp(OFFICE_READ.source), new RegExp(PDF_READ.source)];
275
- if (reads.some((pattern) => pattern.test(openers))) {
276
- return 'write';
277
- }
278
- return 'read';
279
- }
280
- export function pythonCreateEffect(args) {
281
- const outputs = parseOutputs(args.outputs);
282
- if (!Array.isArray(outputs) || outputs.length === 0) {
283
- return 'write';
284
- }
285
- if (!outputs.every((name) => ARTIFACT_OUTPUT.test(name))) {
286
- return 'write';
287
- }
288
- const packages = parsePackages(args.packages);
289
- if (packages && 'error' in packages) {
290
- return 'write';
291
- }
292
- for (const spec of packages ?? []) {
293
- const match = PACKAGE_SPEC.exec(spec);
294
- if (!match) {
295
- return 'write';
296
- }
297
- const name = match[1].toLowerCase().replace(/[_.]+/g, '-');
298
- if (!CREATE_PACKAGES.has(name)) {
299
- return 'write';
300
- }
301
- if (CREATE_PACKAGES.get(name) !== (match[2] ?? null)) {
302
- return 'write';
303
- }
304
- }
305
- const inputs = parseInputs(args.inputs);
306
- if (inputs && 'error' in inputs) {
307
- return 'write';
308
- }
309
- if ((inputs ?? []).some((input) => !IMAGE_INPUT.test(input.path))) {
310
- return 'write';
311
- }
312
- const command = String(args.command ?? '');
313
- const source = unwrapPythonSource(command) ?? command;
314
- if (!source.trim()) {
315
- return 'write';
316
- }
317
- return lintCreateSource(source);
318
- }