@agent-webui/ai-desk-harness-gimp 1.0.29-beta1

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 (48) hide show
  1. package/README.md +4 -0
  2. package/manifest.json +22 -0
  3. package/package.json +11 -0
  4. package/python/agent-harness/GIMP.md +301 -0
  5. package/python/agent-harness/build/lib/cli_anything/gimp/__init__.py +1 -0
  6. package/python/agent-harness/build/lib/cli_anything/gimp/__main__.py +3 -0
  7. package/python/agent-harness/build/lib/cli_anything/gimp/core/__init__.py +1 -0
  8. package/python/agent-harness/build/lib/cli_anything/gimp/core/canvas.py +193 -0
  9. package/python/agent-harness/build/lib/cli_anything/gimp/core/export.py +479 -0
  10. package/python/agent-harness/build/lib/cli_anything/gimp/core/filters.py +382 -0
  11. package/python/agent-harness/build/lib/cli_anything/gimp/core/layers.py +249 -0
  12. package/python/agent-harness/build/lib/cli_anything/gimp/core/media.py +174 -0
  13. package/python/agent-harness/build/lib/cli_anything/gimp/core/project.py +131 -0
  14. package/python/agent-harness/build/lib/cli_anything/gimp/core/session.py +130 -0
  15. package/python/agent-harness/build/lib/cli_anything/gimp/gimp_cli.py +788 -0
  16. package/python/agent-harness/build/lib/cli_anything/gimp/tests/__init__.py +1 -0
  17. package/python/agent-harness/build/lib/cli_anything/gimp/tests/test_core.py +478 -0
  18. package/python/agent-harness/build/lib/cli_anything/gimp/tests/test_full_e2e.py +578 -0
  19. package/python/agent-harness/build/lib/cli_anything/gimp/utils/__init__.py +1 -0
  20. package/python/agent-harness/build/lib/cli_anything/gimp/utils/gimp_backend.py +208 -0
  21. package/python/agent-harness/build/lib/cli_anything/gimp/utils/repl_skin.py +498 -0
  22. package/python/agent-harness/cli_anything/gimp/README.md +202 -0
  23. package/python/agent-harness/cli_anything/gimp/__init__.py +1 -0
  24. package/python/agent-harness/cli_anything/gimp/__main__.py +3 -0
  25. package/python/agent-harness/cli_anything/gimp/core/__init__.py +1 -0
  26. package/python/agent-harness/cli_anything/gimp/core/canvas.py +193 -0
  27. package/python/agent-harness/cli_anything/gimp/core/export.py +479 -0
  28. package/python/agent-harness/cli_anything/gimp/core/filters.py +382 -0
  29. package/python/agent-harness/cli_anything/gimp/core/layers.py +249 -0
  30. package/python/agent-harness/cli_anything/gimp/core/media.py +174 -0
  31. package/python/agent-harness/cli_anything/gimp/core/project.py +131 -0
  32. package/python/agent-harness/cli_anything/gimp/core/session.py +130 -0
  33. package/python/agent-harness/cli_anything/gimp/gimp_cli.py +788 -0
  34. package/python/agent-harness/cli_anything/gimp/tests/TEST.md +137 -0
  35. package/python/agent-harness/cli_anything/gimp/tests/__init__.py +1 -0
  36. package/python/agent-harness/cli_anything/gimp/tests/test_core.py +478 -0
  37. package/python/agent-harness/cli_anything/gimp/tests/test_full_e2e.py +578 -0
  38. package/python/agent-harness/cli_anything/gimp/utils/__init__.py +1 -0
  39. package/python/agent-harness/cli_anything/gimp/utils/gimp_backend.py +208 -0
  40. package/python/agent-harness/cli_anything/gimp/utils/repl_skin.py +498 -0
  41. package/python/agent-harness/cli_anything_gimp.egg-info/PKG-INFO +236 -0
  42. package/python/agent-harness/cli_anything_gimp.egg-info/SOURCES.txt +25 -0
  43. package/python/agent-harness/cli_anything_gimp.egg-info/dependency_links.txt +1 -0
  44. package/python/agent-harness/cli_anything_gimp.egg-info/entry_points.txt +2 -0
  45. package/python/agent-harness/cli_anything_gimp.egg-info/not-zip-safe +1 -0
  46. package/python/agent-harness/cli_anything_gimp.egg-info/requires.txt +7 -0
  47. package/python/agent-harness/cli_anything_gimp.egg-info/top_level.txt +1 -0
  48. package/python/agent-harness/setup.py +54 -0
@@ -0,0 +1,208 @@
1
+ """GIMP backend — invoke GIMP in batch mode for image processing.
2
+
3
+ Uses GIMP's Script-Fu batch mode for true image processing.
4
+
5
+ Requires: gimp (system package)
6
+ apt install gimp
7
+ """
8
+
9
+ import os
10
+ import shutil
11
+ import subprocess
12
+ from typing import Optional
13
+
14
+
15
+ def find_gimp() -> str:
16
+ """Find the GIMP executable. Raises RuntimeError if not found."""
17
+ for name in ("gimp", "gimp-2.10", "gimp-2.99"):
18
+ path = shutil.which(name)
19
+ if path:
20
+ return path
21
+ raise RuntimeError(
22
+ "GIMP is not installed. Install it with:\n"
23
+ " apt install gimp # Debian/Ubuntu"
24
+ )
25
+
26
+
27
+ def get_version() -> str:
28
+ """Get the installed GIMP version string."""
29
+ gimp = find_gimp()
30
+ result = subprocess.run(
31
+ [gimp, "--version"],
32
+ capture_output=True, text=True, timeout=30,
33
+ )
34
+ return result.stdout.strip()
35
+
36
+
37
+ def batch_script_fu(
38
+ script: str,
39
+ timeout: int = 120,
40
+ ) -> dict:
41
+ """Run a Script-Fu command in GIMP batch mode.
42
+
43
+ Args:
44
+ script: Script-Fu command string (single-quoted safe)
45
+ timeout: Maximum seconds to wait
46
+
47
+ Returns:
48
+ Dict with stdout, stderr, return code
49
+ """
50
+ gimp = find_gimp()
51
+ cmd = [gimp, "-i", "-b", script, "-b", "(gimp-quit 0)"]
52
+
53
+ result = subprocess.run(
54
+ cmd,
55
+ capture_output=True, text=True,
56
+ timeout=timeout,
57
+ )
58
+
59
+ return {
60
+ "command": " ".join(cmd),
61
+ "returncode": result.returncode,
62
+ "stdout": result.stdout,
63
+ "stderr": result.stderr,
64
+ }
65
+
66
+
67
+ def create_and_export(
68
+ width: int,
69
+ height: int,
70
+ output_path: str,
71
+ fill_color: str = "white",
72
+ timeout: int = 120,
73
+ ) -> dict:
74
+ """Create a new image in GIMP and export it."""
75
+ abs_output = os.path.abspath(output_path)
76
+ os.makedirs(os.path.dirname(abs_output), exist_ok=True)
77
+
78
+ ext = os.path.splitext(output_path)[1].lower()
79
+
80
+ # Build the export command based on format
81
+ if ext == ".png":
82
+ export_cmd = (
83
+ f'(file-png-save RUN-NONINTERACTIVE image layer '
84
+ f'"{abs_output}" "{abs_output}" 0 9 1 1 1 1 1)'
85
+ )
86
+ elif ext in (".jpg", ".jpeg"):
87
+ export_cmd = (
88
+ f'(file-jpeg-save RUN-NONINTERACTIVE image layer '
89
+ f'"{abs_output}" "{abs_output}" 0.85 0.0 0 0 "" 0 1 0 2)'
90
+ )
91
+ elif ext == ".bmp":
92
+ export_cmd = (
93
+ f'(file-bmp-save RUN-NONINTERACTIVE image layer '
94
+ f'"{abs_output}" "{abs_output}" 0)'
95
+ )
96
+ else:
97
+ export_cmd = (
98
+ f'(gimp-file-overwrite RUN-NONINTERACTIVE image layer '
99
+ f'"{abs_output}" "{abs_output}")'
100
+ )
101
+
102
+ # Color mapping
103
+ color_map = {
104
+ "white": "255 255 255",
105
+ "black": "0 0 0",
106
+ "red": "255 0 0",
107
+ "green": "0 255 0",
108
+ "blue": "0 0 255",
109
+ }
110
+ rgb = color_map.get(fill_color, "255 255 255")
111
+
112
+ # Build Script-Fu — use plain strings, subprocess handles quoting
113
+ script = (
114
+ f'(let* ('
115
+ f'(image (car (gimp-image-new {width} {height} RGB)))'
116
+ f'(layer (car (gimp-layer-new image {width} {height} '
117
+ f'RGB-IMAGE "BG" 100 LAYER-MODE-NORMAL)))'
118
+ f')'
119
+ f'(gimp-image-insert-layer image layer 0 -1)'
120
+ f'(gimp-image-set-active-layer image layer)'
121
+ f"(gimp-palette-set-foreground '({rgb}))"
122
+ f'(gimp-edit-fill layer FILL-FOREGROUND)'
123
+ f'{export_cmd}'
124
+ f'(gimp-image-delete image))'
125
+ )
126
+
127
+ result = batch_script_fu(script, timeout=timeout)
128
+
129
+ if not os.path.exists(abs_output):
130
+ raise RuntimeError(
131
+ f"GIMP export produced no output file.\n"
132
+ f" Expected: {abs_output}\n"
133
+ f" stderr: {result['stderr'][-500:]}\n"
134
+ f" stdout: {result['stdout'][-500:]}"
135
+ )
136
+
137
+ return {
138
+ "output": abs_output,
139
+ "format": ext.lstrip("."),
140
+ "method": "gimp-batch",
141
+ "gimp_version": get_version(),
142
+ "file_size": os.path.getsize(abs_output),
143
+ }
144
+
145
+
146
+ def apply_filter_and_export(
147
+ input_path: str,
148
+ output_path: str,
149
+ script_fu_filter: str = "",
150
+ timeout: int = 120,
151
+ ) -> dict:
152
+ """Load an image in GIMP, apply a Script-Fu filter, and export.
153
+
154
+ Args:
155
+ input_path: Path to input image
156
+ output_path: Path for output image
157
+ script_fu_filter: Script-Fu commands to apply (uses 'image' and 'drawable' vars)
158
+ timeout: Max seconds
159
+ """
160
+ if not os.path.exists(input_path):
161
+ raise FileNotFoundError(f"Input file not found: {input_path}")
162
+
163
+ abs_input = os.path.abspath(input_path)
164
+ abs_output = os.path.abspath(output_path)
165
+ os.makedirs(os.path.dirname(abs_output), exist_ok=True)
166
+
167
+ ext = os.path.splitext(output_path)[1].lower()
168
+ if ext == ".png":
169
+ export_cmd = (
170
+ f'(file-png-save RUN-NONINTERACTIVE image drawable '
171
+ f'"{abs_output}" "{abs_output}" 0 9 1 1 1 1 1)'
172
+ )
173
+ elif ext in (".jpg", ".jpeg"):
174
+ export_cmd = (
175
+ f'(file-jpeg-save RUN-NONINTERACTIVE image drawable '
176
+ f'"{abs_output}" "{abs_output}" 0.85 0.0 0 0 "" 0 1 0 2)'
177
+ )
178
+ else:
179
+ export_cmd = (
180
+ f'(gimp-file-overwrite RUN-NONINTERACTIVE image drawable '
181
+ f'"{abs_output}" "{abs_output}")'
182
+ )
183
+
184
+ script = (
185
+ f'(let* ('
186
+ f'(image (car (file-png-load RUN-NONINTERACTIVE "{abs_input}" "{abs_input}")))'
187
+ f'(drawable (car (gimp-image-flatten image)))'
188
+ f')'
189
+ f'{script_fu_filter}'
190
+ f'(set! drawable (car (gimp-image-flatten image)))'
191
+ f'{export_cmd}'
192
+ f'(gimp-image-delete image))'
193
+ )
194
+
195
+ result = batch_script_fu(script, timeout=timeout)
196
+
197
+ if not os.path.exists(abs_output):
198
+ raise RuntimeError(
199
+ f"GIMP filter+export produced no output.\n"
200
+ f" stderr: {result['stderr'][-500:]}"
201
+ )
202
+
203
+ return {
204
+ "output": abs_output,
205
+ "format": ext.lstrip("."),
206
+ "method": "gimp-batch",
207
+ "file_size": os.path.getsize(abs_output),
208
+ }