portal-aiatende-api 0.5.2 → 0.5.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +39 -3
- package/dist/index.d.ts +39 -3
- package/dist/index.js +36 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +36 -0
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -17,7 +17,7 @@ declare abstract class BaseResource {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
interface Client {
|
|
20
|
-
id:
|
|
20
|
+
id: string;
|
|
21
21
|
name: string;
|
|
22
22
|
email: string;
|
|
23
23
|
phone: string;
|
|
@@ -408,7 +408,7 @@ interface MessagingMessage {
|
|
|
408
408
|
id: string;
|
|
409
409
|
externalId: string;
|
|
410
410
|
conversationId: string;
|
|
411
|
-
direction: 'inbound' | 'outbound';
|
|
411
|
+
direction: 'inbound' | 'outbound' | 'internal';
|
|
412
412
|
type: string;
|
|
413
413
|
status: string;
|
|
414
414
|
content: Record<string, unknown>;
|
|
@@ -454,6 +454,8 @@ interface PatchConversationPayload {
|
|
|
454
454
|
aiResumeMode?: AiResumeMode | null;
|
|
455
455
|
/** Só aceito de chamadores com papel de sistema/staff (ex.: atende360-assistant via APP_API_KEY) — o Portal rejeita se vier de um OWNER/MEMBER comum. */
|
|
456
456
|
kommoLeadId?: number | null;
|
|
457
|
+
/** Status de triagem, puramente organizacional (não bloqueia envio/recebimento). Reabre sozinho ('open') com qualquer mensagem inbound nova. */
|
|
458
|
+
status?: 'open' | 'closed';
|
|
457
459
|
}
|
|
458
460
|
/**
|
|
459
461
|
* `actor` só é respeitado pelo Portal quando o chamador é um papel de sistema/staff (ex.: o
|
|
@@ -509,6 +511,14 @@ interface DownloadMediaResponse {
|
|
|
509
511
|
data: Buffer;
|
|
510
512
|
contentType: string;
|
|
511
513
|
}
|
|
514
|
+
interface CreateToolEventPayload {
|
|
515
|
+
toolName: string;
|
|
516
|
+
/** Id do tool-call do provider — usado como externalId (idempotência), evita duplicar em retry. */
|
|
517
|
+
toolCallId?: string;
|
|
518
|
+
args?: unknown;
|
|
519
|
+
result?: unknown;
|
|
520
|
+
status?: string;
|
|
521
|
+
}
|
|
512
522
|
|
|
513
523
|
/**
|
|
514
524
|
* Canal WhatsApp direto (aiatendeWa) — proxy pras rotas `/api/clients/{clientId}/messaging/...`
|
|
@@ -533,6 +543,32 @@ declare class MessagingResource extends BaseResource {
|
|
|
533
543
|
* registro de mídia local pra conexões Cloud API).
|
|
534
544
|
*/
|
|
535
545
|
uploadMedia(clientId: string, payload: UploadMediaPayload): Promise<UploadMediaResponse>;
|
|
546
|
+
/**
|
|
547
|
+
* Registra a execução de uma tool da IA (canal aiatendeWa) como uma mensagem 'internal' na
|
|
548
|
+
* conversa — visível no Portal, nunca despachada pro WhatsApp. Idempotente por `toolCallId`
|
|
549
|
+
* (upsert do lado do Portal — retry seguro).
|
|
550
|
+
*/
|
|
551
|
+
createToolEvent(clientId: string, conversationId: string, payload: CreateToolEventPayload): Promise<{
|
|
552
|
+
id: string;
|
|
553
|
+
}>;
|
|
554
|
+
/**
|
|
555
|
+
* Resolve a conversa local (aiatendeWa) vinculada a um lead da Kommo, se existir — usado pelo
|
|
556
|
+
* KommoServices.setAiStateTag do atende360-assistant pra também atualizar
|
|
557
|
+
* Conversation.aiCareStatus no Portal quando a tag de IA de um lead muda pela Kommo/Wazzup.
|
|
558
|
+
*/
|
|
559
|
+
getConversationByKommoLead(clientId: string, kommoLeadId: number): Promise<{
|
|
560
|
+
conversationId: string | null;
|
|
561
|
+
}>;
|
|
562
|
+
/**
|
|
563
|
+
* Mostra "digitando..." pro lead no WhatsApp (canal aiatendeWa, só Cloud API — sem
|
|
564
|
+
* equivalente no WhatsApp Lite). A Portal já implementa isso ponta a ponta (usado hoje só
|
|
565
|
+
* pelo atendente humano digitando no textarea do Portal); este método só expõe a mesma rota
|
|
566
|
+
* pro atende360-assistant poder chamar antes/durante o processamento da IA.
|
|
567
|
+
*/
|
|
568
|
+
sendTyping(clientId: string, conversationId: string): Promise<{
|
|
569
|
+
status: string;
|
|
570
|
+
reason?: string;
|
|
571
|
+
}>;
|
|
536
572
|
/**
|
|
537
573
|
* Baixa os bytes de um anexo (Message.media[].id) — usado hoje só pelo caminho de imagem do
|
|
538
574
|
* canal aiatendeWa (monta uma data URI pra mandar pro modelo de visão, já que a URL não pode
|
|
@@ -557,4 +593,4 @@ declare class PortalApiError extends Error {
|
|
|
557
593
|
constructor(message: string, info?: Partial<PortalApiError>);
|
|
558
594
|
}
|
|
559
595
|
|
|
560
|
-
export { type AiActivationSchedule, type AiCareStatus, type AiConfig, type AiExperiment, type AiExperimentConfig, type AiExperimentCriteria, type AiExperimentVariant, type AiPrompt, type AiResumeMode, type BusinessHours, type CalendarConfig, type Client, type ClientsListResponse, ClientsResource, type ConversationHistoryMessage, type DateException, type DownloadMediaResponse, type GetInternalContextParams, type InternalContextChunk, type InternalContextResult, KnowledgeResource, type KommoAccount, type KommoConfig, type ListConversationsFilters, type LossReasonMapping, type MessagingContact, type MessagingConversation, type MessagingConversationsListResponse, type MessagingMediaItem, type MessagingMessage, type MessagingMessagesListResponse, type MessagingPhoneNumber, MessagingResource, type NudgeSettings, type PatchConversationPayload, type PipelineMapping, PortalApiError, PortalClient, type PortalClientConfig, type SendMessagePayload, type SendMessageResponse, type StageMapping, type TimeRange, type UploadMediaPayload, type UploadMediaResponse, type Wazzup, type WazzupChannel };
|
|
596
|
+
export { type AiActivationSchedule, type AiCareStatus, type AiConfig, type AiExperiment, type AiExperimentConfig, type AiExperimentCriteria, type AiExperimentVariant, type AiPrompt, type AiResumeMode, type BusinessHours, type CalendarConfig, type Client, type ClientsListResponse, ClientsResource, type ConversationHistoryMessage, type CreateToolEventPayload, type DateException, type DownloadMediaResponse, type GetInternalContextParams, type InternalContextChunk, type InternalContextResult, KnowledgeResource, type KommoAccount, type KommoConfig, type ListConversationsFilters, type LossReasonMapping, type MessagingContact, type MessagingConversation, type MessagingConversationsListResponse, type MessagingMediaItem, type MessagingMessage, type MessagingMessagesListResponse, type MessagingPhoneNumber, MessagingResource, type NudgeSettings, type PatchConversationPayload, type PipelineMapping, PortalApiError, PortalClient, type PortalClientConfig, type SendMessagePayload, type SendMessageResponse, type StageMapping, type TimeRange, type UploadMediaPayload, type UploadMediaResponse, type Wazzup, type WazzupChannel };
|
package/dist/index.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ declare abstract class BaseResource {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
interface Client {
|
|
20
|
-
id:
|
|
20
|
+
id: string;
|
|
21
21
|
name: string;
|
|
22
22
|
email: string;
|
|
23
23
|
phone: string;
|
|
@@ -408,7 +408,7 @@ interface MessagingMessage {
|
|
|
408
408
|
id: string;
|
|
409
409
|
externalId: string;
|
|
410
410
|
conversationId: string;
|
|
411
|
-
direction: 'inbound' | 'outbound';
|
|
411
|
+
direction: 'inbound' | 'outbound' | 'internal';
|
|
412
412
|
type: string;
|
|
413
413
|
status: string;
|
|
414
414
|
content: Record<string, unknown>;
|
|
@@ -454,6 +454,8 @@ interface PatchConversationPayload {
|
|
|
454
454
|
aiResumeMode?: AiResumeMode | null;
|
|
455
455
|
/** Só aceito de chamadores com papel de sistema/staff (ex.: atende360-assistant via APP_API_KEY) — o Portal rejeita se vier de um OWNER/MEMBER comum. */
|
|
456
456
|
kommoLeadId?: number | null;
|
|
457
|
+
/** Status de triagem, puramente organizacional (não bloqueia envio/recebimento). Reabre sozinho ('open') com qualquer mensagem inbound nova. */
|
|
458
|
+
status?: 'open' | 'closed';
|
|
457
459
|
}
|
|
458
460
|
/**
|
|
459
461
|
* `actor` só é respeitado pelo Portal quando o chamador é um papel de sistema/staff (ex.: o
|
|
@@ -509,6 +511,14 @@ interface DownloadMediaResponse {
|
|
|
509
511
|
data: Buffer;
|
|
510
512
|
contentType: string;
|
|
511
513
|
}
|
|
514
|
+
interface CreateToolEventPayload {
|
|
515
|
+
toolName: string;
|
|
516
|
+
/** Id do tool-call do provider — usado como externalId (idempotência), evita duplicar em retry. */
|
|
517
|
+
toolCallId?: string;
|
|
518
|
+
args?: unknown;
|
|
519
|
+
result?: unknown;
|
|
520
|
+
status?: string;
|
|
521
|
+
}
|
|
512
522
|
|
|
513
523
|
/**
|
|
514
524
|
* Canal WhatsApp direto (aiatendeWa) — proxy pras rotas `/api/clients/{clientId}/messaging/...`
|
|
@@ -533,6 +543,32 @@ declare class MessagingResource extends BaseResource {
|
|
|
533
543
|
* registro de mídia local pra conexões Cloud API).
|
|
534
544
|
*/
|
|
535
545
|
uploadMedia(clientId: string, payload: UploadMediaPayload): Promise<UploadMediaResponse>;
|
|
546
|
+
/**
|
|
547
|
+
* Registra a execução de uma tool da IA (canal aiatendeWa) como uma mensagem 'internal' na
|
|
548
|
+
* conversa — visível no Portal, nunca despachada pro WhatsApp. Idempotente por `toolCallId`
|
|
549
|
+
* (upsert do lado do Portal — retry seguro).
|
|
550
|
+
*/
|
|
551
|
+
createToolEvent(clientId: string, conversationId: string, payload: CreateToolEventPayload): Promise<{
|
|
552
|
+
id: string;
|
|
553
|
+
}>;
|
|
554
|
+
/**
|
|
555
|
+
* Resolve a conversa local (aiatendeWa) vinculada a um lead da Kommo, se existir — usado pelo
|
|
556
|
+
* KommoServices.setAiStateTag do atende360-assistant pra também atualizar
|
|
557
|
+
* Conversation.aiCareStatus no Portal quando a tag de IA de um lead muda pela Kommo/Wazzup.
|
|
558
|
+
*/
|
|
559
|
+
getConversationByKommoLead(clientId: string, kommoLeadId: number): Promise<{
|
|
560
|
+
conversationId: string | null;
|
|
561
|
+
}>;
|
|
562
|
+
/**
|
|
563
|
+
* Mostra "digitando..." pro lead no WhatsApp (canal aiatendeWa, só Cloud API — sem
|
|
564
|
+
* equivalente no WhatsApp Lite). A Portal já implementa isso ponta a ponta (usado hoje só
|
|
565
|
+
* pelo atendente humano digitando no textarea do Portal); este método só expõe a mesma rota
|
|
566
|
+
* pro atende360-assistant poder chamar antes/durante o processamento da IA.
|
|
567
|
+
*/
|
|
568
|
+
sendTyping(clientId: string, conversationId: string): Promise<{
|
|
569
|
+
status: string;
|
|
570
|
+
reason?: string;
|
|
571
|
+
}>;
|
|
536
572
|
/**
|
|
537
573
|
* Baixa os bytes de um anexo (Message.media[].id) — usado hoje só pelo caminho de imagem do
|
|
538
574
|
* canal aiatendeWa (monta uma data URI pra mandar pro modelo de visão, já que a URL não pode
|
|
@@ -557,4 +593,4 @@ declare class PortalApiError extends Error {
|
|
|
557
593
|
constructor(message: string, info?: Partial<PortalApiError>);
|
|
558
594
|
}
|
|
559
595
|
|
|
560
|
-
export { type AiActivationSchedule, type AiCareStatus, type AiConfig, type AiExperiment, type AiExperimentConfig, type AiExperimentCriteria, type AiExperimentVariant, type AiPrompt, type AiResumeMode, type BusinessHours, type CalendarConfig, type Client, type ClientsListResponse, ClientsResource, type ConversationHistoryMessage, type DateException, type DownloadMediaResponse, type GetInternalContextParams, type InternalContextChunk, type InternalContextResult, KnowledgeResource, type KommoAccount, type KommoConfig, type ListConversationsFilters, type LossReasonMapping, type MessagingContact, type MessagingConversation, type MessagingConversationsListResponse, type MessagingMediaItem, type MessagingMessage, type MessagingMessagesListResponse, type MessagingPhoneNumber, MessagingResource, type NudgeSettings, type PatchConversationPayload, type PipelineMapping, PortalApiError, PortalClient, type PortalClientConfig, type SendMessagePayload, type SendMessageResponse, type StageMapping, type TimeRange, type UploadMediaPayload, type UploadMediaResponse, type Wazzup, type WazzupChannel };
|
|
596
|
+
export { type AiActivationSchedule, type AiCareStatus, type AiConfig, type AiExperiment, type AiExperimentConfig, type AiExperimentCriteria, type AiExperimentVariant, type AiPrompt, type AiResumeMode, type BusinessHours, type CalendarConfig, type Client, type ClientsListResponse, ClientsResource, type ConversationHistoryMessage, type CreateToolEventPayload, type DateException, type DownloadMediaResponse, type GetInternalContextParams, type InternalContextChunk, type InternalContextResult, KnowledgeResource, type KommoAccount, type KommoConfig, type ListConversationsFilters, type LossReasonMapping, type MessagingContact, type MessagingConversation, type MessagingConversationsListResponse, type MessagingMediaItem, type MessagingMessage, type MessagingMessagesListResponse, type MessagingPhoneNumber, MessagingResource, type NudgeSettings, type PatchConversationPayload, type PipelineMapping, PortalApiError, PortalClient, type PortalClientConfig, type SendMessagePayload, type SendMessageResponse, type StageMapping, type TimeRange, type UploadMediaPayload, type UploadMediaResponse, type Wazzup, type WazzupChannel };
|
package/dist/index.js
CHANGED
|
@@ -185,6 +185,42 @@ var MessagingResource = class extends BaseResource {
|
|
|
185
185
|
);
|
|
186
186
|
return response.data;
|
|
187
187
|
}
|
|
188
|
+
/**
|
|
189
|
+
* Registra a execução de uma tool da IA (canal aiatendeWa) como uma mensagem 'internal' na
|
|
190
|
+
* conversa — visível no Portal, nunca despachada pro WhatsApp. Idempotente por `toolCallId`
|
|
191
|
+
* (upsert do lado do Portal — retry seguro).
|
|
192
|
+
*/
|
|
193
|
+
async createToolEvent(clientId, conversationId, payload) {
|
|
194
|
+
const response = await this.client.post(
|
|
195
|
+
`/api/clients/${clientId}/messaging/conversations/${conversationId}/tool-events`,
|
|
196
|
+
payload
|
|
197
|
+
);
|
|
198
|
+
return response.data;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Resolve a conversa local (aiatendeWa) vinculada a um lead da Kommo, se existir — usado pelo
|
|
202
|
+
* KommoServices.setAiStateTag do atende360-assistant pra também atualizar
|
|
203
|
+
* Conversation.aiCareStatus no Portal quando a tag de IA de um lead muda pela Kommo/Wazzup.
|
|
204
|
+
*/
|
|
205
|
+
async getConversationByKommoLead(clientId, kommoLeadId) {
|
|
206
|
+
const response = await this.client.get(
|
|
207
|
+
`/api/clients/${clientId}/messaging/conversations/by-kommo-lead`,
|
|
208
|
+
{ params: { kommoLeadId } }
|
|
209
|
+
);
|
|
210
|
+
return response.data;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Mostra "digitando..." pro lead no WhatsApp (canal aiatendeWa, só Cloud API — sem
|
|
214
|
+
* equivalente no WhatsApp Lite). A Portal já implementa isso ponta a ponta (usado hoje só
|
|
215
|
+
* pelo atendente humano digitando no textarea do Portal); este método só expõe a mesma rota
|
|
216
|
+
* pro atende360-assistant poder chamar antes/durante o processamento da IA.
|
|
217
|
+
*/
|
|
218
|
+
async sendTyping(clientId, conversationId) {
|
|
219
|
+
const response = await this.client.post(
|
|
220
|
+
`/api/clients/${clientId}/messaging/conversations/${conversationId}/typing`
|
|
221
|
+
);
|
|
222
|
+
return response.data;
|
|
223
|
+
}
|
|
188
224
|
/**
|
|
189
225
|
* Baixa os bytes de um anexo (Message.media[].id) — usado hoje só pelo caminho de imagem do
|
|
190
226
|
* canal aiatendeWa (monta uma data URI pra mandar pro modelo de visão, já que a URL não pode
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/client.ts","../src/base.ts","../src/errors.ts","../src/resources/clients/index.ts","../src/resources/knowledge/index.ts","../src/resources/messaging/index.ts"],"sourcesContent":["export * from './client';\nexport * from './types';\nexport * from './errors';\nexport * from './resources/clients';\nexport * from './resources/clients/types';\nexport * from './resources/knowledge';\nexport * from './resources/knowledge/types';\nexport * from './resources/messaging';\nexport * from './resources/messaging/types';\n","import axios, { AxiosInstance } from 'axios';\nimport { PortalClientConfig } from './types';\nimport { ClientsResource } from './resources/clients';\nimport { KnowledgeResource } from './resources/knowledge';\nimport { MessagingResource } from './resources/messaging';\nimport { PortalApiError } from './errors';\n\nconst DEFAULT_BASE_URL = 'https://portal.aiatende.com.br';\n\nexport class PortalClient {\n public config: PortalClientConfig;\n public httpClient: AxiosInstance;\n\n public clients: ClientsResource;\n public knowledge: KnowledgeResource;\n public messaging: MessagingResource;\n\n constructor(config: PortalClientConfig) {\n this.config = config;\n\n this.httpClient = axios.create({\n baseURL: config.baseUrl || DEFAULT_BASE_URL,\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': config.apiKey,\n 'x-internal-token': config.apiKey\n }\n });\n\n this.httpClient.interceptors.response.use(\n (response) => response,\n (error) => {\n if (error.response) {\n const errData = error.response.data;\n throw new PortalApiError(errData?.error || 'Portal API Error', {\n status: error.response.status,\n raw: errData\n });\n }\n throw error;\n }\n );\n\n this.clients = new ClientsResource(this.httpClient);\n this.knowledge = new KnowledgeResource(this.httpClient);\n this.messaging = new MessagingResource(this.httpClient);\n }\n}\n","import { AxiosInstance } from 'axios';\n\nexport abstract class BaseResource {\n protected client: AxiosInstance;\n\n constructor(client: AxiosInstance) {\n this.client = client;\n }\n}\n","export class PortalApiError extends Error {\n status?: number;\n raw?: unknown;\n\n constructor(message: string, info?: Partial<PortalApiError>) {\n super(message);\n this.name = 'PortalApiError';\n\n if (info) {\n this.status = info.status;\n this.raw = info.raw;\n }\n }\n}\n","import { BaseResource } from '../../base';\nimport { PortalApiError } from '../../errors';\nimport { Client, ClientsListResponse } from './types';\n\nexport class ClientsResource extends BaseResource {\n /** Busca o Client dono de um subdomínio Kommo (`GET /api/clients?kommo_subdomain=...`). */\n async getByKommoSubdomain(subdomain: string): Promise<Client | null> {\n const response = await this.client.get<ClientsListResponse>('/api/clients', {\n params: { kommo_subdomain: subdomain, includeConfigs: true }\n });\n return response.data.data[0] ?? null;\n }\n\n /** Busca o Client dono de um canal Wazzup (`GET /api/clients?channelId=...`). */\n async getByWazzupChannelId(channelId: string): Promise<Client | null> {\n const response = await this.client.get<ClientsListResponse>('/api/clients', {\n params: { channelId, includeConfigs: true }\n });\n return response.data.data[0] ?? null;\n }\n\n /**\n * Busca o Client pelo seu id direto (`GET /api/clients/{id}`) — inclui `aiConfig` e\n * `messagingConfig` (sem a API key criptografada). Usado pelo canal aiatendeWa, que resolve o\n * tenant a partir do `clientId` já incluído no payload do relay de webhook, sem precisar de\n * um identificador de canal indireto como Kommo subdomain ou Wazzup channelId.\n */\n async getById(clientId: string): Promise<Client | null> {\n try {\n const response = await this.client.get<Client>(`/api/clients/${clientId}`);\n return response.data;\n } catch (error) {\n if (error instanceof PortalApiError && error.status === 404) return null;\n throw error;\n }\n }\n}\n","import { z } from 'zod';\nimport { BaseResource } from '../../base';\nimport { PortalApiError } from '../../errors';\nimport { GetInternalContextParams, InternalContextResult } from './types';\n\nconst internalContextChunkSchema = z.object({\n chunkId: z.string().optional(),\n content: z.string(),\n similarity: z.number(),\n metadata: z.record(z.string(), z.unknown()).optional(),\n});\n\nconst internalContextResultSchema = z.array(internalContextChunkSchema);\n\nexport class KnowledgeResource extends BaseResource {\n /** Recupera trechos relevantes da base RAG do cliente pra uma pergunta em texto puro (`POST /api/knowledge/internal/context`). O Portal gera o embedding internamente usando a `apiKey` do AiConfig. */\n async getInternalContext(params: GetInternalContextParams): Promise<InternalContextResult> {\n const response = await this.client.post<unknown>('/api/knowledge/internal/context', {\n aiConfigId: params.aiConfigId,\n question: params.question,\n conversationHistory: params.conversationHistory\n });\n\n const parsed = internalContextResultSchema.safeParse(response.data);\n if (!parsed.success) {\n throw new PortalApiError('Resposta inválida de /api/knowledge/internal/context', {\n raw: parsed.error.flatten(),\n });\n }\n return parsed.data;\n }\n}\n","import { BaseResource } from '../../base';\nimport {\n DownloadMediaResponse,\n ListConversationsFilters,\n MessagingConversation,\n MessagingConversationsListResponse,\n MessagingMessagesListResponse,\n PatchConversationPayload,\n SendMessagePayload,\n SendMessageResponse,\n UploadMediaPayload,\n UploadMediaResponse,\n} from './types';\n\n/**\n * Canal WhatsApp direto (aiatendeWa) — proxy pras rotas `/api/clients/{clientId}/messaging/...`\n * do Portal, que por sua vez falam com a plataforma de mensageria própria\n * (plataforma_whatsapp_coexistence). O atende360-assistant nunca fala direto com essa\n * plataforma — sempre por aqui, autenticado via a mesma `apiKey`/APP_API_KEY já usada pro\n * resto do SDK.\n */\nexport class MessagingResource extends BaseResource {\n async listConversations(\n clientId: string,\n filters?: ListConversationsFilters,\n ): Promise<MessagingConversationsListResponse> {\n const response = await this.client.get<MessagingConversationsListResponse>(\n `/api/clients/${clientId}/messaging/conversations`,\n { params: filters },\n );\n return response.data;\n }\n\n async getConversation(clientId: string, conversationId: string): Promise<MessagingConversation> {\n const response = await this.client.get<MessagingConversation>(\n `/api/clients/${clientId}/messaging/conversations/${conversationId}`,\n );\n return response.data;\n }\n\n async patchConversation(\n clientId: string,\n conversationId: string,\n payload: PatchConversationPayload,\n ): Promise<MessagingConversation> {\n const response = await this.client.patch<MessagingConversation>(\n `/api/clients/${clientId}/messaging/conversations/${conversationId}`,\n payload,\n );\n return response.data;\n }\n\n async getMessages(\n clientId: string,\n conversationId: string,\n params?: { page?: number; limit?: number },\n ): Promise<MessagingMessagesListResponse> {\n const response = await this.client.get<MessagingMessagesListResponse>(\n `/api/clients/${clientId}/messaging/conversations/${conversationId}/messages`,\n { params },\n );\n return response.data;\n }\n\n async sendMessage(\n clientId: string,\n conversationId: string,\n payload: SendMessagePayload,\n ): Promise<SendMessageResponse> {\n const response = await this.client.post<SendMessageResponse>(\n `/api/clients/${clientId}/messaging/conversations/${conversationId}/messages`,\n payload,\n );\n return response.data;\n }\n\n /**\n * Upload de mídia (ex.: áudio gerado por TTS) — pré-requisito pra mandar `type: 'audio'` no\n * `sendMessage` (o `content.id` retornado aqui vira `content.id` do envio; `storagePath`\n * precisa ser ecoado em `content._media.storagePath` no send, senão a plataforma não cria o\n * registro de mídia local pra conexões Cloud API).\n */\n async uploadMedia(clientId: string, payload: UploadMediaPayload): Promise<UploadMediaResponse> {\n const form = new FormData();\n // Ordem importa: phoneNumberId/isVoiceNote precisam vir ANTES de file no multipart — o\n // parser do Portal (e o da plataforma por trás dele) lê em stream.\n form.append('phoneNumberId', payload.phoneNumberId);\n if (payload.isVoiceNote) form.append('isVoiceNote', 'true');\n form.append(\n 'file',\n new Blob([new Uint8Array(payload.file)], { type: payload.mimeType }),\n payload.filename,\n );\n\n const response = await this.client.post<UploadMediaResponse>(\n `/api/clients/${clientId}/messaging/media`,\n form,\n // A instância do axios (client.ts) fixa Content-Type: application/json como default —\n // sem isto, esse header explícito vence e nunca deixa o axios detectar o FormData nativo\n // e gerar o multipart/form-data; boundary=... correto. `undefined` remove o default do\n // merge de headers só nesta chamada, liberando a detecção automática.\n { headers: { 'Content-Type': undefined } },\n );\n return response.data;\n }\n\n /**\n * Baixa os bytes de um anexo (Message.media[].id) — usado hoje só pelo caminho de imagem do\n * canal aiatendeWa (monta uma data URI pra mandar pro modelo de visão, já que a URL não pode\n * ser pública). Áudio/PDF seguem passando a URL autenticada direto pros métodos de\n * transcrição/extração do atende360-assistant, sem baixar bytes por aqui.\n */\n async downloadMedia(clientId: string, mediaId: string): Promise<DownloadMediaResponse> {\n const response = await this.client.get<ArrayBuffer>(\n `/api/clients/${clientId}/messaging/media/${mediaId}`,\n { responseType: 'arraybuffer' },\n );\n return {\n data: Buffer.from(response.data),\n contentType: (response.headers['content-type'] as string) || 'application/octet-stream',\n };\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,mBAAqC;;;ACE9B,IAAe,eAAf,MAA4B;AAAA,EACvB;AAAA,EAEV,YAAY,QAAuB;AACjC,SAAK,SAAS;AAAA,EAChB;AACF;;;ACRO,IAAM,iBAAN,cAA6B,MAAM;AAAA,EACxC;AAAA,EACA;AAAA,EAEA,YAAY,SAAiB,MAAgC;AAC3D,UAAM,OAAO;AACb,SAAK,OAAO;AAEZ,QAAI,MAAM;AACR,WAAK,SAAS,KAAK;AACnB,WAAK,MAAM,KAAK;AAAA,IAClB;AAAA,EACF;AACF;;;ACTO,IAAM,kBAAN,cAA8B,aAAa;AAAA;AAAA,EAEhD,MAAM,oBAAoB,WAA2C;AACnE,UAAM,WAAW,MAAM,KAAK,OAAO,IAAyB,gBAAgB;AAAA,MAC1E,QAAQ,EAAE,iBAAiB,WAAW,gBAAgB,KAAK;AAAA,IAC7D,CAAC;AACD,WAAO,SAAS,KAAK,KAAK,CAAC,KAAK;AAAA,EAClC;AAAA;AAAA,EAGA,MAAM,qBAAqB,WAA2C;AACpE,UAAM,WAAW,MAAM,KAAK,OAAO,IAAyB,gBAAgB;AAAA,MAC1E,QAAQ,EAAE,WAAW,gBAAgB,KAAK;AAAA,IAC5C,CAAC;AACD,WAAO,SAAS,KAAK,KAAK,CAAC,KAAK;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,QAAQ,UAA0C;AACtD,QAAI;AACF,YAAM,WAAW,MAAM,KAAK,OAAO,IAAY,gBAAgB,QAAQ,EAAE;AACzE,aAAO,SAAS;AAAA,IAClB,SAAS,OAAO;AACd,UAAI,iBAAiB,kBAAkB,MAAM,WAAW,IAAK,QAAO;AACpE,YAAM;AAAA,IACR;AAAA,EACF;AACF;;;ACpCA,iBAAkB;AAKlB,IAAM,6BAA6B,aAAE,OAAO;AAAA,EAC1C,SAAS,aAAE,OAAO,EAAE,SAAS;AAAA,EAC7B,SAAS,aAAE,OAAO;AAAA,EAClB,YAAY,aAAE,OAAO;AAAA,EACrB,UAAU,aAAE,OAAO,aAAE,OAAO,GAAG,aAAE,QAAQ,CAAC,EAAE,SAAS;AACvD,CAAC;AAED,IAAM,8BAA8B,aAAE,MAAM,0BAA0B;AAE/D,IAAM,oBAAN,cAAgC,aAAa;AAAA;AAAA,EAElD,MAAM,mBAAmB,QAAkE;AACzF,UAAM,WAAW,MAAM,KAAK,OAAO,KAAc,mCAAmC;AAAA,MAClF,YAAY,OAAO;AAAA,MACnB,UAAU,OAAO;AAAA,MACjB,qBAAqB,OAAO;AAAA,IAC9B,CAAC;AAED,UAAM,SAAS,4BAA4B,UAAU,SAAS,IAAI;AAClE,QAAI,CAAC,OAAO,SAAS;AACnB,YAAM,IAAI,eAAe,2DAAwD;AAAA,QAC/E,KAAK,OAAO,MAAM,QAAQ;AAAA,MAC5B,CAAC;AAAA,IACH;AACA,WAAO,OAAO;AAAA,EAChB;AACF;;;ACVO,IAAM,oBAAN,cAAgC,aAAa;AAAA,EAClD,MAAM,kBACJ,UACA,SAC6C;AAC7C,UAAM,WAAW,MAAM,KAAK,OAAO;AAAA,MACjC,gBAAgB,QAAQ;AAAA,MACxB,EAAE,QAAQ,QAAQ;AAAA,IACpB;AACA,WAAO,SAAS;AAAA,EAClB;AAAA,EAEA,MAAM,gBAAgB,UAAkB,gBAAwD;AAC9F,UAAM,WAAW,MAAM,KAAK,OAAO;AAAA,MACjC,gBAAgB,QAAQ,4BAA4B,cAAc;AAAA,IACpE;AACA,WAAO,SAAS;AAAA,EAClB;AAAA,EAEA,MAAM,kBACJ,UACA,gBACA,SACgC;AAChC,UAAM,WAAW,MAAM,KAAK,OAAO;AAAA,MACjC,gBAAgB,QAAQ,4BAA4B,cAAc;AAAA,MAClE;AAAA,IACF;AACA,WAAO,SAAS;AAAA,EAClB;AAAA,EAEA,MAAM,YACJ,UACA,gBACA,QACwC;AACxC,UAAM,WAAW,MAAM,KAAK,OAAO;AAAA,MACjC,gBAAgB,QAAQ,4BAA4B,cAAc;AAAA,MAClE,EAAE,OAAO;AAAA,IACX;AACA,WAAO,SAAS;AAAA,EAClB;AAAA,EAEA,MAAM,YACJ,UACA,gBACA,SAC8B;AAC9B,UAAM,WAAW,MAAM,KAAK,OAAO;AAAA,MACjC,gBAAgB,QAAQ,4BAA4B,cAAc;AAAA,MAClE;AAAA,IACF;AACA,WAAO,SAAS;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,YAAY,UAAkB,SAA2D;AAC7F,UAAM,OAAO,IAAI,SAAS;AAG1B,SAAK,OAAO,iBAAiB,QAAQ,aAAa;AAClD,QAAI,QAAQ,YAAa,MAAK,OAAO,eAAe,MAAM;AAC1D,SAAK;AAAA,MACH;AAAA,MACA,IAAI,KAAK,CAAC,IAAI,WAAW,QAAQ,IAAI,CAAC,GAAG,EAAE,MAAM,QAAQ,SAAS,CAAC;AAAA,MACnE,QAAQ;AAAA,IACV;AAEA,UAAM,WAAW,MAAM,KAAK,OAAO;AAAA,MACjC,gBAAgB,QAAQ;AAAA,MACxB;AAAA;AAAA;AAAA;AAAA;AAAA,MAKA,EAAE,SAAS,EAAE,gBAAgB,OAAU,EAAE;AAAA,IAC3C;AACA,WAAO,SAAS;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,cAAc,UAAkB,SAAiD;AACrF,UAAM,WAAW,MAAM,KAAK,OAAO;AAAA,MACjC,gBAAgB,QAAQ,oBAAoB,OAAO;AAAA,MACnD,EAAE,cAAc,cAAc;AAAA,IAChC;AACA,WAAO;AAAA,MACL,MAAM,OAAO,KAAK,SAAS,IAAI;AAAA,MAC/B,aAAc,SAAS,QAAQ,cAAc,KAAgB;AAAA,IAC/D;AAAA,EACF;AACF;;;ALnHA,IAAM,mBAAmB;AAElB,IAAM,eAAN,MAAmB;AAAA,EACjB;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EAEP,YAAY,QAA4B;AACtC,SAAK,SAAS;AAEd,SAAK,aAAa,aAAAA,QAAM,OAAO;AAAA,MAC7B,SAAS,OAAO,WAAW;AAAA,MAC3B,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,aAAa,OAAO;AAAA,QACpB,oBAAoB,OAAO;AAAA,MAC7B;AAAA,IACF,CAAC;AAED,SAAK,WAAW,aAAa,SAAS;AAAA,MACpC,CAAC,aAAa;AAAA,MACd,CAAC,UAAU;AACT,YAAI,MAAM,UAAU;AAClB,gBAAM,UAAU,MAAM,SAAS;AAC/B,gBAAM,IAAI,eAAe,SAAS,SAAS,oBAAoB;AAAA,YAC7D,QAAQ,MAAM,SAAS;AAAA,YACvB,KAAK;AAAA,UACP,CAAC;AAAA,QACH;AACA,cAAM;AAAA,MACR;AAAA,IACF;AAEA,SAAK,UAAU,IAAI,gBAAgB,KAAK,UAAU;AAClD,SAAK,YAAY,IAAI,kBAAkB,KAAK,UAAU;AACtD,SAAK,YAAY,IAAI,kBAAkB,KAAK,UAAU;AAAA,EACxD;AACF;","names":["axios"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/client.ts","../src/base.ts","../src/errors.ts","../src/resources/clients/index.ts","../src/resources/knowledge/index.ts","../src/resources/messaging/index.ts"],"sourcesContent":["export * from './client';\nexport * from './types';\nexport * from './errors';\nexport * from './resources/clients';\nexport * from './resources/clients/types';\nexport * from './resources/knowledge';\nexport * from './resources/knowledge/types';\nexport * from './resources/messaging';\nexport * from './resources/messaging/types';\n","import axios, { AxiosInstance } from 'axios';\nimport { PortalClientConfig } from './types';\nimport { ClientsResource } from './resources/clients';\nimport { KnowledgeResource } from './resources/knowledge';\nimport { MessagingResource } from './resources/messaging';\nimport { PortalApiError } from './errors';\n\nconst DEFAULT_BASE_URL = 'https://portal.aiatende.com.br';\n\nexport class PortalClient {\n public config: PortalClientConfig;\n public httpClient: AxiosInstance;\n\n public clients: ClientsResource;\n public knowledge: KnowledgeResource;\n public messaging: MessagingResource;\n\n constructor(config: PortalClientConfig) {\n this.config = config;\n\n this.httpClient = axios.create({\n baseURL: config.baseUrl || DEFAULT_BASE_URL,\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': config.apiKey,\n 'x-internal-token': config.apiKey\n }\n });\n\n this.httpClient.interceptors.response.use(\n (response) => response,\n (error) => {\n if (error.response) {\n const errData = error.response.data;\n throw new PortalApiError(errData?.error || 'Portal API Error', {\n status: error.response.status,\n raw: errData\n });\n }\n throw error;\n }\n );\n\n this.clients = new ClientsResource(this.httpClient);\n this.knowledge = new KnowledgeResource(this.httpClient);\n this.messaging = new MessagingResource(this.httpClient);\n }\n}\n","import { AxiosInstance } from 'axios';\n\nexport abstract class BaseResource {\n protected client: AxiosInstance;\n\n constructor(client: AxiosInstance) {\n this.client = client;\n }\n}\n","export class PortalApiError extends Error {\n status?: number;\n raw?: unknown;\n\n constructor(message: string, info?: Partial<PortalApiError>) {\n super(message);\n this.name = 'PortalApiError';\n\n if (info) {\n this.status = info.status;\n this.raw = info.raw;\n }\n }\n}\n","import { BaseResource } from '../../base';\nimport { PortalApiError } from '../../errors';\nimport { Client, ClientsListResponse } from './types';\n\nexport class ClientsResource extends BaseResource {\n /** Busca o Client dono de um subdomínio Kommo (`GET /api/clients?kommo_subdomain=...`). */\n async getByKommoSubdomain(subdomain: string): Promise<Client | null> {\n const response = await this.client.get<ClientsListResponse>('/api/clients', {\n params: { kommo_subdomain: subdomain, includeConfigs: true }\n });\n return response.data.data[0] ?? null;\n }\n\n /** Busca o Client dono de um canal Wazzup (`GET /api/clients?channelId=...`). */\n async getByWazzupChannelId(channelId: string): Promise<Client | null> {\n const response = await this.client.get<ClientsListResponse>('/api/clients', {\n params: { channelId, includeConfigs: true }\n });\n return response.data.data[0] ?? null;\n }\n\n /**\n * Busca o Client pelo seu id direto (`GET /api/clients/{id}`) — inclui `aiConfig` e\n * `messagingConfig` (sem a API key criptografada). Usado pelo canal aiatendeWa, que resolve o\n * tenant a partir do `clientId` já incluído no payload do relay de webhook, sem precisar de\n * um identificador de canal indireto como Kommo subdomain ou Wazzup channelId.\n */\n async getById(clientId: string): Promise<Client | null> {\n try {\n const response = await this.client.get<Client>(`/api/clients/${clientId}`);\n return response.data;\n } catch (error) {\n if (error instanceof PortalApiError && error.status === 404) return null;\n throw error;\n }\n }\n}\n","import { z } from 'zod';\nimport { BaseResource } from '../../base';\nimport { PortalApiError } from '../../errors';\nimport { GetInternalContextParams, InternalContextResult } from './types';\n\nconst internalContextChunkSchema = z.object({\n chunkId: z.string().optional(),\n content: z.string(),\n similarity: z.number(),\n metadata: z.record(z.string(), z.unknown()).optional(),\n});\n\nconst internalContextResultSchema = z.array(internalContextChunkSchema);\n\nexport class KnowledgeResource extends BaseResource {\n /** Recupera trechos relevantes da base RAG do cliente pra uma pergunta em texto puro (`POST /api/knowledge/internal/context`). O Portal gera o embedding internamente usando a `apiKey` do AiConfig. */\n async getInternalContext(params: GetInternalContextParams): Promise<InternalContextResult> {\n const response = await this.client.post<unknown>('/api/knowledge/internal/context', {\n aiConfigId: params.aiConfigId,\n question: params.question,\n conversationHistory: params.conversationHistory\n });\n\n const parsed = internalContextResultSchema.safeParse(response.data);\n if (!parsed.success) {\n throw new PortalApiError('Resposta inválida de /api/knowledge/internal/context', {\n raw: parsed.error.flatten(),\n });\n }\n return parsed.data;\n }\n}\n","import { BaseResource } from '../../base';\nimport {\n CreateToolEventPayload,\n DownloadMediaResponse,\n ListConversationsFilters,\n MessagingConversation,\n MessagingConversationsListResponse,\n MessagingMessagesListResponse,\n PatchConversationPayload,\n SendMessagePayload,\n SendMessageResponse,\n UploadMediaPayload,\n UploadMediaResponse,\n} from './types';\n\n/**\n * Canal WhatsApp direto (aiatendeWa) — proxy pras rotas `/api/clients/{clientId}/messaging/...`\n * do Portal, que por sua vez falam com a plataforma de mensageria própria\n * (plataforma_whatsapp_coexistence). O atende360-assistant nunca fala direto com essa\n * plataforma — sempre por aqui, autenticado via a mesma `apiKey`/APP_API_KEY já usada pro\n * resto do SDK.\n */\nexport class MessagingResource extends BaseResource {\n async listConversations(\n clientId: string,\n filters?: ListConversationsFilters,\n ): Promise<MessagingConversationsListResponse> {\n const response = await this.client.get<MessagingConversationsListResponse>(\n `/api/clients/${clientId}/messaging/conversations`,\n { params: filters },\n );\n return response.data;\n }\n\n async getConversation(clientId: string, conversationId: string): Promise<MessagingConversation> {\n const response = await this.client.get<MessagingConversation>(\n `/api/clients/${clientId}/messaging/conversations/${conversationId}`,\n );\n return response.data;\n }\n\n async patchConversation(\n clientId: string,\n conversationId: string,\n payload: PatchConversationPayload,\n ): Promise<MessagingConversation> {\n const response = await this.client.patch<MessagingConversation>(\n `/api/clients/${clientId}/messaging/conversations/${conversationId}`,\n payload,\n );\n return response.data;\n }\n\n async getMessages(\n clientId: string,\n conversationId: string,\n params?: { page?: number; limit?: number },\n ): Promise<MessagingMessagesListResponse> {\n const response = await this.client.get<MessagingMessagesListResponse>(\n `/api/clients/${clientId}/messaging/conversations/${conversationId}/messages`,\n { params },\n );\n return response.data;\n }\n\n async sendMessage(\n clientId: string,\n conversationId: string,\n payload: SendMessagePayload,\n ): Promise<SendMessageResponse> {\n const response = await this.client.post<SendMessageResponse>(\n `/api/clients/${clientId}/messaging/conversations/${conversationId}/messages`,\n payload,\n );\n return response.data;\n }\n\n /**\n * Upload de mídia (ex.: áudio gerado por TTS) — pré-requisito pra mandar `type: 'audio'` no\n * `sendMessage` (o `content.id` retornado aqui vira `content.id` do envio; `storagePath`\n * precisa ser ecoado em `content._media.storagePath` no send, senão a plataforma não cria o\n * registro de mídia local pra conexões Cloud API).\n */\n async uploadMedia(clientId: string, payload: UploadMediaPayload): Promise<UploadMediaResponse> {\n const form = new FormData();\n // Ordem importa: phoneNumberId/isVoiceNote precisam vir ANTES de file no multipart — o\n // parser do Portal (e o da plataforma por trás dele) lê em stream.\n form.append('phoneNumberId', payload.phoneNumberId);\n if (payload.isVoiceNote) form.append('isVoiceNote', 'true');\n form.append(\n 'file',\n new Blob([new Uint8Array(payload.file)], { type: payload.mimeType }),\n payload.filename,\n );\n\n const response = await this.client.post<UploadMediaResponse>(\n `/api/clients/${clientId}/messaging/media`,\n form,\n // A instância do axios (client.ts) fixa Content-Type: application/json como default —\n // sem isto, esse header explícito vence e nunca deixa o axios detectar o FormData nativo\n // e gerar o multipart/form-data; boundary=... correto. `undefined` remove o default do\n // merge de headers só nesta chamada, liberando a detecção automática.\n { headers: { 'Content-Type': undefined } },\n );\n return response.data;\n }\n\n /**\n * Registra a execução de uma tool da IA (canal aiatendeWa) como uma mensagem 'internal' na\n * conversa — visível no Portal, nunca despachada pro WhatsApp. Idempotente por `toolCallId`\n * (upsert do lado do Portal — retry seguro).\n */\n async createToolEvent(\n clientId: string,\n conversationId: string,\n payload: CreateToolEventPayload,\n ): Promise<{ id: string }> {\n const response = await this.client.post<{ id: string }>(\n `/api/clients/${clientId}/messaging/conversations/${conversationId}/tool-events`,\n payload,\n );\n return response.data;\n }\n\n /**\n * Resolve a conversa local (aiatendeWa) vinculada a um lead da Kommo, se existir — usado pelo\n * KommoServices.setAiStateTag do atende360-assistant pra também atualizar\n * Conversation.aiCareStatus no Portal quando a tag de IA de um lead muda pela Kommo/Wazzup.\n */\n async getConversationByKommoLead(\n clientId: string,\n kommoLeadId: number,\n ): Promise<{ conversationId: string | null }> {\n const response = await this.client.get<{ conversationId: string | null }>(\n `/api/clients/${clientId}/messaging/conversations/by-kommo-lead`,\n { params: { kommoLeadId } },\n );\n return response.data;\n }\n\n /**\n * Mostra \"digitando...\" pro lead no WhatsApp (canal aiatendeWa, só Cloud API — sem\n * equivalente no WhatsApp Lite). A Portal já implementa isso ponta a ponta (usado hoje só\n * pelo atendente humano digitando no textarea do Portal); este método só expõe a mesma rota\n * pro atende360-assistant poder chamar antes/durante o processamento da IA.\n */\n async sendTyping(\n clientId: string,\n conversationId: string,\n ): Promise<{ status: string; reason?: string }> {\n const response = await this.client.post<{ status: string; reason?: string }>(\n `/api/clients/${clientId}/messaging/conversations/${conversationId}/typing`,\n );\n return response.data;\n }\n\n /**\n * Baixa os bytes de um anexo (Message.media[].id) — usado hoje só pelo caminho de imagem do\n * canal aiatendeWa (monta uma data URI pra mandar pro modelo de visão, já que a URL não pode\n * ser pública). Áudio/PDF seguem passando a URL autenticada direto pros métodos de\n * transcrição/extração do atende360-assistant, sem baixar bytes por aqui.\n */\n async downloadMedia(clientId: string, mediaId: string): Promise<DownloadMediaResponse> {\n const response = await this.client.get<ArrayBuffer>(\n `/api/clients/${clientId}/messaging/media/${mediaId}`,\n { responseType: 'arraybuffer' },\n );\n return {\n data: Buffer.from(response.data),\n contentType: (response.headers['content-type'] as string) || 'application/octet-stream',\n };\n }\n}\n"],"mappings":"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","names":["axios"]}
|
package/dist/index.mjs
CHANGED
|
@@ -145,6 +145,42 @@ var MessagingResource = class extends BaseResource {
|
|
|
145
145
|
);
|
|
146
146
|
return response.data;
|
|
147
147
|
}
|
|
148
|
+
/**
|
|
149
|
+
* Registra a execução de uma tool da IA (canal aiatendeWa) como uma mensagem 'internal' na
|
|
150
|
+
* conversa — visível no Portal, nunca despachada pro WhatsApp. Idempotente por `toolCallId`
|
|
151
|
+
* (upsert do lado do Portal — retry seguro).
|
|
152
|
+
*/
|
|
153
|
+
async createToolEvent(clientId, conversationId, payload) {
|
|
154
|
+
const response = await this.client.post(
|
|
155
|
+
`/api/clients/${clientId}/messaging/conversations/${conversationId}/tool-events`,
|
|
156
|
+
payload
|
|
157
|
+
);
|
|
158
|
+
return response.data;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Resolve a conversa local (aiatendeWa) vinculada a um lead da Kommo, se existir — usado pelo
|
|
162
|
+
* KommoServices.setAiStateTag do atende360-assistant pra também atualizar
|
|
163
|
+
* Conversation.aiCareStatus no Portal quando a tag de IA de um lead muda pela Kommo/Wazzup.
|
|
164
|
+
*/
|
|
165
|
+
async getConversationByKommoLead(clientId, kommoLeadId) {
|
|
166
|
+
const response = await this.client.get(
|
|
167
|
+
`/api/clients/${clientId}/messaging/conversations/by-kommo-lead`,
|
|
168
|
+
{ params: { kommoLeadId } }
|
|
169
|
+
);
|
|
170
|
+
return response.data;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Mostra "digitando..." pro lead no WhatsApp (canal aiatendeWa, só Cloud API — sem
|
|
174
|
+
* equivalente no WhatsApp Lite). A Portal já implementa isso ponta a ponta (usado hoje só
|
|
175
|
+
* pelo atendente humano digitando no textarea do Portal); este método só expõe a mesma rota
|
|
176
|
+
* pro atende360-assistant poder chamar antes/durante o processamento da IA.
|
|
177
|
+
*/
|
|
178
|
+
async sendTyping(clientId, conversationId) {
|
|
179
|
+
const response = await this.client.post(
|
|
180
|
+
`/api/clients/${clientId}/messaging/conversations/${conversationId}/typing`
|
|
181
|
+
);
|
|
182
|
+
return response.data;
|
|
183
|
+
}
|
|
148
184
|
/**
|
|
149
185
|
* Baixa os bytes de um anexo (Message.media[].id) — usado hoje só pelo caminho de imagem do
|
|
150
186
|
* canal aiatendeWa (monta uma data URI pra mandar pro modelo de visão, já que a URL não pode
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client.ts","../src/base.ts","../src/errors.ts","../src/resources/clients/index.ts","../src/resources/knowledge/index.ts","../src/resources/messaging/index.ts"],"sourcesContent":["import axios, { AxiosInstance } from 'axios';\nimport { PortalClientConfig } from './types';\nimport { ClientsResource } from './resources/clients';\nimport { KnowledgeResource } from './resources/knowledge';\nimport { MessagingResource } from './resources/messaging';\nimport { PortalApiError } from './errors';\n\nconst DEFAULT_BASE_URL = 'https://portal.aiatende.com.br';\n\nexport class PortalClient {\n public config: PortalClientConfig;\n public httpClient: AxiosInstance;\n\n public clients: ClientsResource;\n public knowledge: KnowledgeResource;\n public messaging: MessagingResource;\n\n constructor(config: PortalClientConfig) {\n this.config = config;\n\n this.httpClient = axios.create({\n baseURL: config.baseUrl || DEFAULT_BASE_URL,\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': config.apiKey,\n 'x-internal-token': config.apiKey\n }\n });\n\n this.httpClient.interceptors.response.use(\n (response) => response,\n (error) => {\n if (error.response) {\n const errData = error.response.data;\n throw new PortalApiError(errData?.error || 'Portal API Error', {\n status: error.response.status,\n raw: errData\n });\n }\n throw error;\n }\n );\n\n this.clients = new ClientsResource(this.httpClient);\n this.knowledge = new KnowledgeResource(this.httpClient);\n this.messaging = new MessagingResource(this.httpClient);\n }\n}\n","import { AxiosInstance } from 'axios';\n\nexport abstract class BaseResource {\n protected client: AxiosInstance;\n\n constructor(client: AxiosInstance) {\n this.client = client;\n }\n}\n","export class PortalApiError extends Error {\n status?: number;\n raw?: unknown;\n\n constructor(message: string, info?: Partial<PortalApiError>) {\n super(message);\n this.name = 'PortalApiError';\n\n if (info) {\n this.status = info.status;\n this.raw = info.raw;\n }\n }\n}\n","import { BaseResource } from '../../base';\nimport { PortalApiError } from '../../errors';\nimport { Client, ClientsListResponse } from './types';\n\nexport class ClientsResource extends BaseResource {\n /** Busca o Client dono de um subdomínio Kommo (`GET /api/clients?kommo_subdomain=...`). */\n async getByKommoSubdomain(subdomain: string): Promise<Client | null> {\n const response = await this.client.get<ClientsListResponse>('/api/clients', {\n params: { kommo_subdomain: subdomain, includeConfigs: true }\n });\n return response.data.data[0] ?? null;\n }\n\n /** Busca o Client dono de um canal Wazzup (`GET /api/clients?channelId=...`). */\n async getByWazzupChannelId(channelId: string): Promise<Client | null> {\n const response = await this.client.get<ClientsListResponse>('/api/clients', {\n params: { channelId, includeConfigs: true }\n });\n return response.data.data[0] ?? null;\n }\n\n /**\n * Busca o Client pelo seu id direto (`GET /api/clients/{id}`) — inclui `aiConfig` e\n * `messagingConfig` (sem a API key criptografada). Usado pelo canal aiatendeWa, que resolve o\n * tenant a partir do `clientId` já incluído no payload do relay de webhook, sem precisar de\n * um identificador de canal indireto como Kommo subdomain ou Wazzup channelId.\n */\n async getById(clientId: string): Promise<Client | null> {\n try {\n const response = await this.client.get<Client>(`/api/clients/${clientId}`);\n return response.data;\n } catch (error) {\n if (error instanceof PortalApiError && error.status === 404) return null;\n throw error;\n }\n }\n}\n","import { z } from 'zod';\nimport { BaseResource } from '../../base';\nimport { PortalApiError } from '../../errors';\nimport { GetInternalContextParams, InternalContextResult } from './types';\n\nconst internalContextChunkSchema = z.object({\n chunkId: z.string().optional(),\n content: z.string(),\n similarity: z.number(),\n metadata: z.record(z.string(), z.unknown()).optional(),\n});\n\nconst internalContextResultSchema = z.array(internalContextChunkSchema);\n\nexport class KnowledgeResource extends BaseResource {\n /** Recupera trechos relevantes da base RAG do cliente pra uma pergunta em texto puro (`POST /api/knowledge/internal/context`). O Portal gera o embedding internamente usando a `apiKey` do AiConfig. */\n async getInternalContext(params: GetInternalContextParams): Promise<InternalContextResult> {\n const response = await this.client.post<unknown>('/api/knowledge/internal/context', {\n aiConfigId: params.aiConfigId,\n question: params.question,\n conversationHistory: params.conversationHistory\n });\n\n const parsed = internalContextResultSchema.safeParse(response.data);\n if (!parsed.success) {\n throw new PortalApiError('Resposta inválida de /api/knowledge/internal/context', {\n raw: parsed.error.flatten(),\n });\n }\n return parsed.data;\n }\n}\n","import { BaseResource } from '../../base';\nimport {\n DownloadMediaResponse,\n ListConversationsFilters,\n MessagingConversation,\n MessagingConversationsListResponse,\n MessagingMessagesListResponse,\n PatchConversationPayload,\n SendMessagePayload,\n SendMessageResponse,\n UploadMediaPayload,\n UploadMediaResponse,\n} from './types';\n\n/**\n * Canal WhatsApp direto (aiatendeWa) — proxy pras rotas `/api/clients/{clientId}/messaging/...`\n * do Portal, que por sua vez falam com a plataforma de mensageria própria\n * (plataforma_whatsapp_coexistence). O atende360-assistant nunca fala direto com essa\n * plataforma — sempre por aqui, autenticado via a mesma `apiKey`/APP_API_KEY já usada pro\n * resto do SDK.\n */\nexport class MessagingResource extends BaseResource {\n async listConversations(\n clientId: string,\n filters?: ListConversationsFilters,\n ): Promise<MessagingConversationsListResponse> {\n const response = await this.client.get<MessagingConversationsListResponse>(\n `/api/clients/${clientId}/messaging/conversations`,\n { params: filters },\n );\n return response.data;\n }\n\n async getConversation(clientId: string, conversationId: string): Promise<MessagingConversation> {\n const response = await this.client.get<MessagingConversation>(\n `/api/clients/${clientId}/messaging/conversations/${conversationId}`,\n );\n return response.data;\n }\n\n async patchConversation(\n clientId: string,\n conversationId: string,\n payload: PatchConversationPayload,\n ): Promise<MessagingConversation> {\n const response = await this.client.patch<MessagingConversation>(\n `/api/clients/${clientId}/messaging/conversations/${conversationId}`,\n payload,\n );\n return response.data;\n }\n\n async getMessages(\n clientId: string,\n conversationId: string,\n params?: { page?: number; limit?: number },\n ): Promise<MessagingMessagesListResponse> {\n const response = await this.client.get<MessagingMessagesListResponse>(\n `/api/clients/${clientId}/messaging/conversations/${conversationId}/messages`,\n { params },\n );\n return response.data;\n }\n\n async sendMessage(\n clientId: string,\n conversationId: string,\n payload: SendMessagePayload,\n ): Promise<SendMessageResponse> {\n const response = await this.client.post<SendMessageResponse>(\n `/api/clients/${clientId}/messaging/conversations/${conversationId}/messages`,\n payload,\n );\n return response.data;\n }\n\n /**\n * Upload de mídia (ex.: áudio gerado por TTS) — pré-requisito pra mandar `type: 'audio'` no\n * `sendMessage` (o `content.id` retornado aqui vira `content.id` do envio; `storagePath`\n * precisa ser ecoado em `content._media.storagePath` no send, senão a plataforma não cria o\n * registro de mídia local pra conexões Cloud API).\n */\n async uploadMedia(clientId: string, payload: UploadMediaPayload): Promise<UploadMediaResponse> {\n const form = new FormData();\n // Ordem importa: phoneNumberId/isVoiceNote precisam vir ANTES de file no multipart — o\n // parser do Portal (e o da plataforma por trás dele) lê em stream.\n form.append('phoneNumberId', payload.phoneNumberId);\n if (payload.isVoiceNote) form.append('isVoiceNote', 'true');\n form.append(\n 'file',\n new Blob([new Uint8Array(payload.file)], { type: payload.mimeType }),\n payload.filename,\n );\n\n const response = await this.client.post<UploadMediaResponse>(\n `/api/clients/${clientId}/messaging/media`,\n form,\n // A instância do axios (client.ts) fixa Content-Type: application/json como default —\n // sem isto, esse header explícito vence e nunca deixa o axios detectar o FormData nativo\n // e gerar o multipart/form-data; boundary=... correto. `undefined` remove o default do\n // merge de headers só nesta chamada, liberando a detecção automática.\n { headers: { 'Content-Type': undefined } },\n );\n return response.data;\n }\n\n /**\n * Baixa os bytes de um anexo (Message.media[].id) — usado hoje só pelo caminho de imagem do\n * canal aiatendeWa (monta uma data URI pra mandar pro modelo de visão, já que a URL não pode\n * ser pública). Áudio/PDF seguem passando a URL autenticada direto pros métodos de\n * transcrição/extração do atende360-assistant, sem baixar bytes por aqui.\n */\n async downloadMedia(clientId: string, mediaId: string): Promise<DownloadMediaResponse> {\n const response = await this.client.get<ArrayBuffer>(\n `/api/clients/${clientId}/messaging/media/${mediaId}`,\n { responseType: 'arraybuffer' },\n );\n return {\n data: Buffer.from(response.data),\n contentType: (response.headers['content-type'] as string) || 'application/octet-stream',\n };\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/client.ts","../src/base.ts","../src/errors.ts","../src/resources/clients/index.ts","../src/resources/knowledge/index.ts","../src/resources/messaging/index.ts"],"sourcesContent":["import axios, { AxiosInstance } from 'axios';\nimport { PortalClientConfig } from './types';\nimport { ClientsResource } from './resources/clients';\nimport { KnowledgeResource } from './resources/knowledge';\nimport { MessagingResource } from './resources/messaging';\nimport { PortalApiError } from './errors';\n\nconst DEFAULT_BASE_URL = 'https://portal.aiatende.com.br';\n\nexport class PortalClient {\n public config: PortalClientConfig;\n public httpClient: AxiosInstance;\n\n public clients: ClientsResource;\n public knowledge: KnowledgeResource;\n public messaging: MessagingResource;\n\n constructor(config: PortalClientConfig) {\n this.config = config;\n\n this.httpClient = axios.create({\n baseURL: config.baseUrl || DEFAULT_BASE_URL,\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': config.apiKey,\n 'x-internal-token': config.apiKey\n }\n });\n\n this.httpClient.interceptors.response.use(\n (response) => response,\n (error) => {\n if (error.response) {\n const errData = error.response.data;\n throw new PortalApiError(errData?.error || 'Portal API Error', {\n status: error.response.status,\n raw: errData\n });\n }\n throw error;\n }\n );\n\n this.clients = new ClientsResource(this.httpClient);\n this.knowledge = new KnowledgeResource(this.httpClient);\n this.messaging = new MessagingResource(this.httpClient);\n }\n}\n","import { AxiosInstance } from 'axios';\n\nexport abstract class BaseResource {\n protected client: AxiosInstance;\n\n constructor(client: AxiosInstance) {\n this.client = client;\n }\n}\n","export class PortalApiError extends Error {\n status?: number;\n raw?: unknown;\n\n constructor(message: string, info?: Partial<PortalApiError>) {\n super(message);\n this.name = 'PortalApiError';\n\n if (info) {\n this.status = info.status;\n this.raw = info.raw;\n }\n }\n}\n","import { BaseResource } from '../../base';\nimport { PortalApiError } from '../../errors';\nimport { Client, ClientsListResponse } from './types';\n\nexport class ClientsResource extends BaseResource {\n /** Busca o Client dono de um subdomínio Kommo (`GET /api/clients?kommo_subdomain=...`). */\n async getByKommoSubdomain(subdomain: string): Promise<Client | null> {\n const response = await this.client.get<ClientsListResponse>('/api/clients', {\n params: { kommo_subdomain: subdomain, includeConfigs: true }\n });\n return response.data.data[0] ?? null;\n }\n\n /** Busca o Client dono de um canal Wazzup (`GET /api/clients?channelId=...`). */\n async getByWazzupChannelId(channelId: string): Promise<Client | null> {\n const response = await this.client.get<ClientsListResponse>('/api/clients', {\n params: { channelId, includeConfigs: true }\n });\n return response.data.data[0] ?? null;\n }\n\n /**\n * Busca o Client pelo seu id direto (`GET /api/clients/{id}`) — inclui `aiConfig` e\n * `messagingConfig` (sem a API key criptografada). Usado pelo canal aiatendeWa, que resolve o\n * tenant a partir do `clientId` já incluído no payload do relay de webhook, sem precisar de\n * um identificador de canal indireto como Kommo subdomain ou Wazzup channelId.\n */\n async getById(clientId: string): Promise<Client | null> {\n try {\n const response = await this.client.get<Client>(`/api/clients/${clientId}`);\n return response.data;\n } catch (error) {\n if (error instanceof PortalApiError && error.status === 404) return null;\n throw error;\n }\n }\n}\n","import { z } from 'zod';\nimport { BaseResource } from '../../base';\nimport { PortalApiError } from '../../errors';\nimport { GetInternalContextParams, InternalContextResult } from './types';\n\nconst internalContextChunkSchema = z.object({\n chunkId: z.string().optional(),\n content: z.string(),\n similarity: z.number(),\n metadata: z.record(z.string(), z.unknown()).optional(),\n});\n\nconst internalContextResultSchema = z.array(internalContextChunkSchema);\n\nexport class KnowledgeResource extends BaseResource {\n /** Recupera trechos relevantes da base RAG do cliente pra uma pergunta em texto puro (`POST /api/knowledge/internal/context`). O Portal gera o embedding internamente usando a `apiKey` do AiConfig. */\n async getInternalContext(params: GetInternalContextParams): Promise<InternalContextResult> {\n const response = await this.client.post<unknown>('/api/knowledge/internal/context', {\n aiConfigId: params.aiConfigId,\n question: params.question,\n conversationHistory: params.conversationHistory\n });\n\n const parsed = internalContextResultSchema.safeParse(response.data);\n if (!parsed.success) {\n throw new PortalApiError('Resposta inválida de /api/knowledge/internal/context', {\n raw: parsed.error.flatten(),\n });\n }\n return parsed.data;\n }\n}\n","import { BaseResource } from '../../base';\nimport {\n CreateToolEventPayload,\n DownloadMediaResponse,\n ListConversationsFilters,\n MessagingConversation,\n MessagingConversationsListResponse,\n MessagingMessagesListResponse,\n PatchConversationPayload,\n SendMessagePayload,\n SendMessageResponse,\n UploadMediaPayload,\n UploadMediaResponse,\n} from './types';\n\n/**\n * Canal WhatsApp direto (aiatendeWa) — proxy pras rotas `/api/clients/{clientId}/messaging/...`\n * do Portal, que por sua vez falam com a plataforma de mensageria própria\n * (plataforma_whatsapp_coexistence). O atende360-assistant nunca fala direto com essa\n * plataforma — sempre por aqui, autenticado via a mesma `apiKey`/APP_API_KEY já usada pro\n * resto do SDK.\n */\nexport class MessagingResource extends BaseResource {\n async listConversations(\n clientId: string,\n filters?: ListConversationsFilters,\n ): Promise<MessagingConversationsListResponse> {\n const response = await this.client.get<MessagingConversationsListResponse>(\n `/api/clients/${clientId}/messaging/conversations`,\n { params: filters },\n );\n return response.data;\n }\n\n async getConversation(clientId: string, conversationId: string): Promise<MessagingConversation> {\n const response = await this.client.get<MessagingConversation>(\n `/api/clients/${clientId}/messaging/conversations/${conversationId}`,\n );\n return response.data;\n }\n\n async patchConversation(\n clientId: string,\n conversationId: string,\n payload: PatchConversationPayload,\n ): Promise<MessagingConversation> {\n const response = await this.client.patch<MessagingConversation>(\n `/api/clients/${clientId}/messaging/conversations/${conversationId}`,\n payload,\n );\n return response.data;\n }\n\n async getMessages(\n clientId: string,\n conversationId: string,\n params?: { page?: number; limit?: number },\n ): Promise<MessagingMessagesListResponse> {\n const response = await this.client.get<MessagingMessagesListResponse>(\n `/api/clients/${clientId}/messaging/conversations/${conversationId}/messages`,\n { params },\n );\n return response.data;\n }\n\n async sendMessage(\n clientId: string,\n conversationId: string,\n payload: SendMessagePayload,\n ): Promise<SendMessageResponse> {\n const response = await this.client.post<SendMessageResponse>(\n `/api/clients/${clientId}/messaging/conversations/${conversationId}/messages`,\n payload,\n );\n return response.data;\n }\n\n /**\n * Upload de mídia (ex.: áudio gerado por TTS) — pré-requisito pra mandar `type: 'audio'` no\n * `sendMessage` (o `content.id` retornado aqui vira `content.id` do envio; `storagePath`\n * precisa ser ecoado em `content._media.storagePath` no send, senão a plataforma não cria o\n * registro de mídia local pra conexões Cloud API).\n */\n async uploadMedia(clientId: string, payload: UploadMediaPayload): Promise<UploadMediaResponse> {\n const form = new FormData();\n // Ordem importa: phoneNumberId/isVoiceNote precisam vir ANTES de file no multipart — o\n // parser do Portal (e o da plataforma por trás dele) lê em stream.\n form.append('phoneNumberId', payload.phoneNumberId);\n if (payload.isVoiceNote) form.append('isVoiceNote', 'true');\n form.append(\n 'file',\n new Blob([new Uint8Array(payload.file)], { type: payload.mimeType }),\n payload.filename,\n );\n\n const response = await this.client.post<UploadMediaResponse>(\n `/api/clients/${clientId}/messaging/media`,\n form,\n // A instância do axios (client.ts) fixa Content-Type: application/json como default —\n // sem isto, esse header explícito vence e nunca deixa o axios detectar o FormData nativo\n // e gerar o multipart/form-data; boundary=... correto. `undefined` remove o default do\n // merge de headers só nesta chamada, liberando a detecção automática.\n { headers: { 'Content-Type': undefined } },\n );\n return response.data;\n }\n\n /**\n * Registra a execução de uma tool da IA (canal aiatendeWa) como uma mensagem 'internal' na\n * conversa — visível no Portal, nunca despachada pro WhatsApp. Idempotente por `toolCallId`\n * (upsert do lado do Portal — retry seguro).\n */\n async createToolEvent(\n clientId: string,\n conversationId: string,\n payload: CreateToolEventPayload,\n ): Promise<{ id: string }> {\n const response = await this.client.post<{ id: string }>(\n `/api/clients/${clientId}/messaging/conversations/${conversationId}/tool-events`,\n payload,\n );\n return response.data;\n }\n\n /**\n * Resolve a conversa local (aiatendeWa) vinculada a um lead da Kommo, se existir — usado pelo\n * KommoServices.setAiStateTag do atende360-assistant pra também atualizar\n * Conversation.aiCareStatus no Portal quando a tag de IA de um lead muda pela Kommo/Wazzup.\n */\n async getConversationByKommoLead(\n clientId: string,\n kommoLeadId: number,\n ): Promise<{ conversationId: string | null }> {\n const response = await this.client.get<{ conversationId: string | null }>(\n `/api/clients/${clientId}/messaging/conversations/by-kommo-lead`,\n { params: { kommoLeadId } },\n );\n return response.data;\n }\n\n /**\n * Mostra \"digitando...\" pro lead no WhatsApp (canal aiatendeWa, só Cloud API — sem\n * equivalente no WhatsApp Lite). A Portal já implementa isso ponta a ponta (usado hoje só\n * pelo atendente humano digitando no textarea do Portal); este método só expõe a mesma rota\n * pro atende360-assistant poder chamar antes/durante o processamento da IA.\n */\n async sendTyping(\n clientId: string,\n conversationId: string,\n ): Promise<{ status: string; reason?: string }> {\n const response = await this.client.post<{ status: string; reason?: string }>(\n `/api/clients/${clientId}/messaging/conversations/${conversationId}/typing`,\n );\n return response.data;\n }\n\n /**\n * Baixa os bytes de um anexo (Message.media[].id) — usado hoje só pelo caminho de imagem do\n * canal aiatendeWa (monta uma data URI pra mandar pro modelo de visão, já que a URL não pode\n * ser pública). Áudio/PDF seguem passando a URL autenticada direto pros métodos de\n * transcrição/extração do atende360-assistant, sem baixar bytes por aqui.\n */\n async downloadMedia(clientId: string, mediaId: string): Promise<DownloadMediaResponse> {\n const response = await this.client.get<ArrayBuffer>(\n `/api/clients/${clientId}/messaging/media/${mediaId}`,\n { responseType: 'arraybuffer' },\n );\n return {\n data: Buffer.from(response.data),\n contentType: (response.headers['content-type'] as string) || 'application/octet-stream',\n };\n }\n}\n"],"mappings":"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","names":[]}
|