@qvac/harness 0.7.3 → 0.8.5

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 (146) hide show
  1. package/CHANGELOG.md +87 -1
  2. package/NOTICE.md +16 -0
  3. package/dist-lib/lib/capabilities/completion.d.ts +1 -0
  4. package/dist-lib/lib/capabilities/completion.js +28 -8
  5. package/dist-lib/lib/capabilities/tool-turns.d.ts +3 -0
  6. package/dist-lib/lib/capabilities/tool-turns.js +7 -0
  7. package/dist-lib/lib/context-window.js +2 -2
  8. package/dist-lib/lib/docx/index.d.ts +1 -1
  9. package/dist-lib/lib/docx/index.js +16 -24
  10. package/dist-lib/lib/helpers/run-outcome.d.ts +4 -1
  11. package/dist-lib/lib/helpers/run-outcome.js +14 -8
  12. package/dist-lib/lib/helpers/tool-call-markup.js +4 -4
  13. package/dist-lib/lib/hooks/built-in/arg-repair.js +7 -0
  14. package/dist-lib/lib/office/engine/codes.d.ts +62 -0
  15. package/dist-lib/lib/office/engine/codes.js +61 -0
  16. package/dist-lib/lib/office/engine/docx/document.d.ts +216 -0
  17. package/dist-lib/lib/office/engine/docx/document.js +1152 -0
  18. package/dist-lib/lib/office/engine/docx/index.d.ts +7 -0
  19. package/dist-lib/lib/office/engine/docx/index.js +8 -0
  20. package/dist-lib/lib/office/engine/docx/styles.d.ts +31 -0
  21. package/dist-lib/lib/office/engine/docx/styles.js +173 -0
  22. package/dist-lib/lib/office/engine/docx/template.d.ts +18 -0
  23. package/dist-lib/lib/office/engine/docx/template.js +133 -0
  24. package/dist-lib/lib/office/engine/docx/text.d.ts +55 -0
  25. package/dist-lib/lib/office/engine/docx/text.js +538 -0
  26. package/dist-lib/lib/office/engine/drawingml/chart.d.ts +63 -0
  27. package/dist-lib/lib/office/engine/drawingml/chart.js +539 -0
  28. package/dist-lib/lib/office/engine/drawingml/text.d.ts +35 -0
  29. package/dist-lib/lib/office/engine/drawingml/text.js +352 -0
  30. package/dist-lib/lib/office/engine/errors.d.ts +6 -0
  31. package/dist-lib/lib/office/engine/errors.js +12 -0
  32. package/dist-lib/lib/office/engine/image.d.ts +8 -0
  33. package/dist-lib/lib/office/engine/image.js +48 -0
  34. package/dist-lib/lib/office/engine/index.d.ts +12 -0
  35. package/dist-lib/lib/office/engine/index.js +7 -0
  36. package/dist-lib/lib/office/engine/opc/content-types.d.ts +17 -0
  37. package/dist-lib/lib/office/engine/opc/content-types.js +74 -0
  38. package/dist-lib/lib/office/engine/opc/media.d.ts +13 -0
  39. package/dist-lib/lib/office/engine/opc/media.js +11 -0
  40. package/dist-lib/lib/office/engine/opc/package.d.ts +51 -0
  41. package/dist-lib/lib/office/engine/opc/package.js +324 -0
  42. package/dist-lib/lib/office/engine/opc/paths.d.ts +7 -0
  43. package/dist-lib/lib/office/engine/opc/paths.js +46 -0
  44. package/dist-lib/lib/office/engine/opc/properties.d.ts +7 -0
  45. package/dist-lib/lib/office/engine/opc/properties.js +75 -0
  46. package/dist-lib/lib/office/engine/opc/relationships.d.ts +23 -0
  47. package/dist-lib/lib/office/engine/opc/relationships.js +65 -0
  48. package/dist-lib/lib/office/engine/opc/zip.d.ts +15 -0
  49. package/dist-lib/lib/office/engine/opc/zip.js +264 -0
  50. package/dist-lib/lib/office/engine/pptx/fit.d.ts +12 -0
  51. package/dist-lib/lib/office/engine/pptx/fit.js +37 -0
  52. package/dist-lib/lib/office/engine/pptx/index.d.ts +8 -0
  53. package/dist-lib/lib/office/engine/pptx/index.js +8 -0
  54. package/dist-lib/lib/office/engine/pptx/inherit.d.ts +29 -0
  55. package/dist-lib/lib/office/engine/pptx/inherit.js +155 -0
  56. package/dist-lib/lib/office/engine/pptx/notes.d.ts +5 -0
  57. package/dist-lib/lib/office/engine/pptx/notes.js +85 -0
  58. package/dist-lib/lib/office/engine/pptx/presentation.d.ts +54 -0
  59. package/dist-lib/lib/office/engine/pptx/presentation.js +749 -0
  60. package/dist-lib/lib/office/engine/pptx/read.d.ts +9 -0
  61. package/dist-lib/lib/office/engine/pptx/read.js +66 -0
  62. package/dist-lib/lib/office/engine/pptx/shapes.d.ts +52 -0
  63. package/dist-lib/lib/office/engine/pptx/shapes.js +276 -0
  64. package/dist-lib/lib/office/engine/pptx/slides.d.ts +27 -0
  65. package/dist-lib/lib/office/engine/pptx/slides.js +123 -0
  66. package/dist-lib/lib/office/engine/pptx/template.d.ts +20 -0
  67. package/dist-lib/lib/office/engine/pptx/template.js +391 -0
  68. package/dist-lib/lib/office/engine/pptx/types.d.ts +120 -0
  69. package/dist-lib/lib/office/engine/pptx/types.js +1 -0
  70. package/dist-lib/lib/office/engine/units.d.ts +3 -0
  71. package/dist-lib/lib/office/engine/units.js +12 -0
  72. package/dist-lib/lib/office/engine/utf8.d.ts +2 -0
  73. package/dist-lib/lib/office/engine/utf8.js +103 -0
  74. package/dist-lib/lib/office/engine/xlsx/formula.d.ts +49 -0
  75. package/dist-lib/lib/office/engine/xlsx/formula.js +381 -0
  76. package/dist-lib/lib/office/engine/xlsx/index.d.ts +8 -0
  77. package/dist-lib/lib/office/engine/xlsx/index.js +8 -0
  78. package/dist-lib/lib/office/engine/xlsx/refs.d.ts +22 -0
  79. package/dist-lib/lib/office/engine/xlsx/refs.js +82 -0
  80. package/dist-lib/lib/office/engine/xlsx/reshape.d.ts +21 -0
  81. package/dist-lib/lib/office/engine/xlsx/reshape.js +424 -0
  82. package/dist-lib/lib/office/engine/xlsx/sheet.d.ts +108 -0
  83. package/dist-lib/lib/office/engine/xlsx/sheet.js +662 -0
  84. package/dist-lib/lib/office/engine/xlsx/strings.d.ts +15 -0
  85. package/dist-lib/lib/office/engine/xlsx/strings.js +93 -0
  86. package/dist-lib/lib/office/engine/xlsx/styles.d.ts +38 -0
  87. package/dist-lib/lib/office/engine/xlsx/styles.js +369 -0
  88. package/dist-lib/lib/office/engine/xlsx/template.d.ts +7 -0
  89. package/dist-lib/lib/office/engine/xlsx/template.js +54 -0
  90. package/dist-lib/lib/office/engine/xlsx/workbook.d.ts +161 -0
  91. package/dist-lib/lib/office/engine/xlsx/workbook.js +850 -0
  92. package/dist-lib/lib/office/engine/xlsx/write.d.ts +2 -0
  93. package/dist-lib/lib/office/engine/xlsx/write.js +42 -0
  94. package/dist-lib/lib/office/engine/xml.d.ts +45 -0
  95. package/dist-lib/lib/office/engine/xml.js +258 -0
  96. package/dist-lib/lib/office/extract.d.ts +9 -0
  97. package/dist-lib/lib/office/extract.js +19 -0
  98. package/dist-lib/lib/office/index.d.ts +1 -1
  99. package/dist-lib/lib/office/index.js +74 -19
  100. package/dist-lib/lib/tools/exec/adapters/python/module-distributions.js +0 -3
  101. package/dist-lib/lib/tools/exec/adapters/python/python-adapter.d.ts +7 -50
  102. package/dist-lib/lib/tools/exec/adapters/python/python-adapter.js +38 -695
  103. package/dist-lib/lib/tools/exec/adapters/python/run-session.js +1 -85
  104. package/dist-lib/lib/tools/exec/scope.d.ts +2 -0
  105. package/dist-lib/lib/tools/exec/scope.js +33 -2
  106. package/dist-lib/lib/tools/exec.js +9 -20
  107. package/dist-lib/lib/tools/office/ops.d.ts +13 -0
  108. package/dist-lib/lib/tools/office/ops.js +100 -0
  109. package/dist-lib/lib/tools/office.d.ts +12 -0
  110. package/dist-lib/lib/tools/office.js +486 -0
  111. package/dist-lib/lib/tools/pdf/build.d.ts +43 -0
  112. package/dist-lib/lib/tools/pdf/build.js +150 -0
  113. package/dist-lib/lib/tools/pdf/images.d.ts +7 -0
  114. package/dist-lib/lib/tools/pdf/images.js +82 -0
  115. package/dist-lib/lib/tools/pdf/request.d.ts +63 -0
  116. package/dist-lib/lib/tools/pdf/request.js +335 -0
  117. package/dist-lib/lib/tools/pdf.d.ts +6 -0
  118. package/dist-lib/lib/tools/pdf.js +302 -0
  119. package/dist-lib/lib/tools/skill.js +14 -0
  120. package/package.json +34 -6
  121. package/wheels/manifest.json +1 -160
  122. package/worker/harness-sidecar.mjs +5 -1
  123. package/dist-lib/lib/office/pptx.d.ts +0 -1
  124. package/dist-lib/lib/office/pptx.js +0 -26
  125. package/dist-lib/lib/office/python.d.ts +0 -8
  126. package/dist-lib/lib/office/python.js +0 -32
  127. package/dist-lib/lib/office/xlsx.d.ts +0 -1
  128. package/dist-lib/lib/office/xlsx.js +0 -26
  129. package/dist-lib/lib/tools/exec/adapters/python/create-effect.d.ts +0 -7
  130. package/dist-lib/lib/tools/exec/adapters/python/create-effect.js +0 -318
  131. package/wheels/cffi-2.0.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl +0 -0
  132. package/wheels/cryptography-47.0.0-cp314-abi3-pyemscripten_2026_0_wasm32.whl +0 -0
  133. package/wheels/defusedxml-0.7.1-py2.py3-none-any.whl +0 -0
  134. package/wheels/et_xmlfile-2.0.0-py3-none-any.whl +0 -0
  135. package/wheels/fonttools-4.62.1-py3-none-any.whl +0 -0
  136. package/wheels/fpdf2-2.8.8-py3-none-any.whl +0 -0
  137. package/wheels/lxml-6.0.2-cp314-cp314-pyemscripten_2026_0_wasm32.whl +0 -0
  138. package/wheels/openpyxl-3.1.5-py2.py3-none-any.whl +0 -0
  139. package/wheels/pillow-12.2.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl +0 -0
  140. package/wheels/pycparser-3.0-py3-none-any.whl +0 -0
  141. package/wheels/pypdf-6.15.0-py3-none-any.whl +0 -0
  142. package/wheels/python_docx-1.2.0-py3-none-any.whl +0 -0
  143. package/wheels/python_pptx-1.0.2-py3-none-any.whl +0 -0
  144. package/wheels/six-1.17.0-py2.py3-none-any.whl +0 -0
  145. package/wheels/typing_extensions-4.15.0-py3-none-any.whl +0 -0
  146. package/wheels/xlsxwriter-3.2.9-py3-none-any.whl +0 -0
@@ -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
- }
Binary file