whatsappd 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.
@@ -0,0 +1,83 @@
1
+ import { s as WireMessage } from "../wire-Bx6OmkxC.mjs";
2
+ import { Channel, ChannelEvents, RouteHandlerArgs } from "eve/channels";
3
+
4
+ //#region src/adapters/eve.d.ts
5
+ /** Durable per-session adapter state — enough to address the sidecar. */
6
+ interface WhatsAppEveState {
7
+ accountId: string;
8
+ chatId: string;
9
+ [key: string]: unknown;
10
+ }
11
+ /** The `channel` context handed to event handlers. */
12
+ interface WhatsAppEveContext {
13
+ readonly accountId: string;
14
+ readonly chatId: string;
15
+ }
16
+ /** Observability projection (a type alias so it satisfies eve's Record constraint). */
17
+ type WhatsAppEveMetadata = {
18
+ readonly accountId: string;
19
+ readonly chatId: string;
20
+ };
21
+ interface WhatsAppEveOptions {
22
+ /** Sidecar base URL. Default: `WHATSAPP_SIDECAR_URL` env var. */
23
+ readonly sidecarUrl?: string;
24
+ /**
25
+ * Shared bearer token: required on inbound `/event` posts and sent on
26
+ * outbound sidecar calls. Default: `WHATSAPP_SIDECAR_TOKEN` env var.
27
+ */
28
+ readonly token?: string;
29
+ /** Mark the conversation read when the agent starts a turn. Default true. */
30
+ readonly markRead?: boolean;
31
+ /** Show typing presence while the agent works on a turn. Default true. */
32
+ readonly typing?: boolean;
33
+ /** `auth.authenticator` value on started sessions. Default "whatsapp-baileys". */
34
+ readonly authenticator?: string;
35
+ /** Testing seam — defaults to global fetch. */
36
+ readonly fetchFn?: typeof fetch;
37
+ }
38
+ type ContentPart = {
39
+ type: "text";
40
+ text: string;
41
+ } | {
42
+ type: "file";
43
+ data: URL;
44
+ mediaType: string;
45
+ };
46
+ /**
47
+ * Project a wire message onto Eve `UserContent` parts: the text (or a
48
+ * `[kind]` preview), plus the media as a URL file part the framework stages
49
+ * through `fetchFile`.
50
+ */
51
+ declare function toUserContent(message: WireMessage, sidecarUrl: string): ContentPart[];
52
+ /**
53
+ * Handler for `POST /event` — the sidecar posts every inbound WhatsApp event
54
+ * here; message events start or resume the Eve session for that chat.
55
+ * Exported for tests; wired into the channel by `whatsappChannel`.
56
+ */
57
+ declare function createEventRoute(opts: WhatsAppEveOptions): (req: Request, args: RouteHandlerArgs<WhatsAppEveState>) => Promise<Response>;
58
+ /**
59
+ * Session lifecycle handlers: agent replies go back to the sidecar for
60
+ * delivery; turn start drives read receipts and typing presence.
61
+ * Exported for tests; wired into the channel by `whatsappChannel`.
62
+ */
63
+ declare function createEventHandlers(opts: WhatsAppEveOptions): ChannelEvents<WhatsAppEveContext>;
64
+ /**
65
+ * `fetchFile` hook — stages media file parts whose URL points at the
66
+ * sidecar, attaching the bearer token. Other URLs pass through untouched.
67
+ * Exported for tests; wired into the channel by `whatsappChannel`.
68
+ */
69
+ declare function createFetchFile(opts: WhatsAppEveOptions): (url: string) => Promise<{
70
+ bytes: Buffer;
71
+ mediaType?: string;
72
+ filename?: string;
73
+ } | null>;
74
+ /**
75
+ * Build the WhatsApp channel for an Eve app. Place the result (or the
76
+ * default export) at `agent/channels/whatsapp.ts` — the file stem becomes
77
+ * the channel id.
78
+ */
79
+ declare function whatsappChannel(opts?: WhatsAppEveOptions): Channel<WhatsAppEveState, Record<string, unknown>, WhatsAppEveMetadata>;
80
+ /** Zero-config channel: reads `WHATSAPP_SIDECAR_URL` / `WHATSAPP_SIDECAR_TOKEN`. */
81
+ declare const _default: Channel<WhatsAppEveState, Record<string, unknown>, WhatsAppEveMetadata>;
82
+ //#endregion
83
+ export { WhatsAppEveContext, WhatsAppEveMetadata, WhatsAppEveOptions, WhatsAppEveState, createEventHandlers, createEventRoute, createFetchFile, _default as default, toUserContent, whatsappChannel };
@@ -0,0 +1,181 @@
1
+ import { t as messagePreview } from "../wire-CsVVkLhn.mjs";
2
+ import { POST, defineChannel } from "eve/channels";
3
+ //#region src/adapters/eve.ts
4
+ /**
5
+ * Eve channel adapter — a plug-and-play WhatsApp channel for the Eve
6
+ * framework, bridged to the WhatsApp sidecar over HTTP.
7
+ *
8
+ * The sidecar owns the socket to WhatsApp; this adapter is a thin HTTP
9
+ * client on both directions:
10
+ *
11
+ * inbound sidecar → POST /event → `send()` starts/resumes the session
12
+ * (`continuationToken` = chatId, so one WhatsApp conversation
13
+ * maps to one Eve session)
14
+ * outbound "message.completed" → POST sidecar /send
15
+ * "turn.started" → POST sidecar /markRead + /setTyping
16
+ * media inbound media arrives as a sidecar URL; `fetchFile` stages
17
+ * the bytes with the shared bearer token
18
+ *
19
+ * Drop it into an Eve app as `agent/channels/whatsapp.ts`:
20
+ *
21
+ * export { default } from "whatsappd/adapters/eve";
22
+ *
23
+ * or configure explicitly:
24
+ *
25
+ * import { whatsappChannel } from "whatsappd/adapters/eve";
26
+ * export default whatsappChannel({ sidecarUrl: "http://localhost:8788" });
27
+ *
28
+ * @packageDocumentation
29
+ */
30
+ /** Resolve lazily so env vars are read per call, not at import time. */
31
+ function resolve(opts) {
32
+ const sidecarUrl = opts.sidecarUrl ?? process.env.WHATSAPP_SIDECAR_URL;
33
+ if (!sidecarUrl) throw new Error("whatsappd: set WHATSAPP_SIDECAR_URL or pass { sidecarUrl }");
34
+ return {
35
+ sidecarUrl,
36
+ token: opts.token ?? process.env.WHATSAPP_SIDECAR_TOKEN,
37
+ markRead: opts.markRead ?? true,
38
+ typing: opts.typing ?? true,
39
+ authenticator: opts.authenticator ?? "whatsapp-baileys",
40
+ fetchFn: opts.fetchFn ?? fetch
41
+ };
42
+ }
43
+ /**
44
+ * Project a wire message onto Eve `UserContent` parts: the text (or a
45
+ * `[kind]` preview), plus the media as a URL file part the framework stages
46
+ * through `fetchFile`.
47
+ */
48
+ function toUserContent(message, sidecarUrl) {
49
+ const parts = [];
50
+ const text = messagePreview(message);
51
+ if (text.length > 0) parts.push({
52
+ type: "text",
53
+ text
54
+ });
55
+ if ("media" in message && message.media.url) parts.push({
56
+ type: "file",
57
+ data: new URL(message.media.url, sidecarUrl),
58
+ mediaType: message.media.mimetype ?? "application/octet-stream"
59
+ });
60
+ return parts;
61
+ }
62
+ /**
63
+ * Handler for `POST /event` — the sidecar posts every inbound WhatsApp event
64
+ * here; message events start or resume the Eve session for that chat.
65
+ * Exported for tests; wired into the channel by `whatsappChannel`.
66
+ */
67
+ function createEventRoute(opts) {
68
+ return async (req, args) => {
69
+ const c = resolve(opts);
70
+ if (c.token && req.headers.get("authorization") !== `Bearer ${c.token}`) return Response.json({ error: "unauthorized" }, { status: 401 });
71
+ const event = await req.json();
72
+ if (event.type !== "message" || event.message.fromMe) return Response.json({ ignored: true });
73
+ const { accountId, chatId, isGroup, from, pushName, message } = event;
74
+ const session = await args.send(toUserContent(message, c.sidecarUrl), {
75
+ auth: {
76
+ authenticator: c.authenticator,
77
+ principalType: "contact",
78
+ principalId: from ?? chatId,
79
+ attributes: {
80
+ accountId,
81
+ chatId,
82
+ isGroup: String(isGroup),
83
+ ...from !== void 0 && { from },
84
+ ...pushName !== void 0 && { pushName }
85
+ }
86
+ },
87
+ continuationToken: chatId,
88
+ state: {
89
+ accountId,
90
+ chatId
91
+ },
92
+ title: `WhatsApp: ${pushName ?? chatId}`
93
+ });
94
+ return Response.json({ sessionId: session.id });
95
+ };
96
+ }
97
+ /**
98
+ * Session lifecycle handlers: agent replies go back to the sidecar for
99
+ * delivery; turn start drives read receipts and typing presence.
100
+ * Exported for tests; wired into the channel by `whatsappChannel`.
101
+ */
102
+ function createEventHandlers(opts) {
103
+ async function post(path, body) {
104
+ const c = resolve(opts);
105
+ return c.fetchFn(new URL(path, c.sidecarUrl), {
106
+ method: "POST",
107
+ headers: {
108
+ "content-type": "application/json",
109
+ ...c.token && { authorization: `Bearer ${c.token}` }
110
+ },
111
+ body: JSON.stringify(body)
112
+ });
113
+ }
114
+ return {
115
+ async "turn.started"(_data, channel) {
116
+ const c = resolve(opts);
117
+ const target = {
118
+ accountId: channel.accountId,
119
+ chatId: channel.chatId
120
+ };
121
+ const signals = [];
122
+ if (c.markRead) signals.push(post("/markRead", target));
123
+ if (c.typing) signals.push(post("/setTyping", {
124
+ ...target,
125
+ kind: "typing"
126
+ }));
127
+ await Promise.allSettled(signals);
128
+ },
129
+ async "message.completed"(data, channel) {
130
+ if (typeof data.message !== "string" || data.message.length === 0) return;
131
+ const res = await post("/send", {
132
+ accountId: channel.accountId,
133
+ chatId: channel.chatId,
134
+ content: { text: data.message }
135
+ });
136
+ if (!res.ok) throw new Error(`whatsappd: sidecar /send failed with ${res.status}`);
137
+ }
138
+ };
139
+ }
140
+ /**
141
+ * `fetchFile` hook — stages media file parts whose URL points at the
142
+ * sidecar, attaching the bearer token. Other URLs pass through untouched.
143
+ * Exported for tests; wired into the channel by `whatsappChannel`.
144
+ */
145
+ function createFetchFile(opts) {
146
+ return async (url) => {
147
+ const c = resolve(opts);
148
+ if (!url.startsWith(new URL("/", c.sidecarUrl).origin)) return null;
149
+ const res = await c.fetchFn(url, { headers: { ...c.token && { authorization: `Bearer ${c.token}` } } });
150
+ if (!res.ok) throw new Error(`whatsappd: media fetch failed with ${res.status}`);
151
+ return {
152
+ bytes: Buffer.from(await res.arrayBuffer()),
153
+ mediaType: res.headers.get("content-type") ?? void 0
154
+ };
155
+ };
156
+ }
157
+ /**
158
+ * Build the WhatsApp channel for an Eve app. Place the result (or the
159
+ * default export) at `agent/channels/whatsapp.ts` — the file stem becomes
160
+ * the channel id.
161
+ */
162
+ function whatsappChannel(opts = {}) {
163
+ return defineChannel({
164
+ kindHint: "whatsapp",
165
+ context: (state) => ({
166
+ accountId: state.accountId,
167
+ chatId: state.chatId
168
+ }),
169
+ metadata: (state) => ({
170
+ accountId: state.accountId,
171
+ chatId: state.chatId
172
+ }),
173
+ routes: [POST("/event", createEventRoute(opts))],
174
+ events: createEventHandlers(opts),
175
+ fetchFile: createFetchFile(opts)
176
+ });
177
+ }
178
+ /** Zero-config channel: reads `WHATSAPP_SIDECAR_URL` / `WHATSAPP_SIDECAR_TOKEN`. */
179
+ var eve_default = whatsappChannel();
180
+ //#endregion
181
+ export { createEventHandlers, createEventRoute, createFetchFile, eve_default as default, toUserContent, whatsappChannel };
@@ -0,0 +1,283 @@
1
+ import { A as MessageFlags, C as SendOptions, D as MediaHandle, E as InboundMessage, O as MediaMeta, S as Outbound, T as Addressing, _ as isRetryable, a as Status, b as BinaryInput, c as isOnline, d as FaultReason, f as PairingError, g as dispositionFor, h as classifyDisconnect, i as PairingState, k as MessageContext, l as isTerminal, m as assertE164, n as Update, o as SyncState, p as WhatsAppFault, r as ConnectionEvent, s as WaIdentity, t as ReceiptStatus, u as Disposition, v as PresenceKind, w as refOf, x as MessageRef, y as PresenceUpdate } from "./update-Bi5ZPUjP.mjs";
2
+ import { i as qrAuth, n as SessionStore, r as pairingAuth, t as AuthStrategy } from "./ports-DeTIMztA.mjs";
3
+ import { memoryStore } from "./stores/memory.mjs";
4
+ import { i as WhatsAppChannelAdapter, n as ChannelHandlers, r as ConversationRef, t as ChannelEvent } from "./types-B8d1OyHV.mjs";
5
+ import { AgentTool, DeleteInput, EditInput, MediaKind, ReactInput, ReplyInput, SendMediaInput, SendTextInput, SetTypingInput, ToolContext, allTools, bindTools, deleteMsg, edit, markRead, react, reply, sendMedia, sendText, setTyping } from "./tools/index.mjs";
6
+ import { Logger } from "pino";
7
+
8
+ //#region src/model/group.d.ts
9
+ interface GroupParticipant {
10
+ readonly id: string;
11
+ readonly role?: string;
12
+ }
13
+ interface GroupMetadata {
14
+ readonly id: string;
15
+ readonly subject?: string;
16
+ readonly participants: readonly GroupParticipant[];
17
+ }
18
+ type GroupParticipantAction = "add" | "remove" | "promote" | "demote" | "modify";
19
+ type GroupUpdate = {
20
+ readonly kind: "metadata";
21
+ readonly id: string;
22
+ readonly subject?: string;
23
+ readonly participants?: readonly GroupParticipant[];
24
+ readonly at: number;
25
+ } | {
26
+ readonly kind: "participants";
27
+ readonly id: string;
28
+ readonly action: GroupParticipantAction;
29
+ readonly participants: readonly GroupParticipant[];
30
+ readonly at: number;
31
+ };
32
+ //#endregion
33
+ //#region src/model/metrics.d.ts
34
+ type MetricEvent = /** A connection state transition (`from` → `to` phase). */{
35
+ type: "transition";
36
+ from: Status["phase"];
37
+ to: Status["phase"];
38
+ } /** A message crossed the inbound stream. */ | {
39
+ type: "message_in";
40
+ kind: InboundMessage["kind"];
41
+ live: boolean;
42
+ } /** An update crossed the updates stream. */ | {
43
+ type: "update_in";
44
+ kind: Update["kind"];
45
+ } /** A WhatsApp address-book contact update crossed the contacts stream. */ | {
46
+ type: "contact_in";
47
+ hasDisplayName: boolean;
48
+ identityCount: number;
49
+ } /** An ephemeral remote presence signal crossed the presence stream. */ | {
50
+ type: "presence_in";
51
+ kind: PresenceUpdate["kind"];
52
+ } /** A WhatsApp group metadata or participant update crossed the groups stream. */ | {
53
+ type: "group_in";
54
+ kind: GroupUpdate["kind"];
55
+ } /** A `send()` completed successfully. */ | {
56
+ type: "message_out";
57
+ } /** A reconnect attempt is starting (`attempt` is the retry count). */ | {
58
+ type: "reconnect";
59
+ attempt: number;
60
+ };
61
+ /** The hook signature a consumer supplies as {@link SessionConfig.metrics}. */
62
+ type MetricsHook = (event: MetricEvent) => void;
63
+ //#endregion
64
+ //#region src/model/contact.d.ts
65
+ /**
66
+ * A contact / address-book update, normalized from a WhatsApp contact event.
67
+ * Consume these to keep a local read model of contacts in sync.
68
+ */
69
+ interface ContactUpdate {
70
+ /** The primary WhatsApp contact id from the event. */
71
+ readonly id: string;
72
+ /** Candidate identities for matching existing records, ordered by confidence. */
73
+ readonly nativeIds: readonly string[];
74
+ /** The saved address-book name, when present. */
75
+ readonly displayName?: string;
76
+ /** The profile/push name set by the remote user, when present. */
77
+ readonly profileName?: string;
78
+ /** A business-verified name, when present. */
79
+ readonly verifiedName?: string;
80
+ /** The contact's WhatsApp username, when present. */
81
+ readonly username?: string;
82
+ /**
83
+ * The profile-photo URL: a URL string, `null` when the contact has none, or
84
+ * omitted when the event carried no photo information.
85
+ */
86
+ readonly imgUrl?: string | null;
87
+ /** The contact's status/about text, when present. */
88
+ readonly status?: string;
89
+ /** When the update occurred, as a millisecond epoch timestamp. */
90
+ readonly at?: number;
91
+ }
92
+ //#endregion
93
+ //#region src/model/history.d.ts
94
+ interface HistoryChat {
95
+ readonly id: string;
96
+ readonly subject?: string;
97
+ readonly isGroup: boolean;
98
+ readonly lastMessageAt?: number;
99
+ readonly participants?: readonly GroupParticipant[];
100
+ }
101
+ interface HistoryContact {
102
+ readonly id: string;
103
+ readonly displayName?: string;
104
+ }
105
+ interface ConversationSyncBatch {
106
+ readonly chats: readonly HistoryChat[];
107
+ readonly contacts: readonly HistoryContact[];
108
+ readonly self?: HistoryContact;
109
+ readonly messages: readonly InboundMessage[];
110
+ }
111
+ type ConversationSyncChat = HistoryChat;
112
+ type ConversationSyncContact = HistoryContact;
113
+ type HistoryBatch = ConversationSyncBatch;
114
+ //#endregion
115
+ //#region src/session.d.ts
116
+ /** Configuration for {@link createSession}. */
117
+ interface SessionConfig {
118
+ /** Where this session's credentials are persisted. */
119
+ store: SessionStore;
120
+ /** How this session logs in — {@link qrAuth} or {@link pairingAuth}. */
121
+ auth: AuthStrategy;
122
+ /**
123
+ * Logger to use.
124
+ *
125
+ * @defaultValue a `pino` logger at the level in `WA_LOG_LEVEL`, or `warn`.
126
+ */
127
+ logger?: Logger;
128
+ /**
129
+ * How long to wait after a pairing attempt for confirmation before treating
130
+ * the silent rejection as final, in milliseconds.
131
+ */
132
+ verdictWindowMs?: number;
133
+ /**
134
+ * Grace period after the socket opens before forcing the `online` status if
135
+ * no history-sync signal has arrived, in milliseconds.
136
+ */
137
+ syncGraceMs?: number;
138
+ /** Base delay for reconnect backoff, in milliseconds. */
139
+ reconnectBaseMs?: number;
140
+ /** Maximum delay for reconnect backoff, in milliseconds. */
141
+ reconnectMaxMs?: number;
142
+ /**
143
+ * Whether to surface WhatsApp Status/story posts (`status@broadcast`) on the
144
+ * inbound stream.
145
+ *
146
+ * @defaultValue `false`
147
+ */
148
+ receiveStatusBroadcast?: boolean;
149
+ /**
150
+ * Minimum gap between outbound sends, in milliseconds, to reduce the risk of
151
+ * rate-limiting. Set to `0` to disable pacing.
152
+ *
153
+ * @defaultValue `1000`
154
+ */
155
+ sendMinGapMs?: number;
156
+ /**
157
+ * Fire-and-forget observability hook. Errors it throws are swallowed so
158
+ * instrumentation can never disrupt the session.
159
+ */
160
+ metrics?: MetricsHook;
161
+ }
162
+ /**
163
+ * One WhatsApp account's live session: a set of read-only event streams plus
164
+ * the commands that act on the connection. Create one with {@link createSession}
165
+ * and call {@link WhatsAppSession.start | start} to connect.
166
+ */
167
+ interface WhatsAppSession {
168
+ /** The current connection status (also emitted on {@link connection}). */
169
+ readonly status: Status;
170
+ /** Connection-lifecycle events: pairing, online, backing off, terminal. */
171
+ readonly connection: AsyncIterable<ConnectionEvent>;
172
+ /** Incoming messages, normalized into a closed union. */
173
+ readonly inbound: AsyncIterable<InboundMessage>;
174
+ /** Batches of conversation history delivered during initial sync. */
175
+ readonly conversationSync: AsyncIterable<ConversationSyncBatch>;
176
+ /** Receipts, reactions, edits, and revokes on existing messages. */
177
+ readonly updates: AsyncIterable<Update>;
178
+ /** Address-book and profile-metadata updates from contact events. */
179
+ readonly contacts: AsyncIterable<ContactUpdate>;
180
+ /** Group metadata and participant updates from group events. */
181
+ readonly groups: AsyncIterable<GroupUpdate>;
182
+ /** Ephemeral remote typing/recording/availability signals. */
183
+ readonly presence: AsyncIterable<PresenceUpdate>;
184
+ /** Connect to WhatsApp and begin emitting on the streams above. */
185
+ start(): Promise<void>;
186
+ /**
187
+ * Send a message to a chat.
188
+ *
189
+ * @param to - The destination chat JID.
190
+ * @param msg - The message content to send.
191
+ * @param opts - Optional quoting and mentions.
192
+ * @returns A reference to the sent message, for later quote/react/edit/delete.
193
+ */
194
+ send(to: string, msg: Outbound, opts?: SendOptions): Promise<MessageRef>;
195
+ /**
196
+ * Mark messages as read (blue ticks).
197
+ *
198
+ * @param refs - References to the messages to acknowledge.
199
+ */
200
+ markRead(refs: MessageRef[]): Promise<void>;
201
+ /**
202
+ * Show or clear the typing indicator in a chat.
203
+ *
204
+ * @param chatId - The chat JID to signal in.
205
+ * @param on - `true` to show typing, `false` to clear it.
206
+ */
207
+ setTyping(chatId: string, on: boolean): Promise<void>;
208
+ /**
209
+ * Fetch normalized metadata for a group.
210
+ *
211
+ * @param chatId - The group JID.
212
+ */
213
+ groupMetadata(chatId: string): Promise<GroupMetadata>;
214
+ /**
215
+ * Fetch a profile picture URL.
216
+ *
217
+ * @param jid - A contact, account, or group JID.
218
+ * @param type - `"image"` for full size or `"preview"` for a thumbnail.
219
+ * @returns The URL, or `undefined` when none is available.
220
+ */
221
+ profilePictureUrl(jid: string, type?: "image" | "preview"): Promise<string | undefined>;
222
+ /**
223
+ * The connected account's own identity.
224
+ *
225
+ * @returns The identity once the socket is open, or `undefined` before then.
226
+ */
227
+ identity(): WaIdentity | undefined;
228
+ /** Tear down the session intentionally; never reported as a fault. */
229
+ stop(): Promise<void>;
230
+ }
231
+ /**
232
+ * Create a single WhatsApp account session.
233
+ *
234
+ * @remarks
235
+ * The returned session is inert until {@link WhatsAppSession.start | start} is
236
+ * called. Consume its streams to observe the connection lifecycle and incoming
237
+ * messages; call its command methods to send and interact.
238
+ *
239
+ * @param config - Store, auth strategy, and optional tuning — see
240
+ * {@link SessionConfig}.
241
+ * @returns A not-yet-connected {@link WhatsAppSession}.
242
+ *
243
+ * @example
244
+ * ```ts
245
+ * import { createSession, qrAuth, fileStore, refOf } from "whatsappd";
246
+ *
247
+ * const session = createSession({
248
+ * store: fileStore("./.wa-auth"),
249
+ * auth: qrAuth(),
250
+ * });
251
+ *
252
+ * for await (const status of session.connection) {
253
+ * if (status.phase === "pairing" && status.pairing.step === "challenge_live") {
254
+ * console.log("scan:", status.pairing.qr ?? status.pairing.code);
255
+ * }
256
+ * }
257
+ *
258
+ * await session.start();
259
+ * ```
260
+ */
261
+ declare function createSession(config: SessionConfig): WhatsAppSession;
262
+ //#endregion
263
+ //#region src/stores/file.d.ts
264
+ declare function fileStore(dir: string): SessionStore;
265
+ //#endregion
266
+ //#region src/channel/adapter.d.ts
267
+ interface CreateChannelAdapterOptions {
268
+ /** The single-account session to wrap. */
269
+ session: WhatsAppSession;
270
+ /** Label carried on status events and sidecar wire payloads. Default "default". */
271
+ accountId?: string;
272
+ /** Optional logger. */
273
+ logger?: Logger;
274
+ }
275
+ /**
276
+ * Create a `WhatsAppChannelAdapter` over one session.
277
+ *
278
+ * Construction does NOT connect — call `start()` to begin pumping the
279
+ * session's streams and open the socket to WhatsApp.
280
+ */
281
+ declare function createChannelAdapter(opts: CreateChannelAdapterOptions): WhatsAppChannelAdapter;
282
+ //#endregion
283
+ export { type Addressing, type AgentTool, type AuthStrategy, type BinaryInput, type ChannelEvent, type ChannelHandlers, type ConnectionEvent, type ContactUpdate, type ConversationRef, type ConversationSyncBatch, type ConversationSyncChat, type ConversationSyncContact, type CreateChannelAdapterOptions, type DeleteInput, type Disposition, type EditInput, type FaultReason, type GroupMetadata, type GroupParticipant, type GroupParticipantAction, type GroupUpdate, type HistoryBatch, type HistoryChat, type HistoryContact, type InboundMessage, type MediaHandle, type MediaKind, type MediaMeta, type MessageContext, type MessageFlags, type MessageRef, type MetricEvent, type MetricsHook, type Outbound, PairingError, type PairingState, type PresenceKind, type PresenceUpdate, type ReactInput, type ReceiptStatus, type ReplyInput, type SendMediaInput, type SendOptions, type SendTextInput, type SessionConfig, type SessionStore, type SetTypingInput, type Status, type SyncState, type ToolContext, type Update, type WaIdentity, type WhatsAppChannelAdapter, type WhatsAppFault, type WhatsAppSession, allTools, assertE164, bindTools, classifyDisconnect, createChannelAdapter, createSession, deleteMsg, dispositionFor, edit, fileStore, isOnline, isRetryable, isTerminal, markRead, memoryStore, pairingAuth, qrAuth, react, refOf, reply, sendMedia, sendText, setTyping };
package/dist/index.mjs ADDED
@@ -0,0 +1,15 @@
1
+ import { a as createSession, c as PairingError, d as dispositionFor, f as isRetryable, i as qrAuth, l as assertE164, n as fileStore, o as isOnline, r as pairingAuth, s as isTerminal, t as createChannelAdapter, u as classifyDisconnect } from "./adapter-BES4PRA9.mjs";
2
+ import { memoryStore } from "./stores/memory.mjs";
3
+ import { allTools, bindTools, deleteMsg, edit, markRead, react, reply, sendMedia, sendText, setTyping } from "./tools/index.mjs";
4
+ //#region src/model/outbound.ts
5
+ /** Build a `MessageRef` from a received message, for react/edit/delete/quote. */
6
+ function refOf(m) {
7
+ return {
8
+ id: m.id,
9
+ chatId: m.chatId,
10
+ fromMe: m.fromMe,
11
+ ...m.isGroup && { participant: m.from }
12
+ };
13
+ }
14
+ //#endregion
15
+ export { PairingError, allTools, assertE164, bindTools, classifyDisconnect, createChannelAdapter, createSession, deleteMsg, dispositionFor, edit, fileStore, isOnline, isRetryable, isTerminal, markRead, memoryStore, pairingAuth, qrAuth, react, refOf, reply, sendMedia, sendText, setTyping };
@@ -0,0 +1,60 @@
1
+ //#region src/ports.d.ts
2
+ /**
3
+ * An opaque key/value store for a session's credentials.
4
+ *
5
+ * @remarks
6
+ * The library serializes authentication state into plain strings (the root
7
+ * `creds` entry plus per-signal-key entries) and reads them back. The store
8
+ * only persists strings — it never interprets them, so values may be encrypted
9
+ * at rest without changing this contract.
10
+ *
11
+ * Keys look like `"creds"`, `"pre-key:42"`, `"session:123_1.0"`, or
12
+ * `"sender-key:…"`; a value is a serialized string, or `null` to delete the
13
+ * entry.
14
+ *
15
+ * @see {@link memoryStore}, {@link fileStore}, and `libsqlStore` for the
16
+ * built-in implementations.
17
+ */
18
+ interface SessionStore {
19
+ /** Read one entry, resolving to `null` when the key is absent. */
20
+ read(key: string): Promise<string | null>;
21
+ /**
22
+ * Apply a batch of writes. Each value is a string to set, or `null` to
23
+ * delete that key.
24
+ */
25
+ write(entries: Record<string, string | null>): Promise<void>;
26
+ /**
27
+ * Erase every entry for this session. Called automatically on a terminal
28
+ * logout so dead credentials are never reused.
29
+ */
30
+ clear(): Promise<void>;
31
+ }
32
+ /**
33
+ * How a session authenticates: scan a QR code, or enter a pairing code on a
34
+ * known phone number.
35
+ *
36
+ * @see {@link qrAuth}, {@link pairingAuth}
37
+ */
38
+ type AuthStrategy = {
39
+ method: "qr";
40
+ } | {
41
+ method: "pairing_code";
42
+ phone: string;
43
+ };
44
+ /**
45
+ * Log in by scanning a QR code shown to the user.
46
+ *
47
+ * @returns A QR {@link AuthStrategy}.
48
+ */
49
+ declare function qrAuth(): AuthStrategy;
50
+ /**
51
+ * Log in with a pairing code delivered to a known phone number.
52
+ *
53
+ * @param phone - The phone number in E.164 format (e.g. `+15551234567`).
54
+ * @returns A pairing-code {@link AuthStrategy}.
55
+ * @throws {@link PairingError} with reason `"invalid_phone"` when `phone` is
56
+ * not valid E.164 — validated here so bad input fails before any network call.
57
+ */
58
+ declare function pairingAuth(phone: string): AuthStrategy;
59
+ //#endregion
60
+ export { qrAuth as i, SessionStore as n, pairingAuth as r, AuthStrategy as t };
@@ -0,0 +1,47 @@
1
+ import { a as WireBinary, c as WireUpdate, d as toWireMessage, f as toWireUpdate, i as SidecarEvent, l as messagePreview, n as SendRequest, o as WireMedia, r as SetTypingRequest, s as WireMessage, t as MarkReadRequest, u as reviveOutbound } from "../wire-Bx6OmkxC.mjs";
2
+ import { i as WhatsAppChannelAdapter } from "../types-B8d1OyHV.mjs";
3
+ import { Logger } from "pino";
4
+ import { Server } from "node:http";
5
+
6
+ //#region src/sidecar/server.d.ts
7
+ /** One URL the sidecar POSTs inbound `SidecarEvent`s to (e.g. Eve's `/event`). */
8
+ interface ForwardTarget {
9
+ readonly url: string;
10
+ /** Sent as `Authorization: Bearer <token>` on every forwarded event. */
11
+ readonly token?: string;
12
+ }
13
+ interface SidecarServerOptions {
14
+ /** The account this sidecar serves. */
15
+ readonly adapter: WhatsAppChannelAdapter;
16
+ /** Where to POST inbound events. Empty = events are not forwarded. */
17
+ readonly forward?: readonly ForwardTarget[];
18
+ /** When set, every incoming request must carry `Authorization: Bearer <token>`. */
19
+ readonly token?: string;
20
+ /** Absolute base for media URLs in events; relative `/media/...` when unset. */
21
+ readonly baseUrl?: string;
22
+ readonly logger?: Logger;
23
+ /** How many recent media handles to keep downloadable. Default 512. */
24
+ readonly mediaCacheSize?: number;
25
+ /** Testing seam — defaults to global fetch. */
26
+ readonly fetchFn?: typeof fetch;
27
+ }
28
+ interface SidecarServer {
29
+ /** The underlying Node server, for hosts that need to attach to it. */
30
+ readonly server: Server;
31
+ listen(port: number, host?: string): Promise<{
32
+ port: number;
33
+ }>;
34
+ /** Unsubscribe from the adapter and close the HTTP server. */
35
+ close(): Promise<void>;
36
+ }
37
+ declare function createSidecarServer(options: SidecarServerOptions): SidecarServer;
38
+ //#endregion
39
+ //#region src/sidecar/index.d.ts
40
+ interface RunningSidecar {
41
+ readonly port: number;
42
+ readonly server: SidecarServer;
43
+ close(): Promise<void>;
44
+ }
45
+ declare function runSidecar(env?: NodeJS.ProcessEnv): Promise<RunningSidecar>;
46
+ //#endregion
47
+ export { type ForwardTarget, MarkReadRequest, RunningSidecar, SendRequest, SetTypingRequest, SidecarEvent, type SidecarServer, type SidecarServerOptions, WireBinary, WireMedia, WireMessage, WireUpdate, createSidecarServer, messagePreview, reviveOutbound, runSidecar, toWireMessage, toWireUpdate };