plazbot-cli 0.2.26 → 0.3.2
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/CLAUDE.md +34 -5
- package/README.md +21 -0
- package/dist/cli.js +32 -20
- package/dist/commands/agent/ai-config.js +98 -50
- package/dist/commands/agent/chat.js +80 -74
- package/dist/commands/agent/copy.js +23 -21
- package/dist/commands/agent/create.js +42 -72
- package/dist/commands/agent/delete.js +29 -30
- package/dist/commands/agent/enable-widget.js +30 -26
- package/dist/commands/agent/export.js +90 -77
- package/dist/commands/agent/files.js +68 -60
- package/dist/commands/agent/get.js +101 -87
- package/dist/commands/agent/index.js +53 -39
- package/dist/commands/agent/list.js +26 -24
- package/dist/commands/agent/monitor.js +91 -86
- package/dist/commands/agent/on-message.js +40 -37
- package/dist/commands/agent/set.js +62 -59
- package/dist/commands/agent/templates.js +109 -108
- package/dist/commands/agent/tools.js +64 -65
- package/dist/commands/agent/update.js +28 -27
- package/dist/commands/agent/validate.js +127 -0
- package/dist/commands/agent/wizard.js +152 -159
- package/dist/commands/auth/index.js +7 -10
- package/dist/commands/auth/login.js +50 -37
- package/dist/commands/auth/logout.js +16 -14
- package/dist/commands/auth/status.js +19 -16
- package/dist/commands/portal/add-agent.js +26 -24
- package/dist/commands/portal/add-link.js +21 -17
- package/dist/commands/portal/clear-links.js +17 -15
- package/dist/commands/portal/create.js +25 -21
- package/dist/commands/portal/delete.js +31 -30
- package/dist/commands/portal/get.js +33 -31
- package/dist/commands/portal/index.js +30 -22
- package/dist/commands/portal/list.js +34 -30
- package/dist/commands/portal/update.js +41 -33
- package/dist/commands/whatsapp/broadcast.js +40 -37
- package/dist/commands/whatsapp/channels.js +40 -34
- package/dist/commands/whatsapp/chat.js +33 -32
- package/dist/commands/whatsapp/connect.js +53 -52
- package/dist/commands/whatsapp/delete-webhook.js +19 -17
- package/dist/commands/whatsapp/index.js +35 -25
- package/dist/commands/whatsapp/register-webhook.js +21 -19
- package/dist/commands/whatsapp/send-template.js +39 -31
- package/dist/commands/whatsapp/send.js +27 -23
- package/dist/commands/whatsapp/widget.js +35 -31
- package/dist/commands/workers/deploy.js +49 -44
- package/dist/commands/workers/index.js +28 -18
- package/dist/commands/workers/list.js +43 -35
- package/dist/commands/workers/logs.js +38 -32
- package/dist/commands/workers/remove.js +38 -37
- package/dist/commands/workers/secret.js +63 -58
- package/dist/commands/workers/test.js +44 -36
- package/dist/schemas/agent.config.schema.json +569 -0
- package/dist/studio/api/sseClient.js +97 -0
- package/dist/studio/api/studioApi.js +25 -0
- package/dist/studio/api/types.js +16 -0
- package/dist/studio/components/AgentPanel.js +35 -0
- package/dist/studio/components/App.js +214 -0
- package/dist/studio/components/ChatLog.js +59 -0
- package/dist/studio/components/Footer.js +11 -0
- package/dist/studio/components/Header.js +8 -0
- package/dist/studio/components/Input.js +15 -0
- package/dist/studio/components/Message.js +56 -0
- package/dist/studio/components/Suggestions.js +11 -0
- package/dist/studio/components/ToolCall.js +33 -0
- package/dist/studio/components/WhatsappConnectCard.js +57 -0
- package/dist/studio/index.js +42 -0
- package/dist/studio/render/json.js +16 -0
- package/dist/studio/render/markdown.js +86 -0
- package/dist/studio/render/steps.js +58 -0
- package/dist/studio/runOneShot.js +96 -0
- package/dist/studio/runRepl.js +52 -0
- package/dist/studio/slash/handlers.js +199 -0
- package/dist/studio/slash/parser.js +46 -0
- package/dist/studio/slash/registry.js +16 -0
- package/dist/studio/state/store.js +181 -0
- package/dist/studio/whatsapp/api.js +63 -0
- package/dist/studio/whatsapp/polling.js +77 -0
- package/dist/studio/whatsapp/types.js +31 -0
- package/dist/types/agent.js +1 -2
- package/dist/types/auth.js +1 -2
- package/dist/types/common.js +1 -2
- package/dist/types/message.js +1 -2
- package/dist/types/portal.js +1 -2
- package/dist/types/workers.js +1 -2
- package/dist/utils/agent-errors.js +46 -0
- package/dist/utils/api.js +8 -9
- package/dist/utils/banner.js +33 -34
- package/dist/utils/credentials.js +12 -20
- package/dist/utils/help.js +44 -0
- package/dist/utils/logger.js +13 -19
- package/dist/utils/ui.js +35 -49
- package/package.json +22 -10
- package/src/cli.ts +24 -8
- package/src/commands/agent/ai-config.ts +89 -34
- package/src/commands/agent/chat.ts +49 -37
- package/src/commands/agent/copy.ts +19 -13
- package/src/commands/agent/create.ts +32 -22
- package/src/commands/agent/delete.ts +24 -18
- package/src/commands/agent/enable-widget.ts +31 -23
- package/src/commands/agent/export.ts +72 -51
- package/src/commands/agent/files.ts +51 -39
- package/src/commands/agent/get.ts +86 -66
- package/src/commands/agent/index.ts +36 -18
- package/src/commands/agent/list.ts +22 -16
- package/src/commands/agent/monitor.ts +67 -56
- package/src/commands/agent/on-message.ts +36 -27
- package/src/commands/agent/set.ts +47 -37
- package/src/commands/agent/templates.ts +90 -82
- package/src/commands/agent/tools.ts +53 -47
- package/src/commands/agent/update.ts +28 -20
- package/src/commands/agent/validate.ts +135 -0
- package/src/commands/agent/wizard.ts +114 -114
- package/src/commands/auth/index.ts +3 -3
- package/src/commands/auth/login.ts +44 -29
- package/src/commands/auth/logout.ts +16 -10
- package/src/commands/auth/status.ts +14 -8
- package/src/commands/portal/add-agent.ts +23 -17
- package/src/commands/portal/add-link.ts +17 -9
- package/src/commands/portal/clear-links.ts +13 -7
- package/src/commands/portal/create.ts +20 -12
- package/src/commands/portal/delete.ts +28 -20
- package/src/commands/portal/get.ts +25 -19
- package/src/commands/portal/index.ts +22 -10
- package/src/commands/portal/list.ts +27 -19
- package/src/commands/portal/update.ts +38 -26
- package/src/commands/whatsapp/broadcast.ts +28 -18
- package/src/commands/whatsapp/channels.ts +31 -20
- package/src/commands/whatsapp/chat.ts +20 -12
- package/src/commands/whatsapp/connect.ts +39 -31
- package/src/commands/whatsapp/delete-webhook.ts +15 -9
- package/src/commands/whatsapp/index.ts +24 -10
- package/src/commands/whatsapp/register-webhook.ts +16 -10
- package/src/commands/whatsapp/send-template.ts +33 -21
- package/src/commands/whatsapp/send.ts +23 -15
- package/src/commands/whatsapp/widget.ts +25 -17
- package/src/commands/workers/deploy.ts +34 -22
- package/src/commands/workers/index.ts +21 -7
- package/src/commands/workers/list.ts +31 -19
- package/src/commands/workers/logs.ts +30 -20
- package/src/commands/workers/remove.ts +30 -22
- package/src/commands/workers/secret.ts +46 -34
- package/src/commands/workers/test.ts +34 -22
- package/src/schemas/agent.config.schema.json +569 -0
- package/src/studio/api/sseClient.ts +91 -0
- package/src/studio/api/studioApi.ts +27 -0
- package/src/studio/api/types.ts +96 -0
- package/src/studio/components/App.tsx +266 -0
- package/src/studio/components/ChatLog.tsx +95 -0
- package/src/studio/components/Footer.tsx +38 -0
- package/src/studio/components/Header.tsx +39 -0
- package/src/studio/components/Input.tsx +32 -0
- package/src/studio/components/Message.tsx +87 -0
- package/src/studio/components/Suggestions.tsx +26 -0
- package/src/studio/components/ToolCall.tsx +58 -0
- package/src/studio/components/WhatsappConnectCard.tsx +139 -0
- package/src/studio/index.ts +58 -0
- package/src/studio/render/markdown.ts +93 -0
- package/src/studio/render/steps.ts +57 -0
- package/src/studio/runOneShot.ts +114 -0
- package/src/studio/runRepl.tsx +76 -0
- package/src/studio/slash/handlers.ts +226 -0
- package/src/studio/slash/parser.ts +41 -0
- package/src/studio/slash/registry.ts +54 -0
- package/src/studio/state/store.ts +273 -0
- package/src/studio/whatsapp/api.ts +96 -0
- package/src/studio/whatsapp/polling.ts +93 -0
- package/src/studio/whatsapp/types.ts +80 -0
- package/src/types/agent.ts +1 -1
- package/src/types/auth.ts +4 -3
- package/src/types/portal.ts +1 -1
- package/src/types/workers.ts +1 -1
- package/src/utils/agent-errors.ts +67 -0
- package/src/utils/api.ts +6 -0
- package/src/utils/banner.ts +14 -9
- package/src/utils/credentials.ts +6 -5
- package/src/utils/help.ts +51 -0
- package/tsconfig.json +9 -6
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { create } from 'zustand';
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
3
|
+
function uid() {
|
|
4
|
+
// randomUUID está disponible en Node >=14.17 con crypto. Fallback simple.
|
|
5
|
+
try {
|
|
6
|
+
return randomUUID();
|
|
7
|
+
}
|
|
8
|
+
catch {
|
|
9
|
+
return Math.random().toString(36).slice(2);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export const useStudioStore = create((set, get) => ({
|
|
13
|
+
messages: [],
|
|
14
|
+
steps: [],
|
|
15
|
+
agentConfig: null,
|
|
16
|
+
agentId: null,
|
|
17
|
+
usage: { inputTokens: 0, outputTokens: 0 },
|
|
18
|
+
streaming: false,
|
|
19
|
+
abortController: null,
|
|
20
|
+
waConnect: null,
|
|
21
|
+
pendingCompact: false,
|
|
22
|
+
pushUserMessage(content) {
|
|
23
|
+
const id = uid();
|
|
24
|
+
set((s) => ({ messages: [...s.messages, { id, role: 'user', content }] }));
|
|
25
|
+
return id;
|
|
26
|
+
},
|
|
27
|
+
pushSyntheticAssistant(content, opts) {
|
|
28
|
+
const id = uid();
|
|
29
|
+
set((s) => ({
|
|
30
|
+
messages: [...s.messages, { id, role: 'assistant', content, synthetic: true, error: opts?.error }],
|
|
31
|
+
}));
|
|
32
|
+
return id;
|
|
33
|
+
},
|
|
34
|
+
startAssistantMessage() {
|
|
35
|
+
const id = uid();
|
|
36
|
+
set((s) => ({ messages: [...s.messages, { id, role: 'assistant', content: '', streaming: true }] }));
|
|
37
|
+
return id;
|
|
38
|
+
},
|
|
39
|
+
appendAssistantText(id, delta) {
|
|
40
|
+
set((s) => ({
|
|
41
|
+
messages: s.messages.map((m) => (m.id === id ? { ...m, content: m.content + delta } : m)),
|
|
42
|
+
}));
|
|
43
|
+
},
|
|
44
|
+
finishAssistantMessage(id) {
|
|
45
|
+
set((s) => ({
|
|
46
|
+
messages: s.messages.map((m) => (m.id === id ? { ...m, streaming: false } : m)),
|
|
47
|
+
}));
|
|
48
|
+
},
|
|
49
|
+
startStep(toolName, afterMessageId) {
|
|
50
|
+
const id = uid();
|
|
51
|
+
set((s) => ({ steps: [...s.steps, { id, toolName, status: 'running', afterMessageId }] }));
|
|
52
|
+
return id;
|
|
53
|
+
},
|
|
54
|
+
resolveLastStep(toolName, status, result, errorMessage) {
|
|
55
|
+
set((s) => {
|
|
56
|
+
const idx = [...s.steps].reverse().findIndex((x) => x.toolName === toolName && x.status === 'running');
|
|
57
|
+
if (idx === -1)
|
|
58
|
+
return {};
|
|
59
|
+
const real = s.steps.length - 1 - idx;
|
|
60
|
+
const updated = [...s.steps];
|
|
61
|
+
updated[real] = { ...updated[real], status, result, errorMessage };
|
|
62
|
+
return { steps: updated };
|
|
63
|
+
});
|
|
64
|
+
},
|
|
65
|
+
failAllRunningSteps(message) {
|
|
66
|
+
set((s) => ({
|
|
67
|
+
steps: s.steps.map((x) => x.status === 'running' ? { ...x, status: 'error', errorMessage: message ?? 'Cancelado' } : x),
|
|
68
|
+
}));
|
|
69
|
+
},
|
|
70
|
+
setAgentConfig(cfg) {
|
|
71
|
+
set({ agentConfig: cfg });
|
|
72
|
+
},
|
|
73
|
+
setAgentId(id) {
|
|
74
|
+
set({ agentId: id });
|
|
75
|
+
},
|
|
76
|
+
addUsage(inT, outT) {
|
|
77
|
+
set((s) => ({
|
|
78
|
+
usage: {
|
|
79
|
+
inputTokens: s.usage.inputTokens + (inT || 0),
|
|
80
|
+
outputTokens: s.usage.outputTokens + (outT || 0),
|
|
81
|
+
},
|
|
82
|
+
}));
|
|
83
|
+
},
|
|
84
|
+
setStreaming(streaming) {
|
|
85
|
+
set({ streaming });
|
|
86
|
+
},
|
|
87
|
+
setAbortController(abortController) {
|
|
88
|
+
set({ abortController });
|
|
89
|
+
},
|
|
90
|
+
abortStream() {
|
|
91
|
+
const ctrl = get().abortController;
|
|
92
|
+
if (ctrl && !ctrl.signal.aborted)
|
|
93
|
+
ctrl.abort();
|
|
94
|
+
},
|
|
95
|
+
startWaConnect(linkData, anchorMessageId) {
|
|
96
|
+
const id = uid();
|
|
97
|
+
set({
|
|
98
|
+
waConnect: {
|
|
99
|
+
id,
|
|
100
|
+
linkData,
|
|
101
|
+
status: 'waiting',
|
|
102
|
+
anchorMessageId,
|
|
103
|
+
attempt: 0,
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
return id;
|
|
107
|
+
},
|
|
108
|
+
updateWaConnect(patch) {
|
|
109
|
+
set((s) => (s.waConnect ? { waConnect: { ...s.waConnect, ...patch } } : {}));
|
|
110
|
+
},
|
|
111
|
+
setWaStatus(status, extra) {
|
|
112
|
+
set((s) => (s.waConnect ? { waConnect: { ...s.waConnect, status, ...(extra ?? {}) } } : {}));
|
|
113
|
+
},
|
|
114
|
+
clearWaConnect() {
|
|
115
|
+
set({ waConnect: null });
|
|
116
|
+
},
|
|
117
|
+
clearMessages() {
|
|
118
|
+
const ctrl = get().abortController;
|
|
119
|
+
if (ctrl && !ctrl.signal.aborted)
|
|
120
|
+
ctrl.abort();
|
|
121
|
+
set({
|
|
122
|
+
messages: [],
|
|
123
|
+
steps: [],
|
|
124
|
+
streaming: false,
|
|
125
|
+
abortController: null,
|
|
126
|
+
waConnect: null,
|
|
127
|
+
pendingCompact: false,
|
|
128
|
+
// agentConfig, agentId, usage se mantienen para seguir trabajando.
|
|
129
|
+
});
|
|
130
|
+
},
|
|
131
|
+
setPendingCompact(v) {
|
|
132
|
+
set({ pendingCompact: v });
|
|
133
|
+
},
|
|
134
|
+
compactInto(id) {
|
|
135
|
+
set((s) => {
|
|
136
|
+
const summary = s.messages.find((m) => m.id === id);
|
|
137
|
+
if (!summary || !summary.content.trim()) {
|
|
138
|
+
return { pendingCompact: false };
|
|
139
|
+
}
|
|
140
|
+
const newId = uid();
|
|
141
|
+
return {
|
|
142
|
+
messages: [
|
|
143
|
+
{
|
|
144
|
+
id: newId,
|
|
145
|
+
role: 'assistant',
|
|
146
|
+
content: summary.content,
|
|
147
|
+
},
|
|
148
|
+
],
|
|
149
|
+
steps: [],
|
|
150
|
+
waConnect: null,
|
|
151
|
+
pendingCompact: false,
|
|
152
|
+
};
|
|
153
|
+
});
|
|
154
|
+
},
|
|
155
|
+
reset() {
|
|
156
|
+
const ctrl = get().abortController;
|
|
157
|
+
if (ctrl && !ctrl.signal.aborted)
|
|
158
|
+
ctrl.abort();
|
|
159
|
+
set({
|
|
160
|
+
messages: [],
|
|
161
|
+
steps: [],
|
|
162
|
+
agentConfig: null,
|
|
163
|
+
agentId: null,
|
|
164
|
+
usage: { inputTokens: 0, outputTokens: 0 },
|
|
165
|
+
streaming: false,
|
|
166
|
+
abortController: null,
|
|
167
|
+
waConnect: null,
|
|
168
|
+
pendingCompact: false,
|
|
169
|
+
});
|
|
170
|
+
},
|
|
171
|
+
}));
|
|
172
|
+
/**
|
|
173
|
+
* Construye el array `messages` que se envía al backend a partir del estado.
|
|
174
|
+
* Filtra mensajes sintéticos (synthetic === true) y los aún en streaming vacíos.
|
|
175
|
+
*/
|
|
176
|
+
export function selectBackendMessages() {
|
|
177
|
+
const { messages } = useStudioStore.getState();
|
|
178
|
+
return messages
|
|
179
|
+
.filter((m) => !m.synthetic && (m.role === 'user' || m.role === 'assistant') && m.content.trim().length > 0)
|
|
180
|
+
.map((m) => ({ role: m.role, content: m.content }));
|
|
181
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { getBaseUrl } from '../api/studioApi.js';
|
|
2
|
+
function headers(opts) {
|
|
3
|
+
const h = {
|
|
4
|
+
'Content-Type': 'application/json',
|
|
5
|
+
Authorization: `Bearer ${opts.apiKey}`,
|
|
6
|
+
'x-workspace-id': opts.workspaceId,
|
|
7
|
+
};
|
|
8
|
+
if (opts.userId)
|
|
9
|
+
h['x-user-id'] = opts.userId;
|
|
10
|
+
return h;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* GET /api/workspace/{workspaceId} — the API returns an array of workspaces
|
|
14
|
+
* (the front uses `[0]`). Returns the first element or null.
|
|
15
|
+
*/
|
|
16
|
+
export async function getWorkspaceById(opts, signal) {
|
|
17
|
+
const url = `${getBaseUrl(opts.zone, opts.dev)}/api/workspace/${encodeURIComponent(opts.workspaceId)}`;
|
|
18
|
+
const res = await fetch(url, { method: 'GET', headers: headers(opts), signal });
|
|
19
|
+
if (!res.ok) {
|
|
20
|
+
throw new Error(`GET workspace ${res.status} ${res.statusText}`);
|
|
21
|
+
}
|
|
22
|
+
const json = (await res.json());
|
|
23
|
+
if (!json)
|
|
24
|
+
return null;
|
|
25
|
+
if (Array.isArray(json))
|
|
26
|
+
return json[0] ?? null;
|
|
27
|
+
return json;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* POST /api/workspace/{workspaceId}/integrations/{integrationId}/activate
|
|
31
|
+
* Body: { isActive: true, platformCode: "whatsapp" }
|
|
32
|
+
*/
|
|
33
|
+
export async function activateIntegration(opts, integrationId, signal) {
|
|
34
|
+
const url = `${getBaseUrl(opts.zone, opts.dev)}/api/workspace/${encodeURIComponent(opts.workspaceId)}/integrations/${encodeURIComponent(integrationId)}/activate`;
|
|
35
|
+
const res = await fetch(url, {
|
|
36
|
+
method: 'POST',
|
|
37
|
+
headers: headers(opts),
|
|
38
|
+
body: JSON.stringify({ isActive: true, platformCode: 'whatsapp' }),
|
|
39
|
+
signal,
|
|
40
|
+
});
|
|
41
|
+
const text = await res.text();
|
|
42
|
+
let parsed = {};
|
|
43
|
+
if (text) {
|
|
44
|
+
try {
|
|
45
|
+
parsed = JSON.parse(text);
|
|
46
|
+
}
|
|
47
|
+
catch { /* not json */ }
|
|
48
|
+
}
|
|
49
|
+
if (!res.ok) {
|
|
50
|
+
return { success: false, message: parsed.message || `HTTP ${res.status} ${res.statusText}` };
|
|
51
|
+
}
|
|
52
|
+
return parsed.success === false ? parsed : { success: true, ...parsed };
|
|
53
|
+
}
|
|
54
|
+
/** Adapter: derives the workspace fetch options from a StudioStreamOptions. */
|
|
55
|
+
export function workspaceOptsFromStream(stream) {
|
|
56
|
+
return {
|
|
57
|
+
apiKey: stream.apiKey,
|
|
58
|
+
workspaceId: stream.workspaceId,
|
|
59
|
+
zone: stream.zone,
|
|
60
|
+
dev: stream.dev,
|
|
61
|
+
userId: stream.userId,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { activateIntegration, getWorkspaceById } from './api.js';
|
|
2
|
+
import { findNewWhatsappIntegration, WA_POLL_INTERVAL_MS, WA_POLL_MAX_ATTEMPTS, } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Starts the polling loop. Returns a handle to cancel it. The loop:
|
|
5
|
+
* 1. GETs the workspace every WA_POLL_INTERVAL_MS up to WA_POLL_MAX_ATTEMPTS.
|
|
6
|
+
* 2. Looks for a whatsapp integration whose id isn't in `existingIntegrationIds`.
|
|
7
|
+
* 3. When found, calls POST .../activate and reports the result.
|
|
8
|
+
*/
|
|
9
|
+
export function startWhatsappPolling(opts, linkData, cb) {
|
|
10
|
+
const knownIds = new Set(linkData.existingIntegrationIds ?? []);
|
|
11
|
+
const controller = new AbortController();
|
|
12
|
+
let attempts = 0;
|
|
13
|
+
let busy = false;
|
|
14
|
+
let stopped = false;
|
|
15
|
+
let timer = null;
|
|
16
|
+
const stop = () => {
|
|
17
|
+
stopped = true;
|
|
18
|
+
if (timer) {
|
|
19
|
+
clearInterval(timer);
|
|
20
|
+
timer = null;
|
|
21
|
+
}
|
|
22
|
+
controller.abort();
|
|
23
|
+
};
|
|
24
|
+
const tick = async () => {
|
|
25
|
+
if (busy || stopped)
|
|
26
|
+
return;
|
|
27
|
+
busy = true;
|
|
28
|
+
attempts++;
|
|
29
|
+
cb.onAttempt(attempts);
|
|
30
|
+
try {
|
|
31
|
+
const ws = await getWorkspaceById(opts, controller.signal);
|
|
32
|
+
const newIntegration = findNewWhatsappIntegration(ws?.integrations, knownIds);
|
|
33
|
+
if (newIntegration?.id) {
|
|
34
|
+
const number = newIntegration.cellphoneNumberFormat ||
|
|
35
|
+
newIntegration.cellphoneNumber ||
|
|
36
|
+
'';
|
|
37
|
+
cb.onActivating(newIntegration.id, number);
|
|
38
|
+
if (timer) {
|
|
39
|
+
clearInterval(timer);
|
|
40
|
+
timer = null;
|
|
41
|
+
}
|
|
42
|
+
const res = await activateIntegration(opts, newIntegration.id, controller.signal);
|
|
43
|
+
if (res.success !== false) {
|
|
44
|
+
cb.onConnected(number);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
cb.onError(res.message || 'Could not activate the integration.');
|
|
48
|
+
}
|
|
49
|
+
stopped = true;
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
if (err.name === 'AbortError')
|
|
55
|
+
return;
|
|
56
|
+
// Transient network errors: swallow and keep polling.
|
|
57
|
+
}
|
|
58
|
+
finally {
|
|
59
|
+
busy = false;
|
|
60
|
+
}
|
|
61
|
+
if (attempts >= WA_POLL_MAX_ATTEMPTS) {
|
|
62
|
+
if (timer) {
|
|
63
|
+
clearInterval(timer);
|
|
64
|
+
timer = null;
|
|
65
|
+
}
|
|
66
|
+
stopped = true;
|
|
67
|
+
cb.onTimeout();
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
timer = setInterval(() => { void tick(); }, WA_POLL_INTERVAL_MS);
|
|
71
|
+
// Trigger the first attempt immediately so the user sees activity right away.
|
|
72
|
+
void tick();
|
|
73
|
+
return {
|
|
74
|
+
cancel() { stop(); },
|
|
75
|
+
get stopped() { return stopped; },
|
|
76
|
+
};
|
|
77
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types and helpers for the "connect WhatsApp from Agent Studio" flow.
|
|
3
|
+
*
|
|
4
|
+
* The backend tool `connect_whatsapp` emits a `tool_result` chunk with
|
|
5
|
+
* `type: "whatsapp_link"`. When `data.shortUrl` is present we render an
|
|
6
|
+
* inline card with the onboarding link and start polling the workspace
|
|
7
|
+
* until the new integration appears. Baseline of "known" ids comes from
|
|
8
|
+
* the server (`existingIntegrationIds`) to avoid race conditions.
|
|
9
|
+
*
|
|
10
|
+
* Mirror of /Plazbot-Front-v2.0/src/modules/agentStudio/whatsappConnect.ts
|
|
11
|
+
*/
|
|
12
|
+
export const WA_POLL_INTERVAL_MS = 5000;
|
|
13
|
+
export const WA_POLL_MAX_ATTEMPTS = 60; // 5 minutes
|
|
14
|
+
/**
|
|
15
|
+
* Returns a new whatsapp integration whose id is NOT in the baseline.
|
|
16
|
+
* If multiple are new, picks the most recently created one.
|
|
17
|
+
*/
|
|
18
|
+
export function findNewWhatsappIntegration(integrations, knownIds) {
|
|
19
|
+
if (!integrations?.length)
|
|
20
|
+
return null;
|
|
21
|
+
const candidates = integrations.filter((i) => (i.type === 'whatsapp' || i.type === 'whatsapp_business') &&
|
|
22
|
+
!!i.id &&
|
|
23
|
+
!knownIds.has(i.id));
|
|
24
|
+
if (!candidates.length)
|
|
25
|
+
return null;
|
|
26
|
+
return candidates.reduce((latest, current) => {
|
|
27
|
+
const a = latest.creationDate ? new Date(latest.creationDate).getTime() : 0;
|
|
28
|
+
const b = current.creationDate ? new Date(current.creationDate).getTime() : 0;
|
|
29
|
+
return b > a ? current : latest;
|
|
30
|
+
});
|
|
31
|
+
}
|
package/dist/types/agent.js
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1
|
+
export {};
|
package/dist/types/auth.js
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1
|
+
export {};
|
package/dist/types/common.js
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1
|
+
export {};
|
package/dist/types/message.js
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1
|
+
export {};
|
package/dist/types/portal.js
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1
|
+
export {};
|
package/dist/types/workers.js
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
/**
|
|
3
|
+
* Resuelve un error proveniente del SDK/axios al cargar un agente y devuelve
|
|
4
|
+
* un mensaje legible que oriente al usuario sobre la causa real (404 = ID o
|
|
5
|
+
* workspace/zona incorrectos, 401 = token expirado, etc.).
|
|
6
|
+
*/
|
|
7
|
+
export function describeAgentLoadError(error, agentId, credentials, context = {}) {
|
|
8
|
+
const err = error;
|
|
9
|
+
const status = err?.response?.status;
|
|
10
|
+
const workspace = context.workspaceOverride || credentials.workspace;
|
|
11
|
+
const zone = context.zoneOverride || credentials.zone;
|
|
12
|
+
const envHint = context.dev ? ' (env: --dev / localhost:5090)' : '';
|
|
13
|
+
if (status === 404) {
|
|
14
|
+
return [
|
|
15
|
+
`Agent "${agentId}" was not found in workspace ${workspace} (zone ${zone})${envHint}.`,
|
|
16
|
+
chalk.gray(' Verify the ID and active workspace:'),
|
|
17
|
+
chalk.gray(' plazbot status'),
|
|
18
|
+
chalk.gray(' plazbot agent list'),
|
|
19
|
+
chalk.gray(' If the agent lives in a different zone, re-init with the right one:'),
|
|
20
|
+
chalk.gray(' plazbot init -z LA # or EU'),
|
|
21
|
+
].join('\n');
|
|
22
|
+
}
|
|
23
|
+
if (status === 401) {
|
|
24
|
+
return [
|
|
25
|
+
'Authentication token rejected (401).',
|
|
26
|
+
chalk.gray(' Re-run: plazbot init -k <jwt> -w <workspace> -z LA|EU'),
|
|
27
|
+
].join('\n');
|
|
28
|
+
}
|
|
29
|
+
if (status === 403) {
|
|
30
|
+
return [
|
|
31
|
+
`No permissions to access agent "${agentId}" in workspace ${workspace}.`,
|
|
32
|
+
chalk.gray(' Confirm your user has access to that workspace.'),
|
|
33
|
+
].join('\n');
|
|
34
|
+
}
|
|
35
|
+
if (status === 429) {
|
|
36
|
+
return 'Rate limit reached. Try again in a few seconds.';
|
|
37
|
+
}
|
|
38
|
+
if (typeof status === 'number' && status >= 500) {
|
|
39
|
+
return [
|
|
40
|
+
`Backend error (HTTP ${status})${envHint}.`,
|
|
41
|
+
chalk.gray(` ${err.response?.data?.message || err.message || ''}`).trimEnd(),
|
|
42
|
+
].join('\n');
|
|
43
|
+
}
|
|
44
|
+
const fallback = err?.response?.data?.message || err?.message || 'Unknown error';
|
|
45
|
+
return fallback;
|
|
46
|
+
}
|
package/dist/utils/api.js
CHANGED
|
@@ -1,25 +1,24 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
// Utilidades para llamadas directas al API de Plazbot
|
|
3
2
|
// Usado por comandos que no tienen clase SDK (ej: workers)
|
|
4
|
-
|
|
5
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
6
|
-
};
|
|
7
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
-
exports.createApiClient = createApiClient;
|
|
9
|
-
const axios_1 = __importDefault(require("axios"));
|
|
3
|
+
import axios from 'axios';
|
|
10
4
|
function getBaseUrl(zone, dev) {
|
|
11
5
|
if (dev)
|
|
12
6
|
return 'http://localhost:5090';
|
|
13
7
|
return zone === 'EU' ? 'https://apieu.plazbot.com' : 'https://api.plazbot.com';
|
|
14
8
|
}
|
|
15
|
-
function createApiClient(options) {
|
|
9
|
+
export function createApiClient(options) {
|
|
16
10
|
const baseURL = getBaseUrl(options.zone, options.dev);
|
|
17
|
-
return
|
|
11
|
+
return axios.create({
|
|
18
12
|
baseURL,
|
|
19
13
|
headers: {
|
|
20
14
|
'Content-Type': 'application/json',
|
|
15
|
+
// El apiKey almacenado en el CLI ya es un JWT válido. El backend espera
|
|
16
|
+
// Authorization: Bearer en endpoints nuevos (ej. /api/agent/studio).
|
|
17
|
+
// x-api-key se mantiene transitorio para no romper endpoints legacy.
|
|
18
|
+
'Authorization': `Bearer ${options.apiKey}`,
|
|
21
19
|
'x-api-key': options.apiKey,
|
|
22
20
|
'x-workspace-id': options.workspace,
|
|
23
21
|
},
|
|
24
22
|
});
|
|
25
23
|
}
|
|
24
|
+
export { getBaseUrl };
|
package/dist/utils/banner.js
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
const credentials_1 = require("./credentials");
|
|
10
|
-
const { version: VERSION } = require('../../package.json');
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { dirname, join } from 'node:path';
|
|
5
|
+
import { getStoredCredentials } from './credentials.js';
|
|
6
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
const pkgPath = join(__dirname, '..', '..', 'package.json');
|
|
8
|
+
const { version: VERSION } = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
11
9
|
// ASCII art del logo Plazbot (burbujas de chat)
|
|
12
10
|
const logo = [
|
|
13
11
|
' ╭──────────╮ ',
|
|
@@ -19,51 +17,52 @@ const logo = [
|
|
|
19
17
|
' │ ╰────╯ │ ',
|
|
20
18
|
' ╰───────────────╯ ',
|
|
21
19
|
];
|
|
22
|
-
const coloredLogo = logo.map(line =>
|
|
23
|
-
async function showBanner() {
|
|
20
|
+
const coloredLogo = logo.map(line => chalk.hex('#2E8B57')(line));
|
|
21
|
+
export async function showBanner() {
|
|
24
22
|
const width = 70;
|
|
25
23
|
// Intentar cargar credenciales
|
|
26
24
|
let credentials = null;
|
|
27
25
|
try {
|
|
28
|
-
credentials = await
|
|
26
|
+
credentials = await getStoredCredentials();
|
|
29
27
|
}
|
|
30
28
|
catch {
|
|
31
29
|
// No hay credenciales guardadas
|
|
32
30
|
}
|
|
33
31
|
console.log();
|
|
34
|
-
console.log(
|
|
32
|
+
console.log(chalk.hex('#4CAF50')('─'.repeat(width)));
|
|
35
33
|
console.log();
|
|
36
34
|
// Layout de 2 columnas
|
|
37
35
|
const leftWidth = 30;
|
|
38
36
|
const rightStart = leftWidth + 4;
|
|
39
37
|
// Línea del título
|
|
40
|
-
const title =
|
|
38
|
+
const title = chalk.bold.hex('#4CAF50')(` Plazbot CLI v${VERSION}`);
|
|
41
39
|
// Info columna derecha
|
|
42
40
|
const rightColumn = [];
|
|
43
41
|
if (credentials) {
|
|
44
|
-
rightColumn.push(
|
|
45
|
-
rightColumn.push(
|
|
46
|
-
|
|
47
|
-
|
|
42
|
+
rightColumn.push(chalk.bold.hex('#4CAF50')('Connected workspace'));
|
|
43
|
+
rightColumn.push(chalk.gray(` ${credentials.workspace || 'N/A'}`));
|
|
44
|
+
if (credentials.email)
|
|
45
|
+
rightColumn.push(chalk.gray(` ${credentials.email}`));
|
|
46
|
+
rightColumn.push(chalk.gray(` Zone: ${credentials.zone || 'N/A'}`));
|
|
48
47
|
rightColumn.push('');
|
|
49
|
-
rightColumn.push(
|
|
48
|
+
rightColumn.push(chalk.hex('#4CAF50')('─'.repeat(35)));
|
|
50
49
|
rightColumn.push('');
|
|
51
|
-
rightColumn.push(
|
|
52
|
-
rightColumn.push(
|
|
53
|
-
rightColumn.push(
|
|
54
|
-
rightColumn.push(
|
|
55
|
-
rightColumn.push(
|
|
50
|
+
rightColumn.push(chalk.bold.hex('#4CAF50')('Quick commands'));
|
|
51
|
+
rightColumn.push(chalk.white(' plazbot agent list'));
|
|
52
|
+
rightColumn.push(chalk.white(' plazbot agent create'));
|
|
53
|
+
rightColumn.push(chalk.white(' plazbot agent chat -a <id>'));
|
|
54
|
+
rightColumn.push(chalk.white(' plazbot whatsapp send-message'));
|
|
56
55
|
}
|
|
57
56
|
else {
|
|
58
|
-
rightColumn.push(
|
|
57
|
+
rightColumn.push(chalk.bold.hex('#FFA726')('Not connected'));
|
|
59
58
|
rightColumn.push('');
|
|
60
|
-
rightColumn.push(
|
|
61
|
-
rightColumn.push(
|
|
59
|
+
rightColumn.push(chalk.bold.hex('#4CAF50')('To get started'));
|
|
60
|
+
rightColumn.push(chalk.white(' plazbot init -e <email> -k <key> -w <workspace> -z <zone>'));
|
|
62
61
|
rightColumn.push('');
|
|
63
|
-
rightColumn.push(
|
|
62
|
+
rightColumn.push(chalk.hex('#4CAF50')('─'.repeat(35)));
|
|
64
63
|
rightColumn.push('');
|
|
65
|
-
rightColumn.push(
|
|
66
|
-
rightColumn.push(
|
|
64
|
+
rightColumn.push(chalk.bold.hex('#4CAF50')('Documentation'));
|
|
65
|
+
rightColumn.push(chalk.gray(' https://docs.plazbot.com'));
|
|
67
66
|
}
|
|
68
67
|
// Imprimir título
|
|
69
68
|
console.log(title);
|
|
@@ -79,9 +78,9 @@ async function showBanner() {
|
|
|
79
78
|
console.log(`${left}${padding} ${right}`);
|
|
80
79
|
}
|
|
81
80
|
console.log();
|
|
82
|
-
console.log(
|
|
81
|
+
console.log(chalk.hex('#4CAF50')('─'.repeat(width)));
|
|
83
82
|
console.log();
|
|
84
83
|
}
|
|
85
|
-
function showMiniHeader(command) {
|
|
86
|
-
console.log(
|
|
84
|
+
export function showMiniHeader(command) {
|
|
85
|
+
console.log(chalk.hex('#4CAF50')(`\n Plazbot`) + chalk.gray(` > ${command}\n`));
|
|
87
86
|
}
|
|
@@ -1,19 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
exports.saveCredentials = saveCredentials;
|
|
8
|
-
exports.removeCredentials = removeCredentials;
|
|
9
|
-
const promises_1 = __importDefault(require("fs/promises"));
|
|
10
|
-
const path_1 = __importDefault(require("path"));
|
|
11
|
-
const os_1 = __importDefault(require("os"));
|
|
12
|
-
const configDir = path_1.default.join(os_1.default.homedir(), '.plazbot');
|
|
13
|
-
const configPath = path_1.default.join(configDir, 'config.json');
|
|
14
|
-
async function getStoredCredentials() {
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
const configDir = path.join(os.homedir(), '.plazbot');
|
|
5
|
+
const configPath = path.join(configDir, 'config.json');
|
|
6
|
+
export async function getStoredCredentials() {
|
|
15
7
|
try {
|
|
16
|
-
const configRaw = await
|
|
8
|
+
const configRaw = await fs.readFile(configPath, 'utf-8');
|
|
17
9
|
const config = JSON.parse(configRaw);
|
|
18
10
|
if (!config.apiKey || !config.workspace || !config.zone) {
|
|
19
11
|
throw new Error('Credenciales incompletas');
|
|
@@ -24,10 +16,10 @@ async function getStoredCredentials() {
|
|
|
24
16
|
throw new Error("No se encontró una sesión activa. Ejecuta 'plazbot init' primero.");
|
|
25
17
|
}
|
|
26
18
|
}
|
|
27
|
-
async function saveCredentials(credentials) {
|
|
28
|
-
await
|
|
29
|
-
await
|
|
19
|
+
export async function saveCredentials(credentials) {
|
|
20
|
+
await fs.mkdir(configDir, { recursive: true });
|
|
21
|
+
await fs.writeFile(configPath, JSON.stringify(credentials, null, 2));
|
|
30
22
|
}
|
|
31
|
-
async function removeCredentials() {
|
|
32
|
-
await
|
|
23
|
+
export async function removeCredentials() {
|
|
24
|
+
await fs.rm(configPath, { force: true });
|
|
33
25
|
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
/**
|
|
3
|
+
* Appends a colored "Examples" section to a Commander command's --help output.
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* addExamples(myCommand, [
|
|
7
|
+
* { description: 'List all agents in the current workspace', command: 'plazbot agent list' },
|
|
8
|
+
* { description: 'List as JSON for scripting', command: 'plazbot agent list --json' },
|
|
9
|
+
* ]);
|
|
10
|
+
*
|
|
11
|
+
* Renders as:
|
|
12
|
+
* Examples:
|
|
13
|
+
* # List all agents in the current workspace
|
|
14
|
+
* $ plazbot agent list
|
|
15
|
+
*
|
|
16
|
+
* # List as JSON for scripting
|
|
17
|
+
* $ plazbot agent list --json
|
|
18
|
+
*/
|
|
19
|
+
export function addExamples(cmd, examples) {
|
|
20
|
+
if (!examples.length)
|
|
21
|
+
return cmd;
|
|
22
|
+
const lines = ['', chalk.bold('Examples:')];
|
|
23
|
+
for (const ex of examples) {
|
|
24
|
+
lines.push(' ' + chalk.gray('# ' + ex.description));
|
|
25
|
+
lines.push(' ' + chalk.cyan('$ ') + ex.command);
|
|
26
|
+
lines.push('');
|
|
27
|
+
}
|
|
28
|
+
cmd.addHelpText('after', lines.join('\n'));
|
|
29
|
+
return cmd;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Appends a "Notes" section after Examples. Use for caveats, links, or
|
|
33
|
+
* prerequisites the user should know before running the command.
|
|
34
|
+
*/
|
|
35
|
+
export function addNotes(cmd, notes) {
|
|
36
|
+
if (!notes.length)
|
|
37
|
+
return cmd;
|
|
38
|
+
const lines = ['', chalk.bold('Notes:')];
|
|
39
|
+
for (const n of notes)
|
|
40
|
+
lines.push(' ' + chalk.gray('• ') + n);
|
|
41
|
+
lines.push('');
|
|
42
|
+
cmd.addHelpText('after', lines.join('\n'));
|
|
43
|
+
return cmd;
|
|
44
|
+
}
|