feat: workflows & plugins (#26727)

feat: plugins

chore: better types

feat: plugins
This commit is contained in:
Jason Rasmussen
2026-05-18 11:09:33 -04:00
committed by GitHub
parent 7384799f19
commit 3d075f2bf8
144 changed files with 6099 additions and 7419 deletions
+2
View File
@@ -0,0 +1,2 @@
/dist
/node_modules
+6
View File
@@ -0,0 +1,6 @@
{
"singleQuote": true,
"trailingComma": "all",
"printWidth": 120,
"semi": true
}
+11
View File
@@ -0,0 +1,11 @@
const esbuild = require('esbuild');
esbuild.build({
entryPoints: ['src/index.ts'],
outdir: 'dist',
bundle: true,
sourcemap: false,
minify: false, // might want to use true for production build
format: 'cjs', // needs to be CJS for now
target: ['es2020'], // don't go over es2020 because quickjs doesn't support it
});
+258
View File
@@ -0,0 +1,258 @@
{
"name": "immich-plugin-core",
"version": "2.0.1",
"title": "Immich Core Plugin",
"description": "Core workflow capabilities for Immich",
"author": "Immich Team",
"wasmPath": "dist/plugin.wasm",
"methods": [
{
"name": "assetFileFilter",
"title": "Filter by filename",
"description": "Filter assets by filename pattern using text matching or regular expressions",
"types": ["AssetV1"],
"schema": {
"type": "object",
"properties": {
"pattern": {
"type": "string",
"title": "Filename pattern",
"description": "Text or regex pattern to match against filename"
},
"matchType": {
"type": "string",
"title": "Match type",
"enum": ["contains", "startsWith", "exact", "regex"],
"default": "contains",
"description": "Type of pattern matching to perform"
},
"caseSensitive": {
"type": "boolean",
"default": false,
"title": "Case sensitive",
"description": "Whether matching should be case-sensitive"
}
},
"required": ["pattern"]
},
"uiHints": ["filter"]
},
{
"name": "filterFileType",
"title": "Filter by file type",
"description": "Filter assets by file type",
"types": ["AssetV1"],
"schema": {
"type": "object",
"properties": {
"fileTypes": {
"title": "File types",
"description": "Allowed file types",
"type": "string",
"array": true,
"enum": ["image", "video"]
}
},
"required": ["fileTypes"]
},
"uiHints": ["filter"]
},
{
"name": "filterPerson",
"title": "Filter by person",
"description": "Filter by detected person",
"types": ["AssetV1"],
"schema": {
"properties": {
"personIds": {
"type": "string",
"array": true,
"title": "Person IDs",
"description": "List of person to match",
"uiHint": "personI"
},
"matchAny": {
"type": "boolean",
"title": "Match any",
"default": true,
"description": "Match any name (true) or require all names (false)"
}
},
"required": ["personIds"]
},
"uiHints": ["filter"]
},
{
"name": "assetArchive",
"title": "Archive asset",
"description": "Change asset visibility to archive",
"types": ["AssetV1"],
"schema": {
"properties": {
"inverse": {
"title": "Inverse",
"description": "When true will unarchive any archived assets",
"type": "boolean"
}
}
}
},
{
"name": "assetLock",
"title": "Move to locked folder",
"description": "Change visibility to locked",
"types": ["AssetV1"]
},
{
"name": "assetTimeline",
"title": "Move to timeline",
"description": "Change visibility to timeline",
"types": ["AssetV1"]
},
{
"name": "assetVisibility",
"title": "Update visibility",
"description": "Change visibility to selected option",
"types": ["AssetV1"],
"schema": {
"properties": {
"visibility": {
"title": "Visibility",
"description": "Asset visibility",
"type": "string",
"enum": ["archive", "timeline", "locked"]
}
},
"required": ["visibility"]
}
},
{
"name": "assetFavorite",
"title": "Favorite",
"description": "Favorite an asset",
"types": ["AssetV1"],
"schema": {
"type": "object",
"properties": {
"inverse": {
"type": "boolean",
"title": "Inverse",
"description": "Unfavorite by default, set to true to favorite instead",
"default": false
}
}
}
},
{
"name": "assetAddToAlbums",
"title": "Add to Album(s)",
"description": "Add asset to selected albums",
"types": ["AssetV1"],
"hostFunctions": true,
"schema": {
"type": "object",
"properties": {
"albumIds": {
"type": "string",
"title": "Album IDs",
"array": true,
"description": "Target album IDs",
"uiHint": "albumId"
}
},
"required": ["albumIds"]
}
},
{
"name": "noop1",
"title": "DEV: Nested properties",
"description": "Example configuration with nested properties",
"types": ["AssetV1"],
"schema": {
"type": "object",
"properties": {
"number1": {
"type": "number",
"title": "Number 1",
"description": "Basic number"
},
"number2": {
"type": "number",
"title": "Number 2",
"array": true,
"description": "List of numbers"
},
"string1": {
"type": "string",
"title": "String 1",
"description": "Basic string"
},
"string2": {
"type": "string",
"title": "String 2",
"array": true,
"description": "List of strings"
},
"string3": {
"type": "string",
"title": "String 3",
"enum": ["choice-1", "choice-2"],
"description": "Select from a list"
},
"nested": {
"type": "object",
"title": "Nested",
"description": "Nested properties for nesting",
"properties": {
"nested1": {
"type": "string",
"title": "Nested 1",
"description": "Nested string"
},
"nested2": {
"type": "number",
"title": "Nested 2",
"description": "Nested number"
},
"nested3": {
"type": "object",
"title": "Nested 3",
"description": "Nested again",
"properties": {
"nested4": {
"type": "boolean",
"title": "Nested 4",
"description": "Nested, nested boolean"
}
}
}
}
}
}
}
},
{
"name": "noop2",
"title": "DEV: Album pickers",
"description": "Example configuration with album pickers",
"types": ["AssetV1"],
"schema": {
"properties": {
"albumId": {
"type": "string",
"title": "Album ID",
"description": "Target album ID",
"uiHint": "albumId"
},
"albumIds": {
"type": "string",
"title": "Album IDs",
"description": "Target album IDs",
"array": true,
"uiHint": "albumId"
}
}
}
}
]
}
+6
View File
@@ -0,0 +1,6 @@
[tasks.install]
run = "pnpm install --frozen-lockfile"
[tasks.build]
depends = ["install"]
run = "pnpm --filter @immich/plugin-sdk --filter @immich/plugin-core build"
@@ -1,5 +1,5 @@
{
"name": "plugins",
"name": "@immich/plugin-core",
"version": "1.0.0",
"description": "",
"main": "src/index.ts",
@@ -13,6 +13,7 @@
"license": "AGPL-3.0",
"devDependencies": {
"@extism/js-pdk": "^1.0.1",
"@immich/plugin-sdk": "workspace:*",
"esbuild": "^0.28.0",
"typescript": "^6.0.0"
}
+19
View File
@@ -0,0 +1,19 @@
// copy from
// import '@immich/plugin-sdk/host-functions';
declare module 'extism:host' {
interface user {
albumAddAssets(ptr: PTR): I64;
addAssetsToAlbums(ptr: PTR): I64;
}
}
declare module 'main' {
export function assetFileFilter(): I32;
export function assetFavorite(): I32;
export function assetVisibility(): I32;
export function assetArchive(): I32;
export function assetLock(): I32;
export function assetTimeline(): I32;
export function assetTrash(): I32;
export function assetAddToAlbums(): I32;
}
+111
View File
@@ -0,0 +1,111 @@
import { AssetStatus, AssetVisibility, WorkflowType, wrapper } from '@immich/plugin-sdk';
type AssetFileFilterConfig = {
pattern: string;
matchType?: 'contains' | 'exact' | 'regex' | 'startsWith';
caseSensitive?: boolean;
};
export const assetFileFilter = () => {
return wrapper<WorkflowType.AssetV1, AssetFileFilterConfig>(({ data, config }) => {
const { pattern, matchType = 'contains', caseSensitive = false } = config;
const { asset } = data;
const fileName = asset.originalFileName || '';
const searchName = caseSensitive ? fileName : fileName.toLowerCase();
const searchPattern = caseSensitive ? pattern : pattern.toLowerCase();
switch (matchType) {
case 'contains': {
return { workflow: { continue: searchName.includes(searchPattern) } };
}
case 'exact': {
return { workflow: { continue: searchName === searchPattern } };
}
case 'startsWith': {
return { workflow: { continue: searchName.startsWith(searchPattern) } };
}
case 'regex': {
const flags = caseSensitive ? '' : 'i';
const regex = new RegExp(searchPattern, flags);
return { workflow: { continue: regex.test(fileName) } };
}
default: {
return {};
}
}
});
};
export const assetFavorite = () => {
return wrapper<WorkflowType.AssetV1, { inverse?: boolean }>(({ config, data }) => {
const target = config.inverse ? false : true;
if (target !== data.asset.isFavorite) {
return {
changes: {
asset: { isFavorite: target },
},
};
}
});
};
export const assetVisibility = () => {
return wrapper<WorkflowType.AssetV1, { visibility: AssetVisibility }>(({ config }) => ({
changes: { asset: { visibility: config.visibility } },
}));
};
export const assetArchive = () => {
return wrapper<WorkflowType.AssetV1, { inverse?: boolean }>(({ config, data }) => {
if (!config.inverse && data.asset.visibility !== AssetVisibility.Archive) {
return { changes: { asset: { visibility: AssetVisibility.Archive } } };
}
if (config.inverse && data.asset.visibility === AssetVisibility.Archive) {
return { changes: { asset: { visibility: AssetVisibility.Timeline } } };
}
return {};
});
};
export const assetLock = () => {
return wrapper<WorkflowType.AssetV1, { inverse?: boolean }>(({ config, data }) => {
if (!config.inverse && data.asset.visibility !== AssetVisibility.Locked) {
return { changes: { asset: { visibility: AssetVisibility.Locked } } };
}
if (config.inverse && data.asset.visibility === AssetVisibility.Locked) {
return { changes: { asset: { visibility: AssetVisibility.Timeline } } };
}
return {};
});
};
export const assetTrash = () => {
return wrapper<WorkflowType.AssetV1, { inverse?: boolean }>(({ config, data }) => ({
changes: {
asset: config.inverse
? { deletedAt: null, status: AssetStatus.Active }
: { deletedAt: new Date(), status: AssetStatus.Trashed },
},
}));
};
export const assetAddToAlbums = () => {
return wrapper<WorkflowType.AssetV1, { albumIds: string[] }>(({ config, data, functions }) => {
if (config.albumIds.length === 1) {
functions.albumAddAssets(config.albumIds[0], [data.asset.id]);
return {};
}
functions.addAssetsToAlbums({ albumIds: config.albumIds, assetIds: [data.asset.id] });
return {};
});
};
@@ -1,20 +1,24 @@
{
"compilerOptions": {
"target": "es2020", // Specify ECMAScript target version
"module": "commonjs", // Specify module code generation
"lib": ["es2020"], // Specify a list of library files to be included in the compilation
"types": ["./src/index.d.ts", "./node_modules/@extism/js-pdk"], // Specify a list of type definition files to be included in the compilation
"strict": true, // Enable all strict type-checking options
"esModuleInterop": true, // Enables compatibility with Babel-style module imports
"skipLibCheck": true, // Skip type checking of declaration files
"allowJs": true, // Allow JavaScript files to be compiled
"declaration": true,
"emitDeclarationOnly": true,
"esModuleInterop": true, // Enables compatibility with Babel-style module imports
"lib": ["es2020"], // Specify a list of library files to be included in the compilation
"module": "nodenext", // Specify module code generation
"moduleResolution": "nodenext",
"noEmit": true, // Do not emit outputs (no .js or .d.ts files)
"outDir": "./dist",
"rootDir": "./src",
"noEmit": true // Do not emit outputs (no .js or .d.ts files)
"skipLibCheck": true, // Skip type checking of declaration files
"strict": true, // Enable all strict type-checking options
"target": "es2020", // Specify ECMAScript target version
"types": ["./src/index.d.ts", "./node_modules/@extism/js-pdk"] // Specify a list of type definition files to be included in the compilation
},
"include": [
"src/**/*.ts" // Include all TypeScript files in src directory
],
"exclude": [
"node_modules" // Exclude the node_modules directory
],
"include": [
"src/**/*.ts" // Include all TypeScript files in src directory
]
}
+2
View File
@@ -0,0 +1,2 @@
/dist
/node_modules
+11
View File
@@ -0,0 +1,11 @@
import esbuild from 'esbuild';
esbuild.build({
entryPoints: ['src/index.ts'],
outdir: 'dist',
bundle: true,
sourcemap: false,
minify: false,
format: 'esm',
target: ['es2020'],
});
+38
View File
@@ -0,0 +1,38 @@
{
"name": "@immich/plugin-sdk",
"version": "0.0.0",
"description": "",
"main": "index.js",
"type": "module",
"exports": {
"./host-functions": {
"import": "./dist/host-functions.js",
"types": "./dist/host-functions.d.ts"
},
".": {
"import": "./dist/index.js",
"types": "./dist/index.d.ts"
}
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "node esbuild.js && tsc --emitDeclarationOnly && tsc-alias"
},
"files": [
"dist"
],
"keywords": [],
"author": "",
"license": "GNU Affero General Public License version 3",
"packageManager": "pnpm@10.30.3",
"devDependencies": {
"@extism/js-pdk": "^1.1.1",
"@types/node": "^24.11.0",
"esbuild": "^0.27.3",
"tsc-alias": "^1.8.16",
"typescript": "^5.9.3"
},
"peerDependencies": {
"@extism/js-pdk": "^1.1.1"
}
}
+33
View File
@@ -0,0 +1,33 @@
export enum WorkflowTrigger {
AssetCreate = 'AssetCreate',
PersonRecognized = 'PersonRecognized',
}
export enum WorkflowType {
AssetV1 = 'AssetV1',
AssetPersonV1 = 'AssetPersonV1',
}
export enum AssetType {
Image = 'IMAGE',
Video = 'VIDEO',
Audio = 'AUDIO',
Other = 'OTHER',
}
export enum AssetStatus {
Active = 'active',
Trashed = 'trashed',
Deleted = 'deleted',
}
export enum AssetVisibility {
Archive = 'archive',
Timeline = 'timeline',
/**
* Video part of the LivePhotos and MotionPhotos
*/
Hidden = 'hidden',
Locked = 'locked',
}
+51
View File
@@ -0,0 +1,51 @@
declare module 'extism:host' {
interface user {
albumAddAssets(ptr: PTR): I64;
addAssetsToAlbums(ptr: PTR): I64;
}
}
const host = Host.getFunctions();
type HostFunctionName = keyof typeof host;
type HostFunctionSuccessResult<T> = { success: true; response: T };
type HostFunctionErrorResult = {
success: false;
status: number;
message: string;
};
type HostFunctionResult<T> =
| HostFunctionSuccessResult<T>
| HostFunctionErrorResult;
const call = <T, R>(name: HostFunctionName, authToken: string, args: T) => {
const pointer1 = Memory.fromString(JSON.stringify({ authToken, args }));
const fn = host[name];
const handler = Memory.find(fn(pointer1.offset));
try {
const result = JSON.parse(handler.readString()) as HostFunctionResult<R>;
if (result.success) {
return result.response;
}
throw new Error(
`Failed to call host function "${String(name)}", received ${result.status} - ${JSON.stringify(result.message)}`,
);
} finally {
handler.free();
pointer1.free();
}
};
type AlbumsToAssets = {
assetIds: string[];
albumIds: string[];
};
export const hostFunctions = (authToken: string) => ({
albumAddAssets: (albumId: string, assetIds: string[]) =>
call('albumAddAssets', authToken, [albumId, { ids: assetIds }]),
addAssetsToAlbums: ({ assetIds, albumIds }: AlbumsToAssets) =>
call('addAssetsToAlbums', authToken, [{ albumIds, assetIds }]),
});
+4
View File
@@ -0,0 +1,4 @@
export * from 'src/enum.js';
export * from 'src/host-functions.js';
export * from 'src/sdk.js';
export * from 'src/types.js';
+43
View File
@@ -0,0 +1,43 @@
import type { WorkflowType } from 'src/enum.js';
import { hostFunctions } from 'src/host-functions.js';
import type {
ConfigValue,
WorkflowEventPayload,
WorkflowResponse,
} from 'src/types.js';
export const wrapper = <
T extends WorkflowType = WorkflowType,
TConfig extends ConfigValue = ConfigValue,
>(
fn: (
payload: WorkflowEventPayload<T, TConfig> & {
functions: ReturnType<typeof hostFunctions>;
},
) => WorkflowResponse<T> | undefined,
) => {
const input = Host.inputString();
try {
const event = JSON.parse(input) as WorkflowEventPayload<T, TConfig>;
// const debug = event.workflow.debug ?? false;
console.debug(
`Inputs: trigger=${event.trigger}, event=${event.type}, config=${JSON.stringify(event.config)}`,
);
const response =
fn({ ...event, functions: hostFunctions(event.workflow.authToken) }) ??
{};
console.debug(
`Outputs: workflow=${JSON.stringify(response.workflow)}, changes=${JSON.stringify(response.changes)}, data=${JSON.stringify(response.data)}`,
);
const output = JSON.stringify(response);
Host.outputString(output);
} catch (error: Error | any) {
console.error(`Unhandled plugin exception: ${error.message || error}`);
throw error;
}
};
+129
View File
@@ -0,0 +1,129 @@
import type {
AssetStatus,
AssetType,
AssetVisibility,
WorkflowTrigger,
WorkflowType,
} from 'src/enum.js';
type DeepPartial<T> = T extends Date
? T
: T extends Record<string, unknown>
? { [K in keyof T]?: DeepPartial<T[K]> }
: T extends Array<infer R>
? DeepPartial<R>[]
: T;
export type WorkflowEventMap = {
[WorkflowType.AssetV1]: AssetV1;
[WorkflowType.AssetPersonV1]: AssetPersonV1;
};
export type WorkflowEventData<T extends WorkflowType> = WorkflowEventMap[T];
export type WorkflowEventPayload<
T extends WorkflowType = WorkflowType,
TConfig = WorkflowStepConfig,
> = {
trigger: WorkflowTrigger;
type: T;
data: WorkflowEventData<T>;
config: TConfig;
workflow: {
id: string;
authToken: string;
stepId: string;
debug?: boolean;
};
};
export type WorkflowChanges<T extends WorkflowType = WorkflowType> =
DeepPartial<WorkflowEventData<T>>;
export type WorkflowResponse<T extends WorkflowType = WorkflowType> = {
workflow?: {
/** stop the workflow */
continue?: boolean;
};
changes?: WorkflowChanges<T>;
/** data to be passed to the next workflow step */
data?: Record<string, unknown>;
};
export type WorkflowStepConfig = {
[key: string]: ConfigValue;
};
export type ConfigValue =
| string
| number
| boolean
| null
| ConfigValue[]
| { [key: string]: ConfigValue };
export type AssetV1 = {
asset: {
id: string;
ownerId: string;
type: AssetType;
originalPath: string;
fileCreatedAt: Date;
fileModifiedAt: Date;
isFavorite: boolean;
checksum: Buffer; // sha1 checksum
livePhotoVideoId: string | null;
updatedAt: Date;
createdAt: Date;
originalFileName: string;
isOffline: boolean;
libraryId: string | null;
isExternal: boolean;
deletedAt: Date | null;
localDateTime: Date;
stackId: string | null;
duplicateId: string | null;
status: AssetStatus;
visibility: AssetVisibility;
isEdited: boolean;
exifInfo: {
make: string | null;
model: string | null;
exifImageWidth: number | null;
exifImageHeight: number | null;
fileSizeInByte: number | null;
orientation: string | null;
dateTimeOriginal: Date | null;
modifyDate: Date | null;
lensModel: string | null;
fNumber: number | null;
focalLength: number | null;
iso: number | null;
latitude: number | null;
longitude: number | null;
city: string | null;
state: string | null;
country: string | null;
description: string | null;
fps: number | null;
exposureTime: string | null;
livePhotoCID: string | null;
timeZone: string | null;
projectionType: string | null;
profileDescription: string | null;
colorspace: string | null;
bitsPerSample: number | null;
autoStackId: string | null;
rating: number | null;
tags: string[] | null;
updatedAt: Date | null;
} | null;
};
};
export type AssetPersonV1 = AssetV1 & {
person: {
id: string;
name: string;
};
};
+26
View File
@@ -0,0 +1,26 @@
{
"compilerOptions": {
"declaration": true,
"emitDeclarationOnly": true,
"esModuleInterop": true,
"exactOptionalPropertyTypes": true,
"isolatedModules": true,
"lib": ["esnext"],
"module": "nodenext",
"moduleDetection": "force",
"noUncheckedIndexedAccess": true,
"noUncheckedSideEffectImports": true,
"outDir": "./dist",
"paths": {
"src/*": ["./src/*"]
},
"removeComments": true,
"rootDir": "./src",
"skipLibCheck": true,
"sourceMap": false,
"strict": true,
"target": "esnext",
"types": ["node", "@extism/js-pdk"],
"verbatimModuleSyntax": true
}
}
-2
View File
@@ -1,2 +0,0 @@
node_modules
dist
-26
View File
@@ -1,26 +0,0 @@
Copyright 2024, The Extism Authors.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-12
View File
@@ -1,12 +0,0 @@
const esbuild = require('esbuild');
esbuild
.build({
entryPoints: ['src/index.ts'],
outdir: 'dist',
bundle: true,
sourcemap: true,
minify: false, // might want to use true for production build
format: 'cjs', // needs to be CJS for now
target: ['es2020'] // don't go over es2020 because quickjs doesn't support it
})
-159
View File
@@ -1,159 +0,0 @@
{
"name": "immich-core",
"version": "2.0.1",
"title": "Immich Core",
"description": "Core workflow capabilities for Immich",
"author": "Immich Team",
"wasm": {
"path": "dist/plugin.wasm"
},
"filters": [
{
"methodName": "filterFileName",
"title": "Filter by filename",
"description": "Filter assets by filename pattern using text matching or regular expressions",
"supportedContexts": [
"asset"
],
"schema": {
"type": "object",
"properties": {
"pattern": {
"type": "string",
"title": "Filename pattern",
"description": "Text or regex pattern to match against filename"
},
"matchType": {
"type": "string",
"title": "Match type",
"enum": [
"contains",
"regex",
"exact"
],
"default": "contains",
"description": "Type of pattern matching to perform"
},
"caseSensitive": {
"type": "boolean",
"default": false,
"description": "Whether matching should be case-sensitive"
}
},
"required": [
"pattern"
]
}
},
{
"methodName": "filterFileType",
"title": "Filter by file type",
"description": "Filter assets by file type",
"supportedContexts": [
"asset"
],
"schema": {
"type": "object",
"properties": {
"fileTypes": {
"type": "array",
"title": "File types",
"items": {
"type": "string",
"enum": [
"image",
"video"
]
},
"description": "Allowed file types"
}
},
"required": [
"fileTypes"
]
}
},
{
"methodName": "filterPerson",
"title": "Filter by person",
"description": "Filter by detected person",
"supportedContexts": [
"person"
],
"schema": {
"type": "object",
"properties": {
"personIds": {
"type": "array",
"title": "Person IDs",
"items": {
"type": "string"
},
"description": "List of person to match",
"subType": "people-picker"
},
"matchAny": {
"type": "boolean",
"default": true,
"description": "Match any name (true) or require all names (false)"
}
},
"required": [
"personIds"
]
}
}
],
"actions": [
{
"methodName": "actionArchive",
"title": "Archive",
"description": "Move the asset to archive",
"supportedContexts": [
"asset"
],
"schema": {}
},
{
"methodName": "actionFavorite",
"title": "Favorite",
"description": "Mark the asset as favorite or unfavorite",
"supportedContexts": [
"asset"
],
"schema": {
"type": "object",
"properties": {
"favorite": {
"type": "boolean",
"default": true,
"description": "Set favorite (true) or unfavorite (false)"
}
}
}
},
{
"methodName": "actionAddToAlbum",
"title": "Add to Album",
"description": "Add the item to a specified album",
"supportedContexts": [
"asset",
"person"
],
"schema": {
"type": "object",
"properties": {
"albumId": {
"type": "string",
"title": "Album ID",
"description": "Target album ID",
"subType": "album-picker"
}
},
"required": [
"albumId"
]
}
}
]
}
-11
View File
@@ -1,11 +0,0 @@
[tools]
"github:extism/cli" = "1.6.3"
"github:webassembly/binaryen" = "version_124"
"github:extism/js-pdk" = "1.6.0"
[tasks.install]
run = "pnpm install --frozen-lockfile"
[tasks.build]
depends = ["install"]
run = "pnpm run build"
-533
View File
@@ -1,533 +0,0 @@
{
"name": "plugins",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "plugins",
"version": "1.0.0",
"license": "AGPL-3.0",
"devDependencies": {
"@extism/js-pdk": "^1.0.1",
"esbuild": "^0.28.0",
"typescript": "^6.0.0"
}
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.0.tgz",
"integrity": "sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"aix"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.0.tgz",
"integrity": "sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-arm64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.0.tgz",
"integrity": "sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-x64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.0.tgz",
"integrity": "sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.0.tgz",
"integrity": "sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/darwin-x64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.0.tgz",
"integrity": "sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/freebsd-arm64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.0.tgz",
"integrity": "sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/freebsd-x64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.0.tgz",
"integrity": "sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-arm": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.0.tgz",
"integrity": "sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-arm64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.0.tgz",
"integrity": "sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-ia32": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.0.tgz",
"integrity": "sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.0.tgz",
"integrity": "sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg==",
"cpu": [
"loong64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-mips64el": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.0.tgz",
"integrity": "sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w==",
"cpu": [
"mips64el"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-ppc64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.0.tgz",
"integrity": "sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-riscv64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.0.tgz",
"integrity": "sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ==",
"cpu": [
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-s390x": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.0.tgz",
"integrity": "sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q==",
"cpu": [
"s390x"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-x64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.0.tgz",
"integrity": "sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/netbsd-arm64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.0.tgz",
"integrity": "sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/netbsd-x64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.0.tgz",
"integrity": "sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openbsd-arm64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.0.tgz",
"integrity": "sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openbsd-x64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.0.tgz",
"integrity": "sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openharmony-arm64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.0.tgz",
"integrity": "sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openharmony"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/sunos-x64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.0.tgz",
"integrity": "sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"sunos"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-arm64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.0.tgz",
"integrity": "sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-ia32": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.0.tgz",
"integrity": "sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-x64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.0.tgz",
"integrity": "sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@extism/js-pdk": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@extism/js-pdk/-/js-pdk-1.1.1.tgz",
"integrity": "sha512-VZLn/dX0ttA1uKk2PZeR/FL3N+nA1S5Vc7E5gdjkR60LuUIwCZT9cYON245V4HowHlBA7YOegh0TLjkx+wNbrA==",
"dev": true,
"license": "BSD-Clause-3",
"dependencies": {
"urlpattern-polyfill": "^8.0.2"
}
},
"node_modules/esbuild": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.0.tgz",
"integrity": "sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"bin": {
"esbuild": "bin/esbuild"
},
"engines": {
"node": ">=18"
},
"optionalDependencies": {
"@esbuild/aix-ppc64": "0.28.0",
"@esbuild/android-arm": "0.28.0",
"@esbuild/android-arm64": "0.28.0",
"@esbuild/android-x64": "0.28.0",
"@esbuild/darwin-arm64": "0.28.0",
"@esbuild/darwin-x64": "0.28.0",
"@esbuild/freebsd-arm64": "0.28.0",
"@esbuild/freebsd-x64": "0.28.0",
"@esbuild/linux-arm": "0.28.0",
"@esbuild/linux-arm64": "0.28.0",
"@esbuild/linux-ia32": "0.28.0",
"@esbuild/linux-loong64": "0.28.0",
"@esbuild/linux-mips64el": "0.28.0",
"@esbuild/linux-ppc64": "0.28.0",
"@esbuild/linux-riscv64": "0.28.0",
"@esbuild/linux-s390x": "0.28.0",
"@esbuild/linux-x64": "0.28.0",
"@esbuild/netbsd-arm64": "0.28.0",
"@esbuild/netbsd-x64": "0.28.0",
"@esbuild/openbsd-arm64": "0.28.0",
"@esbuild/openbsd-x64": "0.28.0",
"@esbuild/openharmony-arm64": "0.28.0",
"@esbuild/sunos-x64": "0.28.0",
"@esbuild/win32-arm64": "0.28.0",
"@esbuild/win32-ia32": "0.28.0",
"@esbuild/win32-x64": "0.28.0"
}
},
"node_modules/typescript": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz",
"integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
},
"node_modules/urlpattern-polyfill": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-8.0.2.tgz",
"integrity": "sha512-Qp95D4TPJl1kC9SKigDcqgyM2VDVO4RiJc2d4qe5GrYm+zbIQCWWKAFaJNQ4BhdFeDGwBmAxqJBwWSJDb9T3BQ==",
"dev": true,
"license": "MIT"
}
}
}
-12
View File
@@ -1,12 +0,0 @@
declare module 'main' {
export function filterFileName(): I32;
export function actionAddToAlbum(): I32;
export function actionArchive(): I32;
}
declare module 'extism:host' {
interface user {
updateAsset(ptr: PTR): I32;
addAssetToAlbum(ptr: PTR): I32;
}
}
-71
View File
@@ -1,71 +0,0 @@
const { updateAsset, addAssetToAlbum } = Host.getFunctions();
function parseInput() {
return JSON.parse(Host.inputString());
}
function returnOutput(output: any) {
Host.outputString(JSON.stringify(output));
return 0;
}
export function filterFileName() {
const input = parseInput();
const { data, config } = input;
const { pattern, matchType = 'contains', caseSensitive = false } = config;
const fileName = data.asset.originalFileName || data.asset.fileName || '';
const searchName = caseSensitive ? fileName : fileName.toLowerCase();
const searchPattern = caseSensitive ? pattern : pattern.toLowerCase();
let passed = false;
if (matchType === 'exact') {
passed = searchName === searchPattern;
} else if (matchType === 'regex') {
const flags = caseSensitive ? '' : 'i';
const regex = new RegExp(searchPattern, flags);
passed = regex.test(fileName);
} else {
// contains
passed = searchName.includes(searchPattern);
}
return returnOutput({ passed });
}
export function actionAddToAlbum() {
const input = parseInput();
const { authToken, config, data } = input;
const { albumId } = config;
const ptr = Memory.fromString(
JSON.stringify({
authToken,
assetId: data.asset.id,
albumId: albumId,
}),
);
addAssetToAlbum(ptr.offset);
ptr.free();
return returnOutput({ success: true });
}
export function actionArchive() {
const input = parseInput();
const { authToken, data } = input;
const ptr = Memory.fromString(
JSON.stringify({
authToken,
id: data.asset.id,
visibility: 'archive',
}),
);
updateAsset(ptr.offset);
ptr.free();
return returnOutput({ success: true });
}
+150 -141
View File
@@ -1478,72 +1478,33 @@ export type PersonStatisticsResponseDto = {
/** Number of assets */
assets: number;
};
export type PluginJsonSchemaProperty = {
additionalProperties?: boolean | PluginJsonSchemaProperty;
"default"?: any;
description?: string;
"enum"?: string[];
items?: PluginJsonSchemaProperty;
properties?: {
[key: string]: PluginJsonSchemaProperty;
};
required?: string[];
"type"?: PluginJsonSchemaType;
};
export type PluginJsonSchema = {
additionalProperties?: boolean;
description?: string;
properties?: {
[key: string]: PluginJsonSchemaProperty;
};
required?: string[];
"type"?: PluginJsonSchemaType;
};
export type PluginActionResponseDto = {
/** Action description */
export type PluginMethodResponseDto = {
/** Description */
description: string;
/** Action ID */
id: string;
/** Method name */
methodName: string;
/** Plugin ID */
pluginId: string;
/** Action schema */
schema: (PluginJsonSchema) | null;
/** Supported contexts */
supportedContexts: PluginContextType[];
/** Action title */
title: string;
};
export type PluginFilterResponseDto = {
/** Filter description */
description: string;
/** Filter ID */
id: string;
/** Method name */
methodName: string;
/** Plugin ID */
pluginId: string;
/** Filter schema */
schema: (PluginJsonSchema) | null;
/** Supported contexts */
supportedContexts: PluginContextType[];
/** Filter title */
hostFunctions: boolean;
/** Key */
key: string;
/** Name */
name: string;
schema?: {};
/** Title */
title: string;
/** Workflow types */
types: WorkflowType[];
/** Ui hints */
uiHints: string[];
};
export type PluginResponseDto = {
/** Plugin actions */
actions: PluginActionResponseDto[];
/** Plugin author */
author: string;
/** Creation date */
createdAt: string;
/** Plugin description */
description: string;
/** Plugin filters */
filters: PluginFilterResponseDto[];
/** Plugin ID */
id: string;
/** Plugin methods */
methods: PluginMethodResponseDto[];
/** Plugin name */
name: string;
/** Plugin title */
@@ -1553,10 +1514,6 @@ export type PluginResponseDto = {
/** Plugin version */
version: string;
};
export type PluginTriggerResponseDto = {
contextType: PluginContextType;
"type": PluginTriggerType;
};
export type QueueResponseDto = {
/** Whether the queue is paused */
isPaused: boolean;
@@ -2710,89 +2667,81 @@ export type CreateProfileImageResponseDto = {
/** User ID */
userId: string;
};
export type PluginConfigValue = any;
export type WorkflowActionConfig = {
[key: string]: PluginConfigValue;
};
export type WorkflowActionResponseDto = {
actionConfig: (WorkflowActionConfig) | null;
/** Action ID */
id: string;
/** Action order */
order: number;
/** Plugin action ID */
pluginActionId: string;
/** Workflow ID */
workflowId: string;
};
export type WorkflowFilterConfig = {
[key: string]: PluginConfigValue;
};
export type WorkflowFilterResponseDto = {
filterConfig: (WorkflowFilterConfig) | null;
/** Filter ID */
id: string;
/** Filter order */
order: number;
/** Plugin filter ID */
pluginFilterId: string;
/** Workflow ID */
workflowId: string;
export type WorkflowStepDto = {
/** Step configuration */
config: {
[key: string]: any;
} | null;
/** Step is enabled */
enabled?: boolean;
/** Step plugin method */
method: string;
};
export type WorkflowResponseDto = {
/** Workflow actions */
actions: WorkflowActionResponseDto[];
/** Creation date */
createdAt: string;
/** Workflow description */
description: string;
description: string | null;
/** Workflow enabled */
enabled: boolean;
/** Workflow filters */
filters: WorkflowFilterResponseDto[];
/** Workflow ID */
id: string;
/** Workflow name */
name: string | null;
/** Owner user ID */
ownerId: string;
triggerType: PluginTriggerType;
};
export type WorkflowActionItemDto = {
actionConfig?: WorkflowActionConfig;
/** Plugin action ID */
pluginActionId: string;
};
export type WorkflowFilterItemDto = {
filterConfig?: WorkflowFilterConfig;
/** Plugin filter ID */
pluginFilterId: string;
/** Workflow steps */
steps: WorkflowStepDto[];
/** Workflow trigger type */
trigger: WorkflowTrigger;
/** Update date */
updatedAt: string;
};
export type WorkflowCreateDto = {
/** Workflow actions */
actions: WorkflowActionItemDto[];
/** Workflow description */
description?: string;
description?: string | null;
/** Workflow enabled */
enabled?: boolean;
/** Workflow filters */
filters: WorkflowFilterItemDto[];
/** Workflow name */
name: string;
triggerType: PluginTriggerType;
name?: string | null;
steps?: WorkflowStepDto[];
/** Workflow trigger type */
trigger: WorkflowTrigger;
};
export type WorkflowTriggerResponseDto = {
/** Trigger type */
trigger: WorkflowTrigger;
/** Workflow types */
types: WorkflowType[];
};
export type WorkflowUpdateDto = {
/** Workflow actions */
actions?: WorkflowActionItemDto[];
/** Workflow description */
description?: string;
description?: string | null;
/** Workflow enabled */
enabled?: boolean;
/** Workflow filters */
filters?: WorkflowFilterItemDto[];
/** Workflow name */
name?: string;
triggerType?: PluginTriggerType;
name?: string | null;
steps?: WorkflowStepDto[];
/** Workflow trigger type */
trigger?: WorkflowTrigger;
};
export type WorkflowShareStepDto = {
/** Step configuration */
config: {
[key: string]: any;
} | null;
/** Step is enabled */
enabled?: boolean;
/** Step plugin method */
method: string;
};
export type WorkflowShareResponseDto = {
/** Workflow description */
description: string | null;
/** Workflow name */
name: string | null;
/** Workflow steps */
steps: WorkflowShareStepDto[];
/** Workflow trigger type */
trigger: WorkflowTrigger;
};
export type LicenseResponseDto = UserLicense;
export type SyncAckV1 = {};
@@ -5240,22 +5189,56 @@ export function getPersonThumbnail({ id }: {
/**
* List all plugins
*/
export function getPlugins(opts?: Oazapfts.RequestOpts) {
export function searchPlugins({ description, enabled, id, name, title, version }: {
description?: string;
enabled?: boolean;
id?: string;
name?: string;
title?: string;
version?: string;
}, opts?: Oazapfts.RequestOpts) {
return oazapfts.ok(oazapfts.fetchJson<{
status: 200;
data: PluginResponseDto[];
}>("/plugins", {
}>(`/plugins${QS.query(QS.explode({
description,
enabled,
id,
name,
title,
version
}))}`, {
...opts
}));
}
/**
* List all plugin triggers
* Retrieve plugin methods
*/
export function getPluginTriggers(opts?: Oazapfts.RequestOpts) {
export function searchPluginMethods({ description, enabled, id, name, pluginName, pluginVersion, title, trigger, $type }: {
description?: string;
enabled?: boolean;
id?: string;
name?: string;
pluginName?: string;
pluginVersion?: string;
title?: string;
trigger?: WorkflowTrigger;
$type?: WorkflowType;
}, opts?: Oazapfts.RequestOpts) {
return oazapfts.ok(oazapfts.fetchJson<{
status: 200;
data: PluginTriggerResponseDto[];
}>("/plugins/triggers", {
data: PluginMethodResponseDto[];
}>(`/plugins/methods${QS.query(QS.explode({
description,
enabled,
id,
name,
pluginName,
pluginVersion,
title,
trigger,
"type": $type
}))}`, {
...opts
}));
}
@@ -6631,11 +6614,23 @@ export function getUniqueOriginalPaths(opts?: Oazapfts.RequestOpts) {
/**
* List all workflows
*/
export function getWorkflows(opts?: Oazapfts.RequestOpts) {
export function searchWorkflows({ description, enabled, id, name, trigger }: {
description?: string;
enabled?: boolean;
id?: string;
name?: string;
trigger?: WorkflowTrigger;
}, opts?: Oazapfts.RequestOpts) {
return oazapfts.ok(oazapfts.fetchJson<{
status: 200;
data: WorkflowResponseDto[];
}>("/workflows", {
}>(`/workflows${QS.query(QS.explode({
description,
enabled,
id,
name,
trigger
}))}`, {
...opts
}));
}
@@ -6654,6 +6649,17 @@ export function createWorkflow({ workflowCreateDto }: {
body: workflowCreateDto
})));
}
/**
* List all workflow triggers
*/
export function getWorkflowTriggers(opts?: Oazapfts.RequestOpts) {
return oazapfts.ok(oazapfts.fetchJson<{
status: 200;
data: WorkflowTriggerResponseDto[];
}>("/workflows/triggers", {
...opts
}));
}
/**
* Delete a workflow
*/
@@ -6694,6 +6700,19 @@ export function updateWorkflow({ id, workflowUpdateDto }: {
body: workflowUpdateDto
})));
}
/**
* Retrieve a workflow
*/
export function getWorkflowForShare({ id }: {
id: string;
}, opts?: Oazapfts.RequestOpts) {
return oazapfts.ok(oazapfts.fetchJson<{
status: 200;
data: WorkflowShareResponseDto;
}>(`/workflows/${encodeURIComponent(id)}/share`, {
...opts
}));
}
export enum ReactionLevel {
Album = "album",
Asset = "asset"
@@ -7017,21 +7036,11 @@ export enum PartnerDirection {
SharedBy = "shared-by",
SharedWith = "shared-with"
}
export enum PluginJsonSchemaType {
String = "string",
Number = "number",
Integer = "integer",
Boolean = "boolean",
Object = "object",
Array = "array",
Null = "null"
export enum WorkflowType {
AssetV1 = "AssetV1",
AssetPersonV1 = "AssetPersonV1"
}
export enum PluginContextType {
Asset = "asset",
Album = "album",
Person = "person"
}
export enum PluginTriggerType {
export enum WorkflowTrigger {
AssetCreate = "AssetCreate",
PersonRecognized = "PersonRecognized"
}
@@ -7098,7 +7107,7 @@ export enum JobName {
VersionCheck = "VersionCheck",
OcrQueueAll = "OcrQueueAll",
Ocr = "Ocr",
WorkflowRun = "WorkflowRun"
WorkflowAssetCreate = "WorkflowAssetCreate"
}
export enum SearchSuggestionType {
Country = "country",