@sentry/junior 0.233.0 → 0.234.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{agent-hooks-DATy_Hnt.js → agent-hooks-BcH6E48y.js} +6 -6
- package/dist/{agent-runner-BKOtZjez.js → agent-runner-DUkGlK89.js} +221 -766
- package/dist/api/schema.d.ts +463 -2
- package/dist/api/schema.js +3 -3
- package/dist/api.js +229 -18
- package/dist/app.js +30 -20
- package/dist/{briefs-ClOTHmQ-.js → briefs-lyAQbDxJ.js} +2 -2
- package/dist/{catalog-runtime-xFtmw3oA.js → catalog-runtime-BSgsPVnr.js} +1 -1
- package/dist/{checkpoint-zkEB448J.js → checkpoint-BNfXlq_1.js} +6 -6
- package/dist/cli/chat.js +13 -13
- package/dist/cli/check.js +3 -3
- package/dist/cli/main.js +1 -1
- package/dist/cli/plugins.js +3 -3
- package/dist/cli/snapshot-create.js +3 -3
- package/dist/cli/upgrade.js +3 -3
- package/dist/{client-CJUZHY9Y.js → client-BX5lJRqW.js} +2 -2
- package/dist/{config-XtycpE-i.js → config-DuCzUoCo.js} +2 -2
- package/dist/{conversation-privacy-B30pEcsR.js → conversation-privacy-CSxvdSoV.js} +1 -1
- package/dist/{credential-sync-vufd6gwK.js → credential-sync-BO4DfH9a.js} +13 -13
- package/dist/{executor-BGDb_cM-.js → executor--jWf-Nnm.js} +1 -1
- package/dist/{images-zuEwjVKU.js → images-BaL19wFC.js} +2 -2
- package/dist/instrumentation.js +2 -2
- package/dist/{job-queue-C5JaRRcE.js → job-queue-cwHVOwVZ.js} +1 -1
- package/dist/{logging-BS563NYn.js → logging-Cox1Mgcv.js} +1 -1
- package/dist/nitro.js +3 -3
- package/dist/{oauth-authorization-DF2dSmNh.js → oauth-authorization-CC8a1JKP.js} +1 -1
- package/dist/{oauth-callback-server-CW9COQ9L.js → oauth-callback-server-Z7QSt416.js} +2 -2
- package/dist/{pending-auth-8Kmti8S3.js → pending-auth-DQhC3eSR.js} +1 -1
- package/dist/{profile-C4_njQYA.js → profile-BdApelHS.js} +4 -4
- package/dist/{registry-BhiLWAY6.js → registry-DiY6B8CN.js} +1 -1
- package/dist/{resolve-ZDwJ6a3N.js → resolve-VpJoiDpc.js} +2 -2
- package/dist/{runner-D1xeORYM.js → runner-BIMegnPE.js} +8 -8
- package/dist/{sandbox-egress-signals-Dx1PzQpy.js → sandbox-egress-signals-Cbtxe492.js} +1 -1
- package/dist/{schema-DuGfOm4u.js → schema-CxwnlTDN.js} +3 -116
- package/dist/{skills-CN-ye6on.js → skills-WttpPJUy.js} +2 -2
- package/dist/{spawn-B8KYOBcU.js → spawn-DhfMKt4y.js} +2 -2
- package/dist/{task-queue-DPaZtxld.js → task-queue-CZqUNJD0.js} +3 -3
- package/dist/{task-runner-D23tSETM.js → task-runner-BZaqoUCT.js} +15 -12
- package/dist/{thread-state-BbxSiDa9.js → thread-state-8tXZhu0y.js} +5 -5
- package/dist/{turn-failure-response-c8irQDMB.js → turn-failure-response-DFJ9L9NQ.js} +7 -7
- package/dist/{turn-lifecycle-DSVG3u_p.js → turn-lifecycle-WbpTUQ_r.js} +1 -1
- package/dist/{turn-session-routing-B2hNxu_N.js → turn-session-routing--EhI76rH.js} +1 -1
- package/dist/types-Du-HbEun.js +621 -0
- package/dist/{validation-BrLB5qbC.js → validation-0F9IgG3b.js} +1 -1
- package/dist/{version-B2rspCmP.js → version-A7-RRBOE.js} +1 -1
- package/dist/version.js +1 -1
- package/dist/{web-authorization-CVSTvY_D.js → web-authorization-BJggXofy.js} +6 -6
- package/dist/{web-input-zUQoZMZw.js → web-input-B-siN5Dr.js} +210 -162
- package/dist/web-message-id-CVVCTWUb.js +464 -0
- package/dist/{work-BXbJVOZI.js → work-CNc0ejGG.js} +7 -7
- package/dist/{worker-ClkQ9S9C.js → worker-BHbqpUAu.js} +5 -5
- package/package.json +4 -4
- package/dist/web-message-id-xPI_EFSC.js +0 -239
|
@@ -1,33 +1,34 @@
|
|
|
1
|
-
import { $
|
|
2
|
-
import { f as escapeXml, i as completeText, r as completeObject, v as isProviderRetryError } from "./client-
|
|
3
|
-
import { C as withLogContext, I as isRecord, L as toOptionalNumber, R as toOptionalString, _ as setSentryUser, b as setTags, d as logException, f as logInfo, n as createChatSdkLogger, o as getActiveTraceId, p as logWarn, t as buildTurnFailureResponse, u as logError, v as setSpanAttributes, w as withSpan } from "./logging-
|
|
4
|
-
import { a as getChatConfig, c as getSlackClientId, d as getSlackSigningSecret, h as normalizeSlackEmojiName, l as getSlackClientSecret, n as botConfig, s as getSlackBotToken } from "./config-
|
|
1
|
+
import { $ as deleteEventAutomation, Cn as postSlackMessage, D as renderBlockText, Dn as createUserTokenStore, E as renderAttachmentText, F as resolveSlackChannelIdFromThreadId, G as saveScheduledAutomationInLock, H as parseScheduledAutomationRow, I as listThreadReplies, M as parseSlackThreadId, Mn as AuthorizationFlowDisabledError, N as readSlackRawMessageContext, Nn as shouldEmitDevAgentTrace, On as buildSteeringPiMessage, P as resolveSlackChannelIdFromMessage, Rt as getConversationWorkState, Sn as addReactionToMessage, St as pluginEventCatalog, Tt as appendAndEnqueueInboundMessage, U as readScheduledAutomation, X as eventAutomationTriggerAvailable, Z as effectiveTaskOutcomes, _n as escapeSlackMrkdwnText, a as isExperimentalFeatureEnabled, bn as normalizeSlackStatusText, bt as getEventCatalog, c as createContextCompactor, et as eventAutomationBelongsToUser, it as parseEventAutomationRow, j as storeAttachment, kn as RetryableDeliveryError, nn as maybePostAutomatedTurnLimitNotice, nt as getEventAutomation, q as withScheduledAutomationLock, rn as recordFinishedTurnForAutomatedLimit, rt as listDeletedEventAutomationsCreatedBy, s as executeAgentRun, t as createAgentRunner, v as PluginCredentialFailureError, vn as formatSlackLink, vt as EVENT_WAIT_MS, wn as removeReactionFromMessage, wt as appendAndEnqueueExclusiveInboundMessage, xn as truncateStatusText, y as latestProgressStatus, yn as normalizeSlackReplyMarkdown, yt as isEventMailboxMetadata } from "./agent-runner-DUkGlK89.js";
|
|
2
|
+
import { f as escapeXml, i as completeText, r as completeObject, v as isProviderRetryError } from "./client-BX5lJRqW.js";
|
|
3
|
+
import { C as withLogContext, I as isRecord, L as toOptionalNumber, R as toOptionalString, _ as setSentryUser, b as setTags, d as logException, f as logInfo, n as createChatSdkLogger, o as getActiveTraceId, p as logWarn, t as buildTurnFailureResponse, u as logError, v as setSpanAttributes, w as withSpan } from "./logging-Cox1Mgcv.js";
|
|
4
|
+
import { a as getChatConfig, c as getSlackClientId, d as getSlackSigningSecret, h as normalizeSlackEmojiName, l as getSlackClientSecret, n as botConfig, s as getSlackBotToken } from "./config-DuCzUoCo.js";
|
|
5
5
|
import { o as modelIdForProfile, r as defaultModelId } from "./model-profile-BwU8mqwD.js";
|
|
6
6
|
import { a as createActor, b as parseSlackMessageTs, c as isActorUserId, l as isUserActor, o as createSlackActor, p as parseSlackChannelId, s as createSlackResumeActor, t as credentialContextForActor, u as parseActorUserId } from "./context-0Et9iVSB.js";
|
|
7
|
-
import { $t as getConversationStore, B as downloadPrivateSlackFile, Bt as recordSlackAttachment, G as isDmChannel, H as getHeaderString, It as buildSentryConversationUrl, K as normalizeSlackConversationId, Lt as getSlackFileId, U as getSlackClient, Zt as getConversationEventStore, _n as createSlackDestination, _t as getDashboardObjectIconLink, en as getDb, gt as getDashboardConversationLink, ht as JUNIOR_THREAD_STATE_TTL_MS, o as getPluginSlackConversationLink, ot as cancelSubscriptions, q as runWithSlackInstallationToken, tn as getSqlExecutor, yn as requireSlackDestination, z as SlackActionError, zt as readAttachmentVisionSummary } from "./agent-hooks-
|
|
7
|
+
import { $t as getConversationStore, B as downloadPrivateSlackFile, Bt as recordSlackAttachment, G as isDmChannel, H as getHeaderString, It as buildSentryConversationUrl, K as normalizeSlackConversationId, Lt as getSlackFileId, U as getSlackClient, Zt as getConversationEventStore, _n as createSlackDestination, _t as getDashboardObjectIconLink, en as getDb, gt as getDashboardConversationLink, ht as JUNIOR_THREAD_STATE_TTL_MS, o as getPluginSlackConversationLink, ot as cancelSubscriptions, q as runWithSlackInstallationToken, tn as getSqlExecutor, yn as requireSlackDestination, z as SlackActionError, zt as readAttachmentVisionSummary } from "./agent-hooks-BcH6E48y.js";
|
|
8
8
|
import { n as juniorDestinations } from "./destinations-BeBiJeEM.js";
|
|
9
|
-
import { D as juniorIdentities, O as juniorUsers, o as juniorSchedulerTasks } from "./executor-
|
|
9
|
+
import { D as juniorIdentities, O as juniorUsers, l as juniorEventAutomations, o as juniorSchedulerTasks, s as juniorAutomationExecutions } from "./executor--jWf-Nnm.js";
|
|
10
10
|
import { N as isVisionImageMediaType, j as readMessageAttachments, nt as instructionProvenanceFor } from "./conversation-CPjHYOV5.js";
|
|
11
|
-
import { $ as fallbackShortTitle, A as commitAcceptedReply, B as recordAuthorizationCompleted, J as hydrateConversationMessages, N as loadConversationProjection, O as trimTrailingAssistantMessages, P as loadProjection, Q as resolveConversationTitle, W as recordSubscribedReplyRoute, Y as persistConversationMessages, Z as persistConversationMessageSummaries, a as completeTurnRecord, at as
|
|
12
|
-
import { E as acquireActiveLock, O as withLock, T as MUTATION_LOCK_TTL_MS, w as getStateAdapter } from "./profile-
|
|
13
|
-
import { a as isPendingAuthLatestRequest, o as buildDeterministicAssistantMessageId, r as clearPendingAuth, s as buildDeterministicTurnId } from "./pending-auth-
|
|
14
|
-
import { _ as markTurnFailed, c as CooperativeTurnYieldError, d as getConversationTurnBoundaryError, f as isCooperativeTurnYieldError, g as markTurnCompleted, h as markTurnClosed, l as TurnInputCommitLostError, m as isTurnInputDeferredError, p as isTurnInputCommitLostError, r as getAssistantReplyText, s as ConversationTurnBoundaryError, u as TurnInputDeferredError, v as startActiveTurn } from "./oauth-authorization-
|
|
11
|
+
import { $ as fallbackShortTitle, A as commitAcceptedReply, B as recordAuthorizationCompleted, J as hydrateConversationMessages, N as loadConversationProjection, O as trimTrailingAssistantMessages, P as loadProjection, Q as resolveConversationTitle, W as recordSubscribedReplyRoute, Y as persistConversationMessages, Z as persistConversationMessageSummaries, a as completeTurnRecord, at as getConversationMessageSlackTs, c as listTurnSummaries, ct as normalizeConversationText, dt as upsertConversationMessage, ft as estimateTextTokens, i as abandonTurnRecord, it as estimateConversationContextTokens, j as commitMessages, l as recordTurnSummary, lt as recordDeliveredAssistantMessage, n as loadTurnCheckpoint, nt as buildConversationContext, o as failTurnRecord, ot as isHumanConversationMessage, p as buildTurnErrorResponse, r as saveTurnCheckpoint, rt as createConversationMemoryService, s as getTurnRecord, st as markConversationMessage, tt as appendThreadContextMessages, u as persistWithRetry, ut as turnHasReply } from "./checkpoint-BNfXlq_1.js";
|
|
12
|
+
import { E as acquireActiveLock, O as withLock, T as MUTATION_LOCK_TTL_MS, w as getStateAdapter } from "./profile-BdApelHS.js";
|
|
13
|
+
import { a as isPendingAuthLatestRequest, o as buildDeterministicAssistantMessageId, r as clearPendingAuth, s as buildDeterministicTurnId } from "./pending-auth-DQhC3eSR.js";
|
|
14
|
+
import { _ as markTurnFailed, c as CooperativeTurnYieldError, d as getConversationTurnBoundaryError, f as isCooperativeTurnYieldError, g as markTurnCompleted, h as markTurnClosed, l as TurnInputCommitLostError, m as isTurnInputDeferredError, p as isTurnInputCommitLostError, r as getAssistantReplyText, s as ConversationTurnBoundaryError, u as TurnInputDeferredError, v as startActiveTurn } from "./oauth-authorization-CC8a1JKP.js";
|
|
15
15
|
import { a as EVENT_SYSTEM_ACTOR, i as EVENT_MESSAGE_AUTHOR, o as isEventConversationMessage } from "./prompt-3D0sSbTC.js";
|
|
16
|
-
import { a as getVercelConversationWorkQueue, f as getTurnRequestDeadline } from "./job-queue-
|
|
17
|
-
import {
|
|
18
|
-
import { C as readAutomationExecutionStatusDays, D as readAutomationRuns, E as readAutomationExecutions, S as readAutomationExecutionHours, T as readAutomationExecutionSummaries, _ as markDispatchFailed, a as confirmDispatchMailboxAppend, b as readAutomationExecutionByConversationId, c as getDispatchInputMessageId, f as isTerminalDispatchStatus, g as markDispatchCompleted, h as markDispatchBlocked, i as claimDispatchMailboxAppend, l as getDispatchRecord, m as markDispatchAwaitingResume, n as scheduleSessionCompletedPluginTasks, s as getDispatchConversationId, t as processPluginTask, u as getDispatchTurnId, v as markDispatchRunning, w as readAutomationExecutionStatusHours, x as readAutomationExecutionDays, y as emptyAutomationRunWindows } from "./task-runner-
|
|
19
|
-
import { i as resolveAgentInvocationId, o as AgentRunError, s as executeTurn, t as createAgentInvocationWorker } from "./work-
|
|
20
|
-
import { n as getTurnLifecycle, t as ConversationTurnLifecycleService } from "./turn-lifecycle-
|
|
21
|
-
import { a as persistThreadState, c as configValueSchema, i as persistThreadRuntimeState, n as getPersistedSandboxState, o as persistThreadStateById, r as getPersistedThreadState, s as coerceThreadConversationState, t as getLocationConfigurationService } from "./thread-state-
|
|
22
|
-
import { a as getPausedTurnRequest, c as getTurnAuthorization, i as createPausedTurns, n as deleteWebAuthorization, s as wakePausedTurn, t as createWebAuthorization } from "./web-authorization-
|
|
23
|
-
import { n as resolveTurnSessionRouting } from "./turn-session-routing
|
|
24
|
-
import { t as bindSpawnAgent } from "./spawn-
|
|
25
|
-
import { a as persistAuthPauseTurnState, c as getTurnUserSlackMessageTs, d as loadPendingMessageCards, i as completeAuthPauseTurn, l as buildDeliveredTurnStatePatch, n as getAgentTurnDiagnosticsAttributes, o as getTurnUserMessage, r as requireTurnFailureEventId, s as getTurnUserReplyAttachmentContext, t as finalizeFailedTurnReplyWithEvent, u as getInterruptionMarker } from "./turn-failure-response-
|
|
26
|
-
import { r as storeInputImages } from "./images-
|
|
16
|
+
import { a as getVercelConversationWorkQueue, f as getTurnRequestDeadline } from "./job-queue-cwHVOwVZ.js";
|
|
17
|
+
import { P as healthReportSchema, S as automationListQuerySchema, c as sumUtcHoursIntoSixHours, t as webMessageId } from "./web-message-id-CVVCTWUb.js";
|
|
18
|
+
import { C as readAutomationExecutionStatusDays, D as readAutomationRuns, E as readAutomationExecutions, S as readAutomationExecutionHours, T as readAutomationExecutionSummaries, _ as markDispatchFailed, a as confirmDispatchMailboxAppend, b as readAutomationExecutionByConversationId, c as getDispatchInputMessageId, f as isTerminalDispatchStatus, g as markDispatchCompleted, h as markDispatchBlocked, i as claimDispatchMailboxAppend, l as getDispatchRecord, m as markDispatchAwaitingResume, n as scheduleSessionCompletedPluginTasks, s as getDispatchConversationId, t as processPluginTask, u as getDispatchTurnId, v as markDispatchRunning, w as readAutomationExecutionStatusHours, x as readAutomationExecutionDays, y as emptyAutomationRunWindows } from "./task-runner-BZaqoUCT.js";
|
|
19
|
+
import { i as resolveAgentInvocationId, o as AgentRunError, s as executeTurn, t as createAgentInvocationWorker } from "./work-CNc0ejGG.js";
|
|
20
|
+
import { n as getTurnLifecycle, t as ConversationTurnLifecycleService } from "./turn-lifecycle-WbpTUQ_r.js";
|
|
21
|
+
import { a as persistThreadState, c as configValueSchema, i as persistThreadRuntimeState, n as getPersistedSandboxState, o as persistThreadStateById, r as getPersistedThreadState, s as coerceThreadConversationState, t as getLocationConfigurationService } from "./thread-state-8tXZhu0y.js";
|
|
22
|
+
import { a as getPausedTurnRequest, c as getTurnAuthorization, i as createPausedTurns, n as deleteWebAuthorization, s as wakePausedTurn, t as createWebAuthorization } from "./web-authorization-BJggXofy.js";
|
|
23
|
+
import { n as resolveTurnSessionRouting } from "./turn-session-routing--EhI76rH.js";
|
|
24
|
+
import { t as bindSpawnAgent } from "./spawn-DhfMKt4y.js";
|
|
25
|
+
import { a as persistAuthPauseTurnState, c as getTurnUserSlackMessageTs, d as loadPendingMessageCards, i as completeAuthPauseTurn, l as buildDeliveredTurnStatePatch, n as getAgentTurnDiagnosticsAttributes, o as getTurnUserMessage, r as requireTurnFailureEventId, s as getTurnUserReplyAttachmentContext, t as finalizeFailedTurnReplyWithEvent, u as getInterruptionMarker } from "./turn-failure-response-DFJ9L9NQ.js";
|
|
26
|
+
import { r as storeInputImages } from "./images-BaL19wFC.js";
|
|
27
27
|
import { z } from "zod";
|
|
28
28
|
import { createHash } from "node:crypto";
|
|
29
|
-
import { and, desc, eq,
|
|
29
|
+
import { and, asc, count, desc, eq, exists, inArray, ne, or, sql } from "drizzle-orm";
|
|
30
30
|
import { createEventSource, createLocalSource, createSlackSource, createWebSource, localDestinationSchema, objectFactFields, objectPresentation, objectTypeSchema } from "@sentry/junior-plugin-api";
|
|
31
|
+
import { unionAll } from "drizzle-orm/pg-core";
|
|
31
32
|
import { del, get, put } from "@vercel/blob";
|
|
32
33
|
import { Message, ThreadImpl, stringifyMarkdown } from "chat";
|
|
33
34
|
import { createSlackAdapter } from "@chat-adapter/slack";
|
|
@@ -3237,99 +3238,143 @@ function recordSkippedConversationMessage(args) {
|
|
|
3237
3238
|
});
|
|
3238
3239
|
}
|
|
3239
3240
|
//#endregion
|
|
3240
|
-
//#region src/chat/
|
|
3241
|
-
|
|
3241
|
+
//#region src/chat/automations/collection.ts
|
|
3242
|
+
/**
|
|
3243
|
+
* The web collection applies access before counts, filter options, and paging.
|
|
3244
|
+
* Keep its ownership rules aligned with the direct Automation reader. A public
|
|
3245
|
+
* Destination only grants access inside a viewer's linked Slack workspaces.
|
|
3246
|
+
*/
|
|
3247
|
+
function viewerAutomationCollection(user) {
|
|
3248
|
+
const db = getDb();
|
|
3249
|
+
const identities = user.identities.filter((identity) => identity.provider === "slack");
|
|
3250
|
+
const teamIds = identities.flatMap((identity) => identity.providerTenantId ? [identity.providerTenantId] : []);
|
|
3251
|
+
const scheduled = juniorSchedulerTasks;
|
|
3252
|
+
const event = juniorEventAutomations;
|
|
3253
|
+
const destination = juniorDestinations;
|
|
3254
|
+
const scheduledOwned = inArray(scheduled.creatorIdentityId, user.identities.map((identity) => identity.id));
|
|
3255
|
+
const eventOwned = or(...identities.map((identity) => and(eq(event.teamId, identity.providerTenantId ?? ""), sql`${event.task}->'createdBy'->>'slackUserId' = ${identity.providerSubjectId}`))) ?? sql`false`;
|
|
3256
|
+
const publicDestination = sql`coalesce(${destination.visibility} = 'public', false)`;
|
|
3257
|
+
const scheduledPublic = and(inArray(scheduled.teamId, teamIds), publicDestination, ne(scheduled.status, "completed"));
|
|
3258
|
+
const eventPublic = and(inArray(event.teamId, teamIds), publicDestination);
|
|
3259
|
+
function fields(record, teamId) {
|
|
3260
|
+
return {
|
|
3261
|
+
creator: sql`${teamId} || ':' || (${record}->'createdBy'->>'slackUserId')`.as("creator"),
|
|
3262
|
+
creatorLabel: sql`coalesce(nullif(trim(${record}->'createdBy'->>'fullName'), ''), nullif(trim(${record}->'createdBy'->>'userName'), ''), ${record}->'createdBy'->>'slackUserId')`.as("creator_label"),
|
|
3263
|
+
destination: sql`${teamId} || ':' || (${record}->'destination'->>'channelId')`.as("destination"),
|
|
3264
|
+
destinationLabel: sql`coalesce(nullif(${destination.displayName}, ''), ${record}->'destination'->>'channelId')`.as("destination_label"),
|
|
3265
|
+
isPublic: publicDestination.as("is_public"),
|
|
3266
|
+
instruction: sql`${record}->'task'->>'text'`.as("instruction")
|
|
3267
|
+
};
|
|
3268
|
+
}
|
|
3269
|
+
function destinationJoin(record, teamId) {
|
|
3270
|
+
return and(eq(destination.provider, "slack"), sql`${destination.providerTenantId} = ${teamId}`, sql`${destination.providerDestinationId} = ${record}->'destination'->>'channelId'`);
|
|
3271
|
+
}
|
|
3272
|
+
const available = or(...Object.entries(pluginEventCatalog(getEventCatalog())).map(([namespace, registration]) => {
|
|
3273
|
+
const events = registration.resourceTypes.flatMap((resource) => resource.supportedEvents);
|
|
3274
|
+
return and(eq(event.namespace, namespace), sql`${event.task}->'trigger'->'events' <@ ${JSON.stringify(events)}::jsonb`);
|
|
3275
|
+
})) ?? sql`false`;
|
|
3276
|
+
const executions = juniorAutomationExecutions;
|
|
3277
|
+
function lastRunNeedsAttention(kind, id) {
|
|
3278
|
+
return sql`coalesce((select ${executions.status} in ('failed', 'blocked') from ${executions}
|
|
3279
|
+
where ${executions.kind} = ${kind} and ${executions.namespace} = 'junior' and ${executions.automationId} = ${id}
|
|
3280
|
+
order by ${executions.executedAtMs} desc, ${executions.executionId} desc limit 1), false)`;
|
|
3281
|
+
}
|
|
3282
|
+
return unionAll(db.select({
|
|
3283
|
+
kind: sql`'scheduled'`.as("kind"),
|
|
3284
|
+
id: scheduled.id,
|
|
3285
|
+
title: sql`${scheduled.title}`.as("title"),
|
|
3286
|
+
createdAtMs: scheduled.createdAtMs,
|
|
3287
|
+
state: sql`${scheduled.status}`.as("state"),
|
|
3288
|
+
unavailable: sql`false`.as("unavailable"),
|
|
3289
|
+
attention: sql`${scheduled.status} not in ('paused', 'completed') and (${scheduled.status} = 'blocked' or ${lastRunNeedsAttention("scheduled", scheduled.id)})`.as("attention"),
|
|
3290
|
+
owned: sql`${scheduledOwned}`.as("owned"),
|
|
3291
|
+
resource: sql`''`.as("resource"),
|
|
3292
|
+
...fields(scheduled.record, scheduled.teamId)
|
|
3293
|
+
}).from(scheduled).leftJoin(destination, destinationJoin(scheduled.record, scheduled.teamId)).where(and(inArray(scheduled.status, [
|
|
3294
|
+
"active",
|
|
3295
|
+
"blocked",
|
|
3296
|
+
"paused",
|
|
3297
|
+
"completed"
|
|
3298
|
+
]), or(scheduledOwned, scheduledPublic))), db.select({
|
|
3299
|
+
kind: sql`'event'`.as("kind"),
|
|
3300
|
+
id: event.id,
|
|
3301
|
+
title: sql`${event.title}`.as("title"),
|
|
3302
|
+
createdAtMs: event.createdAtMs,
|
|
3303
|
+
state: sql`${event.status}`.as("state"),
|
|
3304
|
+
unavailable: sql`not (${available})`.as("unavailable"),
|
|
3305
|
+
attention: sql`${event.status} <> 'paused' and (not (${available}) or ${lastRunNeedsAttention("event", event.id)})`.as("attention"),
|
|
3306
|
+
owned: sql`${eventOwned}`.as("owned"),
|
|
3307
|
+
resource: sql`concat_ws(' ', ${event.identifier}, ${event.task}->'trigger'->>'label', ${event.namespace})`.as("resource"),
|
|
3308
|
+
...fields(event.task, event.teamId)
|
|
3309
|
+
}).from(event).leftJoin(destination, destinationJoin(event.task, event.teamId)).where(and(ne(event.status, "deleted"), or(eventOwned, eventPublic)))).as("accessible_automations");
|
|
3310
|
+
}
|
|
3311
|
+
/** Read an SQL page and complete, access-scoped counts and filter options. */
|
|
3312
|
+
async function readAutomationCollection(user, input) {
|
|
3313
|
+
const db = getDb();
|
|
3314
|
+
const collection = viewerAutomationCollection(user);
|
|
3315
|
+
const where = and(input.scope === "mine" ? eq(collection.owned, true) : input.scope === "public" ? eq(collection.isPublic, true) : input.scope === "attention" ? eq(collection.attention, true) : void 0, input.type === "all" ? void 0 : eq(collection.kind, input.type), input.state === "all" ? void 0 : input.state === "unavailable" ? eq(collection.unavailable, true) : eq(collection.state, input.state), input.creator ? eq(collection.creator, input.creator) : void 0, input.destination ? eq(collection.destination, input.destination) : void 0, input.q ? sql`strpos(lower(concat_ws(' ', ${collection.title}, ${collection.instruction}, ${collection.resource})), ${input.q.toLowerCase()}) > 0` : void 0);
|
|
3316
|
+
const [totals, counts, creators, destinations] = await Promise.all([
|
|
3317
|
+
db.select({ total: count() }).from(collection).where(where),
|
|
3318
|
+
db.select({
|
|
3319
|
+
all: count(),
|
|
3320
|
+
mine: sql`count(*) filter (where ${collection.owned})::int`,
|
|
3321
|
+
public: sql`count(*) filter (where ${collection.isPublic})::int`,
|
|
3322
|
+
private: sql`count(*) filter (where not ${collection.isPublic})::int`
|
|
3323
|
+
}).from(collection),
|
|
3324
|
+
db.select({
|
|
3325
|
+
value: collection.creator,
|
|
3326
|
+
label: sql`min(${collection.creatorLabel})`
|
|
3327
|
+
}).from(collection).groupBy(collection.creator).orderBy(sql`min(${collection.creatorLabel})`, collection.creator),
|
|
3328
|
+
db.select({
|
|
3329
|
+
value: collection.destination,
|
|
3330
|
+
label: sql`min(${collection.destinationLabel})`
|
|
3331
|
+
}).from(collection).groupBy(collection.destination).orderBy(sql`min(${collection.destinationLabel})`, collection.destination)
|
|
3332
|
+
]);
|
|
3333
|
+
const total = totals[0].total;
|
|
3334
|
+
const page = Math.min(input.page, Math.max(1, Math.ceil(total / input.pageSize)));
|
|
3335
|
+
const order = input.sort === "oldest" ? asc(collection.createdAtMs) : input.sort === "title" ? asc(sql`lower(coalesce(nullif(trim(${collection.title}), ''), ${collection.instruction}))`) : desc(collection.createdAtMs);
|
|
3336
|
+
return {
|
|
3337
|
+
rows: await db.select({
|
|
3338
|
+
id: collection.id,
|
|
3339
|
+
kind: collection.kind
|
|
3340
|
+
}).from(collection).where(where).orderBy(order, desc(collection.id), asc(collection.kind)).limit(input.pageSize).offset((page - 1) * input.pageSize),
|
|
3341
|
+
total,
|
|
3342
|
+
page,
|
|
3343
|
+
pageSize: input.pageSize,
|
|
3344
|
+
counts: counts[0],
|
|
3345
|
+
creators,
|
|
3346
|
+
destinations
|
|
3347
|
+
};
|
|
3348
|
+
}
|
|
3349
|
+
z.object({
|
|
3242
3350
|
createdAtMs: z.number().finite(),
|
|
3243
3351
|
id: z.string().min(1),
|
|
3244
3352
|
query: z.string().max(200).optional(),
|
|
3245
3353
|
version: z.literal(1)
|
|
3246
3354
|
}).strict();
|
|
3247
|
-
var InvalidScheduledAutomationCursorError = class extends Error {
|
|
3248
|
-
constructor() {
|
|
3249
|
-
super("Scheduled automation cursor is invalid.");
|
|
3250
|
-
this.name = "InvalidScheduledAutomationCursorError";
|
|
3251
|
-
}
|
|
3252
|
-
};
|
|
3253
3355
|
var PersonalScheduledAutomationNotFoundError = class extends Error {
|
|
3254
3356
|
constructor() {
|
|
3255
3357
|
super("Scheduled automation was not found.");
|
|
3256
3358
|
this.name = "PersonalScheduledAutomationNotFoundError";
|
|
3257
3359
|
}
|
|
3258
3360
|
};
|
|
3259
|
-
function normalizeQuery(query) {
|
|
3260
|
-
return query?.trim().toLowerCase() || void 0;
|
|
3261
|
-
}
|
|
3262
|
-
function decodeCursor(value, query) {
|
|
3263
|
-
if (!value) return void 0;
|
|
3264
|
-
try {
|
|
3265
|
-
const parsed = cursorSchema.parse(JSON.parse(Buffer.from(value, "base64url").toString("utf8")));
|
|
3266
|
-
if (parsed.query !== query) throw new InvalidScheduledAutomationCursorError();
|
|
3267
|
-
return {
|
|
3268
|
-
createdAtMs: parsed.createdAtMs,
|
|
3269
|
-
id: parsed.id
|
|
3270
|
-
};
|
|
3271
|
-
} catch {
|
|
3272
|
-
throw new InvalidScheduledAutomationCursorError();
|
|
3273
|
-
}
|
|
3274
|
-
}
|
|
3275
|
-
function encodeCursor(task, query) {
|
|
3276
|
-
return Buffer.from(JSON.stringify({
|
|
3277
|
-
createdAtMs: task.createdAtMs,
|
|
3278
|
-
id: task.id,
|
|
3279
|
-
...query ? { query } : void 0,
|
|
3280
|
-
version: 1
|
|
3281
|
-
}), "utf8").toString("base64url");
|
|
3282
|
-
}
|
|
3283
3361
|
/** Delete one scheduled automation created by the viewer. */
|
|
3284
3362
|
async function deleteViewerScheduledAutomation(db, user, id, nowMs = Date.now()) {
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3363
|
+
await withScheduledAutomationLock(db, id, async (tx) => {
|
|
3364
|
+
const identityIds = new Set(user.identities.map((identity) => identity.id));
|
|
3365
|
+
const task = await readScheduledAutomation(tx, id);
|
|
3366
|
+
if (!task || task.status === "deleted" || !identityIds.has(task.creatorIdentityId)) throw new PersonalScheduledAutomationNotFoundError();
|
|
3367
|
+
await saveScheduledAutomationInLock(tx, {
|
|
3368
|
+
...task,
|
|
3369
|
+
nextRunAtMs: void 0,
|
|
3370
|
+
runNowAtMs: void 0,
|
|
3371
|
+
status: "deleted",
|
|
3372
|
+
updatedAtMs: nowMs
|
|
3373
|
+
}, task);
|
|
3294
3374
|
});
|
|
3295
3375
|
}
|
|
3296
|
-
/** List scheduled automations created by the viewer with a stable SQL cursor. */
|
|
3297
|
-
async function listViewerScheduledAutomations(db, user, input) {
|
|
3298
|
-
const identityIds = user.identities.map((identity) => identity.id);
|
|
3299
|
-
if (identityIds.length === 0) return { automations: [] };
|
|
3300
|
-
const query = normalizeQuery(input.query);
|
|
3301
|
-
const cursor = decodeCursor(input.cursor, query);
|
|
3302
|
-
const automations = [];
|
|
3303
|
-
let before = cursor;
|
|
3304
|
-
const fetchLimit = input.limit + 1;
|
|
3305
|
-
while (automations.length < fetchLimit) {
|
|
3306
|
-
const cursorFilter = before ? or(lt(juniorSchedulerTasks.createdAtMs, before.createdAtMs), and(eq(juniorSchedulerTasks.createdAtMs, before.createdAtMs), lt(juniorSchedulerTasks.id, before.id))) : void 0;
|
|
3307
|
-
const search = query ? sql`strpos(lower(coalesce(${juniorSchedulerTasks.title}, ${juniorSchedulerTasks.record}->'task'->>'text')), ${query}) > 0` : void 0;
|
|
3308
|
-
const rows = await db.select({
|
|
3309
|
-
createdAtMs: juniorSchedulerTasks.createdAtMs,
|
|
3310
|
-
creatorIdentityId: juniorSchedulerTasks.creatorIdentityId,
|
|
3311
|
-
id: juniorSchedulerTasks.id,
|
|
3312
|
-
record: juniorSchedulerTasks.record,
|
|
3313
|
-
title: juniorSchedulerTasks.title
|
|
3314
|
-
}).from(juniorSchedulerTasks).where(and(notInArray(juniorSchedulerTasks.status, ["deleted", "paused"]), inArray(juniorSchedulerTasks.creatorIdentityId, identityIds), cursorFilter, search)).orderBy(desc(juniorSchedulerTasks.createdAtMs), desc(juniorSchedulerTasks.id)).limit(fetchLimit);
|
|
3315
|
-
automations.push(...rows.map(parseScheduledAutomationRow).filter(isListedScheduledAutomation));
|
|
3316
|
-
if (rows.length < fetchLimit) break;
|
|
3317
|
-
const last = rows.at(-1);
|
|
3318
|
-
before = {
|
|
3319
|
-
createdAtMs: last.createdAtMs,
|
|
3320
|
-
id: last.id
|
|
3321
|
-
};
|
|
3322
|
-
}
|
|
3323
|
-
const page = automations.slice(0, input.limit);
|
|
3324
|
-
return {
|
|
3325
|
-
automations: page,
|
|
3326
|
-
...automations.length > input.limit && page.length > 0 ? { nextCursor: encodeCursor(page.at(-1), query) } : void 0
|
|
3327
|
-
};
|
|
3328
|
-
}
|
|
3329
3376
|
//#endregion
|
|
3330
3377
|
//#region src/chat/automations/read.ts
|
|
3331
|
-
const TASK_LIST_LIMIT = 100;
|
|
3332
|
-
const TASK_FETCH_LIMIT = 101;
|
|
3333
3378
|
const TASK_EXECUTION_LIST_LIMIT = 100;
|
|
3334
3379
|
function creatorLabel(creator) {
|
|
3335
3380
|
return creator.fullName?.trim() || (creator.userName?.trim() ? `@${creator.userName.trim()}` : "") || creator.slackUserId;
|
|
@@ -3337,7 +3382,7 @@ function creatorLabel(creator) {
|
|
|
3337
3382
|
function creatorKey(teamId, slackUserId) {
|
|
3338
3383
|
return `${teamId}:${slackUserId}`;
|
|
3339
3384
|
}
|
|
3340
|
-
async function
|
|
3385
|
+
async function creatorProfiles(candidates) {
|
|
3341
3386
|
const selectors = new Map(candidates.map(({ task }) => {
|
|
3342
3387
|
const teamId = task.destination.teamId;
|
|
3343
3388
|
const slackUserId = task.createdBy.slackUserId;
|
|
@@ -3348,11 +3393,16 @@ async function creatorProfileEmails(candidates) {
|
|
|
3348
3393
|
}));
|
|
3349
3394
|
if (selectors.size === 0) return /* @__PURE__ */ new Map();
|
|
3350
3395
|
const rows = await getDb().select({
|
|
3396
|
+
avatarUrl: juniorIdentities.avatarUrl,
|
|
3351
3397
|
email: juniorUsers.primaryEmailNormalized,
|
|
3398
|
+
emailVerified: juniorIdentities.emailVerified,
|
|
3352
3399
|
slackUserId: juniorIdentities.providerSubjectId,
|
|
3353
3400
|
teamId: juniorIdentities.providerTenantId
|
|
3354
|
-
}).from(juniorIdentities).
|
|
3355
|
-
return new Map(rows.map((row) => [creatorKey(row.teamId, row.slackUserId),
|
|
3401
|
+
}).from(juniorIdentities).leftJoin(juniorUsers, eq(juniorUsers.id, juniorIdentities.userId)).where(and(eq(juniorIdentities.kind, "user"), eq(juniorIdentities.provider, "slack"), or(...[...selectors.values()].map((selector) => and(eq(juniorIdentities.providerTenantId, selector.teamId), eq(juniorIdentities.providerSubjectId, selector.slackUserId))))));
|
|
3402
|
+
return new Map(rows.map((row) => [creatorKey(row.teamId, row.slackUserId), {
|
|
3403
|
+
email: row.emailVerified ? row.email ?? void 0 : void 0,
|
|
3404
|
+
avatarUrl: row.avatarUrl ?? void 0
|
|
3405
|
+
}]));
|
|
3356
3406
|
}
|
|
3357
3407
|
function destinationKey(destination) {
|
|
3358
3408
|
return `${destination.teamId}:${destination.channelId}`;
|
|
@@ -3386,20 +3436,22 @@ async function destinationDetails(destinations) {
|
|
|
3386
3436
|
}
|
|
3387
3437
|
function executionSummaryFields(stats) {
|
|
3388
3438
|
return {
|
|
3439
|
+
lastRunStatus: stats?.lastRunStatus,
|
|
3389
3440
|
...stats?.lastConversationId ? { lastConversationId: stats.lastConversationId } : void 0,
|
|
3390
3441
|
...stats?.lastExecutedAtMs ? { lastRunAt: new Date(stats.lastExecutedAtMs).toISOString() } : void 0,
|
|
3391
3442
|
runs: stats?.runs ?? emptyAutomationRunWindows(),
|
|
3392
3443
|
totalRuns: stats?.totalRuns ?? 0
|
|
3393
3444
|
};
|
|
3394
3445
|
}
|
|
3395
|
-
function scheduledAutomationSummary(task, ownedByViewer, destination, stats,
|
|
3446
|
+
function scheduledAutomationSummary(task, ownedByViewer, destination, stats, creator) {
|
|
3396
3447
|
if (task.status === "deleted") throw new Error("Deleted scheduled automations cannot enter the Automations view");
|
|
3397
|
-
const nextRunAtMs = task.runNowAtMs ?? task.nextRunAtMs;
|
|
3448
|
+
const nextRunAtMs = task.status === "active" ? task.runNowAtMs ?? task.nextRunAtMs : void 0;
|
|
3398
3449
|
const instruction = displayText(task.task.text, "Untitled scheduled automation");
|
|
3399
3450
|
return {
|
|
3400
3451
|
createdAt: new Date(task.createdAtMs).toISOString(),
|
|
3401
3452
|
createdBy: creatorLabel(task.createdBy),
|
|
3402
|
-
|
|
3453
|
+
createdByEmail: creator?.email,
|
|
3454
|
+
createdByAvatarUrl: creator?.avatarUrl,
|
|
3403
3455
|
destination: {
|
|
3404
3456
|
channelId: task.destination.channelId,
|
|
3405
3457
|
label: destination.label,
|
|
@@ -3414,16 +3466,20 @@ function scheduledAutomationSummary(task, ownedByViewer, destination, stats, cre
|
|
|
3414
3466
|
ownedByViewer,
|
|
3415
3467
|
schedule: displayText(task.schedule.description, "Schedule unavailable"),
|
|
3416
3468
|
status: task.status,
|
|
3469
|
+
statusReason: task.statusReason,
|
|
3470
|
+
timezone: task.schedule.timezone,
|
|
3471
|
+
credentialMode: task.credentialMode,
|
|
3417
3472
|
outcomes: effectiveTaskOutcomes(task.outcomes, task.destination),
|
|
3418
3473
|
title: taskDisplayTitle(task.title, instruction, "Untitled scheduled automation")
|
|
3419
3474
|
};
|
|
3420
3475
|
}
|
|
3421
|
-
function eventAutomationSummary(task, ownedByViewer, destination, stats,
|
|
3476
|
+
function eventAutomationSummary(task, ownedByViewer, destination, stats, creator) {
|
|
3422
3477
|
const instruction = task.task.text;
|
|
3423
3478
|
return {
|
|
3424
3479
|
createdAt: new Date(task.createdAtMs).toISOString(),
|
|
3425
3480
|
createdBy: creatorLabel(task.createdBy),
|
|
3426
|
-
|
|
3481
|
+
createdByEmail: creator?.email,
|
|
3482
|
+
createdByAvatarUrl: creator?.avatarUrl,
|
|
3427
3483
|
destination: {
|
|
3428
3484
|
channelId: task.destination.channelId,
|
|
3429
3485
|
label: destination.label,
|
|
@@ -3431,6 +3487,7 @@ function eventAutomationSummary(task, ownedByViewer, destination, stats, created
|
|
|
3431
3487
|
visibility: destination.visibility
|
|
3432
3488
|
},
|
|
3433
3489
|
events: task.trigger.events,
|
|
3490
|
+
match: task.trigger.match,
|
|
3434
3491
|
id: task.id,
|
|
3435
3492
|
instruction,
|
|
3436
3493
|
kind: "event",
|
|
@@ -3438,8 +3495,10 @@ function eventAutomationSummary(task, ownedByViewer, destination, stats, created
|
|
|
3438
3495
|
ownedByViewer,
|
|
3439
3496
|
resource: `${task.trigger.label} · ${task.trigger.identifier}`,
|
|
3440
3497
|
source: task.trigger.namespace,
|
|
3498
|
+
credentialMode: task.credentialMode,
|
|
3441
3499
|
outcomes: effectiveTaskOutcomes(task.outcomes, task.destination),
|
|
3442
3500
|
title: taskDisplayTitle(task.title, instruction, "Untitled event automation"),
|
|
3501
|
+
status: task.status === "paused" ? "paused" : "active",
|
|
3443
3502
|
triggerAvailable: eventAutomationTriggerAvailable(task, getEventCatalog())
|
|
3444
3503
|
};
|
|
3445
3504
|
}
|
|
@@ -3460,7 +3519,6 @@ async function resolveViewerTaskCandidate(user, kind, id) {
|
|
|
3460
3519
|
return {
|
|
3461
3520
|
kind: "scheduled",
|
|
3462
3521
|
ownedByViewer,
|
|
3463
|
-
publicToViewer,
|
|
3464
3522
|
task
|
|
3465
3523
|
};
|
|
3466
3524
|
}
|
|
@@ -3473,7 +3531,6 @@ async function resolveViewerTaskCandidate(user, kind, id) {
|
|
|
3473
3531
|
return {
|
|
3474
3532
|
kind: "event",
|
|
3475
3533
|
ownedByViewer,
|
|
3476
|
-
publicToViewer,
|
|
3477
3534
|
task
|
|
3478
3535
|
};
|
|
3479
3536
|
}
|
|
@@ -3484,20 +3541,19 @@ async function readViewerAutomationSummary(user, id) {
|
|
|
3484
3541
|
return automationSummaryForCandidate(candidate);
|
|
3485
3542
|
}
|
|
3486
3543
|
async function automationSummaryForCandidate(candidate) {
|
|
3487
|
-
const [destinations,
|
|
3544
|
+
const [destinations, creators, stats] = await Promise.all([
|
|
3488
3545
|
destinationDetails([candidate.task.destination]),
|
|
3489
|
-
|
|
3490
|
-
readAutomationExecutionSummaries(candidate.kind, "junior")
|
|
3546
|
+
creatorProfiles([candidate]),
|
|
3547
|
+
readAutomationExecutionSummaries(candidate.kind, "junior", { automationIds: [candidate.task.id] })
|
|
3491
3548
|
]);
|
|
3492
3549
|
const destination = destinations.get(destinationKey(candidate.task.destination)) ?? {
|
|
3493
3550
|
label: `Channel ${candidate.task.destination.channelId}`,
|
|
3494
3551
|
visibility: "private"
|
|
3495
3552
|
};
|
|
3496
|
-
const
|
|
3497
|
-
if (candidate.kind === "scheduled") return scheduledAutomationSummary(candidate.task, candidate.ownedByViewer, destination, stats.get(candidate.task.id),
|
|
3498
|
-
return eventAutomationSummary(candidate.task, candidate.ownedByViewer, destination, stats.get(candidate.task.id),
|
|
3553
|
+
const creator = creators.get(creatorKey(candidate.task.destination.teamId, candidate.task.createdBy.slackUserId));
|
|
3554
|
+
if (candidate.kind === "scheduled") return scheduledAutomationSummary(candidate.task, candidate.ownedByViewer, destination, stats.get(candidate.task.id), creator);
|
|
3555
|
+
return eventAutomationSummary(candidate.task, candidate.ownedByViewer, destination, stats.get(candidate.task.id), creator);
|
|
3499
3556
|
}
|
|
3500
|
-
/** Read viewer-owned and public-workspace automations as one bounded newest-first projection. */
|
|
3501
3557
|
function emptyAutomationExecutionDay(date) {
|
|
3502
3558
|
return {
|
|
3503
3559
|
costUsd: 0,
|
|
@@ -3528,67 +3584,59 @@ function automationExecutionStatusSixHours(hours, nowMs = Date.now()) {
|
|
|
3528
3584
|
nowMs
|
|
3529
3585
|
});
|
|
3530
3586
|
}
|
|
3531
|
-
/**
|
|
3532
|
-
async function readViewerAutomations(user, input = {}) {
|
|
3587
|
+
/** Read a page from the full accessible Automation collection. */
|
|
3588
|
+
async function readViewerAutomations(user, input = automationListQuerySchema.parse({})) {
|
|
3589
|
+
const { rows, ...page } = await readAutomationCollection(user, input);
|
|
3533
3590
|
const db = getDb();
|
|
3534
|
-
const
|
|
3591
|
+
const ids = rows.map((row) => row.id);
|
|
3592
|
+
const [scheduled, events] = ids.length ? await Promise.all([db.select().from(juniorSchedulerTasks).where(inArray(juniorSchedulerTasks.id, ids)), db.select().from(juniorEventAutomations).where(inArray(juniorEventAutomations.id, ids))]) : [[], []];
|
|
3535
3593
|
const identityIds = new Set(user.identities.map((identity) => identity.id));
|
|
3536
|
-
const
|
|
3537
|
-
const
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
task
|
|
3557
|
-
});
|
|
3558
|
-
for (const task of [...eventAutomations, ...publicEventAutomations]) candidatesById.set(`event:${task.id}`, {
|
|
3559
|
-
kind: "event",
|
|
3560
|
-
ownedByViewer: eventAutomationBelongsToUser(task, user),
|
|
3561
|
-
publicToViewer: publicEventAutomationIds.has(task.id),
|
|
3562
|
-
task
|
|
3594
|
+
const candidates = /* @__PURE__ */ new Map();
|
|
3595
|
+
for (const row of scheduled) {
|
|
3596
|
+
const task = parseScheduledAutomationRow(row);
|
|
3597
|
+
if (task && task.status !== "deleted") candidates.set(`scheduled:${task.id}`, {
|
|
3598
|
+
kind: "scheduled",
|
|
3599
|
+
task,
|
|
3600
|
+
ownedByViewer: identityIds.has(task.creatorIdentityId)
|
|
3601
|
+
});
|
|
3602
|
+
}
|
|
3603
|
+
for (const row of events) {
|
|
3604
|
+
const task = parseEventAutomationRow(row);
|
|
3605
|
+
if (task.status !== "deleted") candidates.set(`event:${task.id}`, {
|
|
3606
|
+
kind: "event",
|
|
3607
|
+
task,
|
|
3608
|
+
ownedByViewer: eventAutomationBelongsToUser(task, user)
|
|
3609
|
+
});
|
|
3610
|
+
}
|
|
3611
|
+
const selected = rows.flatMap((row) => {
|
|
3612
|
+
const candidate = candidates.get(`${row.kind}:${row.id}`);
|
|
3613
|
+
return candidate ? [candidate] : [];
|
|
3563
3614
|
});
|
|
3564
|
-
const
|
|
3565
|
-
const ownedCandidates = candidates.filter((candidate) => candidate.ownedByViewer);
|
|
3566
|
-
const publicCandidates = candidates.filter((candidate) => candidate.publicToViewer);
|
|
3567
|
-
const selectedCandidates = /* @__PURE__ */ new Set([...ownedCandidates.slice(0, TASK_LIST_LIMIT), ...publicCandidates.slice(0, TASK_LIST_LIMIT)]);
|
|
3568
|
-
const selected = candidates.filter((candidate) => selectedCandidates.has(candidate));
|
|
3569
|
-
const [destinations, creatorEmails] = await Promise.all([destinationDetails(selected.map(({ task }) => task.destination)), creatorProfileEmails(selected)]);
|
|
3615
|
+
const [destinations, creators] = await Promise.all([destinationDetails(selected.map(({ task }) => task.destination)), creatorProfiles(selected)]);
|
|
3570
3616
|
const visible = selected.filter((candidate) => candidate.ownedByViewer || destinations.get(destinationKey(candidate.task.destination))?.visibility === "public");
|
|
3617
|
+
const collection = viewerAutomationCollection(user);
|
|
3618
|
+
const access = exists(getDb().select({ id: collection.id }).from(collection).where(and(eq(collection.id, juniorAutomationExecutions.automationId), eq(collection.kind, juniorAutomationExecutions.kind), eq(juniorAutomationExecutions.namespace, "junior"))));
|
|
3571
3619
|
const [executionDays, executionHours, scheduledStats, eventStats] = await Promise.all([
|
|
3572
|
-
readAutomationExecutionDays(),
|
|
3573
|
-
readAutomationExecutionHours(),
|
|
3574
|
-
readAutomationExecutionSummaries("scheduled", "junior"),
|
|
3575
|
-
readAutomationExecutionSummaries("event", "junior")
|
|
3620
|
+
readAutomationExecutionDays(90, { access }),
|
|
3621
|
+
readAutomationExecutionHours(168, { access }),
|
|
3622
|
+
readAutomationExecutionSummaries("scheduled", "junior", { automationIds: ids }),
|
|
3623
|
+
readAutomationExecutionSummaries("event", "junior", { automationIds: ids })
|
|
3576
3624
|
]);
|
|
3577
3625
|
const automations = visible.map((candidate) => {
|
|
3578
3626
|
const destination = destinations.get(destinationKey(candidate.task.destination)) ?? {
|
|
3579
3627
|
label: `Channel ${candidate.task.destination.channelId}`,
|
|
3580
3628
|
visibility: "private"
|
|
3581
3629
|
};
|
|
3582
|
-
const
|
|
3583
|
-
if (candidate.kind === "scheduled") return scheduledAutomationSummary(candidate.task, candidate.ownedByViewer, destination, scheduledStats.get(candidate.task.id),
|
|
3584
|
-
return eventAutomationSummary(candidate.task, candidate.ownedByViewer, destination, eventStats.get(candidate.task.id),
|
|
3630
|
+
const creator = creators.get(creatorKey(candidate.task.destination.teamId, candidate.task.createdBy.slackUserId));
|
|
3631
|
+
if (candidate.kind === "scheduled") return scheduledAutomationSummary(candidate.task, candidate.ownedByViewer, destination, scheduledStats.get(candidate.task.id), creator);
|
|
3632
|
+
return eventAutomationSummary(candidate.task, candidate.ownedByViewer, destination, eventStats.get(candidate.task.id), creator);
|
|
3585
3633
|
});
|
|
3586
3634
|
return {
|
|
3587
3635
|
executionDays,
|
|
3588
3636
|
executionHours,
|
|
3589
3637
|
executionSixHours: automationExecutionSixHours(executionHours),
|
|
3590
3638
|
automations,
|
|
3591
|
-
|
|
3639
|
+
...page
|
|
3592
3640
|
};
|
|
3593
3641
|
}
|
|
3594
3642
|
/** Load deleted scheduled automations the viewer still owns, newest first. */
|
|
@@ -3625,14 +3673,14 @@ function addDeletedAutomationRun(args, automationTitles, automations) {
|
|
|
3625
3673
|
*/
|
|
3626
3674
|
async function readViewerAutomationRuns(user) {
|
|
3627
3675
|
const [taskList, deletedScheduled, deletedEvent] = await Promise.all([
|
|
3628
|
-
|
|
3676
|
+
getDb().select().from(viewerAutomationCollection(user)),
|
|
3629
3677
|
readDeletedOwnedScheduledAutomations(user),
|
|
3630
3678
|
readDeletedOwnedEventAutomations(user)
|
|
3631
3679
|
]);
|
|
3632
3680
|
const automationTitles = /* @__PURE__ */ new Map();
|
|
3633
3681
|
const automations = [];
|
|
3634
|
-
for (const task of taskList
|
|
3635
|
-
automationTitles.set(`${task.kind}:${task.id}`, task.title);
|
|
3682
|
+
for (const task of taskList) {
|
|
3683
|
+
automationTitles.set(`${task.kind}:${task.id}`, taskDisplayTitle(task.title ?? void 0, task.instruction, `Untitled ${task.kind} automation`));
|
|
3636
3684
|
automations.push({
|
|
3637
3685
|
kind: task.kind,
|
|
3638
3686
|
automationId: task.id
|