plazbot-cli 0.3.7 → 0.4.1

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.
Files changed (117) hide show
  1. package/README.md +38 -2
  2. package/dist/commands/auth/browser-login.js +121 -0
  3. package/dist/commands/auth/index.js +5 -1
  4. package/dist/commands/auth/login.js +1 -3
  5. package/dist/commands/auth/logout.js +4 -3
  6. package/dist/commands/auth/status.js +8 -5
  7. package/dist/commands/auth/workspace.js +73 -0
  8. package/dist/commands/whatsapp/connect-flow.js +137 -0
  9. package/dist/commands/whatsapp/connect.js +26 -160
  10. package/dist/commands/whatsapp/index.js +4 -0
  11. package/dist/commands/whatsapp/setup.js +242 -0
  12. package/dist/schemas/agent.config.schema.json +24 -0
  13. package/dist/studio/components/App.js +1 -1
  14. package/dist/studio/runOneShot.js +2 -2
  15. package/dist/studio/runRepl.js +1 -1
  16. package/dist/utils/agent-errors.js +2 -2
  17. package/dist/utils/banner.js +10 -9
  18. package/dist/utils/credentials.js +31 -3
  19. package/dist/utils/open-browser.js +17 -0
  20. package/package.json +14 -6
  21. package/.claude/settings.local.json +0 -19
  22. package/CLAUDE.md +0 -121
  23. package/agent.config.json +0 -21
  24. package/dist/studio/components/AgentPanel.js +0 -35
  25. package/dist/studio/render/json.js +0 -16
  26. package/dist/studio/whatsapp/api.js +0 -63
  27. package/dist/studio/whatsapp/polling.js +0 -77
  28. package/dist/studio/whatsapp/types.js +0 -31
  29. package/src/cli.ts +0 -57
  30. package/src/commands/agent/ai-config.ts +0 -164
  31. package/src/commands/agent/chat.ts +0 -225
  32. package/src/commands/agent/copy.ts +0 -46
  33. package/src/commands/agent/create.ts +0 -103
  34. package/src/commands/agent/delete.ts +0 -71
  35. package/src/commands/agent/enable-widget.ts +0 -67
  36. package/src/commands/agent/export.ts +0 -430
  37. package/src/commands/agent/files.ts +0 -171
  38. package/src/commands/agent/get.ts +0 -233
  39. package/src/commands/agent/index.ts +0 -55
  40. package/src/commands/agent/list.ts +0 -58
  41. package/src/commands/agent/monitor.ts +0 -375
  42. package/src/commands/agent/on-message.ts +0 -82
  43. package/src/commands/agent/set.ts +0 -149
  44. package/src/commands/agent/templates.ts +0 -892
  45. package/src/commands/agent/tools.ts +0 -168
  46. package/src/commands/agent/update.ts +0 -61
  47. package/src/commands/agent/validate.ts +0 -135
  48. package/src/commands/agent/wizard.ts +0 -756
  49. package/src/commands/auth/index.ts +0 -10
  50. package/src/commands/auth/login.ts +0 -72
  51. package/src/commands/auth/logout.ts +0 -28
  52. package/src/commands/auth/status.ts +0 -28
  53. package/src/commands/portal/add-agent.ts +0 -66
  54. package/src/commands/portal/add-link.ts +0 -47
  55. package/src/commands/portal/clear-links.ts +0 -33
  56. package/src/commands/portal/create.ts +0 -58
  57. package/src/commands/portal/delete.ts +0 -70
  58. package/src/commands/portal/get.ts +0 -77
  59. package/src/commands/portal/index.ts +0 -32
  60. package/src/commands/portal/list.ts +0 -79
  61. package/src/commands/portal/update.ts +0 -93
  62. package/src/commands/whatsapp/broadcast.ts +0 -110
  63. package/src/commands/whatsapp/channels.ts +0 -109
  64. package/src/commands/whatsapp/chat.ts +0 -85
  65. package/src/commands/whatsapp/connect.ts +0 -220
  66. package/src/commands/whatsapp/delete-webhook.ts +0 -43
  67. package/src/commands/whatsapp/index.ts +0 -37
  68. package/src/commands/whatsapp/register-webhook.ts +0 -47
  69. package/src/commands/whatsapp/send-template.ts +0 -94
  70. package/src/commands/whatsapp/send.ts +0 -55
  71. package/src/commands/whatsapp/widget.ts +0 -75
  72. package/src/commands/workers/deploy.ts +0 -156
  73. package/src/commands/workers/index.ts +0 -30
  74. package/src/commands/workers/list.ts +0 -105
  75. package/src/commands/workers/logs.ts +0 -99
  76. package/src/commands/workers/remove.ts +0 -103
  77. package/src/commands/workers/secret.ts +0 -191
  78. package/src/commands/workers/test.ts +0 -145
  79. package/src/schemas/agent.config.schema.json +0 -569
  80. package/src/studio/api/sseClient.ts +0 -91
  81. package/src/studio/api/studioApi.ts +0 -27
  82. package/src/studio/api/types.ts +0 -96
  83. package/src/studio/components/App.tsx +0 -274
  84. package/src/studio/components/ChatLog.tsx +0 -121
  85. package/src/studio/components/ConnectCard.tsx +0 -171
  86. package/src/studio/components/Footer.tsx +0 -38
  87. package/src/studio/components/Header.tsx +0 -39
  88. package/src/studio/components/Input.tsx +0 -176
  89. package/src/studio/components/Message.tsx +0 -87
  90. package/src/studio/components/Suggestions.tsx +0 -26
  91. package/src/studio/components/ToolCall.tsx +0 -58
  92. package/src/studio/connect/api.ts +0 -98
  93. package/src/studio/connect/polling.ts +0 -94
  94. package/src/studio/connect/types.ts +0 -141
  95. package/src/studio/index.ts +0 -58
  96. package/src/studio/render/markdown.ts +0 -93
  97. package/src/studio/render/steps.ts +0 -57
  98. package/src/studio/runOneShot.ts +0 -114
  99. package/src/studio/runRepl.tsx +0 -82
  100. package/src/studio/slash/handlers.ts +0 -226
  101. package/src/studio/slash/parser.ts +0 -41
  102. package/src/studio/slash/registry.ts +0 -54
  103. package/src/studio/state/store.ts +0 -279
  104. package/src/types/agent.ts +0 -63
  105. package/src/types/auth.ts +0 -9
  106. package/src/types/common.ts +0 -10
  107. package/src/types/message.ts +0 -34
  108. package/src/types/portal.ts +0 -56
  109. package/src/types/workers.ts +0 -86
  110. package/src/utils/agent-errors.ts +0 -67
  111. package/src/utils/api.ts +0 -35
  112. package/src/utils/banner.ts +0 -99
  113. package/src/utils/credentials.ts +0 -38
  114. package/src/utils/help.ts +0 -51
  115. package/src/utils/logger.ts +0 -40
  116. package/src/utils/ui.ts +0 -109
  117. 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
- }
@@ -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
- }
@@ -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
- }
@@ -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
- }