@pired/lightroom-mcp 3.1.0
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/LICENSE +21 -0
- package/README.md +70 -0
- package/dist/LightroomMCP.lrplugin/HandlerAI.lua +466 -0
- package/dist/LightroomMCP.lrplugin/HandlerAIMasks.lua +549 -0
- package/dist/LightroomMCP.lrplugin/HandlerCatalog.lua +256 -0
- package/dist/LightroomMCP.lrplugin/HandlerCollections.lua +337 -0
- package/dist/LightroomMCP.lrplugin/HandlerDevelop.lua +1668 -0
- package/dist/LightroomMCP.lrplugin/HandlerExport.lua +149 -0
- package/dist/LightroomMCP.lrplugin/HandlerImport.lua +108 -0
- package/dist/LightroomMCP.lrplugin/HandlerLocalAdjustments.lua +330 -0
- package/dist/LightroomMCP.lrplugin/HandlerMetadata.lua +169 -0
- package/dist/LightroomMCP.lrplugin/HandlerOrganization.lua +783 -0
- package/dist/LightroomMCP.lrplugin/HandlerPreview.lua +189 -0
- package/dist/LightroomMCP.lrplugin/HandlerSearch.lua +185 -0
- package/dist/LightroomMCP.lrplugin/HandlerSelection.lua +302 -0
- package/dist/LightroomMCP.lrplugin/HandlerSpots.lua +313 -0
- package/dist/LightroomMCP.lrplugin/HandlerWatermark.lua +59 -0
- package/dist/LightroomMCP.lrplugin/Info.lua +25 -0
- package/dist/LightroomMCP.lrplugin/JSON.lua +270 -0
- package/dist/LightroomMCP.lrplugin/Log.lua +92 -0
- package/dist/LightroomMCP.lrplugin/MenuShowStatus.lua +22 -0
- package/dist/LightroomMCP.lrplugin/PhotoLookup.lua +39 -0
- package/dist/LightroomMCP.lrplugin/PluginInfoProvider.lua +865 -0
- package/dist/LightroomMCP.lrplugin/PluginInit.lua +48 -0
- package/dist/cli.d.ts +6 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +31 -0
- package/dist/cli.js.map +1 -0
- package/dist/configs/claude-code.mcp.json +9 -0
- package/dist/configs/claude-desktop.json +12 -0
- package/dist/configs/generic-stdio-client.json +10 -0
- package/dist/configs/mcp-proxy-http.json +9 -0
- package/dist/create-server.d.ts +12 -0
- package/dist/create-server.d.ts.map +1 -0
- package/dist/create-server.js +17 -0
- package/dist/create-server.js.map +1 -0
- package/dist/deepseek-harness/README.md +112 -0
- package/dist/deepseek-harness/deepseek-mcp-harness.mjs +292 -0
- package/dist/dispatcher.d.ts +34 -0
- package/dist/dispatcher.d.ts.map +1 -0
- package/dist/dispatcher.js +72 -0
- package/dist/dispatcher.js.map +1 -0
- package/dist/heartbeat.d.ts +17 -0
- package/dist/heartbeat.d.ts.map +1 -0
- package/dist/heartbeat.js +36 -0
- package/dist/heartbeat.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +255 -0
- package/dist/index.js.map +1 -0
- package/dist/install-plugin.d.ts +16 -0
- package/dist/install-plugin.d.ts.map +1 -0
- package/dist/install-plugin.js +125 -0
- package/dist/install-plugin.js.map +1 -0
- package/dist/instance-lock.d.ts +5 -0
- package/dist/instance-lock.d.ts.map +1 -0
- package/dist/instance-lock.js +77 -0
- package/dist/instance-lock.js.map +1 -0
- package/dist/list-tools-handler.d.ts +16 -0
- package/dist/list-tools-handler.d.ts.map +1 -0
- package/dist/list-tools-handler.js +50 -0
- package/dist/list-tools-handler.js.map +1 -0
- package/dist/plugin-liveness.d.ts +48 -0
- package/dist/plugin-liveness.d.ts.map +1 -0
- package/dist/plugin-liveness.js +75 -0
- package/dist/plugin-liveness.js.map +1 -0
- package/dist/plugin-socket.d.ts +32 -0
- package/dist/plugin-socket.d.ts.map +1 -0
- package/dist/plugin-socket.js +97 -0
- package/dist/plugin-socket.js.map +1 -0
- package/dist/ports.d.ts +3 -0
- package/dist/ports.d.ts.map +1 -0
- package/dist/ports.js +22 -0
- package/dist/ports.js.map +1 -0
- package/dist/token.d.ts +3 -0
- package/dist/token.d.ts.map +1 -0
- package/dist/token.js +23 -0
- package/dist/token.js.map +1 -0
- package/dist/tool-contracts.d.ts +13 -0
- package/dist/tool-contracts.d.ts.map +1 -0
- package/dist/tool-contracts.js +1422 -0
- package/dist/tool-contracts.js.map +1 -0
- package/dist/tool-handler.d.ts +23 -0
- package/dist/tool-handler.d.ts.map +1 -0
- package/dist/tool-handler.js +96 -0
- package/dist/tool-handler.js.map +1 -0
- package/dist/validate-args.d.ts +7 -0
- package/dist/validate-args.d.ts.map +1 -0
- package/dist/validate-args.js +37 -0
- package/dist/validate-args.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +4 -0
- package/dist/version.js.map +1 -0
- package/dist/wait-until.d.ts +7 -0
- package/dist/wait-until.d.ts.map +1 -0
- package/dist/wait-until.js +15 -0
- package/dist/wait-until.js.map +1 -0
- package/package.json +73 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
-- Reliable file logging for the plugin.
|
|
2
|
+
--
|
|
3
|
+
-- `LrLogger:enable("logfile")` alone proved unreliable on Windows (issue #134):
|
|
4
|
+
-- its target directory `~/Documents/LrClassicLogs` is not created by us, and on
|
|
5
|
+
-- Windows `Documents` is frequently redirected (OneDrive), so the file lands
|
|
6
|
+
-- somewhere the user cannot find -- or nowhere. We therefore ALSO write our own
|
|
7
|
+
-- copy via io.open to an OS-resolved path we control, create the directory
|
|
8
|
+
-- ourselves, and expose the resolved absolute path so the Plug-in Manager can
|
|
9
|
+
-- show exactly where logs go. The LrLogger channel is kept as a secondary sink.
|
|
10
|
+
--
|
|
11
|
+
-- pcall around `import` keeps this module loadable under the busted specs, whose
|
|
12
|
+
-- mock `import` only stubs the handful of namespaces each spec needs; a missing
|
|
13
|
+
-- LrPathUtils/LrFileUtils there simply disables the file sink (no stray writes).
|
|
14
|
+
local function tryImport(name)
|
|
15
|
+
local ok, mod = pcall(import, name)
|
|
16
|
+
if ok then return mod end
|
|
17
|
+
return nil
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
local LrLogger = tryImport('LrLogger')
|
|
21
|
+
local LrPathUtils = tryImport('LrPathUtils')
|
|
22
|
+
local LrFileUtils = tryImport('LrFileUtils')
|
|
23
|
+
|
|
24
|
+
local lrLogger = LrLogger and LrLogger('LightroomMCP') or nil
|
|
25
|
+
if lrLogger then lrLogger:enable("logfile") end
|
|
26
|
+
|
|
27
|
+
local Log = {}
|
|
28
|
+
|
|
29
|
+
local resolvedPath
|
|
30
|
+
local pathResolved = false
|
|
31
|
+
|
|
32
|
+
-- Resolve (once) the file we own. nil if the SDK path utils are unavailable
|
|
33
|
+
-- (test environment) or the OS path cannot be determined.
|
|
34
|
+
local function filePath()
|
|
35
|
+
if pathResolved then return resolvedPath end
|
|
36
|
+
pathResolved = true
|
|
37
|
+
if not LrPathUtils then return nil end
|
|
38
|
+
local ok, docs = pcall(function() return LrPathUtils.getStandardFilePath("documents") end)
|
|
39
|
+
if not ok or not docs then return nil end
|
|
40
|
+
local dir = LrPathUtils.child(docs, "LrClassicLogs")
|
|
41
|
+
if LrFileUtils then
|
|
42
|
+
pcall(function() LrFileUtils.createAllDirectories(dir) end)
|
|
43
|
+
end
|
|
44
|
+
resolvedPath = LrPathUtils.child(dir, "LightroomMCP.log")
|
|
45
|
+
return resolvedPath
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
Log.filePath = filePath
|
|
49
|
+
|
|
50
|
+
-- Single-file rotation cap. Without it the append-only file grows unbounded
|
|
51
|
+
-- (e.g. if the server reconnect loop ever floods requests) on the same
|
|
52
|
+
-- OneDrive-synced Windows path the file sink exists to reach.
|
|
53
|
+
local MAX_LOG_BYTES = 5 * 1024 * 1024
|
|
54
|
+
|
|
55
|
+
-- Best-effort: when the log passes the cap, move it aside to <path>.1
|
|
56
|
+
-- (replacing any prior roll) and start fresh. Wrapped so a failed rotation
|
|
57
|
+
-- never disrupts logging; skipped in tests where LrFileUtils is absent.
|
|
58
|
+
local function rotateIfLarge(path, size)
|
|
59
|
+
if not size or size <= MAX_LOG_BYTES or not LrFileUtils then return end
|
|
60
|
+
pcall(function()
|
|
61
|
+
local rolled = path .. ".1"
|
|
62
|
+
if LrFileUtils.exists(rolled) then LrFileUtils.delete(rolled) end
|
|
63
|
+
LrFileUtils.move(path, rolled)
|
|
64
|
+
end)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
local function write(level, msg)
|
|
68
|
+
msg = tostring(msg)
|
|
69
|
+
if lrLogger then
|
|
70
|
+
if level == "warn" then
|
|
71
|
+
lrLogger:warn(msg)
|
|
72
|
+
elseif level == "error" then
|
|
73
|
+
lrLogger:error(msg)
|
|
74
|
+
else
|
|
75
|
+
lrLogger:info(msg)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
local path = filePath()
|
|
79
|
+
if not path then return end
|
|
80
|
+
local fh = io.open(path, "a")
|
|
81
|
+
if not fh then return end
|
|
82
|
+
fh:write(os.date("%Y-%m-%d %H:%M:%S") .. " [" .. level .. "] " .. msg .. "\n")
|
|
83
|
+
local size = fh:seek("end")
|
|
84
|
+
fh:close()
|
|
85
|
+
rotateIfLarge(path, size)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
function Log.info(msg) write("info", msg) end
|
|
89
|
+
function Log.warn(msg) write("warn", msg) end
|
|
90
|
+
function Log.error(msg) write("error", msg) end
|
|
91
|
+
|
|
92
|
+
return Log
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
local LrDialogs = import 'LrDialogs'
|
|
2
|
+
|
|
3
|
+
local state = _G.LightroomMCP_State or {}
|
|
4
|
+
|
|
5
|
+
local lines = {
|
|
6
|
+
"Running: " .. tostring(state.running),
|
|
7
|
+
"Request socket connected: " .. tostring(state.receiveConnected),
|
|
8
|
+
"Response socket connected: " .. tostring(state.sendConnected),
|
|
9
|
+
"Last event: " .. tostring(state.lastEvent or "Never"),
|
|
10
|
+
"Requests processed: " .. tostring(state.requestsProcessed or 0),
|
|
11
|
+
"",
|
|
12
|
+
"Recent logs:",
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if state.log then
|
|
16
|
+
local startIdx = math.max(1, #state.log - 30)
|
|
17
|
+
for i = startIdx, #state.log do
|
|
18
|
+
table.insert(lines, " " .. state.log[i])
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
LrDialogs.message("Lightroom MCP Status", table.concat(lines, "\n"), "info")
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
local PhotoLookup = {}
|
|
2
|
+
|
|
3
|
+
-- Resolve a list of photo identifiers to photo objects.
|
|
4
|
+
-- Each id may be a numeric local identifier (string or number) or a file path.
|
|
5
|
+
-- Builds the path index AT MOST ONCE per call, and only when at least one id
|
|
6
|
+
-- missed local-id lookup. Returns a parallel array:
|
|
7
|
+
-- results[i] = { id = inputId, photo = photoOrNil }
|
|
8
|
+
function PhotoLookup.resolveMany(catalog, photoIds)
|
|
9
|
+
local results = {}
|
|
10
|
+
for i, id in ipairs(photoIds) do
|
|
11
|
+
results[i] = { id = id, photo = nil }
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
-- LrCatalog has no findPhotoByLocalIdentifier; one getAllPhotos pass
|
|
15
|
+
-- builds both id and path indexes. localIdentifier is numeric in
|
|
16
|
+
-- production but tests pass strings — normalize via tostring.
|
|
17
|
+
local byLocalId = {}
|
|
18
|
+
local byPath = {}
|
|
19
|
+
for _, p in ipairs(catalog:getAllPhotos()) do
|
|
20
|
+
local lid = p.localIdentifier
|
|
21
|
+
if lid ~= nil then byLocalId[tostring(lid)] = p end
|
|
22
|
+
local path = p:getRawMetadata('path')
|
|
23
|
+
if path ~= nil then byPath[path] = p end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
for i, id in ipairs(photoIds) do
|
|
27
|
+
local photo = byLocalId[tostring(id)]
|
|
28
|
+
if not photo then photo = byPath[id] end
|
|
29
|
+
results[i].photo = photo
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
return results
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
function PhotoLookup.resolveOne(catalog, photoId)
|
|
36
|
+
return PhotoLookup.resolveMany(catalog, { photoId })[1].photo
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
return PhotoLookup
|