@automagik/omni 2.260624.3 → 2.260624.4
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/dist/index.js +1 -1
- package/dist/server/index.js +150 -12
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -125004,7 +125004,7 @@ import { fileURLToPath } from "url";
|
|
|
125004
125004
|
// package.json
|
|
125005
125005
|
var package_default = {
|
|
125006
125006
|
name: "@automagik/omni",
|
|
125007
|
-
version: "2.260624.
|
|
125007
|
+
version: "2.260624.4",
|
|
125008
125008
|
description: "LLM-optimized CLI for Omni",
|
|
125009
125009
|
type: "module",
|
|
125010
125010
|
bin: {
|
package/dist/server/index.js
CHANGED
|
@@ -225291,7 +225291,7 @@ var init_sentry_scrub = __esm(() => {
|
|
|
225291
225291
|
var require_package7 = __commonJS((exports, module) => {
|
|
225292
225292
|
module.exports = {
|
|
225293
225293
|
name: "@omni/api",
|
|
225294
|
-
version: "2.260624.
|
|
225294
|
+
version: "2.260624.4",
|
|
225295
225295
|
type: "module",
|
|
225296
225296
|
exports: {
|
|
225297
225297
|
".": {
|
|
@@ -332957,6 +332957,62 @@ var init_batch_pricing = __esm(() => {
|
|
|
332957
332957
|
// ../api/src/services/media-storage.ts
|
|
332958
332958
|
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync6, statSync as statSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
332959
332959
|
import { dirname as dirname6, extname as extname2, join as join21, relative as relative2 } from "path";
|
|
332960
|
+
function normalizeMimeType2(value) {
|
|
332961
|
+
return value?.split(";")[0]?.trim().toLowerCase() || undefined;
|
|
332962
|
+
}
|
|
332963
|
+
function isHtmlMime2(value) {
|
|
332964
|
+
const mimeType = normalizeMimeType2(value);
|
|
332965
|
+
return mimeType === "text/html" || mimeType === "application/xhtml+xml";
|
|
332966
|
+
}
|
|
332967
|
+
function looksLikeHtml2(buffer3) {
|
|
332968
|
+
const preview = buffer3.subarray(0, 512).toString("utf8").trimStart().toLowerCase();
|
|
332969
|
+
return preview.startsWith("<!doctype html") || preview.startsWith("<html>") || preview.startsWith("<html ");
|
|
332970
|
+
}
|
|
332971
|
+
function shouldRejectHtmlMedia(expectedMimeType, responseMimeType, buffer3) {
|
|
332972
|
+
const expected = normalizeMimeType2(expectedMimeType);
|
|
332973
|
+
if (!expected || isHtmlMime2(expected))
|
|
332974
|
+
return false;
|
|
332975
|
+
return isHtmlMime2(responseMimeType) || looksLikeHtml2(buffer3);
|
|
332976
|
+
}
|
|
332977
|
+
function hostMatchesSuffix2(hostname3, suffix) {
|
|
332978
|
+
const normalizedHost = hostname3.toLowerCase();
|
|
332979
|
+
const normalizedSuffix = suffix.toLowerCase();
|
|
332980
|
+
return normalizedHost === normalizedSuffix || normalizedHost.endsWith(`.${normalizedSuffix}`);
|
|
332981
|
+
}
|
|
332982
|
+
function shouldPreserveAuthForRedirect(url, suffixes) {
|
|
332983
|
+
return Boolean(suffixes?.some((suffix) => hostMatchesSuffix2(url.hostname, suffix)));
|
|
332984
|
+
}
|
|
332985
|
+
function headersWithOptionalAuthorization2(headers, preserveAuthorization) {
|
|
332986
|
+
const nextHeaders = new Headers(headers);
|
|
332987
|
+
if (!preserveAuthorization)
|
|
332988
|
+
nextHeaders.delete("authorization");
|
|
332989
|
+
return nextHeaders;
|
|
332990
|
+
}
|
|
332991
|
+
async function fetchWithOptionalAuthenticatedRedirects(url, fetchOptions) {
|
|
332992
|
+
const { preserveAuthRedirectHostSuffixes, ...init5 } = fetchOptions ?? {};
|
|
332993
|
+
if (!preserveAuthRedirectHostSuffixes?.length) {
|
|
332994
|
+
return fetch(url, init5);
|
|
332995
|
+
}
|
|
332996
|
+
let currentUrl = new URL(url);
|
|
332997
|
+
let currentHeaders = new Headers(init5.headers);
|
|
332998
|
+
for (let redirects = 0;redirects <= 5; redirects++) {
|
|
332999
|
+
const response = await fetch(currentUrl.toString(), {
|
|
333000
|
+
...init5,
|
|
333001
|
+
headers: currentHeaders,
|
|
333002
|
+
redirect: "manual"
|
|
333003
|
+
});
|
|
333004
|
+
if (response.status < 300 || response.status >= 400)
|
|
333005
|
+
return response;
|
|
333006
|
+
const location = response.headers.get("location");
|
|
333007
|
+
if (!location)
|
|
333008
|
+
return response;
|
|
333009
|
+
const nextUrl = new URL(location, currentUrl);
|
|
333010
|
+
const preserveAuthorization = shouldPreserveAuthForRedirect(currentUrl, preserveAuthRedirectHostSuffixes) && shouldPreserveAuthForRedirect(nextUrl, preserveAuthRedirectHostSuffixes);
|
|
333011
|
+
currentHeaders = headersWithOptionalAuthorization2(init5.headers, preserveAuthorization);
|
|
333012
|
+
currentUrl = nextUrl;
|
|
333013
|
+
}
|
|
333014
|
+
throw new Error("Failed to download media: too many redirects");
|
|
333015
|
+
}
|
|
332960
333016
|
function getExtensionFromMime2(mimeType) {
|
|
332961
333017
|
const mimeToExt = {
|
|
332962
333018
|
"image/jpeg": ".jpg",
|
|
@@ -333045,12 +333101,16 @@ class MediaStorageService {
|
|
|
333045
333101
|
};
|
|
333046
333102
|
}
|
|
333047
333103
|
async storeFromUrl(instanceId, messageId, url, mimeType, timestamp3, fetchOptions) {
|
|
333048
|
-
const response = await
|
|
333104
|
+
const response = await fetchWithOptionalAuthenticatedRedirects(url, fetchOptions);
|
|
333049
333105
|
if (!response.ok) {
|
|
333050
333106
|
throw new Error(`Failed to download media: ${response.status}`);
|
|
333051
333107
|
}
|
|
333108
|
+
const responseContentType = response.headers.get("content-type") ?? undefined;
|
|
333052
333109
|
const buffer3 = Buffer.from(await response.arrayBuffer());
|
|
333053
|
-
|
|
333110
|
+
if (shouldRejectHtmlMedia(mimeType, responseContentType, buffer3)) {
|
|
333111
|
+
throw new Error(`Downloaded media content mismatch: expected ${mimeType}, received ${responseContentType ?? "unknown content type"}`);
|
|
333112
|
+
}
|
|
333113
|
+
const contentType = mimeType ?? responseContentType;
|
|
333054
333114
|
return this.storeFromBuffer(instanceId, messageId, buffer3, contentType, timestamp3);
|
|
333055
333115
|
}
|
|
333056
333116
|
async updateMessageLocalPath(messageId, localPath) {
|
|
@@ -356791,6 +356851,27 @@ async function resolveMessageFromRef(services, ref) {
|
|
|
356791
356851
|
if ("messageId" in ref) {
|
|
356792
356852
|
return services.messages.getById(ref.messageId);
|
|
356793
356853
|
}
|
|
356854
|
+
if ("chatExternalId" in ref) {
|
|
356855
|
+
const chat2 = await services.chats.findByExternalIdSmart(ref.instanceId, ref.chatExternalId);
|
|
356856
|
+
if (!chat2) {
|
|
356857
|
+
throw new OmniError({
|
|
356858
|
+
code: ERROR_CODES.NOT_FOUND,
|
|
356859
|
+
message: `Chat not found for instanceId=${ref.instanceId}, chatExternalId=${ref.chatExternalId}`,
|
|
356860
|
+
context: { instanceId: ref.instanceId, chatExternalId: ref.chatExternalId },
|
|
356861
|
+
recoverable: false
|
|
356862
|
+
});
|
|
356863
|
+
}
|
|
356864
|
+
const found2 = await services.messages.getByExternalId(chat2.id, ref.externalId);
|
|
356865
|
+
if (!found2) {
|
|
356866
|
+
throw new OmniError({
|
|
356867
|
+
code: ERROR_CODES.NOT_FOUND,
|
|
356868
|
+
message: `Message not found for chatExternalId=${ref.chatExternalId}, externalId=${ref.externalId}`,
|
|
356869
|
+
context: { instanceId: ref.instanceId, chatExternalId: ref.chatExternalId, externalId: ref.externalId },
|
|
356870
|
+
recoverable: false
|
|
356871
|
+
});
|
|
356872
|
+
}
|
|
356873
|
+
return found2;
|
|
356874
|
+
}
|
|
356794
356875
|
const found = await services.messages.getByExternalId(ref.chatId, ref.externalId);
|
|
356795
356876
|
if (!found) {
|
|
356796
356877
|
throw new OmniError({
|
|
@@ -356802,6 +356883,17 @@ async function resolveMessageFromRef(services, ref) {
|
|
|
356802
356883
|
}
|
|
356803
356884
|
return found;
|
|
356804
356885
|
}
|
|
356886
|
+
function buildMediaDownloadFetchOptions(instance4) {
|
|
356887
|
+
if (instance4.channel !== "slack")
|
|
356888
|
+
return;
|
|
356889
|
+
const slackBotToken = typeof instance4.slackBotToken === "string" ? instance4.slackBotToken : undefined;
|
|
356890
|
+
if (!slackBotToken)
|
|
356891
|
+
return;
|
|
356892
|
+
return {
|
|
356893
|
+
headers: { Authorization: `Bearer ${slackBotToken}` },
|
|
356894
|
+
preserveAuthRedirectHostSuffixes: ["slack.com"]
|
|
356895
|
+
};
|
|
356896
|
+
}
|
|
356805
356897
|
async function computeCloseContactTerminalState(db2, chatUuid, outcome, auditRowId) {
|
|
356806
356898
|
const cfg = resolveCloseContactConfig(outcome, null);
|
|
356807
356899
|
if (isHardTerminalOutcome(outcome)) {
|
|
@@ -357089,7 +357181,8 @@ var init_messages5 = __esm(() => {
|
|
|
357089
357181
|
});
|
|
357090
357182
|
messageRefSchema = exports_external.union([
|
|
357091
357183
|
exports_external.object({ messageId: exports_external.string().uuid() }),
|
|
357092
|
-
exports_external.object({ chatId: exports_external.string().uuid(), externalId: exports_external.string().min(1) })
|
|
357184
|
+
exports_external.object({ chatId: exports_external.string().uuid(), externalId: exports_external.string().min(1) }),
|
|
357185
|
+
exports_external.object({ instanceId: exports_external.string().uuid(), chatExternalId: exports_external.string().min(1), externalId: exports_external.string().min(1) })
|
|
357093
357186
|
]);
|
|
357094
357187
|
messagesRoutes.post("/media/download", zValidator("json", messageRefSchema), async (c) => {
|
|
357095
357188
|
const body = c.req.valid("json");
|
|
@@ -357107,6 +357200,7 @@ var init_messages5 = __esm(() => {
|
|
|
357107
357200
|
recoverable: false
|
|
357108
357201
|
});
|
|
357109
357202
|
}
|
|
357203
|
+
const instance4 = await services.instances.getById(instanceId);
|
|
357110
357204
|
checkInstanceAccess2(apiKey, instanceId);
|
|
357111
357205
|
if (!message2.hasMedia || !message2.mediaUrl) {
|
|
357112
357206
|
return c.json({
|
|
@@ -357134,7 +357228,7 @@ var init_messages5 = __esm(() => {
|
|
|
357134
357228
|
}
|
|
357135
357229
|
if (!cached) {
|
|
357136
357230
|
try {
|
|
357137
|
-
const result = await mediaStorage2.storeFromUrl(instanceId, message2.id, mediaUrl, message2.mediaMimeType ?? undefined, message2.platformTimestamp ?? undefined);
|
|
357231
|
+
const result = await mediaStorage2.storeFromUrl(instanceId, message2.id, mediaUrl, message2.mediaMimeType ?? undefined, message2.platformTimestamp ?? undefined, buildMediaDownloadFetchOptions(instance4));
|
|
357138
357232
|
mediaLocalPath = result.localPath;
|
|
357139
357233
|
await mediaStorage2.updateMessageLocalPath(message2.id, result.localPath);
|
|
357140
357234
|
mediaDownloadLog.info("Downloaded and cached media", {
|
|
@@ -357155,7 +357249,7 @@ var init_messages5 = __esm(() => {
|
|
|
357155
357249
|
});
|
|
357156
357250
|
}
|
|
357157
357251
|
}
|
|
357158
|
-
const downloadUrl = `/api/v2/media/${
|
|
357252
|
+
const downloadUrl = `/api/v2/media/${mediaLocalPath}`;
|
|
357159
357253
|
return c.json({
|
|
357160
357254
|
data: {
|
|
357161
357255
|
messageId: message2.id,
|
|
@@ -367806,6 +367900,51 @@ function setupCommandHandlers(app, instanceId, commandNames, callbacks, logger5)
|
|
|
367806
367900
|
init_src2();
|
|
367807
367901
|
init_types10();
|
|
367808
367902
|
var downloadGuard2 = createDownloadGuard();
|
|
367903
|
+
function normalizeMimeType(value) {
|
|
367904
|
+
return value?.split(";")[0]?.trim().toLowerCase() || undefined;
|
|
367905
|
+
}
|
|
367906
|
+
function isHtmlMime(value) {
|
|
367907
|
+
const mimeType = normalizeMimeType(value);
|
|
367908
|
+
return mimeType === "text/html" || mimeType === "application/xhtml+xml";
|
|
367909
|
+
}
|
|
367910
|
+
function looksLikeHtml(buffer2) {
|
|
367911
|
+
const preview = buffer2.subarray(0, 512).toString("utf8").trimStart().toLowerCase();
|
|
367912
|
+
return preview.startsWith("<!doctype html") || preview.startsWith("<html>") || preview.startsWith("<html ");
|
|
367913
|
+
}
|
|
367914
|
+
function hostMatchesSuffix(hostname, suffix) {
|
|
367915
|
+
const normalizedHost = hostname.toLowerCase();
|
|
367916
|
+
const normalizedSuffix = suffix.toLowerCase();
|
|
367917
|
+
return normalizedHost === normalizedSuffix || normalizedHost.endsWith(`.${normalizedSuffix}`);
|
|
367918
|
+
}
|
|
367919
|
+
function isSlackHost(url) {
|
|
367920
|
+
return hostMatchesSuffix(url.hostname, "slack.com");
|
|
367921
|
+
}
|
|
367922
|
+
function headersWithOptionalAuthorization(botToken, preserveAuthorization) {
|
|
367923
|
+
const headers = new Headers;
|
|
367924
|
+
if (preserveAuthorization)
|
|
367925
|
+
headers.set("Authorization", `Bearer ${botToken}`);
|
|
367926
|
+
headers.set("Accept", "application/octet-stream");
|
|
367927
|
+
return headers;
|
|
367928
|
+
}
|
|
367929
|
+
async function fetchSlackPrivateUrl(url, botToken) {
|
|
367930
|
+
let currentUrl = new URL(url);
|
|
367931
|
+
let preserveAuthorization = true;
|
|
367932
|
+
for (let redirects = 0;redirects <= 5; redirects++) {
|
|
367933
|
+
const response = await fetch(currentUrl.toString(), {
|
|
367934
|
+
headers: headersWithOptionalAuthorization(botToken, preserveAuthorization),
|
|
367935
|
+
redirect: "manual"
|
|
367936
|
+
});
|
|
367937
|
+
if (response.status < 300 || response.status >= 400)
|
|
367938
|
+
return response;
|
|
367939
|
+
const location = response.headers.get("location");
|
|
367940
|
+
if (!location)
|
|
367941
|
+
return response;
|
|
367942
|
+
const nextUrl = new URL(location, currentUrl);
|
|
367943
|
+
preserveAuthorization = isSlackHost(currentUrl) && isSlackHost(nextUrl);
|
|
367944
|
+
currentUrl = nextUrl;
|
|
367945
|
+
}
|
|
367946
|
+
throw new SlackError(SlackErrorCode.FILE_DOWNLOAD_FAILED, "Failed to download file: too many redirects");
|
|
367947
|
+
}
|
|
367809
367948
|
function extractFileInfo(files) {
|
|
367810
367949
|
if (!files || !Array.isArray(files))
|
|
367811
367950
|
return [];
|
|
@@ -367822,20 +367961,19 @@ function extractFileInfo(files) {
|
|
|
367822
367961
|
};
|
|
367823
367962
|
});
|
|
367824
367963
|
}
|
|
367825
|
-
async function downloadSlackFile(url, botToken, logger5) {
|
|
367964
|
+
async function downloadSlackFile(url, botToken, logger5, expectedMimeType) {
|
|
367826
367965
|
logger5.debug("Downloading file from Slack CDN", { url: url.substring(0, 50) });
|
|
367827
367966
|
try {
|
|
367828
|
-
const response = await
|
|
367829
|
-
headers: {
|
|
367830
|
-
Authorization: `Bearer ${botToken}`
|
|
367831
|
-
}
|
|
367832
|
-
});
|
|
367967
|
+
const response = await fetchSlackPrivateUrl(url, botToken);
|
|
367833
367968
|
if (!response.ok) {
|
|
367834
367969
|
throw new SlackError(SlackErrorCode.FILE_DOWNLOAD_FAILED, `Failed to download file: HTTP ${response.status} ${response.statusText}`);
|
|
367835
367970
|
}
|
|
367836
367971
|
downloadGuard2.checkResponse(response, logger5, { channel: "slack", url: url.substring(0, 50) });
|
|
367837
367972
|
const buffer2 = Buffer.from(await response.arrayBuffer());
|
|
367838
367973
|
const mimeType = response.headers.get("content-type") ?? "application/octet-stream";
|
|
367974
|
+
if (!isHtmlMime(expectedMimeType) && (isHtmlMime(mimeType) || looksLikeHtml(buffer2))) {
|
|
367975
|
+
throw new SlackError(SlackErrorCode.FILE_DOWNLOAD_FAILED, `Failed to download file bytes: Slack returned HTML instead of ${expectedMimeType ?? "media"}`);
|
|
367976
|
+
}
|
|
367839
367977
|
logger5.debug("File downloaded successfully", { size: buffer2.length, mimeType });
|
|
367840
367978
|
return { buffer: buffer2, mimeType };
|
|
367841
367979
|
} catch (error) {
|