@sentry/junior 0.203.0 → 0.205.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-D4nu8MQV.js +17521 -0
- package/dist/agent-hooks-DXHpTJ4m.js +4042 -0
- package/dist/agent-runner-C04colea.js +678 -0
- package/dist/api/schema.d.ts +2736 -2639
- package/dist/api/schema.js +4 -169
- package/dist/api.d.ts +23 -17
- package/dist/api.js +3881 -5326
- package/dist/app-Cb776lL8.d.ts +247 -0
- package/dist/app.d.ts +4 -520
- package/dist/app.js +9524 -11875
- package/dist/auth-pause-state-C2P3guS1.js +168 -0
- package/dist/catalog-runtime-CXw3asJD.js +7 -0
- package/dist/checkpoint-CrViJ8Ps.js +2450 -0
- package/dist/cli/chat.d.ts +20 -0
- package/dist/cli/chat.js +229 -336
- package/dist/cli/check.d.ts +8 -0
- package/dist/cli/check.js +513 -806
- package/dist/cli/env.d.ts +13 -0
- package/dist/cli/env.js +63 -12
- package/dist/cli/init.d.ts +3 -0
- package/dist/cli/init.js +90 -159
- package/dist/cli/main.d.ts +6 -0
- package/dist/cli/main.js +43 -64
- package/dist/cli/plugins.d.ts +13 -0
- package/dist/cli/plugins.js +148 -227
- package/dist/cli/run.d.ts +16 -0
- package/dist/cli/run.js +52 -8
- package/dist/cli/snapshot-create.d.ts +4 -0
- package/dist/cli/snapshot-create.js +71 -125
- package/dist/cli/upgrade.d.ts +15 -0
- package/dist/cli/upgrade.js +232 -335
- package/dist/config-BOtJQ7Pc.js +941 -0
- package/dist/context-B45_WqVX.js +298 -0
- package/dist/conversation-privacy-BZRA6Pes.js +182 -0
- package/dist/conversation-work-C6y3T5Hx.js +51 -0
- package/dist/credential-sync-Bep34IB_.js +2661 -0
- package/dist/db-BeyXKJOI.d.ts +5 -0
- package/dist/destinations-BeBiJeEM.js +29 -0
- package/dist/execution-stats-B6lgWxCK.js +373 -0
- package/dist/executor-BlUhFL7_.js +721 -0
- package/dist/experimental-CIXf7Uye.js +46 -0
- package/dist/history-ClE4toxe.d.ts +32 -0
- package/dist/history-CqTuSAdI.js +441 -0
- package/dist/instrumentation.d.ts +3 -3
- package/dist/instrumentation.js +130 -176
- package/dist/job-queue-DLMqIK74.js +63 -0
- package/dist/logging-BMJzdDlP.js +1210 -0
- package/dist/model-profile-CZmVoGdX.js +49 -0
- package/dist/nitro.d.ts +31 -36
- package/dist/nitro.js +275 -456
- package/dist/oauth-callback-server-WwG5_Zgm.js +130 -0
- package/dist/oauth-relay-CdCnu80W.js +62 -0
- package/dist/package-discovery-7a9YqLhE.js +455 -0
- package/dist/pending-auth-CUbXq06G.js +185 -0
- package/dist/plugin-module-BKGOXca2.js +81 -0
- package/dist/plugins-BI9A1vKA.d.ts +95 -0
- package/dist/plugins-egKWjgue.js +99 -0
- package/dist/private-trace-result-BLvBjy_e.js +16 -0
- package/dist/profile-T0tXE4zu.js +861 -0
- package/dist/registry-BA0__8ai.js +1419 -0
- package/dist/reporting-schema-DosTN__Q.js +105 -0
- package/dist/resolve-bvD1ORdv.js +546 -0
- package/dist/rolldown-runtime-B4FMCO8f.js +28 -0
- package/dist/runner-BXYxfIUL.js +392 -0
- package/dist/sandbox-egress-signals-ngEPjSEW.js +46 -0
- package/dist/schema-Rn8-XEaN.js +1140 -0
- package/dist/sentry-CD3SdJl-.js +26 -0
- package/dist/skills-DJOlYdBs.js +316 -0
- package/dist/spawn-h0R1bTEo.js +29 -0
- package/dist/stats-3YZn-Swi.js +2648 -0
- package/dist/store-C5qxF2ac.d.ts +208 -0
- package/dist/task-queue-CoXwd2eq.js +136 -0
- package/dist/task-runner-B2_mJZj0.js +639 -0
- package/dist/thread-state-CqZ1m-es.js +431 -0
- package/dist/turn-execution-fI6PZ0iI.js +47 -0
- package/dist/turn-lifecycle-CcnwrOcp.js +54 -0
- package/dist/usage-B5SfMb3H.js +296 -0
- package/dist/usage-schema-CKaDkH_j.js +20 -0
- package/dist/validation-DfWcvhuS.js +40 -0
- package/dist/vercel-queue-DIkVcfK4.js +167 -0
- package/dist/vercel.d.ts +6 -6
- package/dist/vercel.js +14 -6
- package/dist/version-CnXZ614E.d.ts +5 -0
- package/dist/version-oD-oPrbO.js +8 -0
- package/dist/version.d.ts +2 -4
- package/dist/version.js +2 -6
- package/dist/web-authorization-Crbzlmzo.js +76 -0
- package/dist/web-input-BOuq3oKK.js +494 -0
- package/dist/work-B91LQKHr.js +509 -0
- package/dist/worker-BcRRTe6k.js +625 -0
- package/package.json +9 -9
- package/dist/agent-hooks-GB2VPTNA.js +0 -51
- package/dist/catalog-runtime-UBLIWD3O.js +0 -14
- package/dist/chunk-2LYHWMR4.js +0 -46
- package/dist/chunk-352OWAZH.js +0 -320
- package/dist/chunk-3Y47VMHV.js +0 -6
- package/dist/chunk-3YJSKOWF.js +0 -164
- package/dist/chunk-4KSSKTQ5.js +0 -229
- package/dist/chunk-4UO6FK4G.js +0 -64
- package/dist/chunk-5AHX53NQ.js +0 -717
- package/dist/chunk-5J3E5FWA.js +0 -700
- package/dist/chunk-5PDAG6O3.js +0 -206
- package/dist/chunk-6Y5B5PEX.js +0 -80
- package/dist/chunk-7GWQ5S5R.js +0 -2086
- package/dist/chunk-7H2VZTWN.js +0 -103
- package/dist/chunk-7NL7T6VU.js +0 -406
- package/dist/chunk-A5L27LE4.js +0 -387
- package/dist/chunk-ADIAWL34.js +0 -81
- package/dist/chunk-ASTEMBKJ.js +0 -519
- package/dist/chunk-CEMMLV5L.js +0 -98
- package/dist/chunk-CM5HNV2W.js +0 -131
- package/dist/chunk-CRTRBBRI.js +0 -105
- package/dist/chunk-DA2KGZZI.js +0 -13
- package/dist/chunk-DIB4GX3G.js +0 -8
- package/dist/chunk-DKIJ2RP5.js +0 -68
- package/dist/chunk-DWPN6SCH.js +0 -172
- package/dist/chunk-EDKUFGPL.js +0 -64
- package/dist/chunk-ENY6SKIU.js +0 -1228
- package/dist/chunk-F4MGCZOX.js +0 -2943
- package/dist/chunk-FA6POGLH.js +0 -70
- package/dist/chunk-FSUR7GTG.js +0 -20475
- package/dist/chunk-GKEOHJL5.js +0 -1856
- package/dist/chunk-GMCB6W5Y.js +0 -333
- package/dist/chunk-IE2VPG7O.js +0 -1135
- package/dist/chunk-IZ5B5IHQ.js +0 -3656
- package/dist/chunk-JAL3CH4G.js +0 -27
- package/dist/chunk-KIIEUXD3.js +0 -1764
- package/dist/chunk-KXU4NIL7.js +0 -692
- package/dist/chunk-MTM5IFCY.js +0 -1190
- package/dist/chunk-N7TMSOZH.js +0 -235
- package/dist/chunk-NCEBK6PH.js +0 -36
- package/dist/chunk-NHJU2ZAL.js +0 -56
- package/dist/chunk-P6KTRNVC.js +0 -321
- package/dist/chunk-PDO5BLNM.js +0 -22
- package/dist/chunk-PDRNLLFT.js +0 -3295
- package/dist/chunk-PEQXYSSE.js +0 -45
- package/dist/chunk-PKEX2AQX.js +0 -198
- package/dist/chunk-PSJCGIGO.js +0 -1163
- package/dist/chunk-PZUSUL55.js +0 -506
- package/dist/chunk-R45M4TUS.js +0 -37
- package/dist/chunk-RC745GGZ.js +0 -122
- package/dist/chunk-SP3QKZXJ.js +0 -820
- package/dist/chunk-TXKUKMVO.js +0 -417
- package/dist/chunk-UE4GPWSD.js +0 -931
- package/dist/chunk-UIE3R5XU.js +0 -17
- package/dist/chunk-UXPG6ZIN.js +0 -84
- package/dist/chunk-VFUK3X5B.js +0 -50
- package/dist/chunk-VNMJWREK.js +0 -787
- package/dist/chunk-VZ7CSOX2.js +0 -706
- package/dist/chunk-WCNQJKFT.js +0 -10
- package/dist/chunk-WDZR3QQM.js +0 -135
- package/dist/chunk-WLYVJCHV.js +0 -1433
- package/dist/chunk-WS7NAFTM.js +0 -1520
- package/dist/chunk-X6GDDWQH.js +0 -60
- package/dist/chunk-XPDEYNQI.js +0 -102
- package/dist/chunk-XRW44VGM.js +0 -58
- package/dist/chunk-Y2CM7HXH.js +0 -111
- package/dist/chunk-Y7X25LFY.js +0 -15
- package/dist/conversation-work-VIOQ5ZF3.js +0 -54
- package/dist/db-NGDJV7YH.js +0 -26
- package/dist/discovery-DWVDM7UK.js +0 -44
- package/dist/experimental-P5FZM4CU.js +0 -12
- package/dist/oauth-callback-server-YFY7YEGK.js +0 -243
- package/dist/oauth-relay-5XPVRPYB.js +0 -8
- package/dist/plugins-YTYJMMJF.js +0 -14
- package/dist/runner-ORTNSB2Q.js +0 -522
- package/dist/sandbox-egress-signals-FIL7EGPR.js +0 -73
- package/dist/sentry-MB3NL3IR.js +0 -42
- package/dist/skills-H376WQOH.js +0 -27
- package/dist/spawn-T43WHLOS.js +0 -42
- package/dist/task-runner-LBZWF32V.js +0 -36
- package/dist/validation-REJLRKI6.js +0 -17
- package/dist/work-3KJ3RE76.js +0 -51
- package/dist/worker-QFEBU72E.js +0 -24
package/dist/chunk-UE4GPWSD.js
DELETED
|
@@ -1,931 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
isExperimentalFeatureEnabled
|
|
3
|
-
} from "./chunk-2LYHWMR4.js";
|
|
4
|
-
import {
|
|
5
|
-
SlackActionError,
|
|
6
|
-
applyPluginFormatMarkdown,
|
|
7
|
-
getSlackClient,
|
|
8
|
-
isDmChannel,
|
|
9
|
-
normalizeSlackConversationId,
|
|
10
|
-
withSlackRetries
|
|
11
|
-
} from "./chunk-IZ5B5IHQ.js";
|
|
12
|
-
import {
|
|
13
|
-
parseDestination
|
|
14
|
-
} from "./chunk-GKEOHJL5.js";
|
|
15
|
-
import {
|
|
16
|
-
getStateAdapter
|
|
17
|
-
} from "./chunk-P6KTRNVC.js";
|
|
18
|
-
import {
|
|
19
|
-
normalizeSlackEmojiName
|
|
20
|
-
} from "./chunk-ENY6SKIU.js";
|
|
21
|
-
import {
|
|
22
|
-
pluginCatalogRuntime
|
|
23
|
-
} from "./chunk-WCNQJKFT.js";
|
|
24
|
-
import {
|
|
25
|
-
parseSlackMessageTs,
|
|
26
|
-
parseSlackUserId
|
|
27
|
-
} from "./chunk-N7TMSOZH.js";
|
|
28
|
-
import {
|
|
29
|
-
isRecord,
|
|
30
|
-
logInfo,
|
|
31
|
-
logWarn
|
|
32
|
-
} from "./chunk-WS7NAFTM.js";
|
|
33
|
-
|
|
34
|
-
// src/chat/agent/types.ts
|
|
35
|
-
var RetryableDeliveryError = class extends Error {
|
|
36
|
-
constructor(cause) {
|
|
37
|
-
super("Assistant delivery was transient or ambiguous", { cause });
|
|
38
|
-
this.name = "RetryableDeliveryError";
|
|
39
|
-
}
|
|
40
|
-
};
|
|
41
|
-
function isAgentRunFeatureDisabled(disabledFeatures, feature) {
|
|
42
|
-
return disabledFeatures?.includes(feature) ?? false;
|
|
43
|
-
}
|
|
44
|
-
function actorFromRun(run) {
|
|
45
|
-
if (run.dispatch?.actor) {
|
|
46
|
-
return run.dispatch.actor;
|
|
47
|
-
}
|
|
48
|
-
if (run.actor) {
|
|
49
|
-
return run.actor;
|
|
50
|
-
}
|
|
51
|
-
if (run.credentialContext && !("type" in run.credentialContext.actor)) {
|
|
52
|
-
return run.credentialContext.actor;
|
|
53
|
-
}
|
|
54
|
-
return void 0;
|
|
55
|
-
}
|
|
56
|
-
function assertRunConsistency(run) {
|
|
57
|
-
const { destination, source } = run;
|
|
58
|
-
switch (source.kind) {
|
|
59
|
-
case "slack": {
|
|
60
|
-
if (destination.platform !== "slack") {
|
|
61
|
-
throw new TypeError(
|
|
62
|
-
"Run source and destination platforms do not match"
|
|
63
|
-
);
|
|
64
|
-
}
|
|
65
|
-
if (source.teamId !== destination.teamId) {
|
|
66
|
-
throw new TypeError("Slack source and destination teams do not match");
|
|
67
|
-
}
|
|
68
|
-
break;
|
|
69
|
-
}
|
|
70
|
-
case "local": {
|
|
71
|
-
if (destination.platform !== "local") {
|
|
72
|
-
throw new TypeError(
|
|
73
|
-
"Run source and destination platforms do not match"
|
|
74
|
-
);
|
|
75
|
-
}
|
|
76
|
-
if (source.conversationId !== destination.conversationId) {
|
|
77
|
-
throw new TypeError(
|
|
78
|
-
"Source and destination conversation IDs do not match"
|
|
79
|
-
);
|
|
80
|
-
}
|
|
81
|
-
if (run.surface !== "internal" && destination.conversationId !== run.conversationId) {
|
|
82
|
-
throw new TypeError(
|
|
83
|
-
"Source, destination, and run conversation IDs do not match"
|
|
84
|
-
);
|
|
85
|
-
}
|
|
86
|
-
break;
|
|
87
|
-
}
|
|
88
|
-
case "web": {
|
|
89
|
-
if (run.surface !== "internal" && source.conversationId !== run.conversationId) {
|
|
90
|
-
throw new TypeError("Web source and run conversation IDs do not match");
|
|
91
|
-
}
|
|
92
|
-
switch (destination.platform) {
|
|
93
|
-
case "local": {
|
|
94
|
-
if (source.conversationId !== destination.conversationId) {
|
|
95
|
-
throw new TypeError(
|
|
96
|
-
"Source and destination conversation IDs do not match"
|
|
97
|
-
);
|
|
98
|
-
}
|
|
99
|
-
if (run.surface !== "internal" && destination.conversationId !== run.conversationId) {
|
|
100
|
-
throw new TypeError(
|
|
101
|
-
"Source, destination, and run conversation IDs do not match"
|
|
102
|
-
);
|
|
103
|
-
}
|
|
104
|
-
break;
|
|
105
|
-
}
|
|
106
|
-
case "slack":
|
|
107
|
-
break;
|
|
108
|
-
}
|
|
109
|
-
break;
|
|
110
|
-
}
|
|
111
|
-
case "resource_event":
|
|
112
|
-
case "scheduled_task":
|
|
113
|
-
case "event_task":
|
|
114
|
-
case "plugin_dispatch":
|
|
115
|
-
case "agent_invocation":
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
const actor = run.dispatch?.actor ?? run.actor;
|
|
119
|
-
if (!actor || actor.platform === "system") {
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
if (actor.platform !== source.kind) {
|
|
123
|
-
throw new TypeError(
|
|
124
|
-
`Actor platform "${actor.platform}" does not match Source kind "${source.kind}"`
|
|
125
|
-
);
|
|
126
|
-
}
|
|
127
|
-
if (actor.platform === "slack" && source.kind === "slack" && actor.teamId !== source.teamId) {
|
|
128
|
-
throw new TypeError("Slack Actor team does not match Source team");
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
function toolInvocationDestination(run) {
|
|
132
|
-
if (run.destination.platform !== "slack" || !run.toolChannelId) {
|
|
133
|
-
return run.destination;
|
|
134
|
-
}
|
|
135
|
-
return {
|
|
136
|
-
platform: "slack",
|
|
137
|
-
teamId: run.destination.teamId,
|
|
138
|
-
channelId: run.toolChannelId
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
function surfaceFromRun(run) {
|
|
142
|
-
if (run.surface) {
|
|
143
|
-
return run.surface;
|
|
144
|
-
}
|
|
145
|
-
if (run.source.kind === "slack") {
|
|
146
|
-
return "slack";
|
|
147
|
-
}
|
|
148
|
-
if (run.source.kind === "web") {
|
|
149
|
-
return "api";
|
|
150
|
-
}
|
|
151
|
-
return "internal";
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// src/chat/runtime/agent-runner.ts
|
|
155
|
-
var AGENT_ABORT_SETTLE_GRACE_MS = 5e3;
|
|
156
|
-
function createAgentRunner(execute, options) {
|
|
157
|
-
const attachmentStorage = options?.attachmentStorage;
|
|
158
|
-
const streamFn = options?.streamFn;
|
|
159
|
-
const tracePropagation = options?.tracePropagation;
|
|
160
|
-
const bindSpawnAgent = options?.bindSpawnAgent;
|
|
161
|
-
const canBindSpawn = Boolean(bindSpawnAgent) && isExperimentalFeatureEnabled("subagents");
|
|
162
|
-
return {
|
|
163
|
-
run: async (run) => {
|
|
164
|
-
const spawnAgent = bindSpawnAgent && canBindSpawn && !isAgentRunFeatureDisabled(run.disabledFeatures, "subagents") ? bindSpawnAgent(run) : void 0;
|
|
165
|
-
const nextRun = {
|
|
166
|
-
...run,
|
|
167
|
-
environment: {
|
|
168
|
-
...run.environment,
|
|
169
|
-
attachmentStorage: run.environment?.attachmentStorage ?? attachmentStorage,
|
|
170
|
-
sandboxTracePropagation: run.environment?.sandboxTracePropagation ?? tracePropagation
|
|
171
|
-
},
|
|
172
|
-
...spawnAgent ? {
|
|
173
|
-
durability: {
|
|
174
|
-
...run.durability,
|
|
175
|
-
spawnAgent
|
|
176
|
-
}
|
|
177
|
-
} : void 0
|
|
178
|
-
};
|
|
179
|
-
return await execute(nextRun, streamFn);
|
|
180
|
-
}
|
|
181
|
-
};
|
|
182
|
-
}
|
|
183
|
-
async function waitForAgentSettlement(runPromise) {
|
|
184
|
-
let timeoutId;
|
|
185
|
-
const settleGrace = new Promise((resolve) => {
|
|
186
|
-
timeoutId = setTimeout(resolve, AGENT_ABORT_SETTLE_GRACE_MS);
|
|
187
|
-
timeoutId.unref?.();
|
|
188
|
-
});
|
|
189
|
-
await Promise.race([
|
|
190
|
-
runPromise.then(
|
|
191
|
-
() => void 0,
|
|
192
|
-
() => void 0
|
|
193
|
-
),
|
|
194
|
-
settleGrace
|
|
195
|
-
]);
|
|
196
|
-
if (timeoutId) {
|
|
197
|
-
clearTimeout(timeoutId);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
async function runAgentWithTimeout(agentRunner, run, timeoutMs) {
|
|
201
|
-
if (typeof timeoutMs !== "number") {
|
|
202
|
-
return await agentRunner.run(run);
|
|
203
|
-
}
|
|
204
|
-
const timeoutError = new Error(
|
|
205
|
-
`executeAgentRun timed out after ${timeoutMs}ms`
|
|
206
|
-
);
|
|
207
|
-
const abortController = new AbortController();
|
|
208
|
-
const signal = run.signal ? AbortSignal.any([run.signal, abortController.signal]) : abortController.signal;
|
|
209
|
-
const runPromise = agentRunner.run({ ...run, signal });
|
|
210
|
-
let timeoutId;
|
|
211
|
-
const timeoutPromise = new Promise((_, reject) => {
|
|
212
|
-
timeoutId = setTimeout(() => {
|
|
213
|
-
abortController.abort(timeoutError);
|
|
214
|
-
reject(timeoutError);
|
|
215
|
-
}, timeoutMs);
|
|
216
|
-
});
|
|
217
|
-
try {
|
|
218
|
-
return await Promise.race([runPromise, timeoutPromise]);
|
|
219
|
-
} catch (error) {
|
|
220
|
-
if (abortController.signal.aborted) {
|
|
221
|
-
await waitForAgentSettlement(runPromise);
|
|
222
|
-
}
|
|
223
|
-
throw error;
|
|
224
|
-
} finally {
|
|
225
|
-
if (timeoutId) {
|
|
226
|
-
clearTimeout(timeoutId);
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
// src/chat/slack/outbound.ts
|
|
232
|
-
var MAX_SLACK_MESSAGE_TEXT_CHARS = 4e4;
|
|
233
|
-
function requireSlackConversationId(channelId, action) {
|
|
234
|
-
const normalized = normalizeSlackConversationId(channelId);
|
|
235
|
-
if (!normalized) {
|
|
236
|
-
throw new Error(`${action} requires a valid channel ID`);
|
|
237
|
-
}
|
|
238
|
-
return normalized;
|
|
239
|
-
}
|
|
240
|
-
function requireSlackThreadTimestamp(threadTs, action) {
|
|
241
|
-
const normalized = threadTs.trim();
|
|
242
|
-
if (!normalized) {
|
|
243
|
-
throw new Error(`${action} requires a thread timestamp`);
|
|
244
|
-
}
|
|
245
|
-
return normalized;
|
|
246
|
-
}
|
|
247
|
-
function requireSlackMessageTimestamp(timestamp, action) {
|
|
248
|
-
const normalized = parseSlackMessageTs(timestamp);
|
|
249
|
-
if (!normalized) {
|
|
250
|
-
throw new Error(`${action} requires a target message timestamp`);
|
|
251
|
-
}
|
|
252
|
-
return normalized;
|
|
253
|
-
}
|
|
254
|
-
function requireSlackMessageText(text, action) {
|
|
255
|
-
if (text.trim().length === 0) {
|
|
256
|
-
throw new Error(`${action} requires non-empty text`);
|
|
257
|
-
}
|
|
258
|
-
if (text.length > MAX_SLACK_MESSAGE_TEXT_CHARS) {
|
|
259
|
-
throw new Error(
|
|
260
|
-
`${action} text exceeds Slack's 40000 character truncation limit`
|
|
261
|
-
);
|
|
262
|
-
}
|
|
263
|
-
return text;
|
|
264
|
-
}
|
|
265
|
-
async function getSlackMessagePermalink(args) {
|
|
266
|
-
try {
|
|
267
|
-
const response = await withSlackRetries(
|
|
268
|
-
() => getSlackClient().chat.getPermalink({
|
|
269
|
-
channel: args.channelId,
|
|
270
|
-
message_ts: args.messageTs
|
|
271
|
-
}),
|
|
272
|
-
3,
|
|
273
|
-
{
|
|
274
|
-
action: "chat.getPermalink",
|
|
275
|
-
idempotent: true,
|
|
276
|
-
spanAttributes: {
|
|
277
|
-
"app.slack.channel_id": args.channelId,
|
|
278
|
-
"app.slack.message_ts": args.messageTs
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
);
|
|
282
|
-
return response.permalink;
|
|
283
|
-
} catch {
|
|
284
|
-
return void 0;
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
async function postSlackMessage(input) {
|
|
288
|
-
const channelId = requireSlackConversationId(
|
|
289
|
-
input.channelId,
|
|
290
|
-
"Slack message posting"
|
|
291
|
-
);
|
|
292
|
-
const text = requireSlackMessageText(input.text, "Slack message posting");
|
|
293
|
-
const threadTs = input.threadTs ? requireSlackThreadTimestamp(
|
|
294
|
-
input.threadTs,
|
|
295
|
-
"Slack thread message posting"
|
|
296
|
-
) : void 0;
|
|
297
|
-
const response = await withSlackRetries(
|
|
298
|
-
() => getSlackClient().chat.postMessage({
|
|
299
|
-
channel: channelId,
|
|
300
|
-
text,
|
|
301
|
-
unfurl_links: false,
|
|
302
|
-
unfurl_media: false,
|
|
303
|
-
...input.blocks?.length ? {
|
|
304
|
-
// @ts-expect-error non-overlapping boundary cast; rule forbids as-unknown-as chains
|
|
305
|
-
blocks: input.blocks
|
|
306
|
-
} : void 0,
|
|
307
|
-
...threadTs ? { thread_ts: threadTs } : void 0
|
|
308
|
-
}),
|
|
309
|
-
3,
|
|
310
|
-
{
|
|
311
|
-
action: "chat.postMessage",
|
|
312
|
-
spanAttributes: {
|
|
313
|
-
"app.slack.channel_id": channelId,
|
|
314
|
-
...threadTs ? { "app.slack.thread_ts": threadTs } : void 0
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
);
|
|
318
|
-
const messageTs = parseSlackMessageTs(response.ts);
|
|
319
|
-
if (!messageTs) {
|
|
320
|
-
throw new Error("Slack message posted without ts");
|
|
321
|
-
}
|
|
322
|
-
return {
|
|
323
|
-
ts: messageTs,
|
|
324
|
-
...input.includePermalink ? {
|
|
325
|
-
permalink: await getSlackMessagePermalink({
|
|
326
|
-
channelId,
|
|
327
|
-
messageTs
|
|
328
|
-
})
|
|
329
|
-
} : void 0
|
|
330
|
-
};
|
|
331
|
-
}
|
|
332
|
-
async function postSlackEphemeralMessage(input) {
|
|
333
|
-
const channelId = requireSlackConversationId(
|
|
334
|
-
input.channelId,
|
|
335
|
-
"Slack ephemeral message posting"
|
|
336
|
-
);
|
|
337
|
-
const userId = parseSlackUserId(input.userId);
|
|
338
|
-
if (!userId) {
|
|
339
|
-
throw new Error("Slack ephemeral message posting requires a user ID");
|
|
340
|
-
}
|
|
341
|
-
const text = requireSlackMessageText(
|
|
342
|
-
input.text,
|
|
343
|
-
"Slack ephemeral message posting"
|
|
344
|
-
);
|
|
345
|
-
const threadTs = input.threadTs ? requireSlackThreadTimestamp(
|
|
346
|
-
input.threadTs,
|
|
347
|
-
"Slack ephemeral thread message posting"
|
|
348
|
-
) : void 0;
|
|
349
|
-
const response = await withSlackRetries(
|
|
350
|
-
() => getSlackClient().chat.postEphemeral({
|
|
351
|
-
channel: channelId,
|
|
352
|
-
user: userId,
|
|
353
|
-
text,
|
|
354
|
-
...threadTs ? { thread_ts: threadTs } : void 0
|
|
355
|
-
}),
|
|
356
|
-
3,
|
|
357
|
-
{
|
|
358
|
-
action: "chat.postEphemeral",
|
|
359
|
-
spanAttributes: {
|
|
360
|
-
"app.slack.channel_id": channelId,
|
|
361
|
-
"app.slack.user_id": userId,
|
|
362
|
-
...threadTs ? { "app.slack.thread_ts": threadTs } : void 0
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
);
|
|
366
|
-
return {
|
|
367
|
-
messageTs: response.message_ts
|
|
368
|
-
};
|
|
369
|
-
}
|
|
370
|
-
async function uploadFilesToConversation(input) {
|
|
371
|
-
const channelId = requireSlackConversationId(
|
|
372
|
-
input.channelId,
|
|
373
|
-
"Slack file upload"
|
|
374
|
-
);
|
|
375
|
-
const threadTs = input.threadTs ? requireSlackThreadTimestamp(input.threadTs, "Slack file upload") : void 0;
|
|
376
|
-
if (input.files.length === 0) {
|
|
377
|
-
throw new Error("Slack file upload requires at least one file");
|
|
378
|
-
}
|
|
379
|
-
const fileUploads = input.files.map((file) => {
|
|
380
|
-
const filename = file.filename.trim();
|
|
381
|
-
if (!filename) {
|
|
382
|
-
throw new Error(
|
|
383
|
-
"Slack file upload requires every file to have a filename"
|
|
384
|
-
);
|
|
385
|
-
}
|
|
386
|
-
return {
|
|
387
|
-
file: file.data,
|
|
388
|
-
filename
|
|
389
|
-
};
|
|
390
|
-
});
|
|
391
|
-
const response = await withSlackRetries(
|
|
392
|
-
() => getSlackClient().filesUploadV2({
|
|
393
|
-
channel_id: channelId,
|
|
394
|
-
...threadTs ? { thread_ts: threadTs } : void 0,
|
|
395
|
-
file_uploads: fileUploads
|
|
396
|
-
}),
|
|
397
|
-
3,
|
|
398
|
-
{
|
|
399
|
-
action: "filesUploadV2",
|
|
400
|
-
spanAttributes: {
|
|
401
|
-
"app.slack.channel_id": channelId,
|
|
402
|
-
...threadTs ? { "app.slack.thread_ts": threadTs } : void 0
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
);
|
|
406
|
-
return {
|
|
407
|
-
files: (response.files ?? []).flatMap(
|
|
408
|
-
(completion) => (completion.files ?? []).map((file) => file.id ? { id: file.id } : {})
|
|
409
|
-
)
|
|
410
|
-
};
|
|
411
|
-
}
|
|
412
|
-
async function addReactionToMessage(input) {
|
|
413
|
-
const channelId = requireSlackConversationId(
|
|
414
|
-
input.channelId,
|
|
415
|
-
"Slack reaction"
|
|
416
|
-
);
|
|
417
|
-
const timestamp = requireSlackMessageTimestamp(
|
|
418
|
-
input.timestamp,
|
|
419
|
-
"Slack reaction"
|
|
420
|
-
);
|
|
421
|
-
const emoji = normalizeSlackEmojiName(input.emoji);
|
|
422
|
-
if (!emoji) {
|
|
423
|
-
throw new Error("Slack reaction requires a valid emoji alias name");
|
|
424
|
-
}
|
|
425
|
-
try {
|
|
426
|
-
await withSlackRetries(
|
|
427
|
-
() => getSlackClient().reactions.add({
|
|
428
|
-
channel: channelId,
|
|
429
|
-
timestamp,
|
|
430
|
-
name: emoji
|
|
431
|
-
}),
|
|
432
|
-
3,
|
|
433
|
-
{
|
|
434
|
-
action: "reactions.add",
|
|
435
|
-
idempotent: true,
|
|
436
|
-
spanAttributes: {
|
|
437
|
-
"app.slack.channel_id": channelId,
|
|
438
|
-
"app.slack.message_ts": timestamp,
|
|
439
|
-
"app.slack.reaction": emoji
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
);
|
|
443
|
-
} catch (error) {
|
|
444
|
-
if (error instanceof SlackActionError && error.code === "already_reacted") {
|
|
445
|
-
return { ok: true };
|
|
446
|
-
}
|
|
447
|
-
throw error;
|
|
448
|
-
}
|
|
449
|
-
return { ok: true };
|
|
450
|
-
}
|
|
451
|
-
async function removeReactionFromMessage(input) {
|
|
452
|
-
const channelId = requireSlackConversationId(
|
|
453
|
-
input.channelId,
|
|
454
|
-
"Slack reaction removal"
|
|
455
|
-
);
|
|
456
|
-
const timestamp = requireSlackMessageTimestamp(
|
|
457
|
-
input.timestamp,
|
|
458
|
-
"Slack reaction removal"
|
|
459
|
-
);
|
|
460
|
-
const emoji = normalizeSlackEmojiName(input.emoji);
|
|
461
|
-
if (!emoji) {
|
|
462
|
-
throw new Error("Slack reaction removal requires a valid emoji alias name");
|
|
463
|
-
}
|
|
464
|
-
try {
|
|
465
|
-
await withSlackRetries(
|
|
466
|
-
() => getSlackClient().reactions.remove({
|
|
467
|
-
channel: channelId,
|
|
468
|
-
timestamp,
|
|
469
|
-
name: emoji
|
|
470
|
-
}),
|
|
471
|
-
3,
|
|
472
|
-
{
|
|
473
|
-
action: "reactions.remove",
|
|
474
|
-
idempotent: true,
|
|
475
|
-
spanAttributes: {
|
|
476
|
-
"app.slack.channel_id": channelId,
|
|
477
|
-
"app.slack.message_ts": timestamp,
|
|
478
|
-
"app.slack.reaction": emoji
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
);
|
|
482
|
-
} catch (error) {
|
|
483
|
-
if (error instanceof SlackActionError && error.code === "no_reaction") {
|
|
484
|
-
return { ok: true };
|
|
485
|
-
}
|
|
486
|
-
throw error;
|
|
487
|
-
}
|
|
488
|
-
return { ok: true };
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
// src/chat/oauth-flow.ts
|
|
492
|
-
import { randomBytes } from "crypto";
|
|
493
|
-
import {
|
|
494
|
-
actorSchema,
|
|
495
|
-
sourceSchema
|
|
496
|
-
} from "@sentry/junior-plugin-api";
|
|
497
|
-
|
|
498
|
-
// src/chat/slack/status-format.ts
|
|
499
|
-
var SLACK_STATUS_MAX_LENGTH = 50;
|
|
500
|
-
function truncateStatusText(text) {
|
|
501
|
-
const trimmed = text.trim();
|
|
502
|
-
if (!trimmed) {
|
|
503
|
-
return "";
|
|
504
|
-
}
|
|
505
|
-
if (trimmed.length <= SLACK_STATUS_MAX_LENGTH) {
|
|
506
|
-
return trimmed;
|
|
507
|
-
}
|
|
508
|
-
return `${trimmed.slice(0, SLACK_STATUS_MAX_LENGTH - 3).trimEnd()}...`;
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
// src/chat/slack/mrkdwn.ts
|
|
512
|
-
function escapeSlackMrkdwnText(text) {
|
|
513
|
-
return text.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
|
|
514
|
-
}
|
|
515
|
-
function escapeSlackLinkUrl(url) {
|
|
516
|
-
return url.replaceAll("&", "&").replaceAll("<", "%3C").replaceAll(">", "%3E");
|
|
517
|
-
}
|
|
518
|
-
function formatSlackLink(url, label) {
|
|
519
|
-
return `<${escapeSlackLinkUrl(url)}|${escapeSlackMrkdwnText(label)}>`;
|
|
520
|
-
}
|
|
521
|
-
function readInlineCodeSpan(line, start) {
|
|
522
|
-
if (line[start] !== "`") {
|
|
523
|
-
return void 0;
|
|
524
|
-
}
|
|
525
|
-
let n = 1;
|
|
526
|
-
while (line[start + n] === "`") {
|
|
527
|
-
n++;
|
|
528
|
-
}
|
|
529
|
-
const marker = "`".repeat(n);
|
|
530
|
-
let search = start + n;
|
|
531
|
-
while (search < line.length) {
|
|
532
|
-
const close = line.indexOf(marker, search);
|
|
533
|
-
if (close === -1) {
|
|
534
|
-
return void 0;
|
|
535
|
-
}
|
|
536
|
-
const after = close + n;
|
|
537
|
-
if (line[after] !== "`") {
|
|
538
|
-
return { text: line.slice(start, after), end: after };
|
|
539
|
-
}
|
|
540
|
-
search = after + 1;
|
|
541
|
-
}
|
|
542
|
-
return void 0;
|
|
543
|
-
}
|
|
544
|
-
function readExistingSlackAngleToken(line, start) {
|
|
545
|
-
if (line[start] !== "<") {
|
|
546
|
-
return void 0;
|
|
547
|
-
}
|
|
548
|
-
const close = line.indexOf(">", start + 1);
|
|
549
|
-
if (close === -1) {
|
|
550
|
-
return void 0;
|
|
551
|
-
}
|
|
552
|
-
const body = line.slice(start + 1, close);
|
|
553
|
-
if (/^(?:https?:\/\/|@|#|!)/.test(body)) {
|
|
554
|
-
return { text: line.slice(start, close + 1), end: close + 1 };
|
|
555
|
-
}
|
|
556
|
-
return void 0;
|
|
557
|
-
}
|
|
558
|
-
function readMarkdownLink(line, start) {
|
|
559
|
-
if (line[start] !== "[") {
|
|
560
|
-
return void 0;
|
|
561
|
-
}
|
|
562
|
-
const labelEnd = line.indexOf("](", start + 1);
|
|
563
|
-
if (labelEnd === -1) {
|
|
564
|
-
return void 0;
|
|
565
|
-
}
|
|
566
|
-
const destStart = labelEnd + 2;
|
|
567
|
-
if (!line.startsWith("http://", destStart) && !line.startsWith("https://", destStart)) {
|
|
568
|
-
return void 0;
|
|
569
|
-
}
|
|
570
|
-
const closeParens = line.indexOf(")", destStart);
|
|
571
|
-
if (closeParens === -1) {
|
|
572
|
-
return void 0;
|
|
573
|
-
}
|
|
574
|
-
return { text: line.slice(start, closeParens + 1), end: closeParens + 1 };
|
|
575
|
-
}
|
|
576
|
-
function hasUnmatchedClosingParen(text) {
|
|
577
|
-
let balance = 0;
|
|
578
|
-
for (const ch of text) {
|
|
579
|
-
if (ch === "(") balance++;
|
|
580
|
-
else if (ch === ")") balance--;
|
|
581
|
-
}
|
|
582
|
-
return balance < 0;
|
|
583
|
-
}
|
|
584
|
-
function readBareUrl(line, start) {
|
|
585
|
-
let end = start;
|
|
586
|
-
while (end < line.length) {
|
|
587
|
-
const ch = line[end];
|
|
588
|
-
if (/\s/.test(ch) || ch === "<" || ch === ">" || ch === '"' || ch === "`" || ch === "|" || ch === "*") {
|
|
589
|
-
break;
|
|
590
|
-
}
|
|
591
|
-
end++;
|
|
592
|
-
}
|
|
593
|
-
if (end === start) {
|
|
594
|
-
return void 0;
|
|
595
|
-
}
|
|
596
|
-
let raw = line.slice(start, end);
|
|
597
|
-
let suffix = "";
|
|
598
|
-
const peel = () => {
|
|
599
|
-
suffix = raw.slice(-1) + suffix;
|
|
600
|
-
raw = raw.slice(0, -1);
|
|
601
|
-
};
|
|
602
|
-
const shouldPeel = () => raw.endsWith("_") || /[.,!?;:]$/.test(raw) || raw.endsWith(")") && hasUnmatchedClosingParen(raw);
|
|
603
|
-
while (raw.length > 0 && shouldPeel()) {
|
|
604
|
-
peel();
|
|
605
|
-
}
|
|
606
|
-
if (!/^https?:\/\/.+/.test(raw)) {
|
|
607
|
-
return void 0;
|
|
608
|
-
}
|
|
609
|
-
return { url: raw, suffix, end };
|
|
610
|
-
}
|
|
611
|
-
function wrapBareUrlsOnLine(line) {
|
|
612
|
-
let result = "";
|
|
613
|
-
let i = 0;
|
|
614
|
-
while (i < line.length) {
|
|
615
|
-
const codeSpan = readInlineCodeSpan(line, i);
|
|
616
|
-
if (codeSpan) {
|
|
617
|
-
result += codeSpan.text;
|
|
618
|
-
i = codeSpan.end;
|
|
619
|
-
continue;
|
|
620
|
-
}
|
|
621
|
-
const angleToken = readExistingSlackAngleToken(line, i);
|
|
622
|
-
if (angleToken) {
|
|
623
|
-
result += angleToken.text;
|
|
624
|
-
i = angleToken.end;
|
|
625
|
-
continue;
|
|
626
|
-
}
|
|
627
|
-
const mdLink = readMarkdownLink(line, i);
|
|
628
|
-
if (mdLink) {
|
|
629
|
-
result += mdLink.text;
|
|
630
|
-
i = mdLink.end;
|
|
631
|
-
continue;
|
|
632
|
-
}
|
|
633
|
-
if (line.startsWith("https://", i) || line.startsWith("http://", i)) {
|
|
634
|
-
const parsed = readBareUrl(line, i);
|
|
635
|
-
if (parsed) {
|
|
636
|
-
result += `<${parsed.url}>${parsed.suffix}`;
|
|
637
|
-
i = parsed.end;
|
|
638
|
-
continue;
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
result += line[i];
|
|
642
|
-
i++;
|
|
643
|
-
}
|
|
644
|
-
return result;
|
|
645
|
-
}
|
|
646
|
-
function wrapBareUrls(text) {
|
|
647
|
-
const lines = text.split("\n");
|
|
648
|
-
const out = [];
|
|
649
|
-
let inCodeBlock = false;
|
|
650
|
-
for (const line of lines) {
|
|
651
|
-
if (line.trimStart().startsWith("```")) {
|
|
652
|
-
inCodeBlock = !inCodeBlock;
|
|
653
|
-
out.push(line);
|
|
654
|
-
continue;
|
|
655
|
-
}
|
|
656
|
-
out.push(inCodeBlock ? line : wrapBareUrlsOnLine(line));
|
|
657
|
-
}
|
|
658
|
-
return out.join("\n");
|
|
659
|
-
}
|
|
660
|
-
function ensureBlockSpacing(text) {
|
|
661
|
-
const codeBlockPattern = /^```/;
|
|
662
|
-
const listItemPattern = /^[-*•]\s|^\d+\.\s/;
|
|
663
|
-
const lines = text.split("\n");
|
|
664
|
-
const result = [];
|
|
665
|
-
let inCodeBlock = false;
|
|
666
|
-
for (let i = 0; i < lines.length; i++) {
|
|
667
|
-
const line = lines[i];
|
|
668
|
-
const isCodeFence = codeBlockPattern.test(line.trimStart());
|
|
669
|
-
if (isCodeFence) {
|
|
670
|
-
if (!inCodeBlock) {
|
|
671
|
-
const prev2 = result.length > 0 ? result[result.length - 1] : void 0;
|
|
672
|
-
if (prev2 !== void 0 && prev2.trim() !== "") {
|
|
673
|
-
result.push("");
|
|
674
|
-
}
|
|
675
|
-
}
|
|
676
|
-
inCodeBlock = !inCodeBlock;
|
|
677
|
-
result.push(line);
|
|
678
|
-
continue;
|
|
679
|
-
}
|
|
680
|
-
if (inCodeBlock) {
|
|
681
|
-
result.push(line);
|
|
682
|
-
continue;
|
|
683
|
-
}
|
|
684
|
-
const prev = result.length > 0 ? result[result.length - 1] : void 0;
|
|
685
|
-
if (prev !== void 0 && prev.trim() !== "" && line.trim() !== "" && !(listItemPattern.test(prev.trimStart()) && listItemPattern.test(line.trimStart()))) {
|
|
686
|
-
result.push("");
|
|
687
|
-
}
|
|
688
|
-
result.push(line);
|
|
689
|
-
}
|
|
690
|
-
return result.join("\n");
|
|
691
|
-
}
|
|
692
|
-
function normalizeSlackReplyMarkdown(text) {
|
|
693
|
-
let normalized = applyPluginFormatMarkdown(text).replace(/\r\n?/g, "\n").replace(/[ \t]+$/gm, "");
|
|
694
|
-
normalized = wrapBareUrls(normalized);
|
|
695
|
-
normalized = ensureBlockSpacing(normalized);
|
|
696
|
-
return normalized.replace(/\n{3,}/g, "\n\n").trim();
|
|
697
|
-
}
|
|
698
|
-
function normalizeSlackStatusText(text) {
|
|
699
|
-
const trimmed = text.trim();
|
|
700
|
-
if (!trimmed) {
|
|
701
|
-
return "";
|
|
702
|
-
}
|
|
703
|
-
return truncateStatusText(trimmed.replace(/(?:\.\s*)+$/, "").trim());
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
// src/chat/slack/oauth-authorization-message.ts
|
|
707
|
-
function formatOAuthAuthorizationMessage(args) {
|
|
708
|
-
return `${formatSlackLink(args.authorizationUrl, args.label)}. ${args.completionText}`;
|
|
709
|
-
}
|
|
710
|
-
|
|
711
|
-
// src/chat/oauth-flow.ts
|
|
712
|
-
var OAUTH_STATE_TTL_MS = 10 * 60 * 1e3;
|
|
713
|
-
function optionalString(value) {
|
|
714
|
-
return typeof value === "string" ? value : void 0;
|
|
715
|
-
}
|
|
716
|
-
function parseOAuthStatePayload(value) {
|
|
717
|
-
if (!isRecord(value)) {
|
|
718
|
-
return void 0;
|
|
719
|
-
}
|
|
720
|
-
if (typeof value.userId !== "string" || typeof value.provider !== "string") {
|
|
721
|
-
return void 0;
|
|
722
|
-
}
|
|
723
|
-
const actor = value.actor === void 0 ? void 0 : actorSchema.safeParse(value.actor);
|
|
724
|
-
if (value.actor !== void 0 && (!actor || !actor.success)) {
|
|
725
|
-
return void 0;
|
|
726
|
-
}
|
|
727
|
-
const destination = parseDestination(value.destination);
|
|
728
|
-
if (value.destination !== void 0 && !destination) {
|
|
729
|
-
return void 0;
|
|
730
|
-
}
|
|
731
|
-
const source = value.source === void 0 ? void 0 : sourceSchema.safeParse(value.source);
|
|
732
|
-
if (value.source !== void 0 && (!source || !source.success)) {
|
|
733
|
-
return void 0;
|
|
734
|
-
}
|
|
735
|
-
return {
|
|
736
|
-
userId: value.userId,
|
|
737
|
-
provider: value.provider,
|
|
738
|
-
...actor?.success ? { actor: actor.data } : void 0,
|
|
739
|
-
...optionalString(value.channelId) ? { channelId: optionalString(value.channelId) } : void 0,
|
|
740
|
-
...destination ? { destination } : void 0,
|
|
741
|
-
...source?.success ? { source: source.data } : void 0,
|
|
742
|
-
...optionalString(value.threadTs) ? { threadTs: optionalString(value.threadTs) } : void 0,
|
|
743
|
-
...optionalString(value.resumeConversationId) ? { resumeConversationId: optionalString(value.resumeConversationId) } : void 0,
|
|
744
|
-
...optionalString(value.resumeSessionId) ? { resumeSessionId: optionalString(value.resumeSessionId) } : void 0,
|
|
745
|
-
...optionalString(value.scope) ? { scope: optionalString(value.scope) } : void 0
|
|
746
|
-
};
|
|
747
|
-
}
|
|
748
|
-
function formatProviderLabel(provider) {
|
|
749
|
-
const displayName = pluginCatalogRuntime.getDisplayName(provider);
|
|
750
|
-
if (!displayName) {
|
|
751
|
-
throw new Error(`Unknown plugin provider display name: "${provider}"`);
|
|
752
|
-
}
|
|
753
|
-
return displayName;
|
|
754
|
-
}
|
|
755
|
-
function resolveBaseUrl() {
|
|
756
|
-
const explicit = process.env.JUNIOR_BASE_URL?.trim();
|
|
757
|
-
if (explicit) return explicit;
|
|
758
|
-
const vercelProd = process.env.VERCEL_PROJECT_PRODUCTION_URL?.trim();
|
|
759
|
-
if (vercelProd) return `https://${vercelProd}`;
|
|
760
|
-
const vercelUrl = process.env.VERCEL_URL?.trim();
|
|
761
|
-
if (vercelUrl) return `https://${vercelUrl}`;
|
|
762
|
-
return void 0;
|
|
763
|
-
}
|
|
764
|
-
async function deliverPrivateMessage(input) {
|
|
765
|
-
let client;
|
|
766
|
-
try {
|
|
767
|
-
client = getSlackClient();
|
|
768
|
-
} catch {
|
|
769
|
-
logWarn("oauth.private_delivery.skipped", {
|
|
770
|
-
"app.reason": "missing_bot_token"
|
|
771
|
-
});
|
|
772
|
-
return false;
|
|
773
|
-
}
|
|
774
|
-
if (input.channelId) {
|
|
775
|
-
try {
|
|
776
|
-
if (isDmChannel(input.channelId)) {
|
|
777
|
-
await postSlackMessage({
|
|
778
|
-
channelId: input.channelId,
|
|
779
|
-
text: input.text,
|
|
780
|
-
threadTs: input.threadTs
|
|
781
|
-
});
|
|
782
|
-
} else {
|
|
783
|
-
await postSlackEphemeralMessage({
|
|
784
|
-
channelId: input.channelId,
|
|
785
|
-
userId: input.userId,
|
|
786
|
-
text: input.text,
|
|
787
|
-
threadTs: input.threadTs
|
|
788
|
-
});
|
|
789
|
-
}
|
|
790
|
-
return "in_context";
|
|
791
|
-
} catch (error) {
|
|
792
|
-
logWarn("oauth.private_delivery.failed", {
|
|
793
|
-
"app.slack.error": error instanceof Error ? error.message : String(error),
|
|
794
|
-
"app.slack.channel": input.channelId
|
|
795
|
-
});
|
|
796
|
-
}
|
|
797
|
-
}
|
|
798
|
-
try {
|
|
799
|
-
const dmChannelId = (await withSlackRetries(
|
|
800
|
-
() => client.conversations.open({ users: input.userId }),
|
|
801
|
-
3,
|
|
802
|
-
{
|
|
803
|
-
action: "conversations.open",
|
|
804
|
-
spanAttributes: { "app.slack.user_id": input.userId }
|
|
805
|
-
}
|
|
806
|
-
)).channel?.id;
|
|
807
|
-
if (!dmChannelId) {
|
|
808
|
-
logWarn("oauth.dm.fallback.failed", { "app.reason": "no_dm_channel_id" });
|
|
809
|
-
return false;
|
|
810
|
-
}
|
|
811
|
-
await postSlackMessage({ channelId: dmChannelId, text: input.text });
|
|
812
|
-
return "fallback_dm";
|
|
813
|
-
} catch (error) {
|
|
814
|
-
logWarn("oauth.dm.fallback.failed", {
|
|
815
|
-
"app.slack.error": error instanceof Error ? error.message : String(error)
|
|
816
|
-
});
|
|
817
|
-
return false;
|
|
818
|
-
}
|
|
819
|
-
}
|
|
820
|
-
async function deliverOAuthAuthorization(request, input) {
|
|
821
|
-
if (input.authorization) {
|
|
822
|
-
await input.authorization.deliver(request);
|
|
823
|
-
return "in_context";
|
|
824
|
-
}
|
|
825
|
-
return await deliverPrivateMessage({
|
|
826
|
-
channelId: input.channelId,
|
|
827
|
-
threadTs: input.threadTs,
|
|
828
|
-
userId: input.userId,
|
|
829
|
-
text: formatOAuthAuthorizationMessage(request)
|
|
830
|
-
});
|
|
831
|
-
}
|
|
832
|
-
async function startOAuthFlow(provider, input) {
|
|
833
|
-
const providerConfig = pluginCatalogRuntime.getOAuthConfig(provider);
|
|
834
|
-
if (!providerConfig) {
|
|
835
|
-
return {
|
|
836
|
-
ok: false,
|
|
837
|
-
error: `Provider "${provider}" does not support OAuth authorization`
|
|
838
|
-
};
|
|
839
|
-
}
|
|
840
|
-
const clientId = process.env[providerConfig.clientIdEnv]?.trim();
|
|
841
|
-
if (!clientId) {
|
|
842
|
-
return {
|
|
843
|
-
ok: false,
|
|
844
|
-
error: `Missing ${providerConfig.clientIdEnv} environment variable`
|
|
845
|
-
};
|
|
846
|
-
}
|
|
847
|
-
const baseUrl = resolveBaseUrl();
|
|
848
|
-
if (!baseUrl) {
|
|
849
|
-
return {
|
|
850
|
-
ok: false,
|
|
851
|
-
error: "Cannot determine base URL (set JUNIOR_BASE_URL or deploy to Vercel)"
|
|
852
|
-
};
|
|
853
|
-
}
|
|
854
|
-
const state = input.authorization ? await input.authorization.createState() : randomBytes(32).toString("hex");
|
|
855
|
-
const requestedScope = input.scope ?? providerConfig.scope;
|
|
856
|
-
await getStateAdapter().set(
|
|
857
|
-
`oauth-state:${state}`,
|
|
858
|
-
{
|
|
859
|
-
userId: input.actorId,
|
|
860
|
-
provider,
|
|
861
|
-
...input.actor ? { actor: input.actor } : void 0,
|
|
862
|
-
...input.channelId ? { channelId: input.channelId } : void 0,
|
|
863
|
-
...input.destination ? { destination: input.destination } : void 0,
|
|
864
|
-
...input.source ? { source: input.source } : void 0,
|
|
865
|
-
...input.threadTs ? { threadTs: input.threadTs } : void 0,
|
|
866
|
-
...input.resumeConversationId ? { resumeConversationId: input.resumeConversationId } : void 0,
|
|
867
|
-
...input.resumeSessionId ? { resumeSessionId: input.resumeSessionId } : void 0,
|
|
868
|
-
...requestedScope ? { scope: requestedScope } : void 0
|
|
869
|
-
},
|
|
870
|
-
OAUTH_STATE_TTL_MS
|
|
871
|
-
);
|
|
872
|
-
const authorizeParams = new URLSearchParams({
|
|
873
|
-
client_id: clientId,
|
|
874
|
-
state,
|
|
875
|
-
redirect_uri: `${baseUrl}${providerConfig.callbackPath}`,
|
|
876
|
-
response_type: "code"
|
|
877
|
-
});
|
|
878
|
-
if (requestedScope) {
|
|
879
|
-
authorizeParams.set("scope", requestedScope);
|
|
880
|
-
}
|
|
881
|
-
for (const [key, value] of Object.entries(
|
|
882
|
-
providerConfig.authorizeParams ?? {}
|
|
883
|
-
)) {
|
|
884
|
-
authorizeParams.set(key, value);
|
|
885
|
-
}
|
|
886
|
-
logInfo("jr_rpc.oauth.started", {
|
|
887
|
-
"app.credential.provider": provider,
|
|
888
|
-
...input.activeSkillName ? { "app.skill.name": input.activeSkillName } : void 0
|
|
889
|
-
});
|
|
890
|
-
const authorizationUrl = `${providerConfig.authorizeEndpoint}?${authorizeParams.toString()}`;
|
|
891
|
-
const authorizationRequest = {
|
|
892
|
-
authorizationUrl,
|
|
893
|
-
label: `Click here to link your ${formatProviderLabel(provider)} account`,
|
|
894
|
-
completionText: input.resumeSessionId ? "Once you've authorized, Junior will continue automatically." : "Once you've authorized, you'll see a confirmation in Slack."
|
|
895
|
-
};
|
|
896
|
-
return {
|
|
897
|
-
ok: true,
|
|
898
|
-
delivery: await deliverOAuthAuthorization(authorizationRequest, {
|
|
899
|
-
authorization: input.authorization,
|
|
900
|
-
channelId: input.channelId,
|
|
901
|
-
threadTs: input.threadTs,
|
|
902
|
-
userId: input.actorId
|
|
903
|
-
})
|
|
904
|
-
};
|
|
905
|
-
}
|
|
906
|
-
|
|
907
|
-
export {
|
|
908
|
-
RetryableDeliveryError,
|
|
909
|
-
isAgentRunFeatureDisabled,
|
|
910
|
-
actorFromRun,
|
|
911
|
-
assertRunConsistency,
|
|
912
|
-
toolInvocationDestination,
|
|
913
|
-
surfaceFromRun,
|
|
914
|
-
getSlackMessagePermalink,
|
|
915
|
-
postSlackMessage,
|
|
916
|
-
uploadFilesToConversation,
|
|
917
|
-
addReactionToMessage,
|
|
918
|
-
removeReactionFromMessage,
|
|
919
|
-
truncateStatusText,
|
|
920
|
-
escapeSlackMrkdwnText,
|
|
921
|
-
formatSlackLink,
|
|
922
|
-
normalizeSlackReplyMarkdown,
|
|
923
|
-
normalizeSlackStatusText,
|
|
924
|
-
parseOAuthStatePayload,
|
|
925
|
-
formatProviderLabel,
|
|
926
|
-
resolveBaseUrl,
|
|
927
|
-
deliverOAuthAuthorization,
|
|
928
|
-
startOAuthFlow,
|
|
929
|
-
createAgentRunner,
|
|
930
|
-
runAgentWithTimeout
|
|
931
|
-
};
|