plazbot-cli 0.3.6 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -2
- package/dist/commands/agent/export.js +25 -2
- package/dist/commands/auth/browser-login.js +121 -0
- package/dist/commands/auth/index.js +5 -1
- package/dist/commands/auth/login.js +1 -3
- package/dist/commands/auth/logout.js +4 -3
- package/dist/commands/auth/status.js +8 -5
- package/dist/commands/auth/workspace.js +73 -0
- package/dist/commands/whatsapp/connect-flow.js +137 -0
- package/dist/commands/whatsapp/connect.js +26 -160
- package/dist/commands/whatsapp/index.js +4 -0
- package/dist/commands/whatsapp/setup.js +242 -0
- package/dist/studio/components/App.js +1 -1
- package/dist/studio/runOneShot.js +2 -2
- package/dist/studio/runRepl.js +1 -1
- package/dist/utils/agent-errors.js +2 -2
- package/dist/utils/banner.js +10 -9
- package/dist/utils/credentials.js +31 -3
- package/dist/utils/open-browser.js +17 -0
- package/package.json +14 -6
- package/.claude/settings.local.json +0 -15
- package/CLAUDE.md +0 -121
- package/agent.config.json +0 -21
- package/dist/studio/components/AgentPanel.js +0 -35
- package/dist/studio/render/json.js +0 -16
- package/dist/studio/whatsapp/api.js +0 -63
- package/dist/studio/whatsapp/polling.js +0 -77
- package/dist/studio/whatsapp/types.js +0 -31
- package/src/cli.ts +0 -57
- package/src/commands/agent/ai-config.ts +0 -164
- package/src/commands/agent/chat.ts +0 -225
- package/src/commands/agent/copy.ts +0 -46
- package/src/commands/agent/create.ts +0 -103
- package/src/commands/agent/delete.ts +0 -71
- package/src/commands/agent/enable-widget.ts +0 -67
- package/src/commands/agent/export.ts +0 -408
- package/src/commands/agent/files.ts +0 -171
- package/src/commands/agent/get.ts +0 -233
- package/src/commands/agent/index.ts +0 -55
- package/src/commands/agent/list.ts +0 -58
- package/src/commands/agent/monitor.ts +0 -375
- package/src/commands/agent/on-message.ts +0 -82
- package/src/commands/agent/set.ts +0 -149
- package/src/commands/agent/templates.ts +0 -892
- package/src/commands/agent/tools.ts +0 -168
- package/src/commands/agent/update.ts +0 -61
- package/src/commands/agent/validate.ts +0 -135
- package/src/commands/agent/wizard.ts +0 -756
- package/src/commands/auth/index.ts +0 -10
- package/src/commands/auth/login.ts +0 -72
- package/src/commands/auth/logout.ts +0 -28
- package/src/commands/auth/status.ts +0 -28
- package/src/commands/portal/add-agent.ts +0 -66
- package/src/commands/portal/add-link.ts +0 -47
- package/src/commands/portal/clear-links.ts +0 -33
- package/src/commands/portal/create.ts +0 -58
- package/src/commands/portal/delete.ts +0 -70
- package/src/commands/portal/get.ts +0 -77
- package/src/commands/portal/index.ts +0 -32
- package/src/commands/portal/list.ts +0 -79
- package/src/commands/portal/update.ts +0 -93
- package/src/commands/whatsapp/broadcast.ts +0 -110
- package/src/commands/whatsapp/channels.ts +0 -109
- package/src/commands/whatsapp/chat.ts +0 -85
- package/src/commands/whatsapp/connect.ts +0 -220
- package/src/commands/whatsapp/delete-webhook.ts +0 -43
- package/src/commands/whatsapp/index.ts +0 -37
- package/src/commands/whatsapp/register-webhook.ts +0 -47
- package/src/commands/whatsapp/send-template.ts +0 -94
- package/src/commands/whatsapp/send.ts +0 -55
- package/src/commands/whatsapp/widget.ts +0 -75
- package/src/commands/workers/deploy.ts +0 -156
- package/src/commands/workers/index.ts +0 -30
- package/src/commands/workers/list.ts +0 -105
- package/src/commands/workers/logs.ts +0 -99
- package/src/commands/workers/remove.ts +0 -103
- package/src/commands/workers/secret.ts +0 -191
- package/src/commands/workers/test.ts +0 -145
- package/src/schemas/agent.config.schema.json +0 -569
- package/src/studio/api/sseClient.ts +0 -91
- package/src/studio/api/studioApi.ts +0 -27
- package/src/studio/api/types.ts +0 -96
- package/src/studio/components/App.tsx +0 -274
- package/src/studio/components/ChatLog.tsx +0 -121
- package/src/studio/components/ConnectCard.tsx +0 -171
- package/src/studio/components/Footer.tsx +0 -38
- package/src/studio/components/Header.tsx +0 -39
- package/src/studio/components/Input.tsx +0 -176
- package/src/studio/components/Message.tsx +0 -87
- package/src/studio/components/Suggestions.tsx +0 -26
- package/src/studio/components/ToolCall.tsx +0 -58
- package/src/studio/connect/api.ts +0 -98
- package/src/studio/connect/polling.ts +0 -94
- package/src/studio/connect/types.ts +0 -141
- package/src/studio/index.ts +0 -58
- package/src/studio/render/markdown.ts +0 -93
- package/src/studio/render/steps.ts +0 -57
- package/src/studio/runOneShot.ts +0 -114
- package/src/studio/runRepl.tsx +0 -82
- package/src/studio/slash/handlers.ts +0 -226
- package/src/studio/slash/parser.ts +0 -41
- package/src/studio/slash/registry.ts +0 -54
- package/src/studio/state/store.ts +0 -279
- package/src/types/agent.ts +0 -63
- package/src/types/auth.ts +0 -9
- package/src/types/common.ts +0 -10
- package/src/types/message.ts +0 -34
- package/src/types/portal.ts +0 -56
- package/src/types/workers.ts +0 -86
- package/src/utils/agent-errors.ts +0 -67
- package/src/utils/api.ts +0 -35
- package/src/utils/banner.ts +0 -99
- package/src/utils/credentials.ts +0 -38
- package/src/utils/help.ts +0 -51
- package/src/utils/logger.ts +0 -40
- package/src/utils/ui.ts +0 -109
- package/tsconfig.json +0 -18
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tipos y helpers del flujo "Conectar canal desde Agent Studio".
|
|
3
|
-
*
|
|
4
|
-
* El backend emite `tool_result` con `type` = `whatsapp_link` |
|
|
5
|
-
* `instagram_link` | `messenger_link` segun la tool invocada. Todos comparten
|
|
6
|
-
* el mismo shape de datos (link corto + expiracion + baseline de integraciones)
|
|
7
|
-
* y el mismo patron de polling (GET workspace cada 5s hasta detectar el nuevo
|
|
8
|
-
* id) y activacion (POST .../activate).
|
|
9
|
-
*
|
|
10
|
-
* Este modulo es la version generalizada de lo que antes vivia en
|
|
11
|
-
* `src/studio/whatsapp/` cuando solo existia WhatsApp.
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
export type ConnectChannel = 'whatsapp' | 'instagram' | 'messenger';
|
|
15
|
-
|
|
16
|
-
/** Mapea el `type` del tool_result al canal logico del cliente. */
|
|
17
|
-
export function channelFromToolResultType(type: string | undefined): ConnectChannel | null {
|
|
18
|
-
if (type === 'whatsapp_link') return 'whatsapp';
|
|
19
|
-
if (type === 'instagram_link') return 'instagram';
|
|
20
|
-
if (type === 'messenger_link') return 'messenger';
|
|
21
|
-
return null;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/** Tipos de integracion (workspace.integrations[].type) a vigilar por canal. */
|
|
25
|
-
export function integrationTypesForChannel(channel: ConnectChannel): string[] {
|
|
26
|
-
switch (channel) {
|
|
27
|
-
case 'whatsapp': return ['whatsapp', 'whatsapp_business'];
|
|
28
|
-
case 'instagram': return ['instagram'];
|
|
29
|
-
case 'messenger': return ['facebook']; // Messenger se guarda como type=facebook
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* `platformCode` que espera POST .../activate. Para WhatsApp el endpoint usa
|
|
35
|
-
* este valor para una validacion de duplicados; para IG/Messenger no ejecuta
|
|
36
|
-
* ninguna logica especifica pero debe enviarse algo coherente.
|
|
37
|
-
*/
|
|
38
|
-
export function platformCodeForChannel(channel: ConnectChannel): string {
|
|
39
|
-
switch (channel) {
|
|
40
|
-
case 'whatsapp': return 'whatsapp';
|
|
41
|
-
case 'instagram': return 'instagram';
|
|
42
|
-
case 'messenger': return 'facebook';
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export interface ChannelLinkData {
|
|
47
|
-
shortUrl?: string | null;
|
|
48
|
-
/** whatsapp | whatsapp_business | instagram | facebook */
|
|
49
|
-
linkType?: string;
|
|
50
|
-
expiresAt?: string;
|
|
51
|
-
isNewLink?: boolean;
|
|
52
|
-
alreadyConnected?: boolean;
|
|
53
|
-
/** Snapshot WhatsApp (legacy, mantenido por compat con la tool de WA). */
|
|
54
|
-
connectedNumbers?: Array<{ id?: string; number?: string; alias?: string }>;
|
|
55
|
-
/** Snapshot IG/Messenger (pageId + nombre legible + alias). */
|
|
56
|
-
connectedItems?: Array<{ id?: string; pageId?: string; name?: string; alias?: string }>;
|
|
57
|
-
existingIntegrationIds?: string[];
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export type ConnectStatus =
|
|
61
|
-
| 'waiting' // card visible, polling activo
|
|
62
|
-
| 'activating' // integracion detectada, activando via API
|
|
63
|
-
| 'connected' // activada con exito
|
|
64
|
-
| 'timeout' // intentos agotados
|
|
65
|
-
| 'cancelled' // usuario cancelo
|
|
66
|
-
| 'error'; // activacion fallo
|
|
67
|
-
|
|
68
|
-
export interface ConnectState {
|
|
69
|
-
/** Id estable para keys/diffs. */
|
|
70
|
-
id: string;
|
|
71
|
-
channel: ConnectChannel;
|
|
72
|
-
linkData: ChannelLinkData;
|
|
73
|
-
status: ConnectStatus;
|
|
74
|
-
/** Mensaje del chat al que ancla la tarjeta. */
|
|
75
|
-
anchorMessageId: string;
|
|
76
|
-
/** Etiqueta humana del item conectado (numero, pagina, etc.) — populado en `connected`. */
|
|
77
|
-
connectedLabel?: string;
|
|
78
|
-
errorMessage?: string;
|
|
79
|
-
attempt: number;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/** Shape minimo de un item de `workspace.integrations` que devuelve el GET. */
|
|
83
|
-
export interface WorkspaceIntegrationItem {
|
|
84
|
-
id?: string;
|
|
85
|
-
type?: string;
|
|
86
|
-
/** whatsapp */
|
|
87
|
-
cellphoneNumber?: string;
|
|
88
|
-
cellphoneNumberFormat?: string;
|
|
89
|
-
/** facebook (messenger) */
|
|
90
|
-
facebookPageId?: string;
|
|
91
|
-
/** instagram */
|
|
92
|
-
instagramPageId?: string;
|
|
93
|
-
/** comun: nombre legible (display name de Meta) */
|
|
94
|
-
srcName?: string;
|
|
95
|
-
/** comun: alias humano puesto manualmente */
|
|
96
|
-
aliasName?: string;
|
|
97
|
-
creationDate?: string;
|
|
98
|
-
isActive?: number | boolean;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
export const CONNECT_POLL_INTERVAL_MS = 5000;
|
|
102
|
-
export const CONNECT_POLL_MAX_ATTEMPTS = 60; // 5 minutos
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Devuelve la integracion mas reciente del canal cuyo id NO este en el baseline.
|
|
106
|
-
* Si hay varias nuevas, gana la de `creationDate` mayor.
|
|
107
|
-
*/
|
|
108
|
-
export function findNewIntegrationForChannel(
|
|
109
|
-
channel: ConnectChannel,
|
|
110
|
-
integrations: WorkspaceIntegrationItem[] | undefined,
|
|
111
|
-
knownIds: Set<string>,
|
|
112
|
-
): WorkspaceIntegrationItem | null {
|
|
113
|
-
if (!integrations?.length) return null;
|
|
114
|
-
|
|
115
|
-
const types = new Set(integrationTypesForChannel(channel));
|
|
116
|
-
const candidates = integrations.filter(
|
|
117
|
-
(i) => !!i.type && types.has(i.type) && !!i.id && !knownIds.has(i.id as string),
|
|
118
|
-
);
|
|
119
|
-
if (!candidates.length) return null;
|
|
120
|
-
|
|
121
|
-
return candidates.reduce((latest, current) => {
|
|
122
|
-
const a = latest.creationDate ? new Date(latest.creationDate).getTime() : 0;
|
|
123
|
-
const b = current.creationDate ? new Date(current.creationDate).getTime() : 0;
|
|
124
|
-
return b > a ? current : latest;
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/** Etiqueta humana del item recien conectado (numero, nombre de pagina, etc.). */
|
|
129
|
-
export function describeIntegration(
|
|
130
|
-
channel: ConnectChannel,
|
|
131
|
-
i: WorkspaceIntegrationItem,
|
|
132
|
-
): string {
|
|
133
|
-
switch (channel) {
|
|
134
|
-
case 'whatsapp':
|
|
135
|
-
return i.cellphoneNumberFormat || i.cellphoneNumber || i.srcName || '';
|
|
136
|
-
case 'instagram':
|
|
137
|
-
return i.srcName || i.aliasName || i.instagramPageId || '';
|
|
138
|
-
case 'messenger':
|
|
139
|
-
return i.srcName || i.aliasName || i.facebookPageId || '';
|
|
140
|
-
}
|
|
141
|
-
}
|
package/src/studio/index.ts
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
|
|
3
|
-
interface StudioCmdOpts {
|
|
4
|
-
dev: boolean;
|
|
5
|
-
agentId?: string;
|
|
6
|
-
message?: string;
|
|
7
|
-
workspaceId?: string;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
interface AskCmdOpts {
|
|
11
|
-
dev: boolean;
|
|
12
|
-
agentId?: string;
|
|
13
|
-
json: boolean;
|
|
14
|
-
workspaceId?: string;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export const studioCommand = new Command('studio')
|
|
18
|
-
.description('Interactive Plazbot Studio REPL (create, diagnose and manage AI agents)')
|
|
19
|
-
.option('--dev', 'Target the local backend (http://localhost:5090)', false)
|
|
20
|
-
.option('-a, --agent-id <id>', 'Preload an agent on startup')
|
|
21
|
-
.option('-w, --workspace-id <id>', 'Override the workspace (support / white-label partner mode)')
|
|
22
|
-
.option('-m, --message <text>', 'Run a one-shot prompt and exit')
|
|
23
|
-
.action(async (opts: StudioCmdOpts) => {
|
|
24
|
-
if (opts.message) {
|
|
25
|
-
const { runOneShot } = await import('./runOneShot.js');
|
|
26
|
-
await runOneShot({
|
|
27
|
-
dev: opts.dev,
|
|
28
|
-
agentId: opts.agentId,
|
|
29
|
-
message: opts.message,
|
|
30
|
-
workspaceOverride: opts.workspaceId,
|
|
31
|
-
});
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
const { runRepl } = await import('./runRepl.js');
|
|
35
|
-
await runRepl({
|
|
36
|
-
dev: opts.dev,
|
|
37
|
-
agentId: opts.agentId,
|
|
38
|
-
workspaceOverride: opts.workspaceId,
|
|
39
|
-
});
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
studioCommand
|
|
43
|
-
.command('ask <message>')
|
|
44
|
-
.description('Run a one-shot studio query without opening the TUI')
|
|
45
|
-
.option('--dev', 'Target the local backend (http://localhost:5090)', false)
|
|
46
|
-
.option('-a, --agent-id <id>', 'Agent to use as context')
|
|
47
|
-
.option('-w, --workspace-id <id>', 'Override the workspace (support / white-label partner mode)')
|
|
48
|
-
.option('--json', 'Print SSE chunks as raw NDJSON', false)
|
|
49
|
-
.action(async (message: string, opts: AskCmdOpts) => {
|
|
50
|
-
const { runOneShot } = await import('./runOneShot.js');
|
|
51
|
-
await runOneShot({
|
|
52
|
-
dev: opts.dev,
|
|
53
|
-
agentId: opts.agentId,
|
|
54
|
-
message,
|
|
55
|
-
json: opts.json,
|
|
56
|
-
workspaceOverride: opts.workspaceId,
|
|
57
|
-
});
|
|
58
|
-
});
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
// @ts-ignore - marked-terminal no provee tipos
|
|
2
|
-
import TerminalRenderer from 'marked-terminal';
|
|
3
|
-
import { marked } from 'marked';
|
|
4
|
-
import chalk from 'chalk';
|
|
5
|
-
// @ts-ignore - cli-highlight no provee tipos completos
|
|
6
|
-
import { highlight, supportsLanguage } from 'cli-highlight';
|
|
7
|
-
|
|
8
|
-
let configured = false;
|
|
9
|
-
|
|
10
|
-
const LANG_ALIASES: Record<string, string> = {
|
|
11
|
-
sh: 'bash',
|
|
12
|
-
shell: 'bash',
|
|
13
|
-
zsh: 'bash',
|
|
14
|
-
curl: 'bash',
|
|
15
|
-
jsx: 'javascript',
|
|
16
|
-
ts: 'typescript',
|
|
17
|
-
tsx: 'typescript',
|
|
18
|
-
yml: 'yaml',
|
|
19
|
-
html: 'xml',
|
|
20
|
-
text: 'plaintext',
|
|
21
|
-
txt: 'plaintext',
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
function resolveLang(raw: string | undefined): string {
|
|
25
|
-
const k = (raw ?? '').toLowerCase().trim();
|
|
26
|
-
const aliased = LANG_ALIASES[k] ?? k;
|
|
27
|
-
if (!aliased) return 'plaintext';
|
|
28
|
-
try {
|
|
29
|
-
return supportsLanguage(aliased) ? aliased : 'plaintext';
|
|
30
|
-
} catch {
|
|
31
|
-
return 'plaintext';
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function renderCodeBlock(code: string, rawLang: string | undefined): string {
|
|
36
|
-
const lang = resolveLang(rawLang);
|
|
37
|
-
let body: string;
|
|
38
|
-
try {
|
|
39
|
-
body = highlight(code, { language: lang, ignoreIllegals: true });
|
|
40
|
-
} catch {
|
|
41
|
-
body = code;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const label = (rawLang ?? '').trim() || lang;
|
|
45
|
-
const width = Math.max(20, Math.min(80, (process.stdout.columns || 80) - 8));
|
|
46
|
-
const headerLabel = ` ${label} `;
|
|
47
|
-
const headerRule = '─'.repeat(Math.max(0, width - headerLabel.length - 4));
|
|
48
|
-
const header = chalk.gray.dim(`──${headerLabel}${headerRule}`);
|
|
49
|
-
const footer = chalk.gray.dim('─'.repeat(width - 2));
|
|
50
|
-
|
|
51
|
-
// Indent del cuerpo para diferenciar del prosa
|
|
52
|
-
const indented = body
|
|
53
|
-
.split('\n')
|
|
54
|
-
.map((l) => ' ' + l)
|
|
55
|
-
.join('\n');
|
|
56
|
-
|
|
57
|
-
return `\n${header}\n${indented}\n${footer}\n`;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function configure(): void {
|
|
61
|
-
if (configured) return;
|
|
62
|
-
|
|
63
|
-
const renderer = new TerminalRenderer({
|
|
64
|
-
width: Math.max(40, (process.stdout.columns || 80) - 8),
|
|
65
|
-
reflowText: true,
|
|
66
|
-
tab: 2,
|
|
67
|
-
// Inline code mejor diferenciado del texto normal
|
|
68
|
-
codespan: chalk.cyan,
|
|
69
|
-
}) as any;
|
|
70
|
-
|
|
71
|
-
// Override del code block para syntax highlight + box delimitado.
|
|
72
|
-
// marked-terminal pasa (code, lang, escaped) al método `code`.
|
|
73
|
-
renderer.code = (code: string, lang: string | undefined): string =>
|
|
74
|
-
renderCodeBlock(code, lang);
|
|
75
|
-
|
|
76
|
-
marked.setOptions({ renderer });
|
|
77
|
-
configured = true;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Renderiza markdown a una cadena con secuencias ANSI lista para imprimir
|
|
82
|
-
* dentro de un <Text/> de Ink. Limpia el salto de línea trailing que añade marked.
|
|
83
|
-
*/
|
|
84
|
-
export function renderMarkdown(md: string): string {
|
|
85
|
-
if (!md) return '';
|
|
86
|
-
configure();
|
|
87
|
-
try {
|
|
88
|
-
const out = marked.parse(md) as string;
|
|
89
|
-
return typeof out === 'string' ? out.replace(/\n+$/, '') : md;
|
|
90
|
-
} catch {
|
|
91
|
-
return md;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Maps backend tool_name values to human-readable step labels for the REPL.
|
|
3
|
-
* Keeps both forms (gerund for "running" and past tense for "success").
|
|
4
|
-
*/
|
|
5
|
-
const LABELS: Record<string, { running: string; done: string; failed: string }> = {
|
|
6
|
-
list_agents: {
|
|
7
|
-
running: 'Listing agents',
|
|
8
|
-
done: 'Agents listed',
|
|
9
|
-
failed: 'Could not list agents',
|
|
10
|
-
},
|
|
11
|
-
load_agent: {
|
|
12
|
-
running: 'Loading agent',
|
|
13
|
-
done: 'Agent loaded',
|
|
14
|
-
failed: 'Could not load agent',
|
|
15
|
-
},
|
|
16
|
-
diagnose_agent: {
|
|
17
|
-
running: 'Diagnosing agent',
|
|
18
|
-
done: 'Diagnosis complete',
|
|
19
|
-
failed: 'Could not diagnose agent',
|
|
20
|
-
},
|
|
21
|
-
create_agent_config: {
|
|
22
|
-
running: 'Generating agent configuration',
|
|
23
|
-
done: 'Configuration generated',
|
|
24
|
-
failed: 'Could not generate configuration',
|
|
25
|
-
},
|
|
26
|
-
update_agent_config: {
|
|
27
|
-
running: 'Updating agent configuration',
|
|
28
|
-
done: 'Configuration updated',
|
|
29
|
-
failed: 'Could not update configuration',
|
|
30
|
-
},
|
|
31
|
-
save_agent: {
|
|
32
|
-
running: 'Saving agent',
|
|
33
|
-
done: 'Agent saved',
|
|
34
|
-
failed: 'Could not save agent',
|
|
35
|
-
},
|
|
36
|
-
delete_agent: {
|
|
37
|
-
running: 'Deleting agent',
|
|
38
|
-
done: 'Agent deleted',
|
|
39
|
-
failed: 'Could not delete agent',
|
|
40
|
-
},
|
|
41
|
-
connect_whatsapp: {
|
|
42
|
-
running: 'Connecting WhatsApp',
|
|
43
|
-
done: 'WhatsApp connected',
|
|
44
|
-
failed: 'Could not connect WhatsApp',
|
|
45
|
-
},
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
export function stepLabel(toolName: string, status: 'running' | 'success' | 'error'): string {
|
|
49
|
-
const entry = LABELS[toolName] ?? {
|
|
50
|
-
running: `Running ${toolName}`,
|
|
51
|
-
done: `Ran ${toolName}`,
|
|
52
|
-
failed: `Failed: ${toolName}`,
|
|
53
|
-
};
|
|
54
|
-
if (status === 'running') return entry.running;
|
|
55
|
-
if (status === 'success') return entry.done;
|
|
56
|
-
return entry.failed;
|
|
57
|
-
}
|
package/src/studio/runOneShot.ts
DELETED
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
import { getStoredCredentials } from '../utils/credentials.js';
|
|
2
|
-
import { logger } from '../utils/logger.js';
|
|
3
|
-
import { streamStudio } from './api/sseClient.js';
|
|
4
|
-
import { StudioHttpError } from './api/types.js';
|
|
5
|
-
import type { StudioChunk, StudioStreamOptions } from './api/types.js';
|
|
6
|
-
import { stepLabel } from './render/steps.js';
|
|
7
|
-
|
|
8
|
-
interface RunOneShotOptions {
|
|
9
|
-
dev: boolean;
|
|
10
|
-
agentId?: string;
|
|
11
|
-
message: string;
|
|
12
|
-
/** Si true, imprime cada chunk como una línea JSON (NDJSON). */
|
|
13
|
-
json?: boolean;
|
|
14
|
-
/** Overrides the workspace from local config (support / white-label partner mode). */
|
|
15
|
-
workspaceOverride?: string;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export async function runOneShot(opts: RunOneShotOptions): Promise<void> {
|
|
19
|
-
let creds;
|
|
20
|
-
try {
|
|
21
|
-
creds = await getStoredCredentials();
|
|
22
|
-
} catch {
|
|
23
|
-
logger.error('No active session. Run:');
|
|
24
|
-
console.log(' plazbot init -e <email> -k <jwt> -w <workspace> -z <LA|EU>');
|
|
25
|
-
process.exit(1);
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const effectiveWorkspace = opts.workspaceOverride ?? creds.workspace;
|
|
30
|
-
const supportMode = !!opts.workspaceOverride && opts.workspaceOverride !== creds.workspace;
|
|
31
|
-
|
|
32
|
-
if (supportMode && !opts.json) {
|
|
33
|
-
process.stderr.write(`! Support mode: acting on workspace "${effectiveWorkspace}" (your account belongs to "${creds.workspace}")\n`);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const stream: StudioStreamOptions = {
|
|
37
|
-
apiKey: creds.apiKey,
|
|
38
|
-
workspaceId: effectiveWorkspace,
|
|
39
|
-
userId: creds.userId ?? creds.email ?? undefined,
|
|
40
|
-
zone: creds.zone,
|
|
41
|
-
dev: opts.dev,
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
const controller = new AbortController();
|
|
45
|
-
process.on('SIGINT', () => controller.abort());
|
|
46
|
-
|
|
47
|
-
let assistantText = '';
|
|
48
|
-
const onChunk = (chunk: StudioChunk) => {
|
|
49
|
-
if (opts.json) {
|
|
50
|
-
process.stdout.write(JSON.stringify(chunk) + '\n');
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
switch (chunk.type) {
|
|
54
|
-
case 'text':
|
|
55
|
-
assistantText += chunk.content ?? '';
|
|
56
|
-
process.stdout.write(chunk.content ?? '');
|
|
57
|
-
break;
|
|
58
|
-
case 'tool_call':
|
|
59
|
-
process.stderr.write(`\n ▸ ${stepLabel(chunk.tool_name, 'running')}…\n`);
|
|
60
|
-
break;
|
|
61
|
-
case 'tool_result': {
|
|
62
|
-
const tr = chunk.tool_result;
|
|
63
|
-
const ok = tr?.success !== false && !tr?.error;
|
|
64
|
-
const label = stepLabel(chunk.tool_name, ok ? 'success' : 'error');
|
|
65
|
-
process.stderr.write(` ${ok ? '✓' : '✗'} ${label}${tr?.error ? ' — ' + tr.error : ''}\n`);
|
|
66
|
-
break;
|
|
67
|
-
}
|
|
68
|
-
case 'usage':
|
|
69
|
-
process.stderr.write(
|
|
70
|
-
` tokens: ${chunk.input_tokens} in / ${chunk.output_tokens} out\n`,
|
|
71
|
-
);
|
|
72
|
-
break;
|
|
73
|
-
case 'error':
|
|
74
|
-
process.stderr.write(`\n ✖ ${chunk.error}\n`);
|
|
75
|
-
break;
|
|
76
|
-
case 'done':
|
|
77
|
-
break;
|
|
78
|
-
}
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
try {
|
|
82
|
-
await streamStudio(
|
|
83
|
-
stream,
|
|
84
|
-
{
|
|
85
|
-
message: opts.message,
|
|
86
|
-
messages: [{ role: 'user', content: opts.message }],
|
|
87
|
-
agentId: opts.agentId ?? null,
|
|
88
|
-
},
|
|
89
|
-
onChunk,
|
|
90
|
-
controller.signal,
|
|
91
|
-
);
|
|
92
|
-
if (!opts.json) process.stdout.write('\n');
|
|
93
|
-
if (!assistantText && !opts.json) process.stderr.write(' (no text response)\n');
|
|
94
|
-
} catch (err) {
|
|
95
|
-
process.stderr.write('\n' + formatError(err, opts.dev) + '\n');
|
|
96
|
-
process.exit(1);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
function formatError(err: unknown, dev: boolean): string {
|
|
101
|
-
if (err instanceof StudioHttpError) {
|
|
102
|
-
if (err.status === 401) return '✖ Token expired or invalid. Run `plazbot init`.';
|
|
103
|
-
if (err.status === 403) return '✖ No permission for this workspace.';
|
|
104
|
-
if (err.status === 429) return '✖ Rate limit reached.';
|
|
105
|
-
if (err.status >= 500) {
|
|
106
|
-
return dev
|
|
107
|
-
? `✖ Backend ${err.status}: ${err.body ?? err.statusText}`
|
|
108
|
-
: `✖ Backend returned ${err.status}. Retry.`;
|
|
109
|
-
}
|
|
110
|
-
return `✖ HTTP ${err.status} ${err.statusText}`;
|
|
111
|
-
}
|
|
112
|
-
if (err instanceof Error) return `✖ ${err.message}`;
|
|
113
|
-
return '✖ Unknown error';
|
|
114
|
-
}
|
package/src/studio/runRepl.tsx
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { render } from 'ink';
|
|
3
|
-
import { readFileSync } from 'node:fs';
|
|
4
|
-
import { fileURLToPath } from 'node:url';
|
|
5
|
-
import { dirname, join } from 'node:path';
|
|
6
|
-
import { App } from './components/App.js';
|
|
7
|
-
import { getStoredCredentials } from '../utils/credentials.js';
|
|
8
|
-
import { logger } from '../utils/logger.js';
|
|
9
|
-
import type { StudioStreamOptions } from './api/types.js';
|
|
10
|
-
|
|
11
|
-
interface RunReplOptions {
|
|
12
|
-
dev: boolean;
|
|
13
|
-
agentId?: string;
|
|
14
|
-
/** Overrides the workspace from local config (support / white-label partner mode). */
|
|
15
|
-
workspaceOverride?: string;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
// Estilo Claude Code: NO usamos alt screen buffer. Razon: en el alt screen el
|
|
19
|
-
// scrollback nativo de la terminal queda inaccesible y el usuario no puede
|
|
20
|
-
// revisar la conversacion anterior. Aceptamos el tradeoff de que en algunos
|
|
21
|
-
// resizes de la terminal pueda haber artifacts visuales temporales (los frames
|
|
22
|
-
// estaticos ya quedaron en scrollback gracias a <Static>).
|
|
23
|
-
|
|
24
|
-
export async function runRepl(opts: RunReplOptions): Promise<void> {
|
|
25
|
-
let creds;
|
|
26
|
-
try {
|
|
27
|
-
creds = await getStoredCredentials();
|
|
28
|
-
} catch (err) {
|
|
29
|
-
logger.error('No active session. Run:');
|
|
30
|
-
console.log(' plazbot init -e <email> -k <jwt> -w <workspace> -z <LA|EU>');
|
|
31
|
-
process.exit(1);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const effectiveWorkspace = opts.workspaceOverride ?? creds.workspace;
|
|
35
|
-
const supportMode = !!opts.workspaceOverride && opts.workspaceOverride !== creds.workspace;
|
|
36
|
-
|
|
37
|
-
if (supportMode) {
|
|
38
|
-
logger.warning(`Support mode: acting on workspace "${effectiveWorkspace}" (your account belongs to "${creds.workspace}")`);
|
|
39
|
-
logger.warning('All actions are tied to your user and will be visible in audit trails.');
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const stream: StudioStreamOptions = {
|
|
43
|
-
apiKey: creds.apiKey,
|
|
44
|
-
workspaceId: effectiveWorkspace,
|
|
45
|
-
userId: creds.userId ?? creds.email ?? undefined,
|
|
46
|
-
zone: creds.zone,
|
|
47
|
-
dev: opts.dev,
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
const version = readVersion();
|
|
51
|
-
|
|
52
|
-
const { waitUntilExit } = render(
|
|
53
|
-
<App
|
|
54
|
-
version={version}
|
|
55
|
-
stream={stream}
|
|
56
|
-
initialAgentId={opts.agentId ?? null}
|
|
57
|
-
dev={opts.dev}
|
|
58
|
-
supportMode={supportMode}
|
|
59
|
-
/>,
|
|
60
|
-
{
|
|
61
|
-
exitOnCtrlC: false,
|
|
62
|
-
},
|
|
63
|
-
);
|
|
64
|
-
|
|
65
|
-
// Cierre limpio con Ctrl+C: la App lo gestiona vía useInput.
|
|
66
|
-
process.on('SIGINT', () => {
|
|
67
|
-
// no-op aquí; App.tsx llama exit() al detectar Ctrl+C.
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
await waitUntilExit();
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function readVersion(): string {
|
|
74
|
-
try {
|
|
75
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
76
|
-
const pkgPath = join(__dirname, '..', '..', 'package.json');
|
|
77
|
-
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
78
|
-
return pkg.version ?? '0.0.0';
|
|
79
|
-
} catch {
|
|
80
|
-
return '0.0.0';
|
|
81
|
-
}
|
|
82
|
-
}
|