jishushell 0.6.18 → 0.7.3
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/apps/anythingllm-container.yaml +1 -0
- package/apps/browserless-chromium-container.yaml +1 -0
- package/apps/filebrowser-container.yaml +1 -0
- package/apps/hermes-container.yaml +1 -7
- package/apps/immich-container-lite.yaml +337 -0
- package/apps/immich-container.yaml +371 -0
- package/apps/jishu-kb-container.yaml +26 -21
- package/apps/ollama-binary.yaml +1 -0
- package/apps/ollama-cpu-container.yaml +1 -0
- package/apps/ollama-with-hollama-binary.yaml +2 -0
- package/apps/openclaw-binary.yaml +4 -8
- package/apps/openclaw-container.yaml +1 -7
- package/apps/openclaw-with-ollama-container.yaml +1 -0
- package/apps/openclaw-with-searxng-container.yaml +20 -0
- package/apps/searxng-container.yaml +20 -0
- package/apps/weknora-container.yaml +5 -0
- package/dependencies/jishushell-panel-0.7.3.tgz +0 -0
- package/dist/cli/core.js +1 -1
- package/dist/cli/core.js.map +1 -1
- package/dist/cli/doctor.js +96 -0
- package/dist/cli/doctor.js.map +1 -1
- package/dist/config.d.ts +9 -1
- package/dist/config.js +72 -2
- package/dist/config.js.map +1 -1
- package/dist/install.js +60 -19
- package/dist/install.js.map +1 -1
- package/dist/routes/admin.d.ts +2 -0
- package/dist/routes/admin.js +72 -0
- package/dist/routes/admin.js.map +1 -0
- package/dist/routes/docker.d.ts +2 -0
- package/dist/routes/docker.js +58 -0
- package/dist/routes/docker.js.map +1 -0
- package/dist/routes/file-mounts.js +5 -8
- package/dist/routes/file-mounts.js.map +1 -1
- package/dist/routes/instances.d.ts +0 -14
- package/dist/routes/instances.js +44 -1184
- package/dist/routes/instances.js.map +1 -1
- package/dist/server.d.ts +6 -0
- package/dist/server.js +53 -20
- package/dist/server.js.map +1 -1
- package/dist/services/app-common/catalog-service.js +15 -5
- package/dist/services/app-common/catalog-service.js.map +1 -1
- package/dist/services/app-common/delete-service.js +5 -0
- package/dist/services/app-common/delete-service.js.map +1 -1
- package/dist/services/app-common/instance-store.js +3 -0
- package/dist/services/app-common/instance-store.js.map +1 -1
- package/dist/services/app-common/lifecycle-service.js +12 -4
- package/dist/services/app-common/lifecycle-service.js.map +1 -1
- package/dist/services/app-common/ownership.d.ts +3 -0
- package/dist/services/app-common/ownership.js +11 -0
- package/dist/services/app-common/ownership.js.map +1 -0
- package/dist/services/app-common/runtime-facts.js +2 -0
- package/dist/services/app-common/runtime-facts.js.map +1 -1
- package/dist/services/app-common/spec-materializer.d.ts +0 -1
- package/dist/services/app-common/spec-materializer.js +21 -87
- package/dist/services/app-common/spec-materializer.js.map +1 -1
- package/dist/services/app-common/status-refresh.js +25 -13
- package/dist/services/app-common/status-refresh.js.map +1 -1
- package/dist/services/app-modules/browserless/routes.js +5 -3
- package/dist/services/app-modules/browserless/routes.js.map +1 -1
- package/dist/services/capabilities/contract.d.ts +1 -2
- package/dist/services/capabilities/contract.js +0 -10
- package/dist/services/capabilities/contract.js.map +1 -1
- package/dist/services/capabilities/endpoint-validator.js +0 -1
- package/dist/services/capabilities/endpoint-validator.js.map +1 -1
- package/dist/services/capabilities/health.js +1 -1
- package/dist/services/capabilities/health.js.map +1 -1
- package/dist/services/capability-proxy/http.d.ts +7 -0
- package/dist/services/capability-proxy/http.js +555 -0
- package/dist/services/capability-proxy/http.js.map +1 -0
- package/dist/services/capability-proxy/terminal.d.ts +4 -0
- package/dist/services/capability-proxy/terminal.js +179 -0
- package/dist/services/capability-proxy/terminal.js.map +1 -0
- package/dist/services/connections/admin.js +19 -9
- package/dist/services/connections/admin.js.map +1 -1
- package/dist/services/connections/apply.d.ts +3 -9
- package/dist/services/connections/apply.js +0 -29
- package/dist/services/connections/apply.js.map +1 -1
- package/dist/services/connections/transactor.js +2 -2
- package/dist/services/connections/transactor.js.map +1 -1
- package/dist/services/files/bootstrap.d.ts +7 -0
- package/dist/services/files/bootstrap.js +16 -0
- package/dist/services/files/bootstrap.js.map +1 -0
- package/dist/services/files/photos/upload-page.d.ts +2 -0
- package/dist/services/files/photos/upload-page.js +248 -0
- package/dist/services/files/photos/upload-page.js.map +1 -0
- package/dist/services/files/photos/upload-store.d.ts +74 -0
- package/dist/services/files/photos/upload-store.js +432 -0
- package/dist/services/files/photos/upload-store.js.map +1 -0
- package/dist/services/http/proxy-utils.d.ts +7 -0
- package/dist/services/http/proxy-utils.js +29 -0
- package/dist/services/http/proxy-utils.js.map +1 -0
- package/dist/services/http/request-utils.d.ts +3 -0
- package/dist/services/http/request-utils.js +23 -0
- package/dist/services/http/request-utils.js.map +1 -0
- package/dist/services/instances/manager.d.ts +6 -5
- package/dist/services/instances/manager.js +45 -51
- package/dist/services/instances/manager.js.map +1 -1
- package/dist/services/instances/pairing.d.ts +17 -0
- package/dist/services/instances/pairing.js +53 -0
- package/dist/services/instances/pairing.js.map +1 -0
- package/dist/services/instances/status.d.ts +2 -0
- package/dist/services/instances/status.js +11 -0
- package/dist/services/instances/status.js.map +1 -0
- package/dist/services/integrations/hermes/integration.d.ts +1 -1
- package/dist/services/integrations/hermes/integration.js +7 -8
- package/dist/services/integrations/hermes/integration.js.map +1 -1
- package/dist/services/integrations/immich/client.d.ts +93 -0
- package/dist/services/integrations/immich/client.js +458 -0
- package/dist/services/integrations/immich/client.js.map +1 -0
- package/dist/services/integrations/immich/config.d.ts +15 -0
- package/dist/services/integrations/immich/config.js +178 -0
- package/dist/services/integrations/immich/config.js.map +1 -0
- package/dist/services/integrations/immich/discovery.d.ts +9 -0
- package/dist/services/integrations/immich/discovery.js +101 -0
- package/dist/services/integrations/immich/discovery.js.map +1 -0
- package/dist/services/integrations/immich/gallery-renderer.d.ts +5 -0
- package/dist/services/integrations/immich/gallery-renderer.js +150 -0
- package/dist/services/integrations/immich/gallery-renderer.js.map +1 -0
- package/dist/services/integrations/immich/immich-shim.d.ts +11 -0
- package/dist/services/integrations/immich/immich-shim.js +439 -0
- package/dist/services/integrations/immich/immich-shim.js.map +1 -0
- package/dist/services/integrations/immich/integration.d.ts +18 -0
- package/dist/services/integrations/immich/integration.js +64 -0
- package/dist/services/integrations/immich/integration.js.map +1 -0
- package/dist/services/integrations/immich/photo-library.d.ts +4 -0
- package/dist/services/integrations/immich/photo-library.js +63 -0
- package/dist/services/integrations/immich/photo-library.js.map +1 -0
- package/dist/services/integrations/immich/review-executor.d.ts +3 -0
- package/dist/services/integrations/immich/review-executor.js +41 -0
- package/dist/services/integrations/immich/review-executor.js.map +1 -0
- package/dist/services/integrations/immich/review-session-service.d.ts +27 -0
- package/dist/services/integrations/immich/review-session-service.js +206 -0
- package/dist/services/integrations/immich/review-session-service.js.map +1 -0
- package/dist/services/integrations/immich/review-store.d.ts +47 -0
- package/dist/services/integrations/immich/review-store.js +347 -0
- package/dist/services/integrations/immich/review-store.js.map +1 -0
- package/dist/services/integrations/immich/routes.d.ts +7 -0
- package/dist/services/integrations/immich/routes.js +363 -0
- package/dist/services/integrations/immich/routes.js.map +1 -0
- package/dist/services/integrations/immich/types.d.ts +186 -0
- package/dist/services/integrations/immich/types.js +2 -0
- package/dist/services/integrations/immich/types.js.map +1 -0
- package/dist/services/integrations/index.d.ts +1 -0
- package/dist/services/integrations/index.js +1 -0
- package/dist/services/integrations/index.js.map +1 -1
- package/dist/services/integrations/installable/installers/integration.js +113 -7
- package/dist/services/integrations/installable/installers/integration.js.map +1 -1
- package/dist/services/integrations/jishukb/integration.d.ts +3 -1
- package/dist/services/integrations/jishukb/integration.js +121 -10
- package/dist/services/integrations/jishukb/integration.js.map +1 -1
- package/dist/services/integrations/openclaw/integration.d.ts +21 -7
- package/dist/services/integrations/openclaw/integration.js +385 -158
- package/dist/services/integrations/openclaw/integration.js.map +1 -1
- package/dist/services/integrations/openclaw/jishukb-native-mcp.d.ts +58 -0
- package/dist/services/integrations/openclaw/jishukb-native-mcp.js +373 -0
- package/dist/services/integrations/openclaw/jishukb-native-mcp.js.map +1 -0
- package/dist/services/integrations/openclaw/jishukb-shim.d.ts +8 -4
- package/dist/services/integrations/openclaw/jishukb-shim.js +624 -17
- package/dist/services/integrations/openclaw/jishukb-shim.js.map +1 -1
- package/dist/services/integrations/openclaw/mcporter.d.ts +13 -0
- package/dist/services/integrations/openclaw/mcporter.js +31 -0
- package/dist/services/integrations/openclaw/mcporter.js.map +1 -1
- package/dist/services/integrations/openclaw/native-mcp.d.ts +48 -0
- package/dist/services/integrations/openclaw/native-mcp.js +125 -0
- package/dist/services/integrations/openclaw/native-mcp.js.map +1 -0
- package/dist/services/integrations/openclaw/routes.js +4 -1
- package/dist/services/integrations/openclaw/routes.js.map +1 -1
- package/dist/services/integrations/types.d.ts +5 -17
- package/dist/services/repair/runtime-repair.js +68 -1
- package/dist/services/repair/runtime-repair.js.map +1 -1
- package/dist/services/runtime/docker-network.d.ts +8 -0
- package/dist/services/runtime/docker-network.js +123 -0
- package/dist/services/runtime/docker-network.js.map +1 -0
- package/dist/services/runtime/driver-registry.d.ts +4 -0
- package/dist/services/runtime/driver-registry.js.map +1 -1
- package/dist/services/runtime/drivers/nomad.d.ts +1 -0
- package/dist/services/runtime/drivers/nomad.js +35 -5
- package/dist/services/runtime/drivers/nomad.js.map +1 -1
- package/dist/services/runtime/service-manager.d.ts +2 -0
- package/dist/services/runtime/service-manager.js +18 -0
- package/dist/services/runtime/service-manager.js.map +1 -0
- package/dist/services/runtime/types.d.ts +1 -0
- package/dist/services/runtime/workload-compiler.js +29 -4
- package/dist/services/runtime/workload-compiler.js.map +1 -1
- package/dist/services/setup/setup-manager.js +29 -73
- package/dist/services/setup/setup-manager.js.map +1 -1
- package/dist/services/system/runtime-ownership.d.ts +36 -0
- package/dist/services/system/runtime-ownership.js +250 -0
- package/dist/services/system/runtime-ownership.js.map +1 -0
- package/dist/services/system/system-reconciler.js +53 -0
- package/dist/services/system/system-reconciler.js.map +1 -1
- package/dist/types.d.ts +19 -3
- package/dist/utils/path-safety.js +1 -1
- package/dist/utils/service-user.d.ts +13 -0
- package/dist/utils/service-user.js +129 -0
- package/dist/utils/service-user.js.map +1 -0
- package/install/jishu-install.sh +0 -1
- package/node_modules/brace-expansion/dist/commonjs/index.js +24 -14
- package/node_modules/brace-expansion/dist/commonjs/index.js.map +1 -1
- package/node_modules/brace-expansion/dist/esm/index.js +24 -14
- package/node_modules/brace-expansion/dist/esm/index.js.map +1 -1
- package/node_modules/brace-expansion/package.json +2 -2
- package/node_modules/fast-uri/index.js +1 -1
- package/node_modules/fast-uri/package.json +1 -1
- package/node_modules/fast-uri/test/security.test.js +28 -0
- package/node_modules/fastify/SECURITY.md +1 -1
- package/node_modules/fastify/SPONSORS.md +6 -4
- package/node_modules/fastify/docs/Guides/Database.md +0 -28
- package/node_modules/fastify/docs/Guides/Ecosystem.md +13 -2
- package/node_modules/fastify/docs/Guides/Serverless.md +2 -2
- package/node_modules/fastify/docs/Guides/Write-Plugin.md +1 -1
- package/node_modules/fastify/docs/Reference/Encapsulation.md +27 -26
- package/node_modules/fastify/docs/Reference/Errors.md +10 -4
- package/node_modules/fastify/docs/Reference/HTTP2.md +10 -10
- package/node_modules/fastify/docs/Reference/Hooks.md +4 -4
- package/node_modules/fastify/docs/Reference/Index.md +14 -16
- package/node_modules/fastify/docs/Reference/LTS.md +12 -13
- package/node_modules/fastify/docs/Reference/Lifecycle.md +9 -8
- package/node_modules/fastify/docs/Reference/Logging.md +44 -39
- package/node_modules/fastify/docs/Reference/Middleware.md +21 -25
- package/node_modules/fastify/docs/Reference/Principles.md +2 -2
- package/node_modules/fastify/docs/Reference/Reply.md +6 -1
- package/node_modules/fastify/docs/Reference/Request.md +27 -16
- package/node_modules/fastify/docs/Reference/Routes.md +5 -2
- package/node_modules/fastify/docs/Reference/Server.md +31 -3
- package/node_modules/fastify/docs/Reference/Type-Providers.md +29 -5
- package/node_modules/fastify/docs/Reference/Validation-and-Serialization.md +15 -2
- package/node_modules/fastify/docs/Reference/Warnings.md +7 -6
- package/node_modules/fastify/eslint.config.js +7 -2
- package/node_modules/fastify/fastify.d.ts +8 -3
- package/node_modules/fastify/fastify.js +43 -14
- package/node_modules/fastify/lib/content-type-parser.js +13 -1
- package/node_modules/fastify/lib/decorate.js +11 -3
- package/node_modules/fastify/lib/error-handler.js +4 -3
- package/node_modules/fastify/lib/error-serializer.js +59 -59
- package/node_modules/fastify/lib/errors.js +16 -1
- package/node_modules/fastify/lib/four-oh-four.js +14 -9
- package/node_modules/fastify/lib/handle-request.js +11 -5
- package/node_modules/fastify/lib/plugin-override.js +2 -1
- package/node_modules/fastify/lib/plugin-utils.js +5 -5
- package/node_modules/fastify/lib/reply.js +63 -8
- package/node_modules/fastify/lib/request.js +14 -4
- package/node_modules/fastify/lib/route.js +20 -6
- package/node_modules/fastify/lib/schema-controller.js +1 -1
- package/node_modules/fastify/lib/schemas.js +37 -30
- package/node_modules/fastify/lib/symbols.js +3 -1
- package/node_modules/fastify/lib/validation.js +1 -13
- package/node_modules/fastify/lib/warnings.js +3 -3
- package/node_modules/fastify/package.json +13 -15
- package/node_modules/fastify/scripts/validate-ecosystem-links.js +1 -0
- package/node_modules/fastify/test/bundler/esbuild/package.json +1 -1
- package/node_modules/fastify/test/close-pipelining.test.js +1 -2
- package/node_modules/fastify/test/custom-http-server.test.js +38 -0
- package/node_modules/fastify/test/decorator-instance-properties.test.js +63 -0
- package/node_modules/fastify/test/diagnostics-channel/async-error-handler.test.js +74 -0
- package/node_modules/fastify/test/hooks.test.js +23 -0
- package/node_modules/fastify/test/http-methods/get.test.js +1 -1
- package/node_modules/fastify/test/http2/plain.test.js +135 -0
- package/node_modules/fastify/test/http2/secure-with-fallback.test.js +1 -1
- package/node_modules/fastify/test/https/https.test.js +1 -2
- package/node_modules/fastify/test/internals/errors.test.js +31 -1
- package/node_modules/fastify/test/internals/plugin.test.js +3 -1
- package/node_modules/fastify/test/internals/request.test.js +27 -3
- package/node_modules/fastify/test/internals/schema-controller-perf.test.js +33 -0
- package/node_modules/fastify/test/logger/logging.test.js +18 -1
- package/node_modules/fastify/test/logger/options.test.js +38 -1
- package/node_modules/fastify/test/reply-error.test.js +1 -1
- package/node_modules/fastify/test/reply-trailers.test.js +70 -0
- package/node_modules/fastify/test/request-media-type.test.js +105 -0
- package/node_modules/fastify/test/route-prefix.test.js +34 -0
- package/node_modules/fastify/test/router-options.test.js +222 -11
- package/node_modules/fastify/test/schema-serialization.test.js +108 -0
- package/node_modules/fastify/test/schema-validation.test.js +24 -0
- package/node_modules/fastify/test/scripts/validate-ecosystem-links.test.js +40 -57
- package/node_modules/fastify/test/throw.test.js +14 -0
- package/node_modules/fastify/test/trust-proxy.test.js +21 -0
- package/node_modules/fastify/test/types/content-type-parser.tst.ts +70 -0
- package/node_modules/fastify/test/types/{decorate-request-reply.test-d.ts → decorate-request-reply.tst.ts} +4 -4
- package/node_modules/fastify/test/types/{dummy-plugin.ts → dummy-plugin.mts} +1 -1
- package/node_modules/fastify/test/types/errors.tst.ts +91 -0
- package/node_modules/fastify/test/types/fastify.tst.ts +351 -0
- package/node_modules/fastify/test/types/hooks.tst.ts +578 -0
- package/node_modules/fastify/test/types/instance.tst.ts +597 -0
- package/node_modules/fastify/test/types/{logger.test-d.ts → logger.tst.ts} +61 -62
- package/node_modules/fastify/test/types/plugin.tst.ts +96 -0
- package/node_modules/fastify/test/types/register.tst.ts +245 -0
- package/node_modules/fastify/test/types/reply.tst.ts +297 -0
- package/node_modules/fastify/test/types/request.tst.ts +199 -0
- package/node_modules/fastify/test/types/route.tst.ts +576 -0
- package/node_modules/fastify/test/types/{schema.test-d.ts → schema.tst.ts} +22 -22
- package/node_modules/fastify/test/types/{serverFactory.test-d.ts → serverFactory.tst.ts} +4 -4
- package/node_modules/fastify/test/types/tsconfig.json +9 -0
- package/node_modules/fastify/test/types/{type-provider.test-d.ts → type-provider.tst.ts} +256 -250
- package/node_modules/fastify/test/types/using.tst.ts +14 -0
- package/node_modules/fastify/types/errors.d.ts +3 -0
- package/node_modules/fastify/types/request.d.ts +23 -2
- package/node_modules/jishushell-panel/output/public/assets/{ApiKeyField-NKcbHjNz.js → ApiKeyField-Ce5d1xna.js} +1 -1
- package/node_modules/jishushell-panel/output/public/assets/{Dashboard-Da1fL38t.js → Dashboard-BXame3yg.js} +1 -1
- package/node_modules/jishushell-panel/output/public/assets/{HermesChatPanel-DZvmYsoh.js → HermesChatPanel-BHZtPCJd.js} +1 -1
- package/node_modules/jishushell-panel/output/public/assets/{HermesConfigForm-BLUWlKwm.js → HermesConfigForm-CB3GbNX9.js} +1 -1
- package/node_modules/jishushell-panel/output/public/assets/{InitPassword-BAKsshzk.js → InitPassword-Boab9F6g.js} +1 -1
- package/node_modules/jishushell-panel/output/public/assets/InstanceDetail-DrIWCqo-.js +14 -0
- package/node_modules/jishushell-panel/output/public/assets/{Login-DHeOmwI8.js → Login-CzpOkNau.js} +1 -1
- package/node_modules/jishushell-panel/output/public/assets/NewInstance-CANXyCcL.js +1 -0
- package/node_modules/jishushell-panel/output/public/assets/{ProviderRecommendations-H0ByEYF0.js → ProviderRecommendations-BABo9VOC.js} +1 -1
- package/node_modules/jishushell-panel/output/public/assets/Settings-CKp5XxFh.js +1 -0
- package/node_modules/jishushell-panel/output/public/assets/Setup-C7xVDPow.js +1 -0
- package/node_modules/jishushell-panel/output/public/assets/{WeixinLoginPanel-D-T6BxkQ.js → WeixinLoginPanel-B765Xz4C.js} +1 -1
- package/node_modules/jishushell-panel/output/public/assets/{index-ERt6_ngA.js → index-Bs6DSbiR.js} +6 -6
- package/node_modules/jishushell-panel/output/public/assets/{registry-DF93EzIb.js → registry-sWIZsIEF.js} +2 -2
- package/node_modules/jishushell-panel/output/public/assets/{usePolling-DeoThIQn.js → usePolling-D4IDOQd_.js} +1 -1
- package/node_modules/jishushell-panel/output/public/assets/vendor-i18n-Df8aUdv8.js +1 -0
- package/node_modules/jishushell-panel/output/public/assets/{vendor-react-Cc84NArf.js → vendor-react-0L0rjmYG.js} +3 -3
- package/node_modules/jishushell-panel/output/public/index.html +3 -3
- package/node_modules/jishushell-panel/package.json +1 -1
- package/node_modules/semver/classes/range.js +6 -2
- package/node_modules/semver/package.json +1 -1
- package/package.json +4 -4
- package/scripts/check-app-spec.mjs +4 -12
- package/scripts/check-architecture-boundaries.mjs +178 -0
- package/scripts/pack-gui-and-send-pi.sh +5 -3
- package/dependencies/jishushell-panel-0.6.18.tgz +0 -0
- package/dist/services/connections/suggestions.d.ts +0 -27
- package/dist/services/connections/suggestions.js +0 -124
- package/dist/services/connections/suggestions.js.map +0 -1
- package/node_modules/fastify/test/types/content-type-parser.test-d.ts +0 -72
- package/node_modules/fastify/test/types/errors.test-d.ts +0 -90
- package/node_modules/fastify/test/types/fastify.test-d.ts +0 -352
- package/node_modules/fastify/test/types/hooks.test-d.ts +0 -550
- package/node_modules/fastify/test/types/import.ts +0 -2
- package/node_modules/fastify/test/types/instance.test-d.ts +0 -588
- package/node_modules/fastify/test/types/plugin.test-d.ts +0 -97
- package/node_modules/fastify/test/types/register.test-d.ts +0 -237
- package/node_modules/fastify/test/types/reply.test-d.ts +0 -254
- package/node_modules/fastify/test/types/request.test-d.ts +0 -188
- package/node_modules/fastify/test/types/route.test-d.ts +0 -553
- package/node_modules/fastify/test/types/using.test-d.ts +0 -17
- package/node_modules/jishushell-panel/output/public/assets/InstanceDetail-Dgyc_TX5.js +0 -14
- package/node_modules/jishushell-panel/output/public/assets/NewInstance-CIy0cYtp.js +0 -1
- package/node_modules/jishushell-panel/output/public/assets/Settings-DAT-UMfP.js +0 -1
- package/node_modules/jishushell-panel/output/public/assets/Setup-g3uckFYR.js +0 -1
- package/node_modules/jishushell-panel/output/public/assets/vendor-i18n-CS8DFbkQ.js +0 -1
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
import { IMMICH_DEFAULT_PHOTO_LIBRARY_NAME, IMMICH_DEFAULT_PHOTO_LIBRARY_PATH, } from "./photo-library.js";
|
|
2
|
+
export const IMMICH_MCP_SHIM_VERSION = "0.3.0";
|
|
3
|
+
export const IMMICH_SHIM_SECRET_FILENAME = "secret.json";
|
|
4
|
+
export { IMMICH_DEFAULT_PHOTO_LIBRARY_NAME, IMMICH_DEFAULT_PHOTO_LIBRARY_PATH, } from "./photo-library.js";
|
|
5
|
+
export const IMMICH_PUBLIC_MCP_TOOL_NAMES = [
|
|
6
|
+
"ping",
|
|
7
|
+
"get_library_summary",
|
|
8
|
+
"get_photo_library_status",
|
|
9
|
+
"scan_photo_library",
|
|
10
|
+
"create_photo_upload_session",
|
|
11
|
+
"get_asset_info",
|
|
12
|
+
"list_assets",
|
|
13
|
+
"search_metadata",
|
|
14
|
+
"search_smart",
|
|
15
|
+
"get_map_markers",
|
|
16
|
+
"list_albums",
|
|
17
|
+
"get_album",
|
|
18
|
+
"create_album",
|
|
19
|
+
"update_album",
|
|
20
|
+
"delete_album",
|
|
21
|
+
"list_tags",
|
|
22
|
+
"get_tag",
|
|
23
|
+
"create_tag",
|
|
24
|
+
"update_tag",
|
|
25
|
+
"delete_tag",
|
|
26
|
+
"generate_review_gallery",
|
|
27
|
+
"restore_assets",
|
|
28
|
+
"get_duplicates",
|
|
29
|
+
"list_people",
|
|
30
|
+
"search_people",
|
|
31
|
+
"get_person",
|
|
32
|
+
"get_asset_faces",
|
|
33
|
+
];
|
|
34
|
+
export const IMMICH_MCP_SHIM_SOURCE = `#!/usr/bin/env node
|
|
35
|
+
// jishushell-immich-shim — generated, do not edit by hand.
|
|
36
|
+
// Source: src/services/integrations/immich/immich-shim.ts
|
|
37
|
+
|
|
38
|
+
const fs = require("node:fs");
|
|
39
|
+
const path = require("node:path");
|
|
40
|
+
const readline = require("node:readline");
|
|
41
|
+
|
|
42
|
+
const VERSION = ${JSON.stringify(IMMICH_MCP_SHIM_VERSION)};
|
|
43
|
+
const SECRET_FILENAME = ${JSON.stringify(IMMICH_SHIM_SECRET_FILENAME)};
|
|
44
|
+
const BASE_URL_LIT = "__JS_IMMICH_BASE_URL__";
|
|
45
|
+
const CORE_URL_LIT = "__JS_CORE_URL__";
|
|
46
|
+
const INSTANCE_ID_LIT = "__JS_INSTANCE_ID__";
|
|
47
|
+
const PHOTO_LIBRARY_ENABLED = __JS_IMMICH_PHOTO_LIBRARY_ENABLED__;
|
|
48
|
+
const PHOTO_LIBRARY_NAME = ${JSON.stringify(IMMICH_DEFAULT_PHOTO_LIBRARY_NAME)};
|
|
49
|
+
const PHOTO_LIBRARY_PATH = ${JSON.stringify(IMMICH_DEFAULT_PHOTO_LIBRARY_PATH)};
|
|
50
|
+
|
|
51
|
+
const BASE_URL = (BASE_URL_LIT.startsWith("__JS_") ? (process.env.IMMICH_BASE_URL || "") : BASE_URL_LIT).replace(/\\/+$/, "");
|
|
52
|
+
const CORE_URL = (CORE_URL_LIT.startsWith("__JS_") ? (process.env.JISHUSHELL_CORE_URL || "") : CORE_URL_LIT).replace(/\\/+$/, "");
|
|
53
|
+
const INSTANCE_ID = INSTANCE_ID_LIT.startsWith("__JS_") ? (process.env.JISHUSHELL_INSTANCE_ID || "") : INSTANCE_ID_LIT;
|
|
54
|
+
|
|
55
|
+
function loadSecret() {
|
|
56
|
+
try {
|
|
57
|
+
const secretPath = path.join(__dirname, SECRET_FILENAME);
|
|
58
|
+
const raw = fs.readFileSync(secretPath, "utf-8");
|
|
59
|
+
const json = JSON.parse(raw);
|
|
60
|
+
return json && typeof json === "object" ? json : {};
|
|
61
|
+
} catch (_) {}
|
|
62
|
+
return {};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function loadApiKey() {
|
|
66
|
+
const secret = loadSecret();
|
|
67
|
+
if (typeof secret.apiKey === "string" && secret.apiKey.trim()) return secret.apiKey.trim();
|
|
68
|
+
return process.env.IMMICH_API_KEY || process.env.JISHUSHELL_DEV_IMMICH_API_KEY || "";
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function loadInternalToken() {
|
|
72
|
+
const secret = loadSecret();
|
|
73
|
+
if (typeof secret.internalToken === "string" && secret.internalToken.trim()) return secret.internalToken.trim();
|
|
74
|
+
return process.env.JISHUSHELL_INTERNAL_TOKEN || "";
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function envCheck() {
|
|
78
|
+
const missing = [];
|
|
79
|
+
if (!BASE_URL) missing.push("IMMICH_BASE_URL");
|
|
80
|
+
if (!loadApiKey()) missing.push("IMMICH_API_KEY");
|
|
81
|
+
if (missing.length) throw new Error("immich-shim: missing config: " + missing.join(", "));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async function coreSend(method, requestPath, body) {
|
|
85
|
+
const internalToken = loadInternalToken();
|
|
86
|
+
if (!CORE_URL || !internalToken || !INSTANCE_ID) {
|
|
87
|
+
throw new Error("Core-backed Immich interaction is unavailable because the Core connection is missing");
|
|
88
|
+
}
|
|
89
|
+
const response = await fetch(CORE_URL + requestPath, {
|
|
90
|
+
method,
|
|
91
|
+
headers: {
|
|
92
|
+
"content-type": "application/json",
|
|
93
|
+
"X-Jishushell-Internal-Token": internalToken,
|
|
94
|
+
"X-Jishushell-Instance": INSTANCE_ID,
|
|
95
|
+
},
|
|
96
|
+
body: body === undefined ? undefined : JSON.stringify(body),
|
|
97
|
+
});
|
|
98
|
+
const text = await response.text();
|
|
99
|
+
let parsed;
|
|
100
|
+
try { parsed = text ? JSON.parse(text) : null; } catch (_) { parsed = { error:text }; }
|
|
101
|
+
if (!response.ok) {
|
|
102
|
+
throw new Error(parsed && (parsed.error || parsed.detail) || ("Core request failed: HTTP " + response.status));
|
|
103
|
+
}
|
|
104
|
+
return parsed;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function jsonText(value) { return JSON.stringify(value, null, 2); }
|
|
108
|
+
function clampInt(value, fallback, min, max) {
|
|
109
|
+
const n = Number(value);
|
|
110
|
+
if (!Number.isFinite(n)) return fallback;
|
|
111
|
+
return Math.max(min, Math.min(max, Math.floor(n)));
|
|
112
|
+
}
|
|
113
|
+
function compactAsset(a) {
|
|
114
|
+
const exif = a && typeof a.exifInfo === "object" ? a.exifInfo : {};
|
|
115
|
+
return {
|
|
116
|
+
id: String(a && a.id || ""),
|
|
117
|
+
type: a && a.type,
|
|
118
|
+
originalFileName: a && a.originalFileName,
|
|
119
|
+
fileCreatedAt: a && a.fileCreatedAt,
|
|
120
|
+
fileModifiedAt: a && a.fileModifiedAt,
|
|
121
|
+
isFavorite: a && a.isFavorite,
|
|
122
|
+
isArchived: a && a.isArchived,
|
|
123
|
+
isTrashed: a && a.isTrashed,
|
|
124
|
+
score: a && typeof a.score === "number" ? a.score : a && typeof a.similarity === "number" ? a.similarity : undefined,
|
|
125
|
+
exifInfo: {
|
|
126
|
+
city: exif.city,
|
|
127
|
+
state: exif.state,
|
|
128
|
+
country: exif.country,
|
|
129
|
+
make: exif.make,
|
|
130
|
+
model: exif.model,
|
|
131
|
+
latitude: exif.latitude,
|
|
132
|
+
longitude: exif.longitude,
|
|
133
|
+
dateTimeOriginal: exif.dateTimeOriginal,
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
function assetList(payload) {
|
|
138
|
+
const root = payload && typeof payload === "object" ? payload : {};
|
|
139
|
+
const node = root.assets && typeof root.assets === "object" ? root.assets : root;
|
|
140
|
+
const items = Array.isArray(node.items) ? node.items : Array.isArray(root.items) ? root.items : Array.isArray(payload) ? payload : [];
|
|
141
|
+
return { items: items.map(compactAsset), total: node.total, page: node.page, size: node.size, nextPage: node.nextPage || null };
|
|
142
|
+
}
|
|
143
|
+
function compactAlbum(album, includeAssets) {
|
|
144
|
+
return {
|
|
145
|
+
id: String(album && album.id || ""),
|
|
146
|
+
albumName: String(album && album.albumName || ""),
|
|
147
|
+
description: album && album.description,
|
|
148
|
+
assetCount: album && album.assetCount,
|
|
149
|
+
createdAt: album && album.createdAt,
|
|
150
|
+
updatedAt: album && album.updatedAt,
|
|
151
|
+
...(includeAssets ? { assets: (Array.isArray(album && album.assets) ? album.assets : []).map(compactAsset) } : {}),
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
function compactTag(tag) {
|
|
155
|
+
return { id: String(tag && tag.id || ""), name: String(tag && (tag.name || tag.value) || ""), color: tag && tag.color, value: tag && tag.value };
|
|
156
|
+
}
|
|
157
|
+
function compactPerson(person) {
|
|
158
|
+
return { id: String(person && person.id || ""), name: person && person.name, birthDate: person && person.birthDate, isHidden: person && person.isHidden, assetCount: person && person.assetCount };
|
|
159
|
+
}
|
|
160
|
+
function searchBody(args) {
|
|
161
|
+
const a = args || {};
|
|
162
|
+
const body = { page: clampInt(a.page, 1, 1, 10000), size: clampInt(a.size, 50, 1, 100) };
|
|
163
|
+
const stringFields = [["city","city"],["state","state"],["country","country"],["make","make"],["model","model"],["type","asset_type"],["takenAfter","taken_after"],["takenBefore","taken_before"],["fileCreatedAfter","file_created_after"],["fileCreatedBefore","file_created_before"]];
|
|
164
|
+
for (const [outKey, inKey] of stringFields) if (typeof a[inKey] === "string" && a[inKey].trim()) body[outKey] = a[inKey].trim();
|
|
165
|
+
const boolFields = [["isFavorite","is_favorite"],["isArchived","is_archived"],["isTrashed","is_trashed"]];
|
|
166
|
+
for (const [outKey, inKey] of boolFields) if (typeof a[inKey] === "boolean") body[outKey] = a[inKey];
|
|
167
|
+
if (Array.isArray(a.tag_ids)) body.tagIds = a.tag_ids.filter((v) => typeof v === "string" && v.trim());
|
|
168
|
+
return body;
|
|
169
|
+
}
|
|
170
|
+
function smartSearchBody(args) {
|
|
171
|
+
const a = args || {};
|
|
172
|
+
const body = { page: clampInt(a.page, 1, 1, 10000), size: clampInt(a.size, 50, 1, 100) };
|
|
173
|
+
const stringFields = [["query","query"],["queryAssetId","query_asset_id"],["city","city"],["state","state"],["country","country"],["takenAfter","taken_after"],["takenBefore","taken_before"]];
|
|
174
|
+
for (const [outKey, inKey] of stringFields) if (typeof a[inKey] === "string" && a[inKey].trim()) body[outKey] = a[inKey].trim();
|
|
175
|
+
if (Array.isArray(a.person_ids)) body.personIds = a.person_ids.filter((v) => typeof v === "string" && v.trim());
|
|
176
|
+
if (!body.query && !body.queryAssetId) throw new Error("search_smart requires query or query_asset_id");
|
|
177
|
+
return body;
|
|
178
|
+
}
|
|
179
|
+
async function immich(method, apiPath, opts) {
|
|
180
|
+
envCheck();
|
|
181
|
+
const apiKey = loadApiKey();
|
|
182
|
+
const url = new URL("/api" + apiPath, BASE_URL + "/");
|
|
183
|
+
for (const [key, value] of Object.entries((opts && opts.params) || {})) {
|
|
184
|
+
if (value !== undefined) url.searchParams.set(key, String(value));
|
|
185
|
+
}
|
|
186
|
+
const headers = { accept: (opts && opts.accept) || "application/json", "x-api-key": apiKey };
|
|
187
|
+
const init = { method, headers };
|
|
188
|
+
if (opts && Object.prototype.hasOwnProperty.call(opts, "body")) {
|
|
189
|
+
headers["content-type"] = "application/json";
|
|
190
|
+
init.body = JSON.stringify(opts.body);
|
|
191
|
+
}
|
|
192
|
+
const ctrl = new AbortController();
|
|
193
|
+
const t = setTimeout(() => ctrl.abort(), 30000);
|
|
194
|
+
init.signal = ctrl.signal;
|
|
195
|
+
try {
|
|
196
|
+
const res = await fetch(url.toString(), init);
|
|
197
|
+
if (!res.ok) {
|
|
198
|
+
const text = (await res.text()).slice(0, 400).split(apiKey).join("[redacted]");
|
|
199
|
+
throw new Error("Immich " + method + " " + apiPath + " failed: HTTP " + res.status + (text ? ": " + text : ""));
|
|
200
|
+
}
|
|
201
|
+
if ((opts && opts.raw) || res.status === 204) return res;
|
|
202
|
+
const text = await res.text();
|
|
203
|
+
return text.trim() ? JSON.parse(text) : null;
|
|
204
|
+
} finally {
|
|
205
|
+
clearTimeout(t);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function compactLibrary(library) {
|
|
210
|
+
return {
|
|
211
|
+
id: String(library && library.id || ""),
|
|
212
|
+
name: String(library && library.name || ""),
|
|
213
|
+
ownerId: String(library && library.ownerId || ""),
|
|
214
|
+
importPaths: Array.isArray(library && library.importPaths) ? library.importPaths.map(String) : [],
|
|
215
|
+
exclusionPatterns: Array.isArray(library && library.exclusionPatterns) ? library.exclusionPatterns.map(String) : [],
|
|
216
|
+
assetCount: Number(library && library.assetCount || 0),
|
|
217
|
+
refreshedAt: library && library.refreshedAt || null,
|
|
218
|
+
createdAt: library && library.createdAt,
|
|
219
|
+
updatedAt: library && library.updatedAt,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
async function listPhotoLibraries() {
|
|
223
|
+
const libraries = await immich("GET", "/libraries");
|
|
224
|
+
return (Array.isArray(libraries) ? libraries : []).map(compactLibrary);
|
|
225
|
+
}
|
|
226
|
+
function selectPhotoLibrary(libraries) {
|
|
227
|
+
const byPath = libraries.filter((library) => library.importPaths.includes(PHOTO_LIBRARY_PATH));
|
|
228
|
+
if (byPath.length > 1) {
|
|
229
|
+
throw new Error("multiple Immich external libraries use " + PHOTO_LIBRARY_PATH + "; resolve the conflict in Immich");
|
|
230
|
+
}
|
|
231
|
+
return byPath[0] || null;
|
|
232
|
+
}
|
|
233
|
+
function unavailablePhotoLibraryStatus() {
|
|
234
|
+
return {
|
|
235
|
+
available: false,
|
|
236
|
+
configured: false,
|
|
237
|
+
reason: "default photo library is available only for JishuShell-managed Immich",
|
|
238
|
+
import_path: PHOTO_LIBRARY_PATH,
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
async function getPhotoLibraryStatus() {
|
|
242
|
+
if (!PHOTO_LIBRARY_ENABLED) return unavailablePhotoLibraryStatus();
|
|
243
|
+
const library = selectPhotoLibrary(await listPhotoLibraries());
|
|
244
|
+
if (!library) {
|
|
245
|
+
return {
|
|
246
|
+
available: true,
|
|
247
|
+
configured: false,
|
|
248
|
+
status: "not_initialized",
|
|
249
|
+
name: PHOTO_LIBRARY_NAME,
|
|
250
|
+
import_path: PHOTO_LIBRARY_PATH,
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
const statistics = await immich("GET", "/libraries/" + encodeURIComponent(library.id) + "/statistics");
|
|
254
|
+
return {
|
|
255
|
+
available: true,
|
|
256
|
+
configured: true,
|
|
257
|
+
status: library.refreshedAt ? "ready" : "not_scanned",
|
|
258
|
+
import_path: PHOTO_LIBRARY_PATH,
|
|
259
|
+
library,
|
|
260
|
+
statistics,
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
async function ensurePhotoLibrary() {
|
|
264
|
+
if (!PHOTO_LIBRARY_ENABLED) {
|
|
265
|
+
throw new Error("default photo library scanning is available only for JishuShell-managed Immich");
|
|
266
|
+
}
|
|
267
|
+
const libraries = await listPhotoLibraries();
|
|
268
|
+
const existing = selectPhotoLibrary(libraries);
|
|
269
|
+
if (existing) return existing;
|
|
270
|
+
|
|
271
|
+
const byName = libraries.filter((library) => library.name === PHOTO_LIBRARY_NAME);
|
|
272
|
+
if (byName.length > 1) {
|
|
273
|
+
throw new Error("multiple Immich external libraries are named " + PHOTO_LIBRARY_NAME + "; resolve the conflict in Immich");
|
|
274
|
+
}
|
|
275
|
+
if (byName.length === 1) {
|
|
276
|
+
const library = byName[0];
|
|
277
|
+
return compactLibrary(await immich("PUT", "/libraries/" + encodeURIComponent(library.id), {
|
|
278
|
+
body: {
|
|
279
|
+
name: library.name,
|
|
280
|
+
importPaths: uniqueStrings(library.importPaths.concat([PHOTO_LIBRARY_PATH])),
|
|
281
|
+
exclusionPatterns: library.exclusionPatterns,
|
|
282
|
+
},
|
|
283
|
+
}));
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const user = await immich("GET", "/users/me");
|
|
287
|
+
if (!user || typeof user.id !== "string" || !user.id) {
|
|
288
|
+
throw new Error("Immich did not return the current user id");
|
|
289
|
+
}
|
|
290
|
+
if (user.isAdmin !== true) {
|
|
291
|
+
throw new Error("an Immich administrator API key is required to create the default photo library");
|
|
292
|
+
}
|
|
293
|
+
return compactLibrary(await immich("POST", "/libraries", {
|
|
294
|
+
body: {
|
|
295
|
+
ownerId: user.id,
|
|
296
|
+
name: PHOTO_LIBRARY_NAME,
|
|
297
|
+
importPaths: [PHOTO_LIBRARY_PATH],
|
|
298
|
+
exclusionPatterns: [],
|
|
299
|
+
},
|
|
300
|
+
}));
|
|
301
|
+
}
|
|
302
|
+
async function scanPhotoLibrary() {
|
|
303
|
+
const library = await ensurePhotoLibrary();
|
|
304
|
+
await immich("POST", "/libraries/" + encodeURIComponent(library.id) + "/scan");
|
|
305
|
+
return {
|
|
306
|
+
queued: true,
|
|
307
|
+
status: "scan_queued",
|
|
308
|
+
library_id: library.id,
|
|
309
|
+
library_name: library.name,
|
|
310
|
+
import_path: PHOTO_LIBRARY_PATH,
|
|
311
|
+
message: "Immich accepted the scan request. New photos are imported in the background.",
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
async function createPhotoUploadSession(args) {
|
|
315
|
+
if (!PHOTO_LIBRARY_ENABLED) {
|
|
316
|
+
throw new Error("photo upload links are available only for JishuShell-managed Immich");
|
|
317
|
+
}
|
|
318
|
+
return await coreSend("POST", "/api/integrations/immich/photo-upload-sessions", {
|
|
319
|
+
folderName: typeof args.folder_name === "string" ? args.folder_name : undefined,
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function uniqueStrings(values) { return [...new Set((Array.isArray(values) ? values : []).filter((v) => typeof v === "string" && v.trim()))]; }
|
|
324
|
+
async function generateReviewGallery(args) {
|
|
325
|
+
return await coreSend("POST", "/api/integrations/immich/reviews", args || {});
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const TOOLS = [
|
|
329
|
+
{ name:"ping", description:"Check Immich connectivity.", inputSchema:{type:"object",properties:{}} },
|
|
330
|
+
{ name:"get_library_summary", description:"Return Immich version, statistics, and connection status.", inputSchema:{type:"object",properties:{}} },
|
|
331
|
+
{ name:"get_photo_library_status", description:"Read the AI Box default external photo library status and statistics.", inputSchema:{type:"object",properties:{}} },
|
|
332
|
+
{ name:"scan_photo_library", description:"Ensure the AI Box default external photo library exists and queue an Immich scan.", inputSchema:{type:"object",properties:{}} },
|
|
333
|
+
{ name:"create_photo_upload_session", description:"Create a short-lived AI Box web page where the user can choose photos, videos, or a folder and upload directly over the local network. The same page can finish and scan multiple upload batches until it expires.", inputSchema:{type:"object",properties:{folder_name:{type:"string",description:"Optional human-readable name for uploaded batches."}}} },
|
|
334
|
+
{ name:"get_asset_info", description:"Read compact metadata for one asset by id.", inputSchema:{type:"object",properties:{asset_id:{type:"string"}},required:["asset_id"]} },
|
|
335
|
+
{ name:"list_assets", description:"List a bounded page of assets. Defaults to page=1,size=50; when no filters are provided, archived assets are excluded.", inputSchema:{type:"object",properties:{page:{type:"number",description:"Page number, default 1."},size:{type:"number",description:"Page size, default 50, maximum 100."},is_favorite:{type:"boolean"},is_archived:{type:"boolean"},is_trashed:{type:"boolean"},asset_type:{type:"string"}}} },
|
|
336
|
+
{ name:"search_metadata", description:"Search a bounded page of assets by exact/structured metadata filters; this is not visual Smart Search / CLIP. Defaults to page=1,size=50.", inputSchema:{type:"object",properties:{city:{type:"string"},state:{type:"string"},country:{type:"string"},make:{type:"string"},model:{type:"string"},taken_after:{type:"string",description:"ISO date/time lower bound for the asset taken time."},taken_before:{type:"string",description:"ISO date/time upper bound for the asset taken time."},file_created_after:{type:"string",description:"ISO date/time lower bound for file creation time."},file_created_before:{type:"string",description:"ISO date/time upper bound for file creation time."},asset_type:{type:"string"},is_favorite:{type:"boolean"},is_archived:{type:"boolean"},is_trashed:{type:"boolean"},tag_ids:{type:"array",items:{type:"string"},description:"Optional Immich tag ids to filter by."},page:{type:"number",description:"Page number, default 1."},size:{type:"number",description:"Page size, default 50, maximum 100."}}} },
|
|
337
|
+
{ name:"search_smart", description:"Return a bounded, ranked candidate page from Immich Smart Search / CLIP using natural language or a query asset. Read-only. Results are semantic similarity candidates, not a guaranteed classifier result or exhaustive set of all matching assets. Defaults to page=1,size=50.", inputSchema:{type:"object",properties:{query:{type:"string",description:"Natural-language visual query, for example 'dog playing in grass'."},query_asset_id:{type:"string",description:"Optional asset id for similar-image search."},taken_after:{type:"string",description:"ISO date/time lower bound for the asset taken time."},taken_before:{type:"string",description:"ISO date/time upper bound for the asset taken time."},city:{type:"string"},state:{type:"string"},country:{type:"string"},person_ids:{type:"array",items:{type:"string"},description:"Optional Immich person ids to narrow results."},page:{type:"number",description:"Page number, default 1."},size:{type:"number",description:"Page size, default 50, maximum 100."}}} },
|
|
338
|
+
{ name:"get_map_markers", description:"Read geotag map markers.", inputSchema:{type:"object",properties:{is_archived:{type:"boolean"},is_favorite:{type:"boolean"},file_created_after:{type:"string"},file_created_before:{type:"string"}}} },
|
|
339
|
+
{ name:"list_albums", description:"List albums.", inputSchema:{type:"object",properties:{shared:{type:"boolean"}}} },
|
|
340
|
+
{ name:"get_album", description:"Read album details and compact asset ids.", inputSchema:{type:"object",properties:{album_id:{type:"string"}},required:["album_id"]} },
|
|
341
|
+
{ name:"create_album", description:"Create an album container.", inputSchema:{type:"object",properties:{name:{type:"string"},description:{type:"string"},asset_ids:{type:"array",items:{type:"string"}}},required:["name"]} },
|
|
342
|
+
{ name:"update_album", description:"Update album name or description.", inputSchema:{type:"object",properties:{album_id:{type:"string"},name:{type:"string"},description:{type:"string"}},required:["album_id"]} },
|
|
343
|
+
{ name:"delete_album", description:"Delete an album container after the user has explicitly confirmed in chat. Photos in the album are NOT deleted and remain in the library. This removes the album grouping and is not simply undoable.", inputSchema:{type:"object",properties:{album_id:{type:"string"}},required:["album_id"]} },
|
|
344
|
+
{ name:"list_tags", description:"List tags.", inputSchema:{type:"object",properties:{}} },
|
|
345
|
+
{ name:"get_tag", description:"Read one tag.", inputSchema:{type:"object",properties:{tag_id:{type:"string"}},required:["tag_id"]} },
|
|
346
|
+
{ name:"create_tag", description:"Create a tag.", inputSchema:{type:"object",properties:{name:{type:"string"},color:{type:"string"}},required:["name"]} },
|
|
347
|
+
{ name:"update_tag", description:"Update tag name or color.", inputSchema:{type:"object",properties:{tag_id:{type:"string"},name:{type:"string"},color:{type:"string"}},required:["tag_id"]} },
|
|
348
|
+
{ name:"delete_tag", description:"Delete a tag after the user has explicitly confirmed in chat. Photos are NOT deleted, but the tag is removed from every associated asset. This is not simply undoable.", inputSchema:{type:"object",properties:{tag_id:{type:"string"}},required:["tag_id"]} },
|
|
349
|
+
{ name:"generate_review_gallery", description:"Create a Core-owned Immich review interaction for user selection and confirmation. Use this for bulk album/tag/trash/duplicate actions whenever the user must choose or confirm candidate assets. Return urls.web to the user; Core executes after UI confirmation, and the model should not claim success unless it later reads an execution result.", inputSchema:{type:"object",properties:{action:{type:"string",enum:["add_assets_to_album","remove_assets_from_album","tag_assets","untag_assets","delete_assets_to_trash","resolve_duplicates"],description:"Bound action to execute after UI confirmation. Use exactly one enum value."},title:{type:"string",description:"Human-readable review title shown in the UI."},asset_ids:{type:"array",items:{type:"string"},description:"Candidate asset ids from list/search/album results. The user chooses or confirms a subset in the UI."},preselected_asset_ids:{type:"array",items:{type:"string"},description:"Recommended asset ids to select by default in the UI. Must be a subset of asset_ids or resolved candidates. Use only for high-confidence recommendations; do not preselect every Smart Search / CLIP candidate by default."},album_id:{type:"string",description:"Required for add_assets_to_album and remove_assets_from_album. Also usable as a candidate source when asset_ids are omitted."},tag_id:{type:"string",description:"Required for tag_assets and untag_assets."},duplicate_id:{type:"string",description:"Duplicate group id for resolve_duplicates. Also usable as a candidate source when asset_ids are omitted."}},required:["action"]} },
|
|
350
|
+
{ name:"restore_assets", description:"Restore specific trashed assets by id.", inputSchema:{type:"object",properties:{asset_ids:{type:"array",items:{type:"string"}}},required:["asset_ids"]} },
|
|
351
|
+
{ name:"get_duplicates", description:"Read Immich duplicate groups.", inputSchema:{type:"object",properties:{}} },
|
|
352
|
+
{ name:"list_people", description:"List people recognized by Immich, read-only.", inputSchema:{type:"object",properties:{page:{type:"number"},size:{type:"number"},with_hidden:{type:"boolean"}}} },
|
|
353
|
+
{ name:"search_people", description:"Search people by name, read-only.", inputSchema:{type:"object",properties:{name:{type:"string"},with_hidden:{type:"boolean"}},required:["name"]} },
|
|
354
|
+
{ name:"get_person", description:"Read one person, read-only.", inputSchema:{type:"object",properties:{person_id:{type:"string"}},required:["person_id"]} },
|
|
355
|
+
{ name:"get_asset_faces", description:"Read faces detected in one asset, read-only.", inputSchema:{type:"object",properties:{asset_id:{type:"string"}},required:["asset_id"]} },
|
|
356
|
+
];
|
|
357
|
+
|
|
358
|
+
function send(msg) { process.stdout.write(JSON.stringify(msg) + "\\n"); }
|
|
359
|
+
function reply(id, result) { if (id !== undefined) send({ jsonrpc:"2.0", id, result }); }
|
|
360
|
+
function replyError(id, code, message) { if (id !== undefined) send({ jsonrpc:"2.0", id, error:{ code, message } }); }
|
|
361
|
+
async function handleCall(name, args) {
|
|
362
|
+
args = args || {};
|
|
363
|
+
if (name === "ping") return await immich("GET", "/server/ping");
|
|
364
|
+
if (name === "get_library_summary") {
|
|
365
|
+
const out = { connected:false, baseUrl:BASE_URL };
|
|
366
|
+
const results = await Promise.allSettled([immich("GET","/server/ping"), immich("GET","/server/version"), immich("GET","/server/statistics")]);
|
|
367
|
+
out.connected = results[0].status === "fulfilled";
|
|
368
|
+
out.ping = results[0].status === "fulfilled" ? results[0].value : { error: String(results[0].reason && results[0].reason.message || results[0].reason) };
|
|
369
|
+
out.version = results[1].status === "fulfilled" ? results[1].value : { error: String(results[1].reason && results[1].reason.message || results[1].reason) };
|
|
370
|
+
out.statistics = results[2].status === "fulfilled" ? results[2].value : { error: String(results[2].reason && results[2].reason.message || results[2].reason) };
|
|
371
|
+
return out;
|
|
372
|
+
}
|
|
373
|
+
if (name === "get_photo_library_status") return await getPhotoLibraryStatus();
|
|
374
|
+
if (name === "scan_photo_library") return await scanPhotoLibrary();
|
|
375
|
+
if (name === "create_photo_upload_session") return await createPhotoUploadSession(args);
|
|
376
|
+
if (name === "get_asset_info") return compactAsset(await immich("GET", "/assets/" + encodeURIComponent(args.asset_id)));
|
|
377
|
+
if (name === "list_assets") { const b = searchBody(args); if (Object.keys(b).length <= 2) b.isArchived = false; return assetList(await immich("POST", "/search/metadata", { body:b })); }
|
|
378
|
+
if (name === "search_metadata") return assetList(await immich("POST", "/search/metadata", { body:searchBody(args) }));
|
|
379
|
+
if (name === "search_smart") return assetList(await immich("POST", "/search/smart", { body:smartSearchBody(args) }));
|
|
380
|
+
if (name === "get_map_markers") return await immich("GET", "/map/markers", { params:{ isArchived: args.is_archived === undefined ? false : !!args.is_archived, isFavorite: args.is_favorite, fileCreatedAfter: args.file_created_after, fileCreatedBefore: args.file_created_before } });
|
|
381
|
+
if (name === "list_albums") return (await immich("GET", "/albums", { params:{ shared: args.shared } })).map((x) => compactAlbum(x, false));
|
|
382
|
+
if (name === "get_album") return compactAlbum(await immich("GET", "/albums/" + encodeURIComponent(args.album_id)), true);
|
|
383
|
+
if (name === "create_album") return compactAlbum(await immich("POST", "/albums", { body:{ albumName: args.name, description: args.description || "", assetIds: uniqueStrings(args.asset_ids) } }), true);
|
|
384
|
+
if (name === "update_album") { const body = {}; if (args.name) body.albumName = args.name; if (args.description !== undefined) body.description = args.description; return compactAlbum(await immich("PATCH", "/albums/" + encodeURIComponent(args.album_id), { body }), true); }
|
|
385
|
+
if (name === "delete_album") { const album = compactAlbum(await immich("GET", "/albums/" + encodeURIComponent(args.album_id)), false); await immich("DELETE", "/albums/" + encodeURIComponent(args.album_id)); return { deleted:true, album_id:album.id, album_name:album.albumName, asset_count:album.assetCount, note:"Album deleted. Photos were not deleted and remain in the Immich library." }; }
|
|
386
|
+
if (name === "list_tags") return (await immich("GET", "/tags")).map(compactTag);
|
|
387
|
+
if (name === "get_tag") return compactTag(await immich("GET", "/tags/" + encodeURIComponent(args.tag_id)));
|
|
388
|
+
if (name === "create_tag") { const body = { name: args.name }; if (args.color) body.color = args.color; return compactTag(await immich("POST", "/tags", { body })); }
|
|
389
|
+
if (name === "update_tag") { const body = {}; if (args.name) body.name = args.name; if (args.color) body.color = args.color; return compactTag(await immich("PUT", "/tags/" + encodeURIComponent(args.tag_id), { body })); }
|
|
390
|
+
if (name === "delete_tag") { const tag = compactTag(await immich("GET", "/tags/" + encodeURIComponent(args.tag_id))); await immich("DELETE", "/tags/" + encodeURIComponent(args.tag_id)); return { deleted:true, tag_id:tag.id, tag_name:tag.name, note:"Tag deleted. Photos were not deleted, but this tag was removed from associated assets." }; }
|
|
391
|
+
if (name === "generate_review_gallery") return await generateReviewGallery(args);
|
|
392
|
+
if (name === "restore_assets") { const ids = uniqueStrings(args.asset_ids); await immich("POST", "/trash/restore/assets", { body:{ ids } }); return { restored:ids.length }; }
|
|
393
|
+
if (name === "get_duplicates") return await immich("GET", "/duplicates");
|
|
394
|
+
if (name === "list_people") { const data = await immich("GET", "/people", { params:{ page:clampInt(args.page,1,1,10000), size:clampInt(args.size,50,1,100), withHidden:!!args.with_hidden } }); const people = Array.isArray(data.people) ? data.people : Array.isArray(data.items) ? data.items : Array.isArray(data) ? data : []; return { ...data, people: people.map(compactPerson) }; }
|
|
395
|
+
if (name === "search_people") return (await immich("GET", "/search/person", { params:{ name:args.name, withHidden:!!args.with_hidden } })).map(compactPerson);
|
|
396
|
+
if (name === "get_person") return compactPerson(await immich("GET", "/people/" + encodeURIComponent(args.person_id)));
|
|
397
|
+
if (name === "get_asset_faces") return await immich("GET", "/faces", { params:{ id:args.asset_id } });
|
|
398
|
+
throw new Error("unknown tool: " + name);
|
|
399
|
+
}
|
|
400
|
+
async function handle(req) {
|
|
401
|
+
const { id, method, params } = req || {};
|
|
402
|
+
try {
|
|
403
|
+
if (method === "initialize") reply(id, { protocolVersion:"2024-11-05", capabilities:{ tools:{} }, serverInfo:{ name:"jishushell-immich-shim", version:VERSION } });
|
|
404
|
+
else if (method === "notifications/initialized") {}
|
|
405
|
+
else if (method === "tools/list") reply(id, { tools: TOOLS });
|
|
406
|
+
else if (method === "tools/call") reply(id, { content:[{ type:"text", text:jsonText(await handleCall(params && params.name, params && params.arguments)) }] });
|
|
407
|
+
else if (method === "ping") reply(id, {});
|
|
408
|
+
else replyError(id, -32601, "method not found: " + method);
|
|
409
|
+
} catch (e) {
|
|
410
|
+
replyError(id, -32000, e && e.message ? e.message : String(e));
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
const keepAlive = setInterval(() => {}, 2147483647);
|
|
414
|
+
function shutdown(code) {
|
|
415
|
+
clearInterval(keepAlive);
|
|
416
|
+
process.exit(code);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
const rl = readline.createInterface({ input: process.stdin, crlfDelay: Infinity, terminal: false });
|
|
420
|
+
rl.on("line", (line) => {
|
|
421
|
+
const text = String(line || "").trim();
|
|
422
|
+
if (!text) return;
|
|
423
|
+
try { void handle(JSON.parse(text)); } catch (_) {}
|
|
424
|
+
});
|
|
425
|
+
rl.on("close", () => shutdown(0));
|
|
426
|
+
process.on("SIGTERM", () => shutdown(0));
|
|
427
|
+
process.on("SIGINT", () => shutdown(0));
|
|
428
|
+
`;
|
|
429
|
+
export function substituteImmichShimPlaceholders(input) {
|
|
430
|
+
return IMMICH_MCP_SHIM_SOURCE
|
|
431
|
+
.replaceAll("__JS_IMMICH_BASE_URL__", jsonLiteralContent(input.baseUrl.replace(/\/+$/, "")))
|
|
432
|
+
.replaceAll("__JS_IMMICH_PHOTO_LIBRARY_ENABLED__", input.photoLibraryEnabled === true ? "true" : "false")
|
|
433
|
+
.replaceAll("__JS_CORE_URL__", jsonLiteralContent(input.coreUrl?.replace(/\/+$/, "") ?? ""))
|
|
434
|
+
.replaceAll("__JS_INSTANCE_ID__", jsonLiteralContent(input.instanceId ?? ""));
|
|
435
|
+
}
|
|
436
|
+
function jsonLiteralContent(value) {
|
|
437
|
+
return JSON.stringify(value).slice(1, -1);
|
|
438
|
+
}
|
|
439
|
+
//# sourceMappingURL=immich-shim.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"immich-shim.js","sourceRoot":"","sources":["../../../../src/services/integrations/immich/immich-shim.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iCAAiC,EACjC,iCAAiC,GAClC,MAAM,oBAAoB,CAAC;AAE5B,MAAM,CAAC,MAAM,uBAAuB,GAAG,OAAO,CAAC;AAC/C,MAAM,CAAC,MAAM,2BAA2B,GAAG,aAAa,CAAC;AACzD,OAAO,EACL,iCAAiC,EACjC,iCAAiC,GAClC,MAAM,oBAAoB,CAAC;AAE5B,MAAM,CAAC,MAAM,4BAA4B,GAAG;IAC1C,MAAM;IACN,qBAAqB;IACrB,0BAA0B;IAC1B,oBAAoB;IACpB,6BAA6B;IAC7B,gBAAgB;IAChB,aAAa;IACb,iBAAiB;IACjB,cAAc;IACd,iBAAiB;IACjB,aAAa;IACb,WAAW;IACX,cAAc;IACd,cAAc;IACd,cAAc;IACd,WAAW;IACX,SAAS;IACT,YAAY;IACZ,YAAY;IACZ,YAAY;IACZ,yBAAyB;IACzB,gBAAgB;IAChB,gBAAgB;IAChB,aAAa;IACb,eAAe;IACf,YAAY;IACZ,iBAAiB;CACT,CAAC;AAEX,MAAM,CAAC,MAAM,sBAAsB,GAAG;;;;;;;;kBAQpB,IAAI,CAAC,SAAS,CAAC,uBAAuB,CAAC;0BAC/B,IAAI,CAAC,SAAS,CAAC,2BAA2B,CAAC;;;;;6BAKxC,IAAI,CAAC,SAAS,CAAC,iCAAiC,CAAC;6BACjD,IAAI,CAAC,SAAS,CAAC,iCAAiC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2X7E,CAAC;AAEF,MAAM,UAAU,gCAAgC,CAAC,KAKhD;IACC,OAAO,sBAAsB;SAC1B,UAAU,CAAC,wBAAwB,EAAE,kBAAkB,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC;SAC3F,UAAU,CAAC,qCAAqC,EAAE,KAAK,CAAC,mBAAmB,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;SACxG,UAAU,CAAC,iBAAiB,EAAE,kBAAkB,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;SAC3F,UAAU,CAAC,oBAAoB,EAAE,kBAAkB,CAAC,KAAK,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,CAAC;AAClF,CAAC;AAED,SAAS,kBAAkB,CAAC,KAAa;IACvC,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AAC5C,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { FastifyInstance } from "fastify";
|
|
2
|
+
import type { AppIntegration, AppIntegrationManifest, CapabilityProfile, ConfigDocument, InstanceConfigMeta, PairingApproveInput, RuntimeSpec, RuntimeVersionInfo } from "../types.js";
|
|
3
|
+
declare class ImmichIntegration implements AppIntegration {
|
|
4
|
+
readonly kind: "immich";
|
|
5
|
+
readonly displayName = "Immich";
|
|
6
|
+
readonly defaultCapabilities: CapabilityProfile;
|
|
7
|
+
readonly manifest: AppIntegrationManifest;
|
|
8
|
+
buildRuntime(_instanceId: string): Promise<RuntimeSpec>;
|
|
9
|
+
getRuntimeVersion(_instanceId: string): Promise<RuntimeVersionInfo>;
|
|
10
|
+
getConfigMeta(_instanceId: string): Promise<InstanceConfigMeta>;
|
|
11
|
+
readConfig(_instanceId: string): Promise<ConfigDocument>;
|
|
12
|
+
writeConfig(_instanceId: string, _doc: ConfigDocument): Promise<ConfigDocument>;
|
|
13
|
+
buildPairingListCommand(_instanceId: string): Promise<string[]>;
|
|
14
|
+
buildPairingApproveCommand(_instanceId: string, _input: PairingApproveInput): Promise<string[]>;
|
|
15
|
+
registerRoutes(app: FastifyInstance): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
export declare const immichIntegration: ImmichIntegration;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { registerIntegration } from "../registry.js";
|
|
2
|
+
import { publicImmichConfigSummary, resolveImmichConfig } from "./config.js";
|
|
3
|
+
const DEFAULT_CAPABILITIES = {
|
|
4
|
+
ui: { http: true, websocket: false, chatPanel: "none" },
|
|
5
|
+
pairing: { list: false, approve: false, revoke: false, clearPending: false },
|
|
6
|
+
configEditor: "custom",
|
|
7
|
+
configSchema: false,
|
|
8
|
+
customProvider: false,
|
|
9
|
+
pluginInstall: false,
|
|
10
|
+
skills: true,
|
|
11
|
+
mcp: true,
|
|
12
|
+
memory: false,
|
|
13
|
+
backupRestore: false,
|
|
14
|
+
usageStats: false,
|
|
15
|
+
restartlessReload: false,
|
|
16
|
+
messagingPlatforms: [],
|
|
17
|
+
};
|
|
18
|
+
class ImmichIntegration {
|
|
19
|
+
kind = "immich";
|
|
20
|
+
displayName = "Immich";
|
|
21
|
+
defaultCapabilities = DEFAULT_CAPABILITIES;
|
|
22
|
+
manifest = {
|
|
23
|
+
kind: "immich",
|
|
24
|
+
displayName: "Immich",
|
|
25
|
+
description: "Immich photo library exposed to agents through reviewed MCP tools",
|
|
26
|
+
defaultCapabilities: DEFAULT_CAPABILITIES,
|
|
27
|
+
catalogGroup: "apps",
|
|
28
|
+
catalogIcon: "gallery",
|
|
29
|
+
supportsClone: false,
|
|
30
|
+
connectionRole: "tool",
|
|
31
|
+
embeddedUiSurface: "none",
|
|
32
|
+
};
|
|
33
|
+
async buildRuntime(_instanceId) {
|
|
34
|
+
throw new Error("Immich MCP integration does not build or own the Immich runtime");
|
|
35
|
+
}
|
|
36
|
+
async getRuntimeVersion(_instanceId) {
|
|
37
|
+
return { integrationKind: "immich", ref: "external", mode: "baseline" };
|
|
38
|
+
}
|
|
39
|
+
async getConfigMeta(_instanceId) {
|
|
40
|
+
throw new Error("Immich MCP endpoint config is not exposed through the generic config editor");
|
|
41
|
+
}
|
|
42
|
+
async readConfig(_instanceId) {
|
|
43
|
+
return {
|
|
44
|
+
format: "json",
|
|
45
|
+
content: publicImmichConfigSummary(await resolveImmichConfig()),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
async writeConfig(_instanceId, _doc) {
|
|
49
|
+
throw new Error("Immich V0 config writes are not available through this endpoint yet");
|
|
50
|
+
}
|
|
51
|
+
async buildPairingListCommand(_instanceId) {
|
|
52
|
+
return [];
|
|
53
|
+
}
|
|
54
|
+
async buildPairingApproveCommand(_instanceId, _input) {
|
|
55
|
+
return [];
|
|
56
|
+
}
|
|
57
|
+
async registerRoutes(app) {
|
|
58
|
+
const { registerImmichRoutes } = await import("./routes.js");
|
|
59
|
+
registerImmichRoutes(app);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
export const immichIntegration = new ImmichIntegration();
|
|
63
|
+
registerIntegration(immichIntegration);
|
|
64
|
+
//# sourceMappingURL=integration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"integration.js","sourceRoot":"","sources":["../../../../src/services/integrations/immich/integration.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAAE,yBAAyB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAE7E,MAAM,oBAAoB,GAAsB;IAC9C,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE;IACvD,OAAO,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE;IAC5E,YAAY,EAAE,QAAQ;IACtB,YAAY,EAAE,KAAK;IACnB,cAAc,EAAE,KAAK;IACrB,aAAa,EAAE,KAAK;IACpB,MAAM,EAAE,IAAI;IACZ,GAAG,EAAE,IAAI;IACT,MAAM,EAAE,KAAK;IACb,aAAa,EAAE,KAAK;IACpB,UAAU,EAAE,KAAK;IACjB,iBAAiB,EAAE,KAAK;IACxB,kBAAkB,EAAE,EAAE;CACvB,CAAC;AAEF,MAAM,iBAAiB;IACZ,IAAI,GAAG,QAAiB,CAAC;IACzB,WAAW,GAAG,QAAQ,CAAC;IACvB,mBAAmB,GAAG,oBAAoB,CAAC;IAC3C,QAAQ,GAA2B;QAC1C,IAAI,EAAE,QAAQ;QACd,WAAW,EAAE,QAAQ;QACrB,WAAW,EAAE,mEAAmE;QAChF,mBAAmB,EAAE,oBAAoB;QACzC,YAAY,EAAE,MAAM;QACpB,WAAW,EAAE,SAAS;QACtB,aAAa,EAAE,KAAK;QACpB,cAAc,EAAE,MAAM;QACtB,iBAAiB,EAAE,MAAM;KAC1B,CAAC;IAEF,KAAK,CAAC,YAAY,CAAC,WAAmB;QACpC,MAAM,IAAI,KAAK,CAAC,iEAAiE,CAAC,CAAC;IACrF,CAAC;IAED,KAAK,CAAC,iBAAiB,CAAC,WAAmB;QACzC,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;IAC1E,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,WAAmB;QACrC,MAAM,IAAI,KAAK,CAAC,6EAA6E,CAAC,CAAC;IACjG,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,WAAmB;QAClC,OAAO;YACL,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,yBAAyB,CAAC,MAAM,mBAAmB,EAAE,CAAC;SAChE,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,WAAmB,EAAE,IAAoB;QACzD,MAAM,IAAI,KAAK,CAAC,qEAAqE,CAAC,CAAC;IACzF,CAAC;IAED,KAAK,CAAC,uBAAuB,CAAC,WAAmB;QAC/C,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,KAAK,CAAC,0BAA0B,CAAC,WAAmB,EAAE,MAA2B;QAC/E,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,KAAK,CAAC,cAAc,CAAC,GAAoB;QACvC,MAAM,EAAE,oBAAoB,EAAE,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,CAAC;QAC7D,oBAAoB,CAAC,GAAG,CAAC,CAAC;IAC5B,CAAC;CACF;AAED,MAAM,CAAC,MAAM,iBAAiB,GAAG,IAAI,iBAAiB,EAAE,CAAC;AACzD,mBAAmB,CAAC,iBAAiB,CAAC,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { ImmichClient } from "./client.js";
|
|
2
|
+
export declare const IMMICH_DEFAULT_PHOTO_LIBRARY_NAME = "AI Box Photos";
|
|
3
|
+
export declare const IMMICH_DEFAULT_PHOTO_LIBRARY_PATH = "/external/photos";
|
|
4
|
+
export declare function queueDefaultPhotoLibraryScan(client: ImmichClient): Promise<Record<string, unknown>>;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
export const IMMICH_DEFAULT_PHOTO_LIBRARY_NAME = "AI Box Photos";
|
|
2
|
+
export const IMMICH_DEFAULT_PHOTO_LIBRARY_PATH = "/external/photos";
|
|
3
|
+
export async function queueDefaultPhotoLibraryScan(client) {
|
|
4
|
+
const library = await ensureDefaultPhotoLibrary(client);
|
|
5
|
+
await client.scanLibrary(library.id);
|
|
6
|
+
return {
|
|
7
|
+
queued: true,
|
|
8
|
+
status: "scan_queued",
|
|
9
|
+
library_id: library.id,
|
|
10
|
+
library_name: library.name,
|
|
11
|
+
import_path: IMMICH_DEFAULT_PHOTO_LIBRARY_PATH,
|
|
12
|
+
message: "Immich accepted the scan request. New photos are imported in the background.",
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
async function ensureDefaultPhotoLibrary(client) {
|
|
16
|
+
const libraries = (await client.listLibraries()).map(compactLibrary);
|
|
17
|
+
const byPath = libraries.find((library) => library.importPaths.includes(IMMICH_DEFAULT_PHOTO_LIBRARY_PATH));
|
|
18
|
+
if (byPath)
|
|
19
|
+
return byPath;
|
|
20
|
+
const byName = libraries.filter((library) => library.name === IMMICH_DEFAULT_PHOTO_LIBRARY_NAME);
|
|
21
|
+
if (byName.length > 1) {
|
|
22
|
+
throw new Error(`multiple Immich external libraries are named ${IMMICH_DEFAULT_PHOTO_LIBRARY_NAME}; resolve the conflict in Immich`);
|
|
23
|
+
}
|
|
24
|
+
if (byName.length === 1) {
|
|
25
|
+
const library = byName[0];
|
|
26
|
+
return compactLibrary(await client.updateLibrary({
|
|
27
|
+
libraryId: library.id,
|
|
28
|
+
name: library.name,
|
|
29
|
+
importPaths: uniqueStrings([
|
|
30
|
+
...library.importPaths,
|
|
31
|
+
IMMICH_DEFAULT_PHOTO_LIBRARY_PATH,
|
|
32
|
+
]),
|
|
33
|
+
exclusionPatterns: library.exclusionPatterns,
|
|
34
|
+
}));
|
|
35
|
+
}
|
|
36
|
+
const user = await client.getCurrentUser();
|
|
37
|
+
const ownerId = typeof user.id === "string" ? user.id : "";
|
|
38
|
+
if (!ownerId)
|
|
39
|
+
throw new Error("Immich did not return the current user id");
|
|
40
|
+
if (user.isAdmin !== true) {
|
|
41
|
+
throw new Error("an Immich administrator API key is required to create the default photo library");
|
|
42
|
+
}
|
|
43
|
+
return compactLibrary(await client.createLibrary({
|
|
44
|
+
ownerId,
|
|
45
|
+
name: IMMICH_DEFAULT_PHOTO_LIBRARY_NAME,
|
|
46
|
+
importPaths: [IMMICH_DEFAULT_PHOTO_LIBRARY_PATH],
|
|
47
|
+
exclusionPatterns: [],
|
|
48
|
+
}));
|
|
49
|
+
}
|
|
50
|
+
function compactLibrary(input) {
|
|
51
|
+
return {
|
|
52
|
+
id: typeof input.id === "string" ? input.id : "",
|
|
53
|
+
name: typeof input.name === "string" ? input.name : "",
|
|
54
|
+
importPaths: uniqueStrings(input.importPaths),
|
|
55
|
+
exclusionPatterns: uniqueStrings(input.exclusionPatterns),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
function uniqueStrings(input) {
|
|
59
|
+
if (!Array.isArray(input))
|
|
60
|
+
return [];
|
|
61
|
+
return [...new Set(input.filter((value) => typeof value === "string" && value.trim().length > 0))];
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=photo-library.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"photo-library.js","sourceRoot":"","sources":["../../../../src/services/integrations/immich/photo-library.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,iCAAiC,GAAG,eAAe,CAAC;AACjE,MAAM,CAAC,MAAM,iCAAiC,GAAG,kBAAkB,CAAC;AASpE,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAChD,MAAoB;IAEpB,MAAM,OAAO,GAAG,MAAM,yBAAyB,CAAC,MAAM,CAAC,CAAC;IACxD,MAAM,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACrC,OAAO;QACL,MAAM,EAAE,IAAI;QACZ,MAAM,EAAE,aAAa;QACrB,UAAU,EAAE,OAAO,CAAC,EAAE;QACtB,YAAY,EAAE,OAAO,CAAC,IAAI;QAC1B,WAAW,EAAE,iCAAiC;QAC9C,OAAO,EAAE,8EAA8E;KACxF,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,yBAAyB,CAAC,MAAoB;IAC3D,MAAM,SAAS,GAAG,CAAC,MAAM,MAAM,CAAC,aAAa,EAAE,CAAC,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IACrE,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CACxC,OAAO,CAAC,WAAW,CAAC,QAAQ,CAAC,iCAAiC,CAAC,CAChE,CAAC;IACF,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAE1B,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAC1C,OAAO,CAAC,IAAI,KAAK,iCAAiC,CACnD,CAAC;IACF,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CACb,gDAAgD,iCAAiC,kCAAkC,CACpH,CAAC;IACJ,CAAC;IACD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,MAAM,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QAC1B,OAAO,cAAc,CAAC,MAAM,MAAM,CAAC,aAAa,CAAC;YAC/C,SAAS,EAAE,OAAO,CAAC,EAAE;YACrB,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,WAAW,EAAE,aAAa,CAAC;gBACzB,GAAG,OAAO,CAAC,WAAW;gBACtB,iCAAiC;aAClC,CAAC;YACF,iBAAiB,EAAE,OAAO,CAAC,iBAAiB;SAC7C,CAAC,CAAC,CAAC;IACN,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,cAAc,EAAE,CAAC;IAC3C,MAAM,OAAO,GAAG,OAAO,IAAI,CAAC,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAC3D,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;IAC3E,IAAI,IAAI,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,iFAAiF,CAAC,CAAC;IACrG,CAAC;IACD,OAAO,cAAc,CAAC,MAAM,MAAM,CAAC,aAAa,CAAC;QAC/C,OAAO;QACP,IAAI,EAAE,iCAAiC;QACvC,WAAW,EAAE,CAAC,iCAAiC,CAAC;QAChD,iBAAiB,EAAE,EAAE;KACtB,CAAC,CAAC,CAAC;AACN,CAAC;AAED,SAAS,cAAc,CAAC,KAA8B;IACpD,OAAO;QACL,EAAE,EAAE,OAAO,KAAK,CAAC,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE;QAChD,IAAI,EAAE,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE;QACtD,WAAW,EAAE,aAAa,CAAC,KAAK,CAAC,WAAW,CAAC;QAC7C,iBAAiB,EAAE,aAAa,CAAC,KAAK,CAAC,iBAAiB,CAAC;KAC1D,CAAC;AACJ,CAAC;AAED,SAAS,aAAa,CAAC,KAAc;IACnC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACrC,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,KAAK,EAAmB,EAAE,CACzD,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CACrD,CAAC,CAAC,CAAC;AACN,CAAC"}
|