@webiny/api-file-manager 6.3.0-beta.1 → 6.3.0-beta.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/package.json
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webiny/api-file-manager",
|
|
3
|
-
"version": "6.3.0-beta.
|
|
3
|
+
"version": "6.3.0-beta.3",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"
|
|
5
|
+
"exports": {
|
|
6
|
+
".": "./index.js",
|
|
7
|
+
"./*": "./*"
|
|
8
|
+
},
|
|
6
9
|
"keywords": [
|
|
7
10
|
"fm:base"
|
|
8
11
|
],
|
|
@@ -19,28 +22,29 @@
|
|
|
19
22
|
],
|
|
20
23
|
"license": "MIT",
|
|
21
24
|
"dependencies": {
|
|
22
|
-
"@webiny/ai-powerups": "6.3.0-beta.
|
|
23
|
-
"@webiny/api": "6.3.0-beta.
|
|
24
|
-
"@webiny/api-core": "6.3.0-beta.
|
|
25
|
-
"@webiny/api-headless-cms": "6.3.0-beta.
|
|
26
|
-
"@webiny/api-websockets": "6.3.0-beta.
|
|
27
|
-
"@webiny/build-tools": "6.3.0-beta.
|
|
25
|
+
"@webiny/ai-powerups": "6.3.0-beta.3",
|
|
26
|
+
"@webiny/api": "6.3.0-beta.3",
|
|
27
|
+
"@webiny/api-core": "6.3.0-beta.3",
|
|
28
|
+
"@webiny/api-headless-cms": "6.3.0-beta.3",
|
|
29
|
+
"@webiny/api-websockets": "6.3.0-beta.3",
|
|
30
|
+
"@webiny/build-tools": "6.3.0-beta.3",
|
|
28
31
|
"@webiny/di": "0.2.3",
|
|
29
|
-
"@webiny/error": "6.3.0-beta.
|
|
30
|
-
"@webiny/feature": "6.3.0-beta.
|
|
31
|
-
"@webiny/handler": "6.3.0-beta.
|
|
32
|
-
"@webiny/handler-graphql": "6.3.0-beta.
|
|
33
|
-
"@webiny/plugins": "6.3.0-beta.
|
|
34
|
-
"@webiny/tasks": "6.3.0-beta.
|
|
35
|
-
"@webiny/wcp": "6.3.0-beta.
|
|
32
|
+
"@webiny/error": "6.3.0-beta.3",
|
|
33
|
+
"@webiny/feature": "6.3.0-beta.3",
|
|
34
|
+
"@webiny/handler": "6.3.0-beta.3",
|
|
35
|
+
"@webiny/handler-graphql": "6.3.0-beta.3",
|
|
36
|
+
"@webiny/plugins": "6.3.0-beta.3",
|
|
37
|
+
"@webiny/tasks": "6.3.0-beta.3",
|
|
38
|
+
"@webiny/wcp": "6.3.0-beta.3",
|
|
39
|
+
"ai": "6.0.175",
|
|
36
40
|
"cache-control-parser": "2.2.0",
|
|
37
41
|
"zod": "4.3.6"
|
|
38
42
|
},
|
|
39
43
|
"devDependencies": {
|
|
40
44
|
"@types/object-hash": "3.0.6",
|
|
41
|
-
"@webiny/handler-aws": "6.3.0-beta.
|
|
42
|
-
"@webiny/project-utils": "6.3.0-beta.
|
|
43
|
-
"@webiny/utils": "6.3.0-beta.
|
|
45
|
+
"@webiny/handler-aws": "6.3.0-beta.3",
|
|
46
|
+
"@webiny/project-utils": "6.3.0-beta.3",
|
|
47
|
+
"@webiny/utils": "6.3.0-beta.3",
|
|
44
48
|
"@webiny/wcp": "0.0.0",
|
|
45
49
|
"rimraf": "6.1.3",
|
|
46
50
|
"typescript": "6.0.3"
|
|
@@ -62,5 +66,5 @@
|
|
|
62
66
|
]
|
|
63
67
|
}
|
|
64
68
|
},
|
|
65
|
-
"gitHead": "
|
|
69
|
+
"gitHead": "e154ec3326903876c357d35422dc60d29e061419"
|
|
66
70
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import "~/types.js";
|
|
2
2
|
import { TaskDefinition } from "@webiny/api-core/features/task/TaskDefinition/index.js";
|
|
3
3
|
import { Ai } from "@webiny/api-core/features/ai/index.js";
|
|
4
|
+
import { Encryption } from "@webiny/api-core/features/encryption/index.js";
|
|
4
5
|
import { GetFileUseCase } from "../features/file/GetFile/index.js";
|
|
5
6
|
import { UpdateFileUseCase } from "../features/file/UpdateFile/index.js";
|
|
6
7
|
import { GetSettingsUseCase } from "../features/settings/GetSettings/abstractions.js";
|
|
@@ -16,6 +17,7 @@ declare class AiImageEnrichmentTaskImpl implements TaskDefinition.Interface<IAiI
|
|
|
16
17
|
private updateFile;
|
|
17
18
|
private ai;
|
|
18
19
|
private aiPowerUpsSettings;
|
|
20
|
+
private encryption;
|
|
19
21
|
private websocketService?;
|
|
20
22
|
id: string;
|
|
21
23
|
title: string;
|
|
@@ -23,7 +25,8 @@ declare class AiImageEnrichmentTaskImpl implements TaskDefinition.Interface<IAiI
|
|
|
23
25
|
maxIterations: number;
|
|
24
26
|
isPrivate: boolean;
|
|
25
27
|
databaseLogs: boolean;
|
|
26
|
-
|
|
28
|
+
readonly selfCleanup: ("onSuccess" | "onAbort")[];
|
|
29
|
+
constructor(getFile: GetFileUseCase.Interface, getSettings: GetSettingsUseCase.Interface, updateFile: UpdateFileUseCase.Interface, ai: Ai.Interface, aiPowerUpsSettings: AiPowerUpsGetSettingsUseCase.Interface, encryption: Encryption.Interface, websocketService?: WebsocketService.Interface | undefined);
|
|
27
30
|
run({ input, controller }: TaskDefinition.RunParams<IAiImageEnrichmentTaskInput>): Promise<TaskDefinition.Result<IAiImageEnrichmentTaskInput>>;
|
|
28
31
|
}
|
|
29
32
|
export declare const AiImageEnrichmentTask: typeof AiImageEnrichmentTaskImpl & {
|
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
import "../types.js";
|
|
2
|
+
import { Output } from "ai";
|
|
3
|
+
import { z } from "zod";
|
|
2
4
|
import { TaskDefinition } from "@webiny/api-core/features/task/TaskDefinition/index.js";
|
|
3
5
|
import { Ai } from "@webiny/api-core/features/ai/index.js";
|
|
6
|
+
import { Encryption } from "@webiny/api-core/features/encryption/index.js";
|
|
4
7
|
import { GetFileUseCase } from "../features/file/GetFile/index.js";
|
|
5
8
|
import { UpdateFileUseCase } from "../features/file/UpdateFile/index.js";
|
|
6
9
|
import { GetSettingsUseCase } from "../features/settings/GetSettings/abstractions.js";
|
|
7
10
|
import { GetSettingsUseCase as AiPowerUpsGetSettingsUseCase } from "@webiny/ai-powerups/api/features/GetSettings/index.js";
|
|
8
11
|
import { WebsocketService } from "@webiny/api-websockets/features/WebsocketService/index.js";
|
|
9
12
|
export const AI_IMAGE_ENRICHMENT_TASK_ID = "fmAiImageEnrichment";
|
|
10
|
-
const AI_PROMPT =
|
|
13
|
+
const AI_PROMPT = "Analyze this image and return up to 5 lowercase descriptive tags and one short sentence describing the image.";
|
|
14
|
+
const aiOutputSchema = Output.object({
|
|
15
|
+
schema: z.object({
|
|
16
|
+
tags: z.array(z.string()),
|
|
17
|
+
description: z.string()
|
|
18
|
+
})
|
|
19
|
+
});
|
|
11
20
|
class AiImageEnrichmentTaskImpl {
|
|
12
21
|
id = AI_IMAGE_ENRICHMENT_TASK_ID;
|
|
13
22
|
title = "File Manager - AI Image Enrichment";
|
|
@@ -15,12 +24,14 @@ class AiImageEnrichmentTaskImpl {
|
|
|
15
24
|
maxIterations = 1;
|
|
16
25
|
isPrivate = true;
|
|
17
26
|
databaseLogs = false;
|
|
18
|
-
|
|
27
|
+
selfCleanup = ["onSuccess", "onAbort"];
|
|
28
|
+
constructor(getFile, getSettings, updateFile, ai, aiPowerUpsSettings, encryption, websocketService) {
|
|
19
29
|
this.getFile = getFile;
|
|
20
30
|
this.getSettings = getSettings;
|
|
21
31
|
this.updateFile = updateFile;
|
|
22
32
|
this.ai = ai;
|
|
23
33
|
this.aiPowerUpsSettings = aiPowerUpsSettings;
|
|
34
|
+
this.encryption = encryption;
|
|
24
35
|
this.websocketService = websocketService;
|
|
25
36
|
}
|
|
26
37
|
async run({
|
|
@@ -64,9 +75,10 @@ class AiImageEnrichmentTaskImpl {
|
|
|
64
75
|
try {
|
|
65
76
|
const aiResult = await this.ai.generateText({
|
|
66
77
|
model: firstProvider.model,
|
|
78
|
+
output: aiOutputSchema,
|
|
67
79
|
connection: {
|
|
68
80
|
sdkName: firstProvider.model.split("/")[0],
|
|
69
|
-
apiKey: firstProvider.
|
|
81
|
+
apiKey: this.encryption.decrypt(firstProvider.apiKeyEncrypted)
|
|
70
82
|
},
|
|
71
83
|
messages: [{
|
|
72
84
|
role: "user",
|
|
@@ -79,16 +91,10 @@ class AiImageEnrichmentTaskImpl {
|
|
|
79
91
|
}]
|
|
80
92
|
}]
|
|
81
93
|
});
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
if (Array.isArray(parsed.tags)) {
|
|
85
|
-
tags = parsed.tags.filter(t => typeof t === "string");
|
|
86
|
-
}
|
|
87
|
-
if (typeof parsed.description === "string") {
|
|
88
|
-
description = parsed.description;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
94
|
+
tags = aiResult.output.tags;
|
|
95
|
+
description = aiResult.output.description;
|
|
91
96
|
} catch (error) {
|
|
97
|
+
console.log("error", error.message);
|
|
92
98
|
return controller.response.error({
|
|
93
99
|
message: `AI enrichment failed: ${error instanceof Error ? error.message : String(error)}`
|
|
94
100
|
});
|
|
@@ -122,7 +128,7 @@ class AiImageEnrichmentTaskImpl {
|
|
|
122
128
|
}
|
|
123
129
|
export const AiImageEnrichmentTask = TaskDefinition.createImplementation({
|
|
124
130
|
implementation: AiImageEnrichmentTaskImpl,
|
|
125
|
-
dependencies: [GetFileUseCase, GetSettingsUseCase, UpdateFileUseCase, Ai, AiPowerUpsGetSettingsUseCase, [WebsocketService, {
|
|
131
|
+
dependencies: [GetFileUseCase, GetSettingsUseCase, UpdateFileUseCase, Ai, AiPowerUpsGetSettingsUseCase, Encryption, [WebsocketService, {
|
|
126
132
|
optional: true
|
|
127
133
|
}]]
|
|
128
134
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["TaskDefinition","Ai","GetFileUseCase","UpdateFileUseCase","GetSettingsUseCase","AiPowerUpsGetSettingsUseCase","WebsocketService","AI_IMAGE_ENRICHMENT_TASK_ID","AI_PROMPT","AiImageEnrichmentTaskImpl","id","title","description","maxIterations","isPrivate","databaseLogs","constructor","getFile","getSettings","updateFile","ai","aiPowerUpsSettings","websocketService","run","input","controller","runtime","isAborted","response","aborted","fileResult","execute","fileId","isFail","error","message","file","value","type","startsWith","done","settingsResult","srcPrefix","isOk","imageUrl","key","aiSettingsResult","aiSettings","firstProvider","providers","presets","tags","aiResult","generateText","model","connection","sdkName","split","apiKey","messages","role","content","image","URL","text","parsed","JSON","parse","Array","isArray","filter","t","Error","String","mergedTags","Set","updateResult","connectionsResult","listConnections","length","sendToConnections","action","data","AiImageEnrichmentTask","createImplementation","implementation","dependencies","optional"],"sources":["AiImageEnrichmentTask.ts"],"sourcesContent":["import \"~/types.js\";\nimport { TaskDefinition } from \"@webiny/api-core/features/task/TaskDefinition/index.js\";\nimport { Ai } from \"@webiny/api-core/features/ai/index.js\";\nimport { GetFileUseCase } from \"~/features/file/GetFile/index.js\";\nimport { UpdateFileUseCase } from \"~/features/file/UpdateFile/index.js\";\nimport { GetSettingsUseCase } from \"~/features/settings/GetSettings/abstractions.js\";\nimport { GetSettingsUseCase as AiPowerUpsGetSettingsUseCase } from \"@webiny/ai-powerups/api/features/GetSettings/index.js\";\nimport { WebsocketService } from \"@webiny/api-websockets/features/WebsocketService/index.js\";\n\nexport const AI_IMAGE_ENRICHMENT_TASK_ID = \"fmAiImageEnrichment\";\n\nconst AI_PROMPT =\n 'Analyze this image and return a JSON object with two keys: \"tags\" (array of up to 5 lowercase descriptive tags) and \"description\" (one short sentence describing the image). Return only the JSON object, nothing else. Example: {\"tags\":[\"nature\",\"landscape\",\"mountain\"],\"description\":\"A mountain landscape with a clear blue sky.\"}';\n\nexport interface IAiImageEnrichmentTaskInput {\n fileId: string;\n}\n\nclass AiImageEnrichmentTaskImpl implements TaskDefinition.Interface<IAiImageEnrichmentTaskInput> {\n id = AI_IMAGE_ENRICHMENT_TASK_ID;\n title = \"File Manager - AI Image Enrichment\";\n description = \"Automatically enriches uploaded images with AI-generated tags and description.\";\n maxIterations = 1;\n isPrivate = true;\n databaseLogs = false;\n\n constructor(\n private getFile: GetFileUseCase.Interface,\n private getSettings: GetSettingsUseCase.Interface,\n private updateFile: UpdateFileUseCase.Interface,\n private ai: Ai.Interface,\n private aiPowerUpsSettings: AiPowerUpsGetSettingsUseCase.Interface,\n private websocketService?: WebsocketService.Interface\n ) {}\n\n async run({\n input,\n controller\n }: TaskDefinition.RunParams<IAiImageEnrichmentTaskInput>): Promise<\n TaskDefinition.Result<IAiImageEnrichmentTaskInput>\n > {\n if (controller.runtime.isAborted()) {\n return controller.response.aborted();\n }\n\n const fileResult = await this.getFile.execute(input.fileId);\n if (fileResult.isFail()) {\n return controller.response.error({\n message: `File not found: ${input.fileId}`\n });\n }\n\n const file = fileResult.value;\n\n if (!file.type.startsWith(\"image/\")) {\n return controller.response.done(\"File is not an image; skipping AI enrichment.\");\n }\n\n const settingsResult = await this.getSettings.execute();\n const srcPrefix = settingsResult.isOk() ? (settingsResult.value.srcPrefix ?? \"\") : \"\";\n const imageUrl = `${srcPrefix}${file.key}`;\n\n const aiSettingsResult = await this.aiPowerUpsSettings.execute();\n\n if (aiSettingsResult.isFail()) {\n return controller.response.error({\n message: \"No AI provider configured. Add a provider in AI Power Ups settings.\"\n });\n }\n\n const aiSettings = aiSettingsResult.value;\n\n // TODO: for now we're loading first provider, but later we'll be loading\n // TODO: the right provider via AI Powerups settings.\n const firstProvider = aiSettings.providers.presets[0];\n\n if (!firstProvider) {\n return controller.response.done({\n message: \"No AI provider configured. Add a provider in AI Power Ups settings.\"\n });\n }\n\n let tags: string[] = [];\n let description = \"\";\n try {\n const aiResult = await this.ai.generateText({\n model: firstProvider.model,\n connection: {\n sdkName: firstProvider.model.split(\"/\")[0],\n apiKey: firstProvider.apiKey\n },\n messages: [\n {\n role: \"user\",\n content: [\n {\n type: \"image\",\n image: new URL(imageUrl)\n },\n {\n type: \"text\",\n text: AI_PROMPT\n }\n ]\n }\n ]\n });\n\n const parsed = JSON.parse(aiResult.text);\n if (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n if (Array.isArray(parsed.tags)) {\n tags = parsed.tags.filter((t: unknown): t is string => typeof t === \"string\");\n }\n if (typeof parsed.description === \"string\") {\n description = parsed.description;\n }\n }\n } catch (error) {\n return controller.response.error({\n message: `AI enrichment failed: ${error instanceof Error ? error.message : String(error)}`\n });\n }\n\n const mergedTags = [...new Set([...file.tags, ...tags])];\n\n const updateResult = await this.updateFile.execute({\n id: file.id,\n tags: mergedTags,\n description\n });\n\n if (updateResult.isFail()) {\n return controller.response.error({\n message: `Failed to update file: ${updateResult.error.message}`\n });\n }\n\n if (this.websocketService) {\n const connectionsResult = await this.websocketService.listConnections();\n if (connectionsResult.isOk() && connectionsResult.value.length > 0) {\n await this.websocketService.sendToConnections(connectionsResult.value, {\n action: \"fm.file.enrichment\",\n data: {\n id: file.id,\n tags: mergedTags,\n description\n }\n });\n }\n }\n\n return controller.response.done(\"AI image enrichment completed successfully.\");\n }\n}\n\nexport const AiImageEnrichmentTask = TaskDefinition.createImplementation({\n implementation: AiImageEnrichmentTaskImpl,\n dependencies: [\n GetFileUseCase,\n GetSettingsUseCase,\n UpdateFileUseCase,\n Ai,\n AiPowerUpsGetSettingsUseCase,\n [WebsocketService, { optional: true }]\n ]\n});\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"names":["Output","z","TaskDefinition","Ai","Encryption","GetFileUseCase","UpdateFileUseCase","GetSettingsUseCase","AiPowerUpsGetSettingsUseCase","WebsocketService","AI_IMAGE_ENRICHMENT_TASK_ID","AI_PROMPT","aiOutputSchema","object","schema","tags","array","string","description","AiImageEnrichmentTaskImpl","id","title","maxIterations","isPrivate","databaseLogs","selfCleanup","constructor","getFile","getSettings","updateFile","ai","aiPowerUpsSettings","encryption","websocketService","run","input","controller","runtime","isAborted","response","aborted","fileResult","execute","fileId","isFail","error","message","file","value","type","startsWith","done","settingsResult","srcPrefix","isOk","imageUrl","key","aiSettingsResult","aiSettings","firstProvider","providers","presets","aiResult","generateText","model","output","connection","sdkName","split","apiKey","decrypt","apiKeyEncrypted","messages","role","content","image","URL","text","console","log","Error","String","mergedTags","Set","updateResult","connectionsResult","listConnections","length","sendToConnections","action","data","AiImageEnrichmentTask","createImplementation","implementation","dependencies","optional"],"sources":["AiImageEnrichmentTask.ts"],"sourcesContent":["import \"~/types.js\";\nimport { Output } from \"ai\";\nimport { z } from \"zod\";\nimport { TaskDefinition } from \"@webiny/api-core/features/task/TaskDefinition/index.js\";\nimport { Ai } from \"@webiny/api-core/features/ai/index.js\";\nimport { Encryption } from \"@webiny/api-core/features/encryption/index.js\";\nimport { GetFileUseCase } from \"~/features/file/GetFile/index.js\";\nimport { UpdateFileUseCase } from \"~/features/file/UpdateFile/index.js\";\nimport { GetSettingsUseCase } from \"~/features/settings/GetSettings/abstractions.js\";\nimport { GetSettingsUseCase as AiPowerUpsGetSettingsUseCase } from \"@webiny/ai-powerups/api/features/GetSettings/index.js\";\nimport { WebsocketService } from \"@webiny/api-websockets/features/WebsocketService/index.js\";\n\nexport const AI_IMAGE_ENRICHMENT_TASK_ID = \"fmAiImageEnrichment\";\n\nconst AI_PROMPT =\n \"Analyze this image and return up to 5 lowercase descriptive tags and one short sentence describing the image.\";\n\nconst aiOutputSchema = Output.object({\n schema: z.object({\n tags: z.array(z.string()),\n description: z.string()\n })\n});\n\nexport interface IAiImageEnrichmentTaskInput {\n fileId: string;\n}\n\nclass AiImageEnrichmentTaskImpl implements TaskDefinition.Interface<IAiImageEnrichmentTaskInput> {\n id = AI_IMAGE_ENRICHMENT_TASK_ID;\n title = \"File Manager - AI Image Enrichment\";\n description = \"Automatically enriches uploaded images with AI-generated tags and description.\";\n maxIterations = 1;\n isPrivate = true;\n databaseLogs = false;\n\n public readonly selfCleanup = [\"onSuccess\" as const, \"onAbort\" as const];\n\n constructor(\n private getFile: GetFileUseCase.Interface,\n private getSettings: GetSettingsUseCase.Interface,\n private updateFile: UpdateFileUseCase.Interface,\n private ai: Ai.Interface,\n private aiPowerUpsSettings: AiPowerUpsGetSettingsUseCase.Interface,\n private encryption: Encryption.Interface,\n private websocketService?: WebsocketService.Interface\n ) {}\n\n async run({\n input,\n controller\n }: TaskDefinition.RunParams<IAiImageEnrichmentTaskInput>): Promise<\n TaskDefinition.Result<IAiImageEnrichmentTaskInput>\n > {\n if (controller.runtime.isAborted()) {\n return controller.response.aborted();\n }\n\n const fileResult = await this.getFile.execute(input.fileId);\n if (fileResult.isFail()) {\n return controller.response.error({\n message: `File not found: ${input.fileId}`\n });\n }\n\n const file = fileResult.value;\n\n if (!file.type.startsWith(\"image/\")) {\n return controller.response.done(\"File is not an image; skipping AI enrichment.\");\n }\n\n const settingsResult = await this.getSettings.execute();\n const srcPrefix = settingsResult.isOk() ? (settingsResult.value.srcPrefix ?? \"\") : \"\";\n const imageUrl = `${srcPrefix}${file.key}`;\n\n const aiSettingsResult = await this.aiPowerUpsSettings.execute();\n\n if (aiSettingsResult.isFail()) {\n return controller.response.error({\n message: \"No AI provider configured. Add a provider in AI Power Ups settings.\"\n });\n }\n\n const aiSettings = aiSettingsResult.value;\n\n // TODO: for now we're loading first provider, but later we'll be loading\n // TODO: the right provider via AI Powerups settings.\n const firstProvider = aiSettings.providers.presets[0];\n\n if (!firstProvider) {\n return controller.response.done({\n message: \"No AI provider configured. Add a provider in AI Power Ups settings.\"\n });\n }\n\n let tags: string[] = [];\n let description = \"\";\n try {\n const aiResult = await this.ai.generateText({\n model: firstProvider.model,\n output: aiOutputSchema,\n connection: {\n sdkName: firstProvider.model.split(\"/\")[0],\n apiKey: this.encryption.decrypt(firstProvider.apiKeyEncrypted)\n },\n messages: [\n {\n role: \"user\",\n content: [\n {\n type: \"image\",\n image: new URL(imageUrl)\n },\n {\n type: \"text\",\n text: AI_PROMPT\n }\n ]\n }\n ]\n });\n\n tags = aiResult.output.tags;\n description = aiResult.output.description;\n } catch (error) {\n console.log(\"error\", error.message);\n return controller.response.error({\n message: `AI enrichment failed: ${error instanceof Error ? error.message : String(error)}`\n });\n }\n\n const mergedTags = [...new Set([...file.tags, ...tags])];\n\n const updateResult = await this.updateFile.execute({\n id: file.id,\n tags: mergedTags,\n description\n });\n\n if (updateResult.isFail()) {\n return controller.response.error({\n message: `Failed to update file: ${updateResult.error.message}`\n });\n }\n\n if (this.websocketService) {\n const connectionsResult = await this.websocketService.listConnections();\n if (connectionsResult.isOk() && connectionsResult.value.length > 0) {\n await this.websocketService.sendToConnections(connectionsResult.value, {\n action: \"fm.file.enrichment\",\n data: {\n id: file.id,\n tags: mergedTags,\n description\n }\n });\n }\n }\n\n return controller.response.done(\"AI image enrichment completed successfully.\");\n }\n}\n\nexport const AiImageEnrichmentTask = TaskDefinition.createImplementation({\n implementation: AiImageEnrichmentTaskImpl,\n dependencies: [\n GetFileUseCase,\n GetSettingsUseCase,\n UpdateFileUseCase,\n Ai,\n AiPowerUpsGetSettingsUseCase,\n Encryption,\n [WebsocketService, { optional: true }]\n ]\n});\n"],"mappings":"AAAA;AACA,SAASA,MAAM,QAAQ,IAAI;AAC3B,SAASC,CAAC,QAAQ,KAAK;AACvB,SAASC,cAAc,QAAQ,wDAAwD;AACvF,SAASC,EAAE,QAAQ,uCAAuC;AAC1D,SAASC,UAAU,QAAQ,+CAA+C;AAC1E,SAASC,cAAc;AACvB,SAASC,iBAAiB;AAC1B,SAASC,kBAAkB;AAC3B,SAASA,kBAAkB,IAAIC,4BAA4B,QAAQ,uDAAuD;AAC1H,SAASC,gBAAgB,QAAQ,2DAA2D;AAE5F,OAAO,MAAMC,2BAA2B,GAAG,qBAAqB;AAEhE,MAAMC,SAAS,GACX,+GAA+G;AAEnH,MAAMC,cAAc,GAAGZ,MAAM,CAACa,MAAM,CAAC;EACjCC,MAAM,EAAEb,CAAC,CAACY,MAAM,CAAC;IACbE,IAAI,EAAEd,CAAC,CAACe,KAAK,CAACf,CAAC,CAACgB,MAAM,CAAC,CAAC,CAAC;IACzBC,WAAW,EAAEjB,CAAC,CAACgB,MAAM,CAAC;EAC1B,CAAC;AACL,CAAC,CAAC;AAMF,MAAME,yBAAyB,CAAkE;EAC7FC,EAAE,GAAGV,2BAA2B;EAChCW,KAAK,GAAG,oCAAoC;EAC5CH,WAAW,GAAG,gFAAgF;EAC9FI,aAAa,GAAG,CAAC;EACjBC,SAAS,GAAG,IAAI;EAChBC,YAAY,GAAG,KAAK;EAEJC,WAAW,GAAG,CAAC,WAAW,EAAW,SAAS,CAAU;EAExEC,WAAWA,CACCC,OAAiC,EACjCC,WAAyC,EACzCC,UAAuC,EACvCC,EAAgB,EAChBC,kBAA0D,EAC1DC,UAAgC,EAChCC,gBAA6C,EACvD;IAAA,KAPUN,OAAiC,GAAjCA,OAAiC;IAAA,KACjCC,WAAyC,GAAzCA,WAAyC;IAAA,KACzCC,UAAuC,GAAvCA,UAAuC;IAAA,KACvCC,EAAgB,GAAhBA,EAAgB;IAAA,KAChBC,kBAA0D,GAA1DA,kBAA0D;IAAA,KAC1DC,UAAgC,GAAhCA,UAAgC;IAAA,KAChCC,gBAA6C,GAA7CA,gBAA6C;EACtD;EAEH,MAAMC,GAAGA,CAAC;IACNC,KAAK;IACLC;EACmD,CAAC,EAEtD;IACE,IAAIA,UAAU,CAACC,OAAO,CAACC,SAAS,CAAC,CAAC,EAAE;MAChC,OAAOF,UAAU,CAACG,QAAQ,CAACC,OAAO,CAAC,CAAC;IACxC;IAEA,MAAMC,UAAU,GAAG,MAAM,IAAI,CAACd,OAAO,CAACe,OAAO,CAACP,KAAK,CAACQ,MAAM,CAAC;IAC3D,IAAIF,UAAU,CAACG,MAAM,CAAC,CAAC,EAAE;MACrB,OAAOR,UAAU,CAACG,QAAQ,CAACM,KAAK,CAAC;QAC7BC,OAAO,EAAE,mBAAmBX,KAAK,CAACQ,MAAM;MAC5C,CAAC,CAAC;IACN;IAEA,MAAMI,IAAI,GAAGN,UAAU,CAACO,KAAK;IAE7B,IAAI,CAACD,IAAI,CAACE,IAAI,CAACC,UAAU,CAAC,QAAQ,CAAC,EAAE;MACjC,OAAOd,UAAU,CAACG,QAAQ,CAACY,IAAI,CAAC,+CAA+C,CAAC;IACpF;IAEA,MAAMC,cAAc,GAAG,MAAM,IAAI,CAACxB,WAAW,CAACc,OAAO,CAAC,CAAC;IACvD,MAAMW,SAAS,GAAGD,cAAc,CAACE,IAAI,CAAC,CAAC,GAAIF,cAAc,CAACJ,KAAK,CAACK,SAAS,IAAI,EAAE,GAAI,EAAE;IACrF,MAAME,QAAQ,GAAG,GAAGF,SAAS,GAAGN,IAAI,CAACS,GAAG,EAAE;IAE1C,MAAMC,gBAAgB,GAAG,MAAM,IAAI,CAAC1B,kBAAkB,CAACW,OAAO,CAAC,CAAC;IAEhE,IAAIe,gBAAgB,CAACb,MAAM,CAAC,CAAC,EAAE;MAC3B,OAAOR,UAAU,CAACG,QAAQ,CAACM,KAAK,CAAC;QAC7BC,OAAO,EAAE;MACb,CAAC,CAAC;IACN;IAEA,MAAMY,UAAU,GAAGD,gBAAgB,CAACT,KAAK;;IAEzC;IACA;IACA,MAAMW,aAAa,GAAGD,UAAU,CAACE,SAAS,CAACC,OAAO,CAAC,CAAC,CAAC;IAErD,IAAI,CAACF,aAAa,EAAE;MAChB,OAAOvB,UAAU,CAACG,QAAQ,CAACY,IAAI,CAAC;QAC5BL,OAAO,EAAE;MACb,CAAC,CAAC;IACN;IAEA,IAAI/B,IAAc,GAAG,EAAE;IACvB,IAAIG,WAAW,GAAG,EAAE;IACpB,IAAI;MACA,MAAM4C,QAAQ,GAAG,MAAM,IAAI,CAAChC,EAAE,CAACiC,YAAY,CAAC;QACxCC,KAAK,EAAEL,aAAa,CAACK,KAAK;QAC1BC,MAAM,EAAErD,cAAc;QACtBsD,UAAU,EAAE;UACRC,OAAO,EAAER,aAAa,CAACK,KAAK,CAACI,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;UAC1CC,MAAM,EAAE,IAAI,CAACrC,UAAU,CAACsC,OAAO,CAACX,aAAa,CAACY,eAAe;QACjE,CAAC;QACDC,QAAQ,EAAE,CACN;UACIC,IAAI,EAAE,MAAM;UACZC,OAAO,EAAE,CACL;YACIzB,IAAI,EAAE,OAAO;YACb0B,KAAK,EAAE,IAAIC,GAAG,CAACrB,QAAQ;UAC3B,CAAC,EACD;YACIN,IAAI,EAAE,MAAM;YACZ4B,IAAI,EAAElE;UACV,CAAC;QAET,CAAC;MAET,CAAC,CAAC;MAEFI,IAAI,GAAG+C,QAAQ,CAACG,MAAM,CAAClD,IAAI;MAC3BG,WAAW,GAAG4C,QAAQ,CAACG,MAAM,CAAC/C,WAAW;IAC7C,CAAC,CAAC,OAAO2B,KAAK,EAAE;MACZiC,OAAO,CAACC,GAAG,CAAC,OAAO,EAAElC,KAAK,CAACC,OAAO,CAAC;MACnC,OAAOV,UAAU,CAACG,QAAQ,CAACM,KAAK,CAAC;QAC7BC,OAAO,EAAE,yBAAyBD,KAAK,YAAYmC,KAAK,GAAGnC,KAAK,CAACC,OAAO,GAAGmC,MAAM,CAACpC,KAAK,CAAC;MAC5F,CAAC,CAAC;IACN;IAEA,MAAMqC,UAAU,GAAG,CAAC,GAAG,IAAIC,GAAG,CAAC,CAAC,GAAGpC,IAAI,CAAChC,IAAI,EAAE,GAAGA,IAAI,CAAC,CAAC,CAAC;IAExD,MAAMqE,YAAY,GAAG,MAAM,IAAI,CAACvD,UAAU,CAACa,OAAO,CAAC;MAC/CtB,EAAE,EAAE2B,IAAI,CAAC3B,EAAE;MACXL,IAAI,EAAEmE,UAAU;MAChBhE;IACJ,CAAC,CAAC;IAEF,IAAIkE,YAAY,CAACxC,MAAM,CAAC,CAAC,EAAE;MACvB,OAAOR,UAAU,CAACG,QAAQ,CAACM,KAAK,CAAC;QAC7BC,OAAO,EAAE,0BAA0BsC,YAAY,CAACvC,KAAK,CAACC,OAAO;MACjE,CAAC,CAAC;IACN;IAEA,IAAI,IAAI,CAACb,gBAAgB,EAAE;MACvB,MAAMoD,iBAAiB,GAAG,MAAM,IAAI,CAACpD,gBAAgB,CAACqD,eAAe,CAAC,CAAC;MACvE,IAAID,iBAAiB,CAAC/B,IAAI,CAAC,CAAC,IAAI+B,iBAAiB,CAACrC,KAAK,CAACuC,MAAM,GAAG,CAAC,EAAE;QAChE,MAAM,IAAI,CAACtD,gBAAgB,CAACuD,iBAAiB,CAACH,iBAAiB,CAACrC,KAAK,EAAE;UACnEyC,MAAM,EAAE,oBAAoB;UAC5BC,IAAI,EAAE;YACFtE,EAAE,EAAE2B,IAAI,CAAC3B,EAAE;YACXL,IAAI,EAAEmE,UAAU;YAChBhE;UACJ;QACJ,CAAC,CAAC;MACN;IACJ;IAEA,OAAOkB,UAAU,CAACG,QAAQ,CAACY,IAAI,CAAC,6CAA6C,CAAC;EAClF;AACJ;AAEA,OAAO,MAAMwC,qBAAqB,GAAGzF,cAAc,CAAC0F,oBAAoB,CAAC;EACrEC,cAAc,EAAE1E,yBAAyB;EACzC2E,YAAY,EAAE,CACVzF,cAAc,EACdE,kBAAkB,EAClBD,iBAAiB,EACjBH,EAAE,EACFK,4BAA4B,EAC5BJ,UAAU,EACV,CAACK,gBAAgB,EAAE;IAAEsF,QAAQ,EAAE;EAAK,CAAC,CAAC;AAE9C,CAAC,CAAC","ignoreList":[]}
|