@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.
Files changed (99) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +70 -0
  3. package/dist/LightroomMCP.lrplugin/HandlerAI.lua +466 -0
  4. package/dist/LightroomMCP.lrplugin/HandlerAIMasks.lua +549 -0
  5. package/dist/LightroomMCP.lrplugin/HandlerCatalog.lua +256 -0
  6. package/dist/LightroomMCP.lrplugin/HandlerCollections.lua +337 -0
  7. package/dist/LightroomMCP.lrplugin/HandlerDevelop.lua +1668 -0
  8. package/dist/LightroomMCP.lrplugin/HandlerExport.lua +149 -0
  9. package/dist/LightroomMCP.lrplugin/HandlerImport.lua +108 -0
  10. package/dist/LightroomMCP.lrplugin/HandlerLocalAdjustments.lua +330 -0
  11. package/dist/LightroomMCP.lrplugin/HandlerMetadata.lua +169 -0
  12. package/dist/LightroomMCP.lrplugin/HandlerOrganization.lua +783 -0
  13. package/dist/LightroomMCP.lrplugin/HandlerPreview.lua +189 -0
  14. package/dist/LightroomMCP.lrplugin/HandlerSearch.lua +185 -0
  15. package/dist/LightroomMCP.lrplugin/HandlerSelection.lua +302 -0
  16. package/dist/LightroomMCP.lrplugin/HandlerSpots.lua +313 -0
  17. package/dist/LightroomMCP.lrplugin/HandlerWatermark.lua +59 -0
  18. package/dist/LightroomMCP.lrplugin/Info.lua +25 -0
  19. package/dist/LightroomMCP.lrplugin/JSON.lua +270 -0
  20. package/dist/LightroomMCP.lrplugin/Log.lua +92 -0
  21. package/dist/LightroomMCP.lrplugin/MenuShowStatus.lua +22 -0
  22. package/dist/LightroomMCP.lrplugin/PhotoLookup.lua +39 -0
  23. package/dist/LightroomMCP.lrplugin/PluginInfoProvider.lua +865 -0
  24. package/dist/LightroomMCP.lrplugin/PluginInit.lua +48 -0
  25. package/dist/cli.d.ts +6 -0
  26. package/dist/cli.d.ts.map +1 -0
  27. package/dist/cli.js +31 -0
  28. package/dist/cli.js.map +1 -0
  29. package/dist/configs/claude-code.mcp.json +9 -0
  30. package/dist/configs/claude-desktop.json +12 -0
  31. package/dist/configs/generic-stdio-client.json +10 -0
  32. package/dist/configs/mcp-proxy-http.json +9 -0
  33. package/dist/create-server.d.ts +12 -0
  34. package/dist/create-server.d.ts.map +1 -0
  35. package/dist/create-server.js +17 -0
  36. package/dist/create-server.js.map +1 -0
  37. package/dist/deepseek-harness/README.md +112 -0
  38. package/dist/deepseek-harness/deepseek-mcp-harness.mjs +292 -0
  39. package/dist/dispatcher.d.ts +34 -0
  40. package/dist/dispatcher.d.ts.map +1 -0
  41. package/dist/dispatcher.js +72 -0
  42. package/dist/dispatcher.js.map +1 -0
  43. package/dist/heartbeat.d.ts +17 -0
  44. package/dist/heartbeat.d.ts.map +1 -0
  45. package/dist/heartbeat.js +36 -0
  46. package/dist/heartbeat.js.map +1 -0
  47. package/dist/index.d.ts +3 -0
  48. package/dist/index.d.ts.map +1 -0
  49. package/dist/index.js +255 -0
  50. package/dist/index.js.map +1 -0
  51. package/dist/install-plugin.d.ts +16 -0
  52. package/dist/install-plugin.d.ts.map +1 -0
  53. package/dist/install-plugin.js +125 -0
  54. package/dist/install-plugin.js.map +1 -0
  55. package/dist/instance-lock.d.ts +5 -0
  56. package/dist/instance-lock.d.ts.map +1 -0
  57. package/dist/instance-lock.js +77 -0
  58. package/dist/instance-lock.js.map +1 -0
  59. package/dist/list-tools-handler.d.ts +16 -0
  60. package/dist/list-tools-handler.d.ts.map +1 -0
  61. package/dist/list-tools-handler.js +50 -0
  62. package/dist/list-tools-handler.js.map +1 -0
  63. package/dist/plugin-liveness.d.ts +48 -0
  64. package/dist/plugin-liveness.d.ts.map +1 -0
  65. package/dist/plugin-liveness.js +75 -0
  66. package/dist/plugin-liveness.js.map +1 -0
  67. package/dist/plugin-socket.d.ts +32 -0
  68. package/dist/plugin-socket.d.ts.map +1 -0
  69. package/dist/plugin-socket.js +97 -0
  70. package/dist/plugin-socket.js.map +1 -0
  71. package/dist/ports.d.ts +3 -0
  72. package/dist/ports.d.ts.map +1 -0
  73. package/dist/ports.js +22 -0
  74. package/dist/ports.js.map +1 -0
  75. package/dist/token.d.ts +3 -0
  76. package/dist/token.d.ts.map +1 -0
  77. package/dist/token.js +23 -0
  78. package/dist/token.js.map +1 -0
  79. package/dist/tool-contracts.d.ts +13 -0
  80. package/dist/tool-contracts.d.ts.map +1 -0
  81. package/dist/tool-contracts.js +1422 -0
  82. package/dist/tool-contracts.js.map +1 -0
  83. package/dist/tool-handler.d.ts +23 -0
  84. package/dist/tool-handler.d.ts.map +1 -0
  85. package/dist/tool-handler.js +96 -0
  86. package/dist/tool-handler.js.map +1 -0
  87. package/dist/validate-args.d.ts +7 -0
  88. package/dist/validate-args.d.ts.map +1 -0
  89. package/dist/validate-args.js +37 -0
  90. package/dist/validate-args.js.map +1 -0
  91. package/dist/version.d.ts +2 -0
  92. package/dist/version.d.ts.map +1 -0
  93. package/dist/version.js +4 -0
  94. package/dist/version.js.map +1 -0
  95. package/dist/wait-until.d.ts +7 -0
  96. package/dist/wait-until.d.ts.map +1 -0
  97. package/dist/wait-until.js +15 -0
  98. package/dist/wait-until.js.map +1 -0
  99. 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