autodev-cli 1.4.150 → 1.4.152
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/media/gimp/gimp_mcp_client.py +204 -0
- package/media/gimp/gimp_mcp_server.py +310 -211
- package/out/commands/mcp.js +36 -30
- package/out/commands/mcp.js.map +1 -1
- package/out/configManager.js +54 -0
- package/out/configManager.js.map +1 -1
- package/out/core/gimpClientSource.d.ts +4 -0
- package/out/core/gimpClientSource.js +221 -0
- package/out/core/gimpClientSource.js.map +1 -0
- package/out/core/gimpPluginInstall.d.ts +45 -6
- package/out/core/gimpPluginInstall.js +184 -63
- package/out/core/gimpPluginInstall.js.map +1 -1
- package/out/core/gimpPluginSource.d.ts +4 -0
- package/out/core/gimpPluginSource.js +383 -0
- package/out/core/gimpPluginSource.js.map +1 -0
- package/out/core/mcpCatalog.d.ts +25 -17
- package/out/core/mcpCatalog.js +68 -36
- package/out/core/mcpCatalog.js.map +1 -1
- package/out/core/mcpCatalogMigrate.d.ts +9 -1
- package/out/core/mcpCatalogMigrate.js +34 -22
- package/out/core/mcpCatalogMigrate.js.map +1 -1
- package/package.json +3 -1
|
@@ -9,15 +9,32 @@ export interface GimpInstallDeps {
|
|
|
9
9
|
writeFile: (p: string, data: string) => void;
|
|
10
10
|
chmod: (p: string, mode: number) => void;
|
|
11
11
|
copyFile: (src: string, dst: string) => void;
|
|
12
|
-
/**
|
|
12
|
+
/** Read an existing file (for idempotent-skip content comparison). '' if unreadable. */
|
|
13
|
+
readFile: (p: string) => string;
|
|
14
|
+
/**
|
|
15
|
+
* The GIMP-side plug-in (server) source. Defaults to the EMBEDDED string
|
|
16
|
+
* (GIMP_PLUGIN_SOURCE) so it works when esbuild-bundled into the VS Code
|
|
17
|
+
* extension where media/gimp/gimp_mcp_server.py is NOT on disk at runtime.
|
|
18
|
+
*/
|
|
13
19
|
readPluginSource: () => string;
|
|
20
|
+
/**
|
|
21
|
+
* The MCP client source. Defaults to the EMBEDDED string (GIMP_CLIENT_SOURCE),
|
|
22
|
+
* same bundle-safety rationale as readPluginSource.
|
|
23
|
+
*/
|
|
24
|
+
readClientSource: () => string;
|
|
14
25
|
/** GIMP detection (to gate on major < 3). Defaults to detectApp('gimp'). */
|
|
15
26
|
detectGimp: () => DetectAppResult;
|
|
16
27
|
}
|
|
17
28
|
export interface GimpInstallResult {
|
|
18
29
|
installed: boolean;
|
|
19
|
-
/**
|
|
30
|
+
/** Primary GIMP-side plug-in .py path written (the real %APPDATA%/config one). */
|
|
31
|
+
serverPath: string | null;
|
|
32
|
+
/** Primary MCP client .py path written (<ver>/mcp/gimp_mcp_client.py). */
|
|
33
|
+
clientPath: string | null;
|
|
34
|
+
/** All plug-in (server) .py paths written (incl. the win32 Store fallback). */
|
|
20
35
|
paths: string[];
|
|
36
|
+
/** All MCP client .py paths written. */
|
|
37
|
+
clientPaths: string[];
|
|
21
38
|
/** The GIMP minor-version dir chosen (e.g. "3.2"), or null when not installed. */
|
|
22
39
|
version: string | null;
|
|
23
40
|
warnings: string[];
|
|
@@ -29,13 +46,35 @@ export interface GimpInstallOpts {
|
|
|
29
46
|
*/
|
|
30
47
|
targetDir?: string;
|
|
31
48
|
}
|
|
32
|
-
/** Absolute path to the vendored
|
|
49
|
+
/** Absolute path to the vendored plug-in (server) shipped in the tarball. */
|
|
33
50
|
export declare function vendoredPluginSourcePath(): string;
|
|
51
|
+
/** Absolute path to the vendored MCP client shipped in the tarball. */
|
|
52
|
+
export declare function vendoredClientSourcePath(): string;
|
|
53
|
+
/** The MCP client .py path for a versioned config dir (sibling `mcp/` folder). */
|
|
54
|
+
export declare function clientPathFor(configDir: string): string;
|
|
34
55
|
/** Extract the GIMP MAJOR version from a launcher path (e.g. gimp-2.10 → 2). */
|
|
35
56
|
export declare function gimpMajorFromPath(p: string | null): number | null;
|
|
36
57
|
/**
|
|
37
|
-
* Install the
|
|
38
|
-
* per-OS and writes the vendored
|
|
39
|
-
*
|
|
58
|
+
* Install BOTH the GIMP-side plug-in (server) and the MCP client. Resolves the
|
|
59
|
+
* target config dir(s) per-OS and writes the vendored files. Does NOT install
|
|
60
|
+
* when a clearly < 3 GIMP is the only one detected.
|
|
40
61
|
*/
|
|
41
62
|
export declare function installGimpPlugin(opts?: GimpInstallOpts, deps?: Partial<GimpInstallDeps>): GimpInstallResult;
|
|
63
|
+
/**
|
|
64
|
+
* Sync-path variant: install ONLY when GIMP 3.x is actually DETECTED. Unlike
|
|
65
|
+
* installGimpPlugin (which will create a 3.0 config dir even with no GIMP present
|
|
66
|
+
* — appropriate for the explicit `mcp detect --enable gimp` / `gimp-install-plugin`
|
|
67
|
+
* flows), this refuses to touch the filesystem when GIMP is absent or is clearly
|
|
68
|
+
* < 3, so a routine ConfigManager.syncProjectMcpServers on a box without GIMP
|
|
69
|
+
* never spuriously creates a config dir. Returns a skip result (installed:false +
|
|
70
|
+
* warning) instead of throwing so callers can swallow it.
|
|
71
|
+
*/
|
|
72
|
+
export declare function installGimpPluginIfDetected(opts?: GimpInstallOpts, deps?: Partial<GimpInstallDeps>): GimpInstallResult;
|
|
73
|
+
/**
|
|
74
|
+
* Resolve the PRIMARY MCP client path for the current machine WITHOUT writing
|
|
75
|
+
* anything — gated on GIMP 3.x being detected (same gate as
|
|
76
|
+
* installGimpPluginIfDetected). Returns null when GIMP is absent / clearly < 3,
|
|
77
|
+
* so callers (the migration, catalog display) can decide not to bake a path in.
|
|
78
|
+
* This is the exact path installGimpPlugin() would write the client to.
|
|
79
|
+
*/
|
|
80
|
+
export declare function resolveGimpClientPath(opts?: GimpInstallOpts, deps?: Partial<GimpInstallDeps>): string | null;
|
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
// ---------------------------------------------------------------------------
|
|
3
|
-
// gimpPluginInstall — drop the vendored
|
|
4
|
-
// correct GIMP 3.x user
|
|
5
|
-
//
|
|
3
|
+
// gimpPluginInstall — drop the vendored, USER-VERIFIED GIMP MCP files into the
|
|
4
|
+
// correct GIMP 3.x user config so the socket bridge works end-to-end:
|
|
5
|
+
// - the GIMP-side PLUG-IN (server) → plug-ins/gimp_mcp_server/gimp_mcp_server.py
|
|
6
|
+
// - the MCP CLIENT → mcp/gimp_mcp_client.py (a sibling dir)
|
|
7
|
+
//
|
|
8
|
+
// The plug-in is MENU-TRIGGERED (the user's verified flow): in GIMP the user runs
|
|
9
|
+
// Tools → MCP → Start MCP Server (or Filters → Development → Start MCP Server) and
|
|
10
|
+
// keeps the "GIMP MCP Server" window open — that holds the out-of-process plug-in
|
|
11
|
+
// alive and the :9876 socket. We do NOT auto-start it (the PERSISTENT approach we
|
|
12
|
+
// tried did not work on GIMP 3.2); we only install the files.
|
|
6
13
|
//
|
|
7
14
|
// GROUNDED RULES (see AutoDev research):
|
|
8
15
|
// - GIMP 3.x user config is MINOR-versioned: GIMP 3.2 → .../GIMP/3.2/ (NOT 3.0).
|
|
@@ -12,14 +19,15 @@
|
|
|
12
19
|
// exists, create 3.0 and warn (user should launch GIMP once).
|
|
13
20
|
// - GIMP 3.x layout: plug-ins/gimp_mcp_server/gimp_mcp_server.py — the .py MUST
|
|
14
21
|
// sit in a sub-folder whose name == the .py basename (a loose .py is ignored).
|
|
15
|
-
// On non-Windows the .py must be executable (0o755).
|
|
22
|
+
// On non-Windows the .py must be executable (0o755). The client sits at
|
|
23
|
+
// <ver>/mcp/gimp_mcp_client.py (also 0o755 on non-Windows).
|
|
16
24
|
// - Windows Store (MSIX) GIMP reads the REAL %APPDATA%\GIMP\<ver>. As a safety
|
|
17
25
|
// net we ALSO write the virtualized path when a package dir exists:
|
|
18
|
-
// %LOCALAPPDATA%\Packages\GIMP.<...>_*\LocalCache\Roaming\GIMP\<ver
|
|
26
|
+
// %LOCALAPPDATA%\Packages\GIMP.<...>_*\LocalCache\Roaming\GIMP\<ver>\… .
|
|
19
27
|
//
|
|
20
28
|
// Everything is dependency-injected (platform/env/homedir/fileExists/listDir/
|
|
21
|
-
// mkdirp/writeFile/chmod/copyFile/readPluginSource/detectGimp) so
|
|
22
|
-
// unit-testable without a real GIMP or filesystem.
|
|
29
|
+
// mkdirp/writeFile/chmod/copyFile/readPluginSource/readClientSource/detectGimp) so
|
|
30
|
+
// the resolver is unit-testable without a real GIMP or filesystem.
|
|
23
31
|
// ---------------------------------------------------------------------------
|
|
24
32
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
25
33
|
if (k2 === undefined) k2 = k;
|
|
@@ -56,17 +64,27 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
56
64
|
})();
|
|
57
65
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
58
66
|
exports.vendoredPluginSourcePath = vendoredPluginSourcePath;
|
|
67
|
+
exports.vendoredClientSourcePath = vendoredClientSourcePath;
|
|
68
|
+
exports.clientPathFor = clientPathFor;
|
|
59
69
|
exports.gimpMajorFromPath = gimpMajorFromPath;
|
|
60
70
|
exports.installGimpPlugin = installGimpPlugin;
|
|
71
|
+
exports.installGimpPluginIfDetected = installGimpPluginIfDetected;
|
|
72
|
+
exports.resolveGimpClientPath = resolveGimpClientPath;
|
|
61
73
|
const fs = __importStar(require("fs"));
|
|
62
74
|
const path = __importStar(require("path"));
|
|
63
75
|
const os = __importStar(require("os"));
|
|
64
76
|
const binDetect_1 = require("./binDetect");
|
|
65
|
-
|
|
77
|
+
const gimpPluginSource_1 = require("./gimpPluginSource");
|
|
78
|
+
const gimpClientSource_1 = require("./gimpClientSource");
|
|
79
|
+
/** Absolute path to the vendored plug-in (server) shipped in the tarball. */
|
|
66
80
|
function vendoredPluginSourcePath() {
|
|
67
81
|
// Compiled to out/core/gimpPluginInstall.js → package root is ../../ .
|
|
68
82
|
return path.join(__dirname, '..', '..', 'media', 'gimp', 'gimp_mcp_server.py');
|
|
69
83
|
}
|
|
84
|
+
/** Absolute path to the vendored MCP client shipped in the tarball. */
|
|
85
|
+
function vendoredClientSourcePath() {
|
|
86
|
+
return path.join(__dirname, '..', '..', 'media', 'gimp', 'gimp_mcp_client.py');
|
|
87
|
+
}
|
|
70
88
|
function realDeps() {
|
|
71
89
|
return {
|
|
72
90
|
platform: process.platform,
|
|
@@ -91,7 +109,16 @@ function realDeps() {
|
|
|
91
109
|
}
|
|
92
110
|
catch { /* non-POSIX fs */ } },
|
|
93
111
|
copyFile: (src, dst) => { fs.copyFileSync(src, dst); },
|
|
94
|
-
|
|
112
|
+
readFile: (p) => { try {
|
|
113
|
+
return fs.readFileSync(p, 'utf8');
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
return '';
|
|
117
|
+
} },
|
|
118
|
+
// Runtime uses the EMBEDDED sources (bundle-safe). media/gimp/*.py stay the
|
|
119
|
+
// canonical source + GPL attribution, but are no longer read at runtime.
|
|
120
|
+
readPluginSource: () => gimpPluginSource_1.GIMP_PLUGIN_SOURCE,
|
|
121
|
+
readClientSource: () => gimpClientSource_1.GIMP_CLIENT_SOURCE,
|
|
95
122
|
detectGimp: () => (0, binDetect_1.detectApp)('gimp'),
|
|
96
123
|
};
|
|
97
124
|
}
|
|
@@ -130,10 +157,38 @@ function chooseVersion(base, d) {
|
|
|
130
157
|
return { version: kids[0].name, existed: true };
|
|
131
158
|
}
|
|
132
159
|
/**
|
|
133
|
-
*
|
|
134
|
-
*
|
|
160
|
+
* The versioned GIMP config dir(s) to install into — the parent of both
|
|
161
|
+
* `plug-ins/` (server) and `mcp/` (client). The first entry is the PRIMARY
|
|
162
|
+
* (the real %APPDATA%/config dir); the rest are the win32 Store safety net.
|
|
163
|
+
*/
|
|
164
|
+
function resolveConfigDirs(opts, d) {
|
|
165
|
+
const warnings = [];
|
|
166
|
+
const override = (opts?.targetDir && opts.targetDir.trim()) || (d.env.GIMP3_DIRECTORY && d.env.GIMP3_DIRECTORY.trim());
|
|
167
|
+
if (override) {
|
|
168
|
+
const version = path.basename(override) || 'custom';
|
|
169
|
+
if (!d.fileExists(override)) {
|
|
170
|
+
warnings.push(`GIMP dir ${override} does not exist yet — creating it. Launch GIMP once so it initialises its config.`);
|
|
171
|
+
}
|
|
172
|
+
return { dirs: [override], version, warnings };
|
|
173
|
+
}
|
|
174
|
+
const base = gimpConfigBase(d);
|
|
175
|
+
const chosen = chooseVersion(base, d);
|
|
176
|
+
if (!chosen.existed) {
|
|
177
|
+
warnings.push(`No GIMP 3.x config dir found under ${base} — creating ${chosen.version}. ` +
|
|
178
|
+
'Launch GIMP 3 at least once (so it creates its real config dir), then re-run to be sure it lands in the right place.');
|
|
179
|
+
}
|
|
180
|
+
const dirs = [path.join(base, chosen.version)];
|
|
181
|
+
// Windows Store virtualized safety net (only when a package dir exists).
|
|
182
|
+
for (const vdir of storeVirtualizedConfigDirs(chosen.version, d)) {
|
|
183
|
+
dirs.push(vdir);
|
|
184
|
+
}
|
|
185
|
+
return { dirs, version: chosen.version, warnings };
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Windows Store virtualized config dir(s) for a version. Only returned when a
|
|
189
|
+
* `GIMP.*` package dir actually exists under %LOCALAPPDATA%\Packages.
|
|
135
190
|
*/
|
|
136
|
-
function
|
|
191
|
+
function storeVirtualizedConfigDirs(version, d) {
|
|
137
192
|
if (d.platform !== 'win32') {
|
|
138
193
|
return [];
|
|
139
194
|
}
|
|
@@ -141,7 +196,15 @@ function storeVirtualizedPluginDirs(version, d) {
|
|
|
141
196
|
const localAppData = d.env.LOCALAPPDATA || path.join(home, 'AppData', 'Local');
|
|
142
197
|
const packagesDir = path.join(localAppData, 'Packages');
|
|
143
198
|
const pkgs = d.listDir(packagesDir).filter((n) => /^GIMP\./i.test(n));
|
|
144
|
-
return pkgs.map((pkg) => path.join(packagesDir, pkg, 'LocalCache', 'Roaming', 'GIMP', version
|
|
199
|
+
return pkgs.map((pkg) => path.join(packagesDir, pkg, 'LocalCache', 'Roaming', 'GIMP', version));
|
|
200
|
+
}
|
|
201
|
+
/** The server plug-in .py path for a versioned config dir. */
|
|
202
|
+
function serverPathFor(configDir) {
|
|
203
|
+
return path.join(configDir, 'plug-ins', 'gimp_mcp_server', 'gimp_mcp_server.py');
|
|
204
|
+
}
|
|
205
|
+
/** The MCP client .py path for a versioned config dir (sibling `mcp/` folder). */
|
|
206
|
+
function clientPathFor(configDir) {
|
|
207
|
+
return path.join(configDir, 'mcp', 'gimp_mcp_client.py');
|
|
145
208
|
}
|
|
146
209
|
/** Extract the GIMP MAJOR version from a launcher path (e.g. gimp-2.10 → 2). */
|
|
147
210
|
function gimpMajorFromPath(p) {
|
|
@@ -152,73 +215,131 @@ function gimpMajorFromPath(p) {
|
|
|
152
215
|
const m = /gimp[-_]?(\d+)/i.exec(base);
|
|
153
216
|
return m ? parseInt(m[1], 10) : null;
|
|
154
217
|
}
|
|
155
|
-
/**
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
218
|
+
/**
|
|
219
|
+
* Write `source` to `target`. IDEMPOTENT: if the target already holds
|
|
220
|
+
* byte-identical content, skip the write (and the .bak) — but still ensure the
|
|
221
|
+
* executable bit on non-Windows. A .bak backup is made ONLY when overwriting
|
|
222
|
+
* content that DIFFERS from the source.
|
|
223
|
+
*/
|
|
224
|
+
function writeIdempotent(target, source, d) {
|
|
225
|
+
d.mkdirp(path.dirname(target));
|
|
226
|
+
if (d.fileExists(target)) {
|
|
227
|
+
const existing = d.readFile(target);
|
|
228
|
+
if (existing === source) {
|
|
229
|
+
if (d.platform !== 'win32') {
|
|
230
|
+
d.chmod(target, 0o755);
|
|
231
|
+
}
|
|
232
|
+
return target;
|
|
233
|
+
}
|
|
234
|
+
d.copyFile(target, target + '.bak');
|
|
162
235
|
}
|
|
163
|
-
d.writeFile(
|
|
236
|
+
d.writeFile(target, source);
|
|
164
237
|
if (d.platform !== 'win32') {
|
|
165
|
-
d.chmod(
|
|
238
|
+
d.chmod(target, 0o755);
|
|
166
239
|
}
|
|
167
|
-
return
|
|
240
|
+
return target;
|
|
168
241
|
}
|
|
169
242
|
/**
|
|
170
|
-
*
|
|
171
|
-
*
|
|
172
|
-
* on launch. Does NOT install when a clearly < 3 GIMP is the only one detected.
|
|
243
|
+
* Gate helper: is this a GIMP we must NOT install for? (detected AND clearly < 3).
|
|
244
|
+
* Returns a warning string to surface, or null when install may proceed.
|
|
173
245
|
*/
|
|
174
|
-
function
|
|
175
|
-
const d = { ...realDeps(), ...deps };
|
|
176
|
-
const warnings = [];
|
|
177
|
-
// Gate: if a GIMP is detected and is clearly < 3, do not install.
|
|
246
|
+
function refuseIfOldGimp(d) {
|
|
178
247
|
const det = d.detectGimp();
|
|
179
248
|
const major = gimpMajorFromPath(det.path);
|
|
180
249
|
if (det.installed && major !== null && major < 3) {
|
|
181
|
-
|
|
182
|
-
'the
|
|
250
|
+
return (`Detected GIMP ${major}.x at ${det.path}. GIMP MCP needs GIMP 3.x — ` +
|
|
251
|
+
'the plugin uses the GIMP 3.0 Python API and was NOT installed. ' +
|
|
183
252
|
'Install GIMP 3 and re-run.');
|
|
184
|
-
return { installed: false, paths: [], version: null, warnings };
|
|
185
253
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
const base = gimpConfigBase(d);
|
|
201
|
-
const chosen = chooseVersion(base, d);
|
|
202
|
-
version = chosen.version;
|
|
203
|
-
if (!chosen.existed) {
|
|
204
|
-
warnings.push(`No GIMP 3.x config dir found under ${base} — creating ${version}. ` +
|
|
205
|
-
'Launch GIMP 3 at least once (so it creates its real config dir), then re-run to be sure it lands in the right place.');
|
|
206
|
-
}
|
|
207
|
-
targets.push(path.join(base, version, 'plug-ins'));
|
|
208
|
-
// Windows Store virtualized safety net (only when a package dir exists).
|
|
209
|
-
for (const vdir of storeVirtualizedPluginDirs(version, d)) {
|
|
210
|
-
targets.push(vdir);
|
|
211
|
-
}
|
|
254
|
+
return null;
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Install BOTH the GIMP-side plug-in (server) and the MCP client. Resolves the
|
|
258
|
+
* target config dir(s) per-OS and writes the vendored files. Does NOT install
|
|
259
|
+
* when a clearly < 3 GIMP is the only one detected.
|
|
260
|
+
*/
|
|
261
|
+
function installGimpPlugin(opts, deps) {
|
|
262
|
+
const d = { ...realDeps(), ...deps };
|
|
263
|
+
const warnings = [];
|
|
264
|
+
const refusal = refuseIfOldGimp(d);
|
|
265
|
+
if (refusal) {
|
|
266
|
+
warnings.push(refusal);
|
|
267
|
+
return { installed: false, serverPath: null, clientPath: null, paths: [], clientPaths: [], version: null, warnings };
|
|
212
268
|
}
|
|
269
|
+
const serverSource = d.readPluginSource();
|
|
270
|
+
const clientSource = d.readClientSource();
|
|
271
|
+
const { dirs, version, warnings: resolveWarnings } = resolveConfigDirs(opts, d);
|
|
272
|
+
warnings.push(...resolveWarnings);
|
|
213
273
|
const paths = [];
|
|
214
|
-
|
|
274
|
+
const clientPaths = [];
|
|
275
|
+
for (const configDir of dirs) {
|
|
215
276
|
try {
|
|
216
|
-
paths.push(
|
|
277
|
+
paths.push(writeIdempotent(serverPathFor(configDir), serverSource, d));
|
|
217
278
|
}
|
|
218
279
|
catch (e) {
|
|
219
|
-
warnings.push(`Failed to write plugin into ${
|
|
280
|
+
warnings.push(`Failed to write plugin into ${configDir}: ${e.message}`);
|
|
220
281
|
}
|
|
282
|
+
try {
|
|
283
|
+
clientPaths.push(writeIdempotent(clientPathFor(configDir), clientSource, d));
|
|
284
|
+
}
|
|
285
|
+
catch (e) {
|
|
286
|
+
warnings.push(`Failed to write client into ${configDir}: ${e.message}`);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return {
|
|
290
|
+
installed: paths.length > 0,
|
|
291
|
+
serverPath: paths[0] ?? null,
|
|
292
|
+
clientPath: clientPaths[0] ?? null,
|
|
293
|
+
paths,
|
|
294
|
+
clientPaths,
|
|
295
|
+
version,
|
|
296
|
+
warnings,
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Sync-path variant: install ONLY when GIMP 3.x is actually DETECTED. Unlike
|
|
301
|
+
* installGimpPlugin (which will create a 3.0 config dir even with no GIMP present
|
|
302
|
+
* — appropriate for the explicit `mcp detect --enable gimp` / `gimp-install-plugin`
|
|
303
|
+
* flows), this refuses to touch the filesystem when GIMP is absent or is clearly
|
|
304
|
+
* < 3, so a routine ConfigManager.syncProjectMcpServers on a box without GIMP
|
|
305
|
+
* never spuriously creates a config dir. Returns a skip result (installed:false +
|
|
306
|
+
* warning) instead of throwing so callers can swallow it.
|
|
307
|
+
*/
|
|
308
|
+
function installGimpPluginIfDetected(opts, deps) {
|
|
309
|
+
const d = { ...realDeps(), ...deps };
|
|
310
|
+
const det = d.detectGimp();
|
|
311
|
+
const major = gimpMajorFromPath(det.path);
|
|
312
|
+
// Require a detected GIMP that is not clearly < 3 (major null = version not in
|
|
313
|
+
// the launcher path, e.g. a macOS app bundle — allowed).
|
|
314
|
+
if (!det.installed || (major !== null && major < 3)) {
|
|
315
|
+
return {
|
|
316
|
+
installed: false,
|
|
317
|
+
serverPath: null,
|
|
318
|
+
clientPath: null,
|
|
319
|
+
paths: [],
|
|
320
|
+
clientPaths: [],
|
|
321
|
+
version: null,
|
|
322
|
+
warnings: ['GIMP 3.x not detected — plugin install skipped (sync path).'],
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
return installGimpPlugin(opts, deps);
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Resolve the PRIMARY MCP client path for the current machine WITHOUT writing
|
|
329
|
+
* anything — gated on GIMP 3.x being detected (same gate as
|
|
330
|
+
* installGimpPluginIfDetected). Returns null when GIMP is absent / clearly < 3,
|
|
331
|
+
* so callers (the migration, catalog display) can decide not to bake a path in.
|
|
332
|
+
* This is the exact path installGimpPlugin() would write the client to.
|
|
333
|
+
*/
|
|
334
|
+
function resolveGimpClientPath(opts, deps) {
|
|
335
|
+
const d = { ...realDeps(), ...deps };
|
|
336
|
+
const det = d.detectGimp();
|
|
337
|
+
const major = gimpMajorFromPath(det.path);
|
|
338
|
+
if (!det.installed || (major !== null && major < 3)) {
|
|
339
|
+
return null;
|
|
221
340
|
}
|
|
222
|
-
|
|
341
|
+
const { dirs } = resolveConfigDirs(opts, d);
|
|
342
|
+
const primary = dirs[0];
|
|
343
|
+
return primary ? clientPathFor(primary) : null;
|
|
223
344
|
}
|
|
224
345
|
//# sourceMappingURL=gimpPluginInstall.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gimpPluginInstall.js","sourceRoot":"","sources":["../../src/core/gimpPluginInstall.ts"],"names":[],"mappings":";AAAA,8EAA8E;AAC9E,
|
|
1
|
+
{"version":3,"file":"gimpPluginInstall.js","sourceRoot":"","sources":["../../src/core/gimpPluginInstall.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** sha256 (first 12 hex) of the canonical media/gimp/gimp_mcp_server.py. */
|
|
2
|
+
export declare const GIMP_PLUGIN_SOURCE_HASH = "c4b85cccece9";
|
|
3
|
+
/** Full body of the user-verified GIMP 3.x MCP server plug-in (media/gimp/gimp_mcp_server.py). */
|
|
4
|
+
export declare const GIMP_PLUGIN_SOURCE = "#!/usr/bin/env python3\n# ---------------------------------------------------------------------------\n# gimp_mcp_server.py \u2014 vendored, USER-VERIFIED GIMP 3.2 MCP bridge (plug-in).\n#\n# Derived from libreearth/gimp-mcp (GPL-3.0; see media/gimp/LICENSE) and fixed by\n# the user / AutoDev for GIMP 3.2 (Store / MSIX). Verified working on real GIMP\n# 3.2.44: a menu-triggered PLUGIN (Tools -> MCP / Filters -> Development -> Start\n# MCP Server) that listens on 127.0.0.1:9876 and drives GIMP. Keep the \"GIMP MCP\n# Server\" window open \u2014 GLib.MainLoop() holds the out-of-process plug-in alive.\n#\n# This is the user's verified file; the body below is kept byte-for-byte. The\n# lines in THIS block are a provenance comment only \u2014 do not edit the code below.\n# ---------------------------------------------------------------------------\n\n# GIMP MCP Server Plugin (GIMP 3.2)\n# Listens on localhost:9876 so the libreearth gimp-mcp client can drive GIMP.\n\nimport gi\ngi.require_version('Gimp', '3.0')\nfrom gi.repository import Gimp\nfrom gi.repository import GLib\nfrom gi.repository import Gio\nfrom gi.repository import GObject\n\nimport io\nimport json\nimport os\nimport socket\nimport sys\nimport threading\nimport traceback\n\nHOST = '127.0.0.1'\nPORT = 9876\n# Store GIMP redirects GLib config dir into the MSIX LocalCache. Also write\n# to the real roaming profile so the log is findable.\n_LOG_PATHS = [\n os.path.join(GLib.get_user_config_dir(), 'GIMP', '3.2', 'gimp_mcp_server.log'),\n os.path.join(os.environ.get('APPDATA', ''), 'GIMP', '3.2', 'gimp_mcp_server.log'),\n]\n\n_server_lock = threading.Lock()\n_server_started = False\n_exec_globals = None\n_main_loop = None\n\n\ndef _log(msg):\n line = msg.rstrip() + '\\n'\n for path in _LOG_PATHS:\n if not path:\n continue\n try:\n os.makedirs(os.path.dirname(path), exist_ok=True)\n with open(path, 'a', encoding='utf-8') as fh:\n fh.write(line)\n except Exception:\n pass\n print(msg, flush=True)\n\n\ndef N_(message):\n return message\n\n\ndef _(message):\n return GLib.dgettext(None, message)\n\n\ndef _serialize(obj):\n if obj is None or isinstance(obj, (str, int, float, bool)):\n return obj\n if isinstance(obj, (list, tuple)):\n return [_serialize(item) for item in obj]\n if isinstance(obj, dict):\n return {str(k): _serialize(v) for k, v in obj.items()}\n data = {'type': obj.__class__.__name__}\n if hasattr(obj, 'get_id'):\n try:\n data['id'] = int(obj.get_id())\n except Exception:\n pass\n elif hasattr(obj, 'ID'):\n data['id'] = obj.ID\n if hasattr(obj, 'get_name'):\n try:\n data['name'] = obj.get_name()\n except Exception:\n pass\n if 'id' not in data and len(data) == 1:\n return str(obj)\n return data\n\n\ndef _resolve_api(api_path):\n parts = [p for p in str(api_path).split('.') if p]\n if parts and parts[0] == 'Gimp':\n parts = parts[1:]\n elif len(parts) >= 3 and parts[0] == 'gi' and parts[1] == 'repository' and parts[2] == 'Gimp':\n parts = parts[3:]\n\n aliases = {\n 'list_images': 'get_images',\n 'list_fonts': 'get_fonts',\n 'fonts_get_list': 'get_fonts',\n }\n\n if not parts:\n return Gimp\n\n obj = Gimp\n for part in parts:\n part = aliases.get(part, part)\n obj = getattr(obj, part)\n return obj\n\n\ndef _exec_namespace():\n global _exec_globals\n if _exec_globals is None:\n ns = {\n '__builtins__': __builtins__,\n '__name__': '__main__',\n 'Gimp': Gimp,\n 'GLib': GLib,\n 'Gio': Gio,\n 'GObject': GObject,\n 'sys': sys,\n 'os': os,\n 'json': json,\n }\n try:\n gi.require_version('Gegl', '0.4')\n from gi.repository import Gegl\n ns['Gegl'] = Gegl\n except Exception as exc:\n _log(f'Gegl import failed: {exc}')\n try:\n gi.require_version('Babl', '0.1')\n from gi.repository import Babl\n ns['Babl'] = Babl\n except Exception:\n pass\n _exec_globals = ns\n return _exec_globals\n\n\ndef _extract_code(args, kwargs):\n if kwargs.get('code'):\n return str(kwargs['code'])\n if not args:\n return ''\n if len(args) >= 2 and args[0] in ('exec', 'eval', 'pyGObject-console'):\n payload = args[1]\n if isinstance(payload, (list, tuple)):\n return '\\n'.join(str(item) for item in payload)\n return str(payload)\n if isinstance(args[0], (list, tuple)):\n return '\\n'.join(str(item) for item in args[0])\n return str(args[0])\n\n\ndef _run_python(code):\n ns = _exec_namespace()\n stdout = io.StringIO()\n old_stdout = sys.stdout\n sys.stdout = stdout\n try:\n exec(code, ns)\n finally:\n sys.stdout = old_stdout\n printed = stdout.getvalue()\n result = ns.get('_result')\n if result is not None:\n return {'printed': printed, 'result': _serialize(result)}\n return {'printed': printed, 'result': None}\n\n\nclass MCPPlugin(Gimp.PlugIn):\n def do_set_i18n(self, name):\n return False\n\n def do_query_procedures(self):\n return ['plug-in-mcp-server']\n\n def do_create_procedure(self, name):\n procedure = Gimp.Procedure.new(\n self, name, Gimp.PDBProcType.PLUGIN, self.run, None\n )\n procedure.set_menu_label(_('Start MCP Server'))\n procedure.set_documentation(\n _('Starts an MCP server to control GIMP externally'),\n _('Listens on 127.0.0.1:9876 for the GIMP MCP client'),\n name,\n )\n procedure.set_attribution('GIMP MCP', 'GIMP MCP', '2026')\n procedure.set_sensitivity_mask(Gimp.ProcedureSensitivityMask.ALWAYS)\n procedure.add_enum_argument(\n 'run-mode',\n _('Run mode'),\n _('The run mode'),\n Gimp.RunMode,\n Gimp.RunMode.INTERACTIVE,\n GObject.ParamFlags.READWRITE,\n )\n procedure.add_menu_path('<Image>/Tools/MCP')\n procedure.add_menu_path('<Image>/Filters/Development/')\n return procedure\n\n def run(self, procedure, config, data):\n global _main_loop\n started, message = self.ensure_server()\n _log(message)\n try:\n Gimp.message(message)\n except Exception:\n pass\n if not started:\n return procedure.new_return_values(Gimp.PDBStatusType.EXECUTION_ERROR, GLib.Error(message))\n\n # GIMP 3 launches Python plug-ins out of process. Returning from run()\n # would exit that process and kill the socket thread. Stay alive and\n # dispatch GLib.idle_add work (API calls) on this main loop.\n # A visible window is required: otherwise \"Start MCP Server\" looks like\n # it did not open anything.\n try:\n self._show_status_window(message)\n except Exception as exc:\n _log(f'Status window failed: {exc}\\n{traceback.format_exc()}')\n\n _main_loop = GLib.MainLoop()\n _log('Entering GLib.MainLoop to keep MCP server process alive')\n try:\n _main_loop.run()\n except Exception as exc:\n _log(f'MainLoop exited: {exc}')\n return procedure.new_return_values(Gimp.PDBStatusType.SUCCESS, GLib.Error())\n\n def _show_status_window(self, message):\n gi.require_version('Gtk', '3.0')\n gi.require_version('GimpUi', '3.0')\n from gi.repository import Gtk\n from gi.repository import GimpUi\n\n GimpUi.init('gimp_mcp_server.py')\n window = Gtk.Window(title='GIMP MCP Server')\n window.set_default_size(420, 160)\n window.set_keep_above(True)\n box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=8)\n box.set_border_width(16)\n label = Gtk.Label(label=message + '\\n\\nLeave this window open while Grok talks to GIMP.')\n label.set_line_wrap(True)\n box.pack_start(label, True, True, 0)\n button = Gtk.Button(label='Stop server')\n button.connect('clicked', lambda *_: window.destroy())\n box.pack_start(button, False, False, 0)\n window.add(box)\n window.connect('destroy', self._on_status_closed)\n window.show_all()\n window.present()\n _log('Status window opened')\n\n def _on_status_closed(self, *_args):\n global _main_loop\n _log('Status window closed; stopping MCP server')\n if _main_loop is not None:\n _main_loop.quit()\n\n def ensure_server(self):\n global _server_started\n with _server_lock:\n if _server_started:\n return True, f'MCP Server already listening on {HOST}:{PORT}'\n try:\n thread = threading.Thread(target=self.start_server, daemon=True)\n thread.start()\n _server_started = True\n return True, f'MCP Server started on {HOST}:{PORT}'\n except Exception as exc:\n return False, f'Failed to start MCP Server: {exc}'\n\n def start_server(self):\n sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)\n try:\n sock.bind((HOST, PORT))\n sock.listen(8)\n _log(f'Server listening on {HOST}:{PORT}')\n except Exception as exc:\n _log(f'Bind/listen failed: {exc}')\n return\n\n while True:\n try:\n client, addr = sock.accept()\n _log(f'Connected to {addr}')\n threading.Thread(target=self.handle_client, args=(client,), daemon=True).start()\n except Exception as exc:\n _log(f'Server accept error: {exc}')\n break\n sock.close()\n\n def handle_client(self, client):\n buffer = b''\n try:\n client.settimeout(120)\n while True:\n chunk = client.recv(65536)\n if not chunk:\n break\n buffer += chunk\n while buffer:\n try:\n command = json.loads(buffer.decode('utf-8'))\n buffer = b''\n except json.JSONDecodeError:\n break\n GLib.idle_add(self.execute_command, command, client)\n except Exception as exc:\n _log(f'Client error: {exc}')\n finally:\n try:\n client.close()\n except Exception:\n pass\n\n def execute_command(self, command, client):\n cmd_type = command.get('type')\n params = command.get('params', {}) or {}\n try:\n if cmd_type == 'ping':\n response = {'status': 'success', 'result': 'pong'}\n elif cmd_type == 'exec':\n code = _extract_code(params.get('args', []), params.get('kwargs', {}))\n if not code:\n code = str(params.get('code', ''))\n response = {'status': 'success', 'result': _run_python(code)}\n elif cmd_type == 'call_api':\n api_path = params.get('api_path', '')\n args = params.get('args', []) or []\n kwargs = params.get('kwargs', {}) or {}\n if api_path in ('exec', 'eval', '__exec__'):\n response = {'status': 'success', 'result': _run_python(_extract_code(args, kwargs))}\n else:\n method = _resolve_api(api_path)\n result = method(*args, **kwargs)\n response = {'status': 'success', 'result': _serialize(result)}\n else:\n response = {'status': 'error', 'message': f'Unknown command: {cmd_type}'}\n except Exception as exc:\n response = {\n 'status': 'error',\n 'message': str(exc),\n 'traceback': traceback.format_exc(),\n }\n _log(f'Command error: {response[\"message\"]}\\n{response[\"traceback\"]}')\n\n try:\n payload = json.dumps(response).encode('utf-8')\n client.sendall(payload)\n except Exception as exc:\n _log(f'Response send failed: {exc}')\n return False\n\n\nGimp.main(MCPPlugin.__gtype__, sys.argv)\n";
|