@andreprado/agentkit 0.1.0-alpha.9 → 0.1.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/README.md +18 -1
- package/docs/guides/add-channel.md +251 -7
- package/docs/guides/add-knowledge.md +10 -0
- package/docs/guides/add-managed-composio.md +165 -0
- package/docs/guides/add-tool.md +10 -3
- package/docs/guides/channel-security.md +162 -32
- package/docs/guides/connect-discord.md +178 -0
- package/docs/guides/connect-slack.md +126 -0
- package/docs/guides/connect-telegram.md +61 -1
- package/docs/guides/connect-whatsapp-evolution.md +121 -0
- package/docs/guides/connect-whatsapp-uazapi.md +139 -0
- package/docs/guides/connect-whatsapp-zapster.md +119 -16
- package/docs/guides/create-agent.md +31 -4
- package/docs/guides/debug-channel.md +159 -0
- package/docs/guides/improve-from-production.md +151 -0
- package/docs/guides/prepare-deploy.md +32 -14
- package/docs/guides/replay-production-traces.md +72 -0
- package/docs/guides/run-evals.md +95 -25
- package/docs/guides/security-rules.md +9 -5
- package/docs/guides/send-feedback.md +135 -0
- package/docs/guides/use-jev.md +67 -0
- package/docs/guides/use-provider.md +70 -3
- package/docs/llms-full.txt +295 -25
- package/docs/llms.txt +54 -7
- package/package.json +3 -7
- package/src/cli/args.ts +23 -2
- package/src/cli/cloud-client.ts +121 -9
- package/src/cli/commands/channels.ts +856 -36
- package/src/cli/commands/feedback.ts +438 -0
- package/src/cli/commands/provider.ts +47 -0
- package/src/cli/commands/transcribe.ts +171 -0
- package/src/cli/deploy-chat-ui.ts +232 -18
- package/src/cli/deploy-readiness.ts +227 -14
- package/src/cli/help.ts +67 -9
- package/src/cli/index.ts +740 -35
- package/src/cli/new-command.ts +41 -0
- package/src/cloud/client.ts +4 -3
- package/src/cloud/contracts.ts +1 -1
- package/src/create-project.ts +18 -35
- package/src/index.ts +565 -11
- package/src/providers/codex-auth.ts +111 -0
- package/src/providers/pi.ts +88 -19
- package/src/providers/test.ts +36 -0
- package/src/providers/types.ts +8 -0
- package/src/runtime/channel-test-harness.ts +21 -1
- package/src/runtime/channels/discord.ts +896 -0
- package/src/runtime/channels/generic-webhook.ts +974 -0
- package/src/runtime/channels/slack.ts +646 -0
- package/src/runtime/channels/telegram.ts +343 -12
- package/src/runtime/channels/whatsapp-evolution.ts +1357 -0
- package/src/runtime/channels/whatsapp-meta.ts +9 -0
- package/src/runtime/channels/whatsapp-uazapi.ts +1323 -0
- package/src/runtime/channels/whatsapp-zapster.ts +674 -40
- package/src/runtime/channels.ts +83 -3
- package/src/runtime/chat.ts +70 -44
- package/src/runtime/config.ts +489 -20
- package/src/runtime/core/manifest.ts +75 -5
- package/src/runtime/core/targets.ts +5 -5
- package/src/runtime/deploy-readiness.ts +34 -4
- package/src/runtime/dev-server.ts +639 -39
- package/src/runtime/env.ts +8 -3
- package/src/runtime/evals.ts +445 -74
- package/src/runtime/improve.ts +868 -0
- package/src/runtime/inspect.ts +173 -4
- package/src/runtime/integrations/composio.ts +425 -0
- package/src/runtime/knowledge/retrieve.ts +25 -5
- package/src/runtime/knowledge/schema.ts +45 -1
- package/src/runtime/prompt-context.ts +141 -0
- package/src/runtime/runtime-contract.ts +71 -7
- package/src/runtime/skills.ts +95 -0
- package/src/runtime/targets/cloudflare/build.ts +1010 -208
- package/src/runtime/targets/container/server.ts +1 -1
- package/src/runtime/targets/vps/deploy.ts +26 -9
- package/src/runtime/tool-runner.ts +9 -1
- package/src/runtime/tools.ts +26 -2
- package/src/runtime/transcription.ts +483 -0
- package/src/storage/sqlite.ts +7 -2
- package/src/templates/blank.ts +37 -9
- package/src/templates/dentista.ts +40 -14
- package/src/templates/skills/agentkit-build-agent/SKILL.md +34 -5
- package/src/templates/skills/agentkit-build-agent/templates/appointment-intake.instructions.md +2 -1
- package/src/templates/skills/agentkit-capsule/SKILL.md +32 -3
- package/src/templates/skills/agentkit-capsule/references/docs-router.md +2 -2
- package/src/templates/skills/agentkit-channels/SKILL.md +66 -1
- package/src/templates/skills/agentkit-channels/references/channel-buffering.md +8 -1
- package/src/templates/skills/agentkit-channels/references/channel-debugging.md +28 -3
- package/src/templates/skills/agentkit-channels/references/discord.md +93 -0
- package/src/templates/skills/agentkit-channels/references/slack.md +56 -0
- package/src/templates/skills/agentkit-channels/references/telegram.md +34 -0
- package/src/templates/skills/agentkit-channels/references/whatsapp-evolution.md +57 -0
- package/src/templates/skills/agentkit-channels/references/whatsapp-uazapi.md +54 -0
- package/src/templates/skills/agentkit-channels/references/whatsapp-zapster.md +42 -8
- package/src/templates/skills/agentkit-database/SKILL.md +11 -0
- package/src/templates/skills/agentkit-deploy/SKILL.md +9 -1
- package/src/templates/skills/agentkit-evals/SKILL.md +77 -13
- package/src/templates/skills/agentkit-evals/templates/multi-turn.eval.md +13 -6
- package/src/templates/skills/agentkit-evals/templates/no-leak.eval.md +8 -4
- package/src/templates/skills/agentkit-evals/templates/smoke.eval.md +8 -4
- package/src/templates/skills/agentkit-evals/templates/tool-call.eval.md +16 -7
- package/src/templates/skills/agentkit-improve/SKILL.md +96 -0
- package/src/templates/skills/agentkit-improve/references/replay-side-effects.md +18 -0
- package/src/templates/skills/agentkit-improve/references/trace-packets.md +22 -0
- package/src/templates/skills/agentkit-improve/templates/regression.eval.md +18 -0
- package/src/templates/skills/agentkit-integrations/SKILL.md +98 -0
- package/src/templates/skills/agentkit-knowledge/SKILL.md +4 -1
- package/src/templates/skills/agentkit-prompts/SKILL.md +3 -1
- package/src/templates/skills/agentkit-provider/SKILL.md +29 -4
- package/src/templates/skills/agentkit-security/SKILL.md +5 -2
- package/src/templates/skills/agentkit-tools/SKILL.md +8 -1
- package/src/templates/skills/agentkit-tools/examples/eval-safe-external-action.tool.md +8 -8
- package/src/templates/skills/agentkit-tools/examples/jev-service-fit.tool.md +110 -0
- package/src/templates/skills/agentkit-troubleshooting/SKILL.md +25 -1
- package/src/templates/support.ts +42 -12
- package/docs/guides/agentkit-skills-architecture.md +0 -471
- package/docs/guides/channels-implementation-map.md +0 -243
- package/docs/guides/channels-production-handoff.md +0 -101
- package/docs/portable-deploy-release-checklist.md +0 -41
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createHmac } from "node:crypto";
|
|
1
2
|
import { readFile } from "node:fs/promises";
|
|
2
3
|
import { resolve } from "node:path";
|
|
3
4
|
|
|
@@ -14,12 +15,18 @@ type DeployState = LocalDeployState;
|
|
|
14
15
|
type CloudChannel = {
|
|
15
16
|
id: string;
|
|
16
17
|
name: string;
|
|
17
|
-
type: "website" | "telegram" | "whatsapp";
|
|
18
|
-
provider: "agentkit" | "telegram" | "zapster" | "meta";
|
|
18
|
+
type: "website" | "telegram" | "whatsapp" | "discord" | "slack" | "webhook";
|
|
19
|
+
provider: "agentkit" | "telegram" | "zapster" | "meta" | "uazapi" | "evolution" | "discord" | "slack" | "generic";
|
|
20
|
+
mode?: "interactions" | "bot";
|
|
21
|
+
direction?: "inbound" | "outbound";
|
|
19
22
|
status: string;
|
|
20
23
|
webhook_url: string;
|
|
21
24
|
required_secrets: Array<{ name: string; status: string }>;
|
|
22
25
|
buffer?: AgentChannel["buffer"];
|
|
26
|
+
audio?: AgentChannel["audio"];
|
|
27
|
+
reply_to?: Extract<AgentChannel, { replyTo?: unknown }>["replyTo"];
|
|
28
|
+
url_secret?: string;
|
|
29
|
+
auth?: Extract<AgentChannel, { type: "webhook"; direction: "outbound" }>["auth"];
|
|
23
30
|
};
|
|
24
31
|
|
|
25
32
|
type CloudDelivery = {
|
|
@@ -43,13 +50,44 @@ type CloudDelivery = {
|
|
|
43
50
|
}>;
|
|
44
51
|
};
|
|
45
52
|
|
|
53
|
+
type CloudChannelBuffer = {
|
|
54
|
+
channel_id: string;
|
|
55
|
+
deploy_id: string;
|
|
56
|
+
conversation_id: string;
|
|
57
|
+
job_id?: string;
|
|
58
|
+
delivery_id?: string;
|
|
59
|
+
buffered_delivery_ids: string[];
|
|
60
|
+
buffered_message_count: number;
|
|
61
|
+
buffered_char_count: number;
|
|
62
|
+
status: string;
|
|
63
|
+
first_received_at: string;
|
|
64
|
+
last_received_at: string;
|
|
65
|
+
available_at?: string;
|
|
66
|
+
age_ms: number;
|
|
67
|
+
stale: boolean;
|
|
68
|
+
message_preview: string;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
type ChannelBufferActionResponse = {
|
|
72
|
+
buffer: CloudChannelBuffer | null;
|
|
73
|
+
job: {
|
|
74
|
+
id: string;
|
|
75
|
+
status: string;
|
|
76
|
+
conversation_id: string;
|
|
77
|
+
} | null;
|
|
78
|
+
cleared?: boolean;
|
|
79
|
+
retried?: boolean;
|
|
80
|
+
};
|
|
81
|
+
|
|
46
82
|
type ChannelSetupResponse = {
|
|
47
83
|
channel: CloudChannel;
|
|
48
84
|
setup?: {
|
|
49
|
-
provider: "telegram";
|
|
85
|
+
provider: "telegram" | "uazapi" | "evolution";
|
|
50
86
|
webhook_url: string;
|
|
51
87
|
status: "configured";
|
|
52
88
|
bot_username?: string;
|
|
89
|
+
webhook_count?: number;
|
|
90
|
+
matched_webhook_id?: string;
|
|
53
91
|
get_webhook_info?: {
|
|
54
92
|
url: string;
|
|
55
93
|
pending_update_count: number;
|
|
@@ -65,6 +103,7 @@ type ChannelDoctorReport = {
|
|
|
65
103
|
message: string;
|
|
66
104
|
fix?: string;
|
|
67
105
|
}>;
|
|
106
|
+
buffers: CloudChannelBuffer[];
|
|
68
107
|
last_delivery: {
|
|
69
108
|
inbound: CloudDelivery | null;
|
|
70
109
|
outbound: CloudDelivery | null;
|
|
@@ -76,7 +115,7 @@ type ChannelDoctorReport = {
|
|
|
76
115
|
export async function handleChannelsCommand(args: ParsedArgs): Promise<void> {
|
|
77
116
|
await loadLocalEnvIntoProcess(process.cwd());
|
|
78
117
|
|
|
79
|
-
const [subcommand, first, second] = args.positional;
|
|
118
|
+
const [subcommand, first, second, third] = args.positional;
|
|
80
119
|
|
|
81
120
|
if (subcommand === "list") {
|
|
82
121
|
const deployState = await readDeployStateIfExists(process.cwd());
|
|
@@ -97,6 +136,7 @@ export async function handleChannelsCommand(args: ParsedArgs): Promise<void> {
|
|
|
97
136
|
name: channel.name,
|
|
98
137
|
type: channel.type,
|
|
99
138
|
provider: channel.provider,
|
|
139
|
+
...(channel.type === "discord" ? { mode: channel.mode } : {}),
|
|
100
140
|
status: "local_config",
|
|
101
141
|
webhook_url: "",
|
|
102
142
|
required_secrets: channel.secrets.map((name) => ({ name, status: "missing" })),
|
|
@@ -109,11 +149,11 @@ export async function handleChannelsCommand(args: ParsedArgs): Promise<void> {
|
|
|
109
149
|
const type = first;
|
|
110
150
|
const name = second;
|
|
111
151
|
|
|
112
|
-
if (!type || !name || (type
|
|
113
|
-
throw new Error("Usage: agentkit channels add <website|telegram|whatsapp> <name> [--provider zapster|meta]");
|
|
152
|
+
if (!type || !name || !isCliChannelType(type)) {
|
|
153
|
+
throw new Error("Usage: agentkit channels add <website|telegram|whatsapp|discord|slack|webhook> <name> [--provider zapster|meta|uazapi|evolution] [--mode interactions|bot]");
|
|
114
154
|
}
|
|
115
155
|
|
|
116
|
-
const response = await createHostedChannel(type, name, args.flags.provider, args.flags.api);
|
|
156
|
+
const response = await createHostedChannel(type, name, args.flags.provider, args.flags.mode, args.flags.api);
|
|
117
157
|
printChannelStatus(response.channel);
|
|
118
158
|
printSecretRepairHint(response.channel);
|
|
119
159
|
return;
|
|
@@ -123,16 +163,18 @@ export async function handleChannelsCommand(args: ParsedArgs): Promise<void> {
|
|
|
123
163
|
const type = first;
|
|
124
164
|
const name = second;
|
|
125
165
|
|
|
126
|
-
if (!type || !name || (type
|
|
127
|
-
throw new Error("Usage: agentkit channels connect <website|telegram|whatsapp> <name> [--provider zapster|meta]");
|
|
166
|
+
if (!type || !name || !isCliChannelType(type)) {
|
|
167
|
+
throw new Error("Usage: agentkit channels connect <website|telegram|whatsapp|discord|slack|webhook> <name> [--provider zapster|meta|uazapi|evolution] [--mode interactions|bot]");
|
|
128
168
|
}
|
|
129
169
|
|
|
130
|
-
const created = await createHostedChannel(type, name, args.flags.provider, args.flags.api);
|
|
170
|
+
const created = await createHostedChannel(type, name, args.flags.provider, args.flags.mode, args.flags.api);
|
|
131
171
|
let channel = created.channel;
|
|
132
172
|
printChannelStatus(channel);
|
|
133
173
|
printSecretRepairHint(channel);
|
|
174
|
+
console.log("Channel resource: created_or_updated");
|
|
134
175
|
|
|
135
176
|
if (hasMissingRequiredSecrets(channel)) {
|
|
177
|
+
console.log("Secrets: missing");
|
|
136
178
|
console.log("");
|
|
137
179
|
console.log("Connect blocked: set the missing managed secrets, then rerun this same command.");
|
|
138
180
|
console.log("Run: npm run agentkit -- secret sync --from-local");
|
|
@@ -140,27 +182,104 @@ export async function handleChannelsCommand(args: ParsedArgs): Promise<void> {
|
|
|
140
182
|
return;
|
|
141
183
|
}
|
|
142
184
|
|
|
185
|
+
console.log("Secrets: validated");
|
|
186
|
+
|
|
143
187
|
if (channel.type === "telegram") {
|
|
144
188
|
const setup = await applyTelegramWebhook(channel, args.flags.api);
|
|
145
189
|
channel = setup.channel ?? channel;
|
|
190
|
+
console.log("Telegram setWebhook: OK");
|
|
146
191
|
console.log("Telegram webhook confirmed.");
|
|
147
192
|
if (setup.setup?.get_webhook_info) {
|
|
148
193
|
console.log(`Telegram pending updates: ${setup.setup.get_webhook_info.pending_update_count}`);
|
|
149
194
|
}
|
|
150
195
|
|
|
151
|
-
const smoke = await runChannelSmoke(channel, args.flags.api, args.flags.message);
|
|
152
|
-
console.log(`Smoke: ${smoke.status}${smoke.delivery_id ? ` (${smoke.delivery_id})` : ""}`);
|
|
196
|
+
const smoke = await runConnectSmoke(() => runChannelSmoke(channel, args.flags.api, args.flags.message));
|
|
153
197
|
printTelegramHumanNextStep(channel.name, setup.setup?.bot_username);
|
|
198
|
+
if (!smoke) {
|
|
199
|
+
process.exitCode = 1;
|
|
200
|
+
}
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (channel.type === "whatsapp" && channel.provider === "uazapi") {
|
|
205
|
+
const setup = await applyProviderWebhook(channel, args.flags.api);
|
|
206
|
+
channel = setup.channel ?? channel;
|
|
207
|
+
console.log("UAZAPI webhook setup: OK");
|
|
208
|
+
console.log("UAZAPI webhook confirmed.");
|
|
209
|
+
if (setup.setup?.webhook_count !== undefined) {
|
|
210
|
+
console.log(`UAZAPI webhooks: ${setup.setup.webhook_count}`);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const smoke = await runConnectSmoke(() => runChannelSmoke(channel, args.flags.api, args.flags.message));
|
|
214
|
+
printUazapiHumanNextStep(channel);
|
|
215
|
+
if (!smoke) {
|
|
216
|
+
process.exitCode = 1;
|
|
217
|
+
}
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (channel.type === "whatsapp" && channel.provider === "evolution") {
|
|
222
|
+
const setup = await applyProviderWebhook(channel, args.flags.api);
|
|
223
|
+
channel = setup.channel ?? channel;
|
|
224
|
+
console.log("Evolution webhook setup: OK");
|
|
225
|
+
console.log("Evolution webhook confirmed.");
|
|
226
|
+
|
|
227
|
+
const smoke = await runConnectSmoke(() => runChannelSmoke(channel, args.flags.api, args.flags.message));
|
|
228
|
+
printEvolutionHumanNextStep(channel);
|
|
229
|
+
if (!smoke) {
|
|
230
|
+
process.exitCode = 1;
|
|
231
|
+
}
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (channel.type === "discord") {
|
|
236
|
+
const smoke = await runConnectSmoke(() => runChannelSmoke(channel, args.flags.api, args.flags.message));
|
|
237
|
+
printDiscordHumanNextStep(channel);
|
|
238
|
+
if (!smoke) {
|
|
239
|
+
process.exitCode = 1;
|
|
240
|
+
}
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (channel.type === "slack") {
|
|
245
|
+
const smoke = await runConnectSmoke(() => runChannelSmoke(channel, args.flags.api, args.flags.message));
|
|
246
|
+
printSlackHumanNextStep(channel);
|
|
247
|
+
if (!smoke) {
|
|
248
|
+
process.exitCode = 1;
|
|
249
|
+
}
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (channel.type === "webhook") {
|
|
254
|
+
if (channel.direction === "outbound") {
|
|
255
|
+
console.log("Outbound webhook: configured");
|
|
256
|
+
console.log(`Next human step: make sure ${channel.url_secret ?? "the webhook URL secret"} is set as a managed secret.`);
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const smoke = await runConnectSmoke(() => runChannelSmoke(channel, args.flags.api, args.flags.message));
|
|
261
|
+
printGenericWebhookHumanNextStep(channel);
|
|
262
|
+
if (!smoke) {
|
|
263
|
+
process.exitCode = 1;
|
|
264
|
+
}
|
|
154
265
|
return;
|
|
155
266
|
}
|
|
156
267
|
|
|
157
|
-
if (channel.type === "whatsapp") {
|
|
268
|
+
if (channel.type === "whatsapp" && channel.provider === "zapster") {
|
|
269
|
+
const smoke = await runConnectSmoke(() => runChannelSmoke(channel, args.flags.api, args.flags.message));
|
|
270
|
+
console.log(`Next human step: paste ${channel.webhook_url} into ${channel.provider}'s webhook settings.`);
|
|
271
|
+
if (!smoke) {
|
|
272
|
+
process.exitCode = 1;
|
|
273
|
+
}
|
|
274
|
+
} else if (channel.type === "whatsapp") {
|
|
158
275
|
console.log("Smoke: skipped until the provider webhook is configured.");
|
|
159
276
|
console.log(`Next human step: paste ${channel.webhook_url} into ${channel.provider}'s webhook settings.`);
|
|
160
277
|
} else {
|
|
161
|
-
const smoke = await runChannelSmoke(channel, args.flags.api, args.flags.message);
|
|
162
|
-
console.log(`Smoke: ${smoke.status}${smoke.delivery_id ? ` (${smoke.delivery_id})` : ""}`);
|
|
278
|
+
const smoke = await runConnectSmoke(() => runChannelSmoke(channel, args.flags.api, args.flags.message));
|
|
163
279
|
console.log(`Next human step: use ${channel.webhook_url} from your website client.`);
|
|
280
|
+
if (!smoke) {
|
|
281
|
+
process.exitCode = 1;
|
|
282
|
+
}
|
|
164
283
|
}
|
|
165
284
|
return;
|
|
166
285
|
}
|
|
@@ -182,8 +301,38 @@ export async function handleChannelsCommand(args: ParsedArgs): Promise<void> {
|
|
|
182
301
|
console.log(`Setup: call Telegram setWebhook with ${channel.webhook_url}`);
|
|
183
302
|
console.log("Use --apply to mutate Telegram webhook settings.");
|
|
184
303
|
}
|
|
304
|
+
} else if (channel.type === "whatsapp" && channel.provider === "uazapi") {
|
|
305
|
+
if (args.flags.apply) {
|
|
306
|
+
const setup = await applyProviderWebhook(channel, args.flags.api);
|
|
307
|
+
console.log("UAZAPI webhook confirmed.");
|
|
308
|
+
printChannelStatus(setup.channel ?? channel);
|
|
309
|
+
printUazapiHumanNextStep(setup.channel ?? channel);
|
|
310
|
+
} else {
|
|
311
|
+
console.log(`Setup: configure UAZAPI /webhook with ${channel.webhook_url}`);
|
|
312
|
+
console.log("Use --apply to mutate UAZAPI webhook settings.");
|
|
313
|
+
}
|
|
314
|
+
} else if (channel.type === "whatsapp" && channel.provider === "evolution") {
|
|
315
|
+
if (args.flags.apply) {
|
|
316
|
+
const setup = await applyProviderWebhook(channel, args.flags.api);
|
|
317
|
+
console.log("Evolution webhook confirmed.");
|
|
318
|
+
printChannelStatus(setup.channel ?? channel);
|
|
319
|
+
printEvolutionHumanNextStep(setup.channel ?? channel);
|
|
320
|
+
} else {
|
|
321
|
+
console.log(`Setup: configure Evolution /webhook/set/{instance} with ${channel.webhook_url}`);
|
|
322
|
+
console.log("Use --apply to mutate Evolution webhook settings.");
|
|
323
|
+
}
|
|
185
324
|
} else if (channel.type === "whatsapp") {
|
|
186
325
|
console.log(`Next step: paste this URL into ${channel.provider}'s webhook settings.`);
|
|
326
|
+
} else if (channel.type === "discord") {
|
|
327
|
+
printDiscordHumanNextStep(channel);
|
|
328
|
+
} else if (channel.type === "slack") {
|
|
329
|
+
printSlackHumanNextStep(channel);
|
|
330
|
+
} else if (channel.type === "webhook") {
|
|
331
|
+
if (channel.direction === "outbound") {
|
|
332
|
+
console.log("Outbound webhook endpoint: configured from managed secrets.");
|
|
333
|
+
} else {
|
|
334
|
+
printGenericWebhookHumanNextStep(channel);
|
|
335
|
+
}
|
|
187
336
|
} else {
|
|
188
337
|
console.log(`Website endpoint: ${channel.webhook_url}`);
|
|
189
338
|
console.log("Use this endpoint from the AgentKit website client or a signed server-to-server test.");
|
|
@@ -225,9 +374,32 @@ export async function handleChannelsCommand(args: ParsedArgs): Promise<void> {
|
|
|
225
374
|
throw new Error('Usage: agentkit channels test <name> [--message "hello"] [--fixture <path>]');
|
|
226
375
|
}
|
|
227
376
|
|
|
377
|
+
const channel = await resolveCloudChannelByName(first, args.flags.api);
|
|
378
|
+
if (channel.type === "webhook" && channel.direction === "outbound") {
|
|
379
|
+
throw new Error("Outbound webhook channels do not have an inbound test endpoint. Test them through a source channel that routes replies to this channel.");
|
|
380
|
+
}
|
|
381
|
+
const apiUrl = parseCloudApiUrl(args.flags.api);
|
|
382
|
+
const signed = channel.type === "whatsapp" && channel.provider === "evolution" && !args.flags.fixture
|
|
383
|
+
? await runServerGeneratedChannelTest(channel, apiUrl, args.flags.message)
|
|
384
|
+
: await runServerSignedChannelTest(channel, apiUrl, await channelTestBody(channel, args.flags.message, args.flags.fixture));
|
|
385
|
+
const response = signed.response;
|
|
386
|
+
const payload = signed.payload;
|
|
387
|
+
|
|
388
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
389
|
+
if (!response.ok) {
|
|
390
|
+
process.exitCode = 1;
|
|
391
|
+
}
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
if (subcommand === "test-audio") {
|
|
396
|
+
if (!first) {
|
|
397
|
+
throw new Error("Usage: agentkit channels test-audio <name> [--fixture voice-note|audio-file|<path>]");
|
|
398
|
+
}
|
|
399
|
+
|
|
228
400
|
const channel = await resolveCloudChannelByName(first, args.flags.api);
|
|
229
401
|
const apiUrl = parseCloudApiUrl(args.flags.api);
|
|
230
|
-
const body = await
|
|
402
|
+
const body = await channelAudioTestBody(channel, args.flags.fixture);
|
|
231
403
|
const signed = await runServerSignedChannelTest(channel, apiUrl, body);
|
|
232
404
|
const response = signed.response;
|
|
233
405
|
const payload = signed.payload;
|
|
@@ -286,27 +458,77 @@ export async function handleChannelsCommand(args: ParsedArgs): Promise<void> {
|
|
|
286
458
|
}
|
|
287
459
|
}
|
|
288
460
|
|
|
461
|
+
if (subcommand === "buffers") {
|
|
462
|
+
if (first === "list") {
|
|
463
|
+
if (!second) {
|
|
464
|
+
throw new Error("Usage: agentkit channels buffers list <name>");
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
const channel = await resolveCloudChannelByName(second, args.flags.api);
|
|
468
|
+
const response = await cloudGet<{ buffers: CloudChannelBuffer[] }>(
|
|
469
|
+
parseCloudApiUrl(args.flags.api),
|
|
470
|
+
`/v1/channels/${encodeURIComponent(channel.id)}/buffers`,
|
|
471
|
+
);
|
|
472
|
+
printChannelBuffers(response.buffers);
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
if (first === "show") {
|
|
477
|
+
if (!second) {
|
|
478
|
+
throw new Error("Usage: agentkit channels buffers show [<name>] <conversation-id>");
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
const target = await resolveChannelBufferTarget(second, third, args.flags.api);
|
|
482
|
+
const response = await cloudGet<{ buffer: CloudChannelBuffer }>(
|
|
483
|
+
parseCloudApiUrl(args.flags.api),
|
|
484
|
+
`/v1/channels/${encodeURIComponent(target.channel.id)}/buffers/${encodeURIComponent(target.conversationId)}`,
|
|
485
|
+
);
|
|
486
|
+
console.log(JSON.stringify(response.buffer, null, 2));
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
if (first === "flush" || first === "clear" || first === "retry") {
|
|
491
|
+
if (!second) {
|
|
492
|
+
throw new Error(`Usage: agentkit channels buffers ${first} [<name>] <conversation-id>`);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
const target = await resolveChannelBufferTarget(second, third, args.flags.api);
|
|
496
|
+
const response = await cloudPost<ChannelBufferActionResponse>(
|
|
497
|
+
parseCloudApiUrl(args.flags.api),
|
|
498
|
+
`/v1/channels/${encodeURIComponent(target.channel.id)}/buffers/${encodeURIComponent(target.conversationId)}/${first}`,
|
|
499
|
+
{},
|
|
500
|
+
);
|
|
501
|
+
printChannelBufferAction(first, response);
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
289
506
|
throw new Error(
|
|
290
|
-
"Usage: agentkit channels list | add | connect | setup | status | doctor | test | deliveries list | deliveries show",
|
|
507
|
+
"Usage: agentkit channels list | add | connect | setup | status | doctor | test | test-audio | deliveries list | deliveries show | buffers list | buffers show | buffers flush | buffers clear | buffers retry",
|
|
291
508
|
);
|
|
292
509
|
}
|
|
293
510
|
|
|
294
511
|
async function createHostedChannel(
|
|
295
|
-
type: "website" | "telegram" | "whatsapp",
|
|
512
|
+
type: "website" | "telegram" | "whatsapp" | "discord" | "slack" | "webhook",
|
|
296
513
|
name: string,
|
|
297
514
|
providerFlag: string | boolean | undefined,
|
|
515
|
+
modeFlag: string | boolean | undefined,
|
|
298
516
|
apiFlag: string | boolean | undefined,
|
|
299
517
|
): Promise<{ channel: CloudChannel }> {
|
|
300
518
|
const deployState = await readDeployStateRequired(process.cwd());
|
|
301
519
|
const provider = channelProviderForCli(type, providerFlag);
|
|
302
520
|
const localChannel = await readLocalChannelConfig(process.cwd(), type, provider, name);
|
|
521
|
+
const mode = discordModeForCli(type, modeFlag, localChannel);
|
|
303
522
|
const payload = {
|
|
304
523
|
name,
|
|
305
524
|
type,
|
|
306
525
|
provider,
|
|
307
|
-
|
|
526
|
+
...(mode ? { mode } : {}),
|
|
527
|
+
secrets: localChannel?.secrets ?? defaultChannelSecretsForCli(type, provider, mode),
|
|
308
528
|
...(localChannel?.limits ? { limits: channelLimitsForApi(localChannel.limits) } : {}),
|
|
309
529
|
...(localChannel?.buffer ? { buffer: localChannel.buffer } : {}),
|
|
530
|
+
...(localChannel?.audio ? { audio: localChannel.audio } : {}),
|
|
531
|
+
...channelExtraConfigForApi(localChannel),
|
|
310
532
|
};
|
|
311
533
|
|
|
312
534
|
return cloudPost<{ channel: CloudChannel }>(
|
|
@@ -321,8 +543,10 @@ async function runChannelSmoke(
|
|
|
321
543
|
apiFlag: string | boolean | undefined,
|
|
322
544
|
messageFlag: string | boolean | undefined,
|
|
323
545
|
): Promise<{ status: string; delivery_id?: string }> {
|
|
324
|
-
const
|
|
325
|
-
const { response, payload } =
|
|
546
|
+
const apiUrl = parseCloudApiUrl(apiFlag);
|
|
547
|
+
const { response, payload } = channel.type === "whatsapp" && channel.provider === "evolution"
|
|
548
|
+
? await runServerGeneratedChannelTest(channel, apiUrl, messageFlag)
|
|
549
|
+
: await runServerSignedChannelTest(channel, apiUrl, await channelTestBody(channel, messageFlag, undefined));
|
|
326
550
|
|
|
327
551
|
if (!response.ok) {
|
|
328
552
|
throw new Error(readCloudError(payload, response.status));
|
|
@@ -338,6 +562,49 @@ async function runChannelSmoke(
|
|
|
338
562
|
return { status: "unknown" };
|
|
339
563
|
}
|
|
340
564
|
|
|
565
|
+
async function runServerGeneratedChannelTest(
|
|
566
|
+
channel: CloudChannel,
|
|
567
|
+
apiUrl: string,
|
|
568
|
+
messageFlag: string | boolean | undefined,
|
|
569
|
+
): Promise<{ response: Response; payload: unknown }> {
|
|
570
|
+
const body: Record<string, unknown> = {};
|
|
571
|
+
|
|
572
|
+
if (typeof messageFlag === "string") {
|
|
573
|
+
body.message = messageFlag;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
const response = await cloudFetch(apiUrl, `/v1/channels/${encodeURIComponent(channel.id)}/test`, {
|
|
577
|
+
method: "POST",
|
|
578
|
+
headers: { "Content-Type": "application/json" },
|
|
579
|
+
body: JSON.stringify(body),
|
|
580
|
+
});
|
|
581
|
+
const payload = await response.json().catch(() => null);
|
|
582
|
+
|
|
583
|
+
if (!response.ok && isUnsupportedChannelTestRoute(payload)) {
|
|
584
|
+
return runLocallySignedChannelTest(
|
|
585
|
+
channel,
|
|
586
|
+
apiUrl,
|
|
587
|
+
await channelTestBody(channel, messageFlag, undefined),
|
|
588
|
+
);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
return { response, payload };
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
async function runConnectSmoke(
|
|
595
|
+
run: () => Promise<{ status: string; delivery_id?: string }>,
|
|
596
|
+
): Promise<{ status: string; delivery_id?: string } | null> {
|
|
597
|
+
try {
|
|
598
|
+
const smoke = await run();
|
|
599
|
+
console.log(`Synthetic smoke: ${smoke.status}${smoke.delivery_id ? ` (${smoke.delivery_id})` : ""}`);
|
|
600
|
+
return smoke;
|
|
601
|
+
} catch (error) {
|
|
602
|
+
console.log("Synthetic smoke: failed");
|
|
603
|
+
console.log(` ${formatCommandError(error)}`);
|
|
604
|
+
return null;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
341
608
|
async function runServerSignedChannelTest(
|
|
342
609
|
channel: CloudChannel,
|
|
343
610
|
apiUrl: string,
|
|
@@ -362,8 +629,22 @@ async function runLocallySignedChannelTest(
|
|
|
362
629
|
apiUrl: string,
|
|
363
630
|
rawBody: string,
|
|
364
631
|
): Promise<{ response: Response; payload: unknown }> {
|
|
365
|
-
const
|
|
366
|
-
|
|
632
|
+
const webhookUrl = new URL(channel.webhook_url);
|
|
633
|
+
|
|
634
|
+
if (channel.required_secrets.some((secret) => secret.name === "ZAPSTER_WEBHOOK_TOKEN")) {
|
|
635
|
+
webhookUrl.searchParams.set("token", process.env.ZAPSTER_WEBHOOK_TOKEN ?? "");
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
if (channel.required_secrets.some((secret) => secret.name === "UAZAPI_WEBHOOK_TOKEN")) {
|
|
639
|
+
webhookUrl.searchParams.set("token", process.env.UAZAPI_WEBHOOK_TOKEN ?? "");
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
if (channel.required_secrets.some((secret) => secret.name === "EVOLUTION_WEBHOOK_TOKEN")) {
|
|
643
|
+
webhookUrl.searchParams.set("token", process.env.EVOLUTION_WEBHOOK_TOKEN ?? "");
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
const path = `${webhookUrl.pathname}${webhookUrl.search}`;
|
|
647
|
+
const headers = channelTestHeaders(channel, rawBody);
|
|
367
648
|
const response = await cloudFetch(apiUrl, path, {
|
|
368
649
|
method: "POST",
|
|
369
650
|
headers,
|
|
@@ -465,6 +746,17 @@ async function applyTelegramWebhook(
|
|
|
465
746
|
};
|
|
466
747
|
}
|
|
467
748
|
|
|
749
|
+
async function applyProviderWebhook(
|
|
750
|
+
channel: CloudChannel,
|
|
751
|
+
apiFlag: string | boolean | undefined,
|
|
752
|
+
): Promise<ChannelSetupResponse> {
|
|
753
|
+
return cloudPost<ChannelSetupResponse>(
|
|
754
|
+
parseCloudApiUrl(apiFlag),
|
|
755
|
+
`/v1/channels/${encodeURIComponent(channel.id)}/setup`,
|
|
756
|
+
{},
|
|
757
|
+
);
|
|
758
|
+
}
|
|
759
|
+
|
|
468
760
|
function shouldFallbackToLocalTelegramSetup(error: unknown): boolean {
|
|
469
761
|
if (!(error instanceof Error)) {
|
|
470
762
|
return false;
|
|
@@ -510,14 +802,80 @@ function printTelegramHumanNextStep(channelName: string, botUsername: string | u
|
|
|
510
802
|
console.log(` Then run: npm run agentkit -- channels deliveries list ${channelName}`);
|
|
511
803
|
}
|
|
512
804
|
|
|
805
|
+
function printUazapiHumanNextStep(channel: CloudChannel): void {
|
|
806
|
+
console.log("");
|
|
807
|
+
console.log("Next human step:");
|
|
808
|
+
console.log(" Confirm the UAZAPI instance is connected to WhatsApp");
|
|
809
|
+
console.log(' Send "hello" from a real WhatsApp contact');
|
|
810
|
+
console.log(` Then run: npm run agentkit -- channels deliveries list ${channel.name}`);
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
function printEvolutionHumanNextStep(channel: CloudChannel): void {
|
|
814
|
+
console.log("");
|
|
815
|
+
console.log("Next human step:");
|
|
816
|
+
console.log(" Confirm the Evolution API instance is connected to WhatsApp");
|
|
817
|
+
console.log(' Send "hello" from a real WhatsApp contact');
|
|
818
|
+
console.log(` Then run: npm run agentkit -- channels deliveries list ${channel.name}`);
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
function printDiscordHumanNextStep(channel: CloudChannel): void {
|
|
822
|
+
console.log("");
|
|
823
|
+
console.log("Next human step:");
|
|
824
|
+
|
|
825
|
+
if (channel.mode === "bot") {
|
|
826
|
+
console.log(" Open the Discord Developer Portal for your application");
|
|
827
|
+
console.log(" Bot page: enable Message Content Intent");
|
|
828
|
+
console.log(" Installation page: install the app into your server with bot permissions");
|
|
829
|
+
console.log(" Grant the bot View Channel, Read Message History, and Send Messages in the channels it should answer");
|
|
830
|
+
console.log(' Send "hello" in an allowed server channel');
|
|
831
|
+
console.log(` Then run: npm run agentkit -- channels deliveries list ${channel.name}`);
|
|
832
|
+
return;
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
console.log(" Open the Discord Developer Portal for your application");
|
|
836
|
+
console.log(` Paste ${channel.webhook_url} into Interactions Endpoint URL`);
|
|
837
|
+
console.log(" Register a slash command with a string option named message, text, prompt, question, query, or input");
|
|
838
|
+
console.log(` Then run: npm run agentkit -- channels deliveries list ${channel.name}`);
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
function printSlackHumanNextStep(channel: CloudChannel): void {
|
|
842
|
+
console.log("");
|
|
843
|
+
console.log("Next human step:");
|
|
844
|
+
console.log(" Open the Slack app configuration");
|
|
845
|
+
console.log(" Event Subscriptions: enable events");
|
|
846
|
+
console.log(` Request URL: paste ${channel.webhook_url}`);
|
|
847
|
+
console.log(" Subscribe to bot events: app_mention and message.im");
|
|
848
|
+
console.log(" OAuth scopes: app_mentions:read, im:history, chat:write");
|
|
849
|
+
console.log(" Install or reinstall the app to the workspace");
|
|
850
|
+
console.log(' Mention the app with "hello" or send it a direct message');
|
|
851
|
+
console.log(` Then run: npm run agentkit -- channels deliveries list ${channel.name}`);
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
function printGenericWebhookHumanNextStep(channel: CloudChannel): void {
|
|
855
|
+
console.log("");
|
|
856
|
+
console.log("Next human step:");
|
|
857
|
+
console.log(` Point n8n, Make, Zapier, or your server to ${channel.webhook_url}`);
|
|
858
|
+
console.log(" Send JSON with event_id, external_id, and message");
|
|
859
|
+
console.log(" Authenticate with Authorization: Bearer <AGENTKIT_WEBHOOK_SECRET>");
|
|
860
|
+
console.log(` Then run: npm run agentkit -- channels deliveries list ${channel.name}`);
|
|
861
|
+
}
|
|
862
|
+
|
|
513
863
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
514
864
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
515
865
|
}
|
|
516
866
|
|
|
867
|
+
function formatCommandError(error: unknown): string {
|
|
868
|
+
return error instanceof Error ? error.message : String(error);
|
|
869
|
+
}
|
|
870
|
+
|
|
517
871
|
async function readDeployStateIfExists(cwd: string): Promise<DeployState | null> {
|
|
518
872
|
return readLocalDeployStateIfExists(cwd);
|
|
519
873
|
}
|
|
520
874
|
|
|
875
|
+
function isCliChannelType(value: string): value is "website" | "telegram" | "whatsapp" | "discord" | "slack" | "webhook" {
|
|
876
|
+
return value === "website" || value === "telegram" || value === "whatsapp" || value === "discord" || value === "slack" || value === "webhook";
|
|
877
|
+
}
|
|
878
|
+
|
|
521
879
|
async function readDeployStateRequired(cwd: string): Promise<DeployState> {
|
|
522
880
|
const state = await readDeployStateIfExists(cwd);
|
|
523
881
|
|
|
@@ -547,12 +905,21 @@ function printChannelStatus(channel: CloudChannel): void {
|
|
|
547
905
|
}
|
|
548
906
|
console.log(`Type: ${channel.type}`);
|
|
549
907
|
console.log(`Provider: ${channel.provider}`);
|
|
908
|
+
if (channel.type === "discord") {
|
|
909
|
+
console.log(`Mode: ${channel.mode ?? "interactions"}`);
|
|
910
|
+
}
|
|
911
|
+
if (channel.type === "webhook" && channel.direction) {
|
|
912
|
+
console.log(`Direction: ${channel.direction}`);
|
|
913
|
+
}
|
|
550
914
|
console.log(`Status: ${channel.status}`);
|
|
551
915
|
if (channel.webhook_url) {
|
|
552
916
|
console.log(`Webhook URL: ${channel.webhook_url}`);
|
|
553
917
|
}
|
|
554
918
|
console.log("Required secrets:");
|
|
555
|
-
if (
|
|
919
|
+
if (
|
|
920
|
+
channel.required_secrets.length === 0 &&
|
|
921
|
+
defaultChannelSecretsForCli(channel.type, channel.provider, channel.mode).length > 0
|
|
922
|
+
) {
|
|
556
923
|
console.log(
|
|
557
924
|
` control-plane channel metadata bug: required_secrets is empty for ${channel.type}/${channel.provider}`,
|
|
558
925
|
);
|
|
@@ -573,7 +940,10 @@ function printChannelStatus(channel: CloudChannel): void {
|
|
|
573
940
|
}
|
|
574
941
|
|
|
575
942
|
function printSecretRepairHint(channel: CloudChannel): void {
|
|
576
|
-
if (
|
|
943
|
+
if (
|
|
944
|
+
channel.required_secrets.length > 0 ||
|
|
945
|
+
defaultChannelSecretsForCli(channel.type, channel.provider, channel.mode).length === 0
|
|
946
|
+
) {
|
|
577
947
|
return;
|
|
578
948
|
}
|
|
579
949
|
|
|
@@ -583,7 +953,7 @@ function printSecretRepairHint(channel: CloudChannel): void {
|
|
|
583
953
|
}
|
|
584
954
|
|
|
585
955
|
function hasMissingRequiredSecrets(channel: CloudChannel): boolean {
|
|
586
|
-
const expected = defaultChannelSecretsForCli(channel.type, channel.provider);
|
|
956
|
+
const expected = defaultChannelSecretsForCli(channel.type, channel.provider, channel.mode);
|
|
587
957
|
|
|
588
958
|
if (channel.required_secrets.length === 0 && expected.length > 0) {
|
|
589
959
|
return true;
|
|
@@ -629,6 +999,16 @@ function printChannelDoctor(report: ChannelDoctorReport): void {
|
|
|
629
999
|
` Outbound: ${report.last_delivery.outbound ? `${report.last_delivery.outbound.status} ${report.last_delivery.outbound.updated_at ?? report.last_delivery.outbound.received_at}` : "none"}`,
|
|
630
1000
|
);
|
|
631
1001
|
|
|
1002
|
+
if (report.buffers.length > 0) {
|
|
1003
|
+
console.log("");
|
|
1004
|
+
console.log("Buffers:");
|
|
1005
|
+
for (const buffer of report.buffers) {
|
|
1006
|
+
console.log(
|
|
1007
|
+
` ${buffer.conversation_id}: ${buffer.status} messages=${buffer.buffered_message_count} stale=${buffer.stale ? "yes" : "no"}`,
|
|
1008
|
+
);
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
|
|
632
1012
|
if (report.probable_error) {
|
|
633
1013
|
console.log("");
|
|
634
1014
|
console.log(`Probable error: ${report.probable_error}`);
|
|
@@ -654,19 +1034,119 @@ function formatCheckStatus(status: ChannelDoctorReport["checks"][number]["status
|
|
|
654
1034
|
return "UNKNOWN";
|
|
655
1035
|
}
|
|
656
1036
|
|
|
1037
|
+
function printChannelBuffers(buffers: CloudChannelBuffer[]): void {
|
|
1038
|
+
if (buffers.length === 0) {
|
|
1039
|
+
console.log("No channel buffers found.");
|
|
1040
|
+
return;
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
console.log("CONVERSATION\tSTATUS\tMESSAGES\tCHARS\tSTALE\tAVAILABLE_AT\tJOB");
|
|
1044
|
+
for (const buffer of buffers) {
|
|
1045
|
+
console.log(
|
|
1046
|
+
[
|
|
1047
|
+
buffer.conversation_id,
|
|
1048
|
+
buffer.status,
|
|
1049
|
+
String(buffer.buffered_message_count),
|
|
1050
|
+
String(buffer.buffered_char_count),
|
|
1051
|
+
buffer.stale ? "yes" : "no",
|
|
1052
|
+
buffer.available_at ?? "",
|
|
1053
|
+
buffer.job_id ?? "",
|
|
1054
|
+
].join("\t"),
|
|
1055
|
+
);
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
function printChannelBufferAction(action: string, response: ChannelBufferActionResponse): void {
|
|
1060
|
+
const conversationId = response.buffer?.conversation_id ?? response.job?.conversation_id ?? "unknown";
|
|
1061
|
+
|
|
1062
|
+
if (action === "flush") {
|
|
1063
|
+
console.log(
|
|
1064
|
+
response.job
|
|
1065
|
+
? `Flushed buffer ${conversationId}; queued job ${response.job.id}.`
|
|
1066
|
+
: `No queued buffer was flushed for ${conversationId}.`,
|
|
1067
|
+
);
|
|
1068
|
+
return;
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
if (action === "clear") {
|
|
1072
|
+
console.log(
|
|
1073
|
+
response.cleared
|
|
1074
|
+
? `Cleared buffer ${conversationId}.`
|
|
1075
|
+
: `No queued buffer was cleared for ${conversationId}.`,
|
|
1076
|
+
);
|
|
1077
|
+
return;
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
console.log(
|
|
1081
|
+
response.retried && response.job
|
|
1082
|
+
? `Retried channel job ${response.job.id} for ${conversationId}.`
|
|
1083
|
+
: `No failed or dead-lettered job was retried for ${conversationId}.`,
|
|
1084
|
+
);
|
|
1085
|
+
}
|
|
1086
|
+
|
|
657
1087
|
async function resolveCloudChannelByName(name: string, apiFlag: string | boolean | undefined): Promise<CloudChannel> {
|
|
1088
|
+
const channels = await listCloudChannelsForCurrentDeploy(apiFlag);
|
|
1089
|
+
const channel = channels.find((candidate) => candidate.name === name);
|
|
1090
|
+
|
|
1091
|
+
if (!channel) {
|
|
1092
|
+
const deployState = await readDeployStateRequired(process.cwd());
|
|
1093
|
+
throw new Error(`Channel "${name}" was not found for deploy ${deployState.deploy_id}.`);
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
return channel;
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
async function listCloudChannelsForCurrentDeploy(apiFlag: string | boolean | undefined): Promise<CloudChannel[]> {
|
|
658
1100
|
const deployState = await readDeployStateRequired(process.cwd());
|
|
659
1101
|
const response = await cloudGet<{ channels: CloudChannel[] }>(
|
|
660
1102
|
parseCloudApiUrl(apiFlag),
|
|
661
1103
|
`/v1/deploys/${encodeURIComponent(deployState.deploy_id)}/channels`,
|
|
662
1104
|
);
|
|
663
|
-
const channel = response.channels.find((candidate) => candidate.name === name);
|
|
664
1105
|
|
|
665
|
-
|
|
666
|
-
|
|
1106
|
+
return response.channels;
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
async function resolveChannelBufferTarget(
|
|
1110
|
+
first: string,
|
|
1111
|
+
second: string | undefined,
|
|
1112
|
+
apiFlag: string | boolean | undefined,
|
|
1113
|
+
): Promise<{ channel: CloudChannel; conversationId: string }> {
|
|
1114
|
+
if (second) {
|
|
1115
|
+
return {
|
|
1116
|
+
channel: await resolveCloudChannelByName(first, apiFlag),
|
|
1117
|
+
conversationId: second,
|
|
1118
|
+
};
|
|
667
1119
|
}
|
|
668
1120
|
|
|
669
|
-
|
|
1121
|
+
const conversationId = first;
|
|
1122
|
+
const channels = await listCloudChannelsForCurrentDeploy(apiFlag);
|
|
1123
|
+
const matches: CloudChannel[] = [];
|
|
1124
|
+
|
|
1125
|
+
for (const channel of channels) {
|
|
1126
|
+
const response = await cloudGet<{ buffers: CloudChannelBuffer[] }>(
|
|
1127
|
+
parseCloudApiUrl(apiFlag),
|
|
1128
|
+
`/v1/channels/${encodeURIComponent(channel.id)}/buffers`,
|
|
1129
|
+
);
|
|
1130
|
+
|
|
1131
|
+
if (response.buffers.some((buffer) => buffer.conversation_id === conversationId)) {
|
|
1132
|
+
matches.push(channel);
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
if (matches.length === 1) {
|
|
1137
|
+
return {
|
|
1138
|
+
channel: matches[0],
|
|
1139
|
+
conversationId,
|
|
1140
|
+
};
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
if (matches.length > 1) {
|
|
1144
|
+
throw new Error(
|
|
1145
|
+
`Conversation ${conversationId} has buffers in multiple channels. Use: agentkit channels buffers show <name> ${conversationId}`,
|
|
1146
|
+
);
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
throw new Error(`No channel buffer found for conversation ${conversationId}.`);
|
|
670
1150
|
}
|
|
671
1151
|
|
|
672
1152
|
function channelProviderForCli(type: string, providerFlag: string | boolean | undefined): string {
|
|
@@ -678,16 +1158,60 @@ function channelProviderForCli(type: string, providerFlag: string | boolean | un
|
|
|
678
1158
|
return "telegram";
|
|
679
1159
|
}
|
|
680
1160
|
|
|
1161
|
+
if (type === "discord") {
|
|
1162
|
+
return "discord";
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
if (type === "slack") {
|
|
1166
|
+
if (providerFlag !== undefined) {
|
|
1167
|
+
throw new Error("--provider is not supported for Slack channels.");
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
return "slack";
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
if (type === "webhook") {
|
|
1174
|
+
if (providerFlag !== undefined) {
|
|
1175
|
+
throw new Error("--provider is not supported for generic webhook channels.");
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
return "generic";
|
|
1179
|
+
}
|
|
1180
|
+
|
|
681
1181
|
const provider = providerFlag ?? "zapster";
|
|
682
1182
|
|
|
683
|
-
if (provider !== "zapster" && provider !== "meta") {
|
|
684
|
-
throw new Error('WhatsApp provider must be "zapster" or "
|
|
1183
|
+
if (provider !== "zapster" && provider !== "meta" && provider !== "uazapi" && provider !== "evolution") {
|
|
1184
|
+
throw new Error('WhatsApp provider must be "zapster", "meta", "uazapi", or "evolution".');
|
|
685
1185
|
}
|
|
686
1186
|
|
|
687
1187
|
return provider;
|
|
688
1188
|
}
|
|
689
1189
|
|
|
690
|
-
function
|
|
1190
|
+
function discordModeForCli(
|
|
1191
|
+
type: string,
|
|
1192
|
+
modeFlag: string | boolean | undefined,
|
|
1193
|
+
localChannel: AgentChannel | null,
|
|
1194
|
+
): "interactions" | "bot" | undefined {
|
|
1195
|
+
if (type !== "discord") {
|
|
1196
|
+
if (modeFlag !== undefined) {
|
|
1197
|
+
throw new Error("--mode is only supported for Discord channels.");
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
return undefined;
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
if (modeFlag !== undefined) {
|
|
1204
|
+
if (modeFlag !== "interactions" && modeFlag !== "bot") {
|
|
1205
|
+
throw new Error('Discord mode must be "interactions" or "bot".');
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
return modeFlag;
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
return localChannel?.type === "discord" ? localChannel.mode : "interactions";
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
function defaultChannelSecretsForCli(type: string, provider: string, mode?: "interactions" | "bot"): string[] {
|
|
691
1215
|
if (type === "website") {
|
|
692
1216
|
return ["AGENTKIT_WEBSITE_CHANNEL_TOKEN"];
|
|
693
1217
|
}
|
|
@@ -697,16 +1221,49 @@ function defaultChannelSecretsForCli(type: string, provider: string): string[] {
|
|
|
697
1221
|
}
|
|
698
1222
|
|
|
699
1223
|
if (type === "whatsapp" && provider === "zapster") {
|
|
700
|
-
return ["ZAPSTER_API_KEY", "
|
|
1224
|
+
return ["ZAPSTER_API_KEY", "ZAPSTER_INSTANCE_ID", "ZAPSTER_WEBHOOK_ID", "ZAPSTER_WEBHOOK_TOKEN"];
|
|
701
1225
|
}
|
|
702
1226
|
|
|
703
1227
|
if (type === "whatsapp" && provider === "meta") {
|
|
704
1228
|
return ["META_WHATSAPP_ACCESS_TOKEN", "META_WHATSAPP_APP_SECRET", "META_WHATSAPP_VERIFY_TOKEN"];
|
|
705
1229
|
}
|
|
706
1230
|
|
|
1231
|
+
if (type === "whatsapp" && provider === "uazapi") {
|
|
1232
|
+
return ["UAZAPI_BASE_URL", "UAZAPI_TOKEN", "UAZAPI_WEBHOOK_TOKEN"];
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
if (type === "whatsapp" && provider === "evolution") {
|
|
1236
|
+
return ["EVOLUTION_API_BASE_URL", "EVOLUTION_API_KEY", "EVOLUTION_INSTANCE_NAME", "EVOLUTION_WEBHOOK_TOKEN"];
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
if (type === "discord") {
|
|
1240
|
+
return mode === "bot" ? ["DISCORD_BOT_TOKEN"] : ["DISCORD_PUBLIC_KEY"];
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
if (type === "slack") {
|
|
1244
|
+
return ["SLACK_BOT_TOKEN", "SLACK_SIGNING_SECRET"];
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
if (type === "webhook") {
|
|
1248
|
+
return ["AGENTKIT_WEBHOOK_SECRET"];
|
|
1249
|
+
}
|
|
1250
|
+
|
|
707
1251
|
return [];
|
|
708
1252
|
}
|
|
709
1253
|
|
|
1254
|
+
function channelExtraConfigForApi(channel: AgentChannel | null): Record<string, unknown> {
|
|
1255
|
+
if (!channel) {
|
|
1256
|
+
return {};
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
return {
|
|
1260
|
+
...("direction" in channel && channel.direction ? { direction: channel.direction } : {}),
|
|
1261
|
+
...(channel.replyTo ? { reply_to: channel.replyTo } : {}),
|
|
1262
|
+
...("urlSecret" in channel && channel.urlSecret ? { url_secret: channel.urlSecret } : {}),
|
|
1263
|
+
...("auth" in channel && channel.auth ? { auth: channel.auth } : {}),
|
|
1264
|
+
};
|
|
1265
|
+
}
|
|
1266
|
+
|
|
710
1267
|
async function readLocalChannelConfig(
|
|
711
1268
|
cwd: string,
|
|
712
1269
|
type: string,
|
|
@@ -754,6 +1311,45 @@ async function channelTestBody(
|
|
|
754
1311
|
});
|
|
755
1312
|
}
|
|
756
1313
|
|
|
1314
|
+
if (channel.type === "whatsapp" && channel.provider === "uazapi") {
|
|
1315
|
+
return JSON.stringify({
|
|
1316
|
+
EventType: "messages",
|
|
1317
|
+
event: "messages",
|
|
1318
|
+
message: {
|
|
1319
|
+
id: `uaz_msg_${Date.now()}`,
|
|
1320
|
+
messageid: `uaz_wamid_${Date.now()}`,
|
|
1321
|
+
chatid: "5511999999999@s.whatsapp.net",
|
|
1322
|
+
sender: "5511999999999@s.whatsapp.net",
|
|
1323
|
+
senderName: "AgentKit",
|
|
1324
|
+
fromMe: false,
|
|
1325
|
+
wasSentByApi: false,
|
|
1326
|
+
messageType: "text",
|
|
1327
|
+
text: message,
|
|
1328
|
+
messageTimestamp: Date.now(),
|
|
1329
|
+
},
|
|
1330
|
+
});
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
if (channel.type === "whatsapp" && channel.provider === "evolution") {
|
|
1334
|
+
return JSON.stringify({
|
|
1335
|
+
event: "MESSAGES_UPSERT",
|
|
1336
|
+
instance: process.env.EVOLUTION_INSTANCE_NAME ?? "local",
|
|
1337
|
+
data: {
|
|
1338
|
+
key: {
|
|
1339
|
+
remoteJid: "5511999999999@s.whatsapp.net",
|
|
1340
|
+
fromMe: false,
|
|
1341
|
+
id: `evo_wamid_${Date.now()}`,
|
|
1342
|
+
},
|
|
1343
|
+
pushName: "AgentKit",
|
|
1344
|
+
messageType: "conversation",
|
|
1345
|
+
message: {
|
|
1346
|
+
conversation: message,
|
|
1347
|
+
},
|
|
1348
|
+
messageTimestamp: Math.floor(Date.now() / 1000),
|
|
1349
|
+
},
|
|
1350
|
+
});
|
|
1351
|
+
}
|
|
1352
|
+
|
|
757
1353
|
if (channel.type === "whatsapp") {
|
|
758
1354
|
return JSON.stringify({
|
|
759
1355
|
event_id: `evt_${Date.now()}`,
|
|
@@ -767,6 +1363,85 @@ async function channelTestBody(
|
|
|
767
1363
|
});
|
|
768
1364
|
}
|
|
769
1365
|
|
|
1366
|
+
if (channel.type === "discord") {
|
|
1367
|
+
if (channel.mode === "bot") {
|
|
1368
|
+
return JSON.stringify({
|
|
1369
|
+
id: `discord_message_${Date.now()}`,
|
|
1370
|
+
guild_id: "agentkit_guild",
|
|
1371
|
+
channel_id: "agentkit_channel",
|
|
1372
|
+
author: {
|
|
1373
|
+
id: "agentkit_user",
|
|
1374
|
+
username: "agentkit",
|
|
1375
|
+
},
|
|
1376
|
+
member: {
|
|
1377
|
+
nick: "AgentKit",
|
|
1378
|
+
},
|
|
1379
|
+
content: message,
|
|
1380
|
+
mentions: [],
|
|
1381
|
+
timestamp: new Date().toISOString(),
|
|
1382
|
+
});
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
return JSON.stringify({
|
|
1386
|
+
id: `discord_interaction_${Date.now()}`,
|
|
1387
|
+
application_id: "agentkit_discord_app",
|
|
1388
|
+
type: 2,
|
|
1389
|
+
data: {
|
|
1390
|
+
id: "agentkit_command",
|
|
1391
|
+
name: "ask",
|
|
1392
|
+
type: 1,
|
|
1393
|
+
options: [
|
|
1394
|
+
{
|
|
1395
|
+
name: "message",
|
|
1396
|
+
type: 3,
|
|
1397
|
+
value: message,
|
|
1398
|
+
},
|
|
1399
|
+
],
|
|
1400
|
+
},
|
|
1401
|
+
guild_id: "agentkit_guild",
|
|
1402
|
+
channel_id: "agentkit_channel",
|
|
1403
|
+
member: {
|
|
1404
|
+
user: {
|
|
1405
|
+
id: "agentkit_user",
|
|
1406
|
+
username: "agentkit",
|
|
1407
|
+
},
|
|
1408
|
+
},
|
|
1409
|
+
token: `agentkit_interaction_token_${Date.now()}`,
|
|
1410
|
+
version: 1,
|
|
1411
|
+
});
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
if (channel.type === "slack") {
|
|
1415
|
+
const now = Date.now();
|
|
1416
|
+
|
|
1417
|
+
return JSON.stringify({
|
|
1418
|
+
token: "deprecated-verification-token",
|
|
1419
|
+
team_id: "T_AGENTKIT",
|
|
1420
|
+
api_app_id: "A_AGENTKIT",
|
|
1421
|
+
type: "event_callback",
|
|
1422
|
+
event_id: `Ev${now}`,
|
|
1423
|
+
event_time: Math.floor(now / 1000),
|
|
1424
|
+
event: {
|
|
1425
|
+
type: "app_mention",
|
|
1426
|
+
user: "UAGENTKITUSER",
|
|
1427
|
+
text: `<@UAGENTKITBOT> ${message}`,
|
|
1428
|
+
ts: `${Math.floor(now / 1000)}.000100`,
|
|
1429
|
+
channel: "C_AGENTKIT",
|
|
1430
|
+
event_ts: `${Math.floor(now / 1000)}.000100`,
|
|
1431
|
+
},
|
|
1432
|
+
authorizations: [],
|
|
1433
|
+
is_ext_shared_channel: false,
|
|
1434
|
+
});
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
if (channel.type === "webhook") {
|
|
1438
|
+
return JSON.stringify({
|
|
1439
|
+
event_id: `evt_${Date.now()}`,
|
|
1440
|
+
external_id: "agentkit-webhook-test",
|
|
1441
|
+
message,
|
|
1442
|
+
});
|
|
1443
|
+
}
|
|
1444
|
+
|
|
770
1445
|
return JSON.stringify({
|
|
771
1446
|
event_id: `web_${Date.now()}`,
|
|
772
1447
|
visitor_id: "visitor_test",
|
|
@@ -777,7 +1452,127 @@ async function channelTestBody(
|
|
|
777
1452
|
});
|
|
778
1453
|
}
|
|
779
1454
|
|
|
780
|
-
function
|
|
1455
|
+
async function channelAudioTestBody(
|
|
1456
|
+
channel: CloudChannel,
|
|
1457
|
+
fixtureFlag: string | boolean | undefined,
|
|
1458
|
+
): Promise<string> {
|
|
1459
|
+
if (typeof fixtureFlag === "string" && isExplicitFixturePath(fixtureFlag)) {
|
|
1460
|
+
return readFile(resolve(process.cwd(), fixtureFlag), "utf8");
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
const fixture = typeof fixtureFlag === "string" ? fixtureFlag : "voice-note";
|
|
1464
|
+
|
|
1465
|
+
if (channel.type === "telegram") {
|
|
1466
|
+
if (fixture !== "voice-note" && fixture !== "telegram-voice-note" && fixture !== "telegram-audio-message") {
|
|
1467
|
+
throw new Error('Telegram audio smoke supports --fixture voice-note or --fixture telegram-voice-note.');
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
return JSON.stringify({
|
|
1471
|
+
update_id: Date.now(),
|
|
1472
|
+
message: {
|
|
1473
|
+
message_id: 1,
|
|
1474
|
+
date: Math.floor(Date.now() / 1000),
|
|
1475
|
+
chat: { id: 456, type: "private" },
|
|
1476
|
+
from: { id: 123, first_name: "AgentKit" },
|
|
1477
|
+
voice: {
|
|
1478
|
+
file_id: `agentkit_voice_${Date.now()}`,
|
|
1479
|
+
file_unique_id: `agentkit_voice_unique_${Date.now()}`,
|
|
1480
|
+
duration: 12,
|
|
1481
|
+
mime_type: "audio/ogg",
|
|
1482
|
+
file_size: 42,
|
|
1483
|
+
},
|
|
1484
|
+
},
|
|
1485
|
+
});
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
if (channel.type === "whatsapp" && channel.provider === "zapster") {
|
|
1489
|
+
if (fixture !== "voice-note" && fixture !== "audio-file" && fixture !== "zapster-audio" && fixture !== "zapster-audio-message") {
|
|
1490
|
+
throw new Error('Zapster audio smoke supports --fixture voice-note, --fixture audio-file, or --fixture zapster-audio.');
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
return JSON.stringify({
|
|
1494
|
+
event_id: `zap_evt_audio_${Date.now()}`,
|
|
1495
|
+
instance_id: "local",
|
|
1496
|
+
message: {
|
|
1497
|
+
id: `wamid.audio.${Date.now()}`,
|
|
1498
|
+
from: "5511999999999",
|
|
1499
|
+
type: "audio",
|
|
1500
|
+
audio: {
|
|
1501
|
+
id: `media_audio_${Date.now()}`,
|
|
1502
|
+
url: `https://api.zapsterapi.com/media/audio/media_audio_${Date.now()}.ogg`,
|
|
1503
|
+
mime_type: "audio/ogg",
|
|
1504
|
+
duration: 12,
|
|
1505
|
+
file_size: 42,
|
|
1506
|
+
...(fixture === "voice-note" ? { ptt: "true" } : {}),
|
|
1507
|
+
},
|
|
1508
|
+
},
|
|
1509
|
+
});
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1512
|
+
if (channel.type === "whatsapp" && channel.provider === "uazapi") {
|
|
1513
|
+
if (fixture !== "voice-note" && fixture !== "audio-file" && fixture !== "uazapi-audio" && fixture !== "uazapi-audio-message") {
|
|
1514
|
+
throw new Error('UAZAPI audio smoke supports --fixture voice-note, --fixture audio-file, or --fixture uazapi-audio.');
|
|
1515
|
+
}
|
|
1516
|
+
|
|
1517
|
+
return JSON.stringify({
|
|
1518
|
+
EventType: "messages",
|
|
1519
|
+
event: "messages",
|
|
1520
|
+
message: {
|
|
1521
|
+
id: `uaz_audio_${Date.now()}`,
|
|
1522
|
+
messageid: `uaz_wamid_audio_${Date.now()}`,
|
|
1523
|
+
chatid: "5511999999999@s.whatsapp.net",
|
|
1524
|
+
sender: "5511999999999@s.whatsapp.net",
|
|
1525
|
+
senderName: "AgentKit",
|
|
1526
|
+
fromMe: false,
|
|
1527
|
+
wasSentByApi: false,
|
|
1528
|
+
messageType: fixture === "voice-note" ? "ptt" : "audio",
|
|
1529
|
+
mimetype: "audio/ogg",
|
|
1530
|
+
duration: 12,
|
|
1531
|
+
file_size: 42,
|
|
1532
|
+
messageTimestamp: Date.now(),
|
|
1533
|
+
},
|
|
1534
|
+
});
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1537
|
+
if (channel.type === "whatsapp" && channel.provider === "evolution") {
|
|
1538
|
+
if (fixture !== "voice-note" && fixture !== "audio-file" && fixture !== "evolution-audio" && fixture !== "evolution-audio-message") {
|
|
1539
|
+
throw new Error(
|
|
1540
|
+
'Evolution audio smoke supports --fixture voice-note, --fixture audio-file, --fixture evolution-audio, or --fixture evolution-audio-message.',
|
|
1541
|
+
);
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
return JSON.stringify({
|
|
1545
|
+
event: "MESSAGES_UPSERT",
|
|
1546
|
+
instance: process.env.EVOLUTION_INSTANCE_NAME ?? "local",
|
|
1547
|
+
data: {
|
|
1548
|
+
key: {
|
|
1549
|
+
remoteJid: "5511999999999@s.whatsapp.net",
|
|
1550
|
+
fromMe: false,
|
|
1551
|
+
id: `evo_wamid_audio_${Date.now()}`,
|
|
1552
|
+
},
|
|
1553
|
+
pushName: "AgentKit",
|
|
1554
|
+
messageType: "audioMessage",
|
|
1555
|
+
message: {
|
|
1556
|
+
audioMessage: {
|
|
1557
|
+
mimetype: "audio/ogg",
|
|
1558
|
+
seconds: 12,
|
|
1559
|
+
ptt: fixture === "voice-note",
|
|
1560
|
+
fileLength: 42,
|
|
1561
|
+
},
|
|
1562
|
+
},
|
|
1563
|
+
messageTimestamp: Math.floor(Date.now() / 1000),
|
|
1564
|
+
},
|
|
1565
|
+
});
|
|
1566
|
+
}
|
|
1567
|
+
|
|
1568
|
+
throw new Error(`Audio smoke is not supported for ${channel.type}/${channel.provider}.`);
|
|
1569
|
+
}
|
|
1570
|
+
|
|
1571
|
+
function isExplicitFixturePath(value: string): boolean {
|
|
1572
|
+
return value.includes("/") || value.startsWith(".") || value.endsWith(".json");
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1575
|
+
function channelTestHeaders(channel: CloudChannel, rawBody: string): Headers {
|
|
781
1576
|
const headers = new Headers({ "Content-Type": "application/json" });
|
|
782
1577
|
|
|
783
1578
|
if (channel.type === "website") {
|
|
@@ -793,7 +1588,32 @@ function channelTestHeaders(channel: CloudChannel): Headers {
|
|
|
793
1588
|
}
|
|
794
1589
|
|
|
795
1590
|
if (channel.type === "whatsapp" && channel.provider === "zapster") {
|
|
796
|
-
headers.set("X-
|
|
1591
|
+
headers.set("X-Instance-ID", process.env.ZAPSTER_INSTANCE_ID ?? "");
|
|
1592
|
+
headers.set("X-Webhook-ID", process.env.ZAPSTER_WEBHOOK_ID ?? "");
|
|
1593
|
+
headers.set("X-Message-ID", `cli_msg_${Date.now()}`);
|
|
1594
|
+
headers.set("X-Attempt-Count", "1");
|
|
1595
|
+
headers.set("User-Agent", "Zapsterapi/agentkit-cli");
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1598
|
+
if (channel.type === "slack") {
|
|
1599
|
+
const signingSecret = process.env.SLACK_SIGNING_SECRET;
|
|
1600
|
+
|
|
1601
|
+
if (signingSecret) {
|
|
1602
|
+
const timestamp = String(Math.floor(Date.now() / 1000));
|
|
1603
|
+
const signature = createHmac("sha256", signingSecret)
|
|
1604
|
+
.update(`v0:${timestamp}:${rawBody}`)
|
|
1605
|
+
.digest("hex");
|
|
1606
|
+
headers.set("X-Slack-Request-Timestamp", timestamp);
|
|
1607
|
+
headers.set("X-Slack-Signature", `v0=${signature}`);
|
|
1608
|
+
}
|
|
1609
|
+
}
|
|
1610
|
+
|
|
1611
|
+
if (channel.type === "webhook") {
|
|
1612
|
+
const secret = process.env.AGENTKIT_WEBHOOK_SECRET;
|
|
1613
|
+
|
|
1614
|
+
if (secret) {
|
|
1615
|
+
headers.set("Authorization", `Bearer ${secret}`);
|
|
1616
|
+
}
|
|
797
1617
|
}
|
|
798
1618
|
|
|
799
1619
|
return headers;
|