@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.
- package/README.md +4 -0
- package/manifest.json +22 -0
- package/package.json +11 -0
- package/python/agent-harness/GIMP.md +301 -0
- package/python/agent-harness/build/lib/cli_anything/gimp/__init__.py +1 -0
- package/python/agent-harness/build/lib/cli_anything/gimp/__main__.py +3 -0
- package/python/agent-harness/build/lib/cli_anything/gimp/core/__init__.py +1 -0
- package/python/agent-harness/build/lib/cli_anything/gimp/core/canvas.py +193 -0
- package/python/agent-harness/build/lib/cli_anything/gimp/core/export.py +479 -0
- package/python/agent-harness/build/lib/cli_anything/gimp/core/filters.py +382 -0
- package/python/agent-harness/build/lib/cli_anything/gimp/core/layers.py +249 -0
- package/python/agent-harness/build/lib/cli_anything/gimp/core/media.py +174 -0
- package/python/agent-harness/build/lib/cli_anything/gimp/core/project.py +131 -0
- package/python/agent-harness/build/lib/cli_anything/gimp/core/session.py +130 -0
- package/python/agent-harness/build/lib/cli_anything/gimp/gimp_cli.py +788 -0
- package/python/agent-harness/build/lib/cli_anything/gimp/tests/__init__.py +1 -0
- package/python/agent-harness/build/lib/cli_anything/gimp/tests/test_core.py +478 -0
- package/python/agent-harness/build/lib/cli_anything/gimp/tests/test_full_e2e.py +578 -0
- package/python/agent-harness/build/lib/cli_anything/gimp/utils/__init__.py +1 -0
- package/python/agent-harness/build/lib/cli_anything/gimp/utils/gimp_backend.py +208 -0
- package/python/agent-harness/build/lib/cli_anything/gimp/utils/repl_skin.py +498 -0
- package/python/agent-harness/cli_anything/gimp/README.md +202 -0
- package/python/agent-harness/cli_anything/gimp/__init__.py +1 -0
- package/python/agent-harness/cli_anything/gimp/__main__.py +3 -0
- package/python/agent-harness/cli_anything/gimp/core/__init__.py +1 -0
- package/python/agent-harness/cli_anything/gimp/core/canvas.py +193 -0
- package/python/agent-harness/cli_anything/gimp/core/export.py +479 -0
- package/python/agent-harness/cli_anything/gimp/core/filters.py +382 -0
- package/python/agent-harness/cli_anything/gimp/core/layers.py +249 -0
- package/python/agent-harness/cli_anything/gimp/core/media.py +174 -0
- package/python/agent-harness/cli_anything/gimp/core/project.py +131 -0
- package/python/agent-harness/cli_anything/gimp/core/session.py +130 -0
- package/python/agent-harness/cli_anything/gimp/gimp_cli.py +788 -0
- package/python/agent-harness/cli_anything/gimp/tests/TEST.md +137 -0
- package/python/agent-harness/cli_anything/gimp/tests/__init__.py +1 -0
- package/python/agent-harness/cli_anything/gimp/tests/test_core.py +478 -0
- package/python/agent-harness/cli_anything/gimp/tests/test_full_e2e.py +578 -0
- package/python/agent-harness/cli_anything/gimp/utils/__init__.py +1 -0
- package/python/agent-harness/cli_anything/gimp/utils/gimp_backend.py +208 -0
- package/python/agent-harness/cli_anything/gimp/utils/repl_skin.py +498 -0
- package/python/agent-harness/cli_anything_gimp.egg-info/PKG-INFO +236 -0
- package/python/agent-harness/cli_anything_gimp.egg-info/SOURCES.txt +25 -0
- package/python/agent-harness/cli_anything_gimp.egg-info/dependency_links.txt +1 -0
- package/python/agent-harness/cli_anything_gimp.egg-info/entry_points.txt +2 -0
- package/python/agent-harness/cli_anything_gimp.egg-info/not-zip-safe +1 -0
- package/python/agent-harness/cli_anything_gimp.egg-info/requires.txt +7 -0
- package/python/agent-harness/cli_anything_gimp.egg-info/top_level.txt +1 -0
- 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
|
+
}
|