@liriraid/agentflow-ai 1.0.16 → 1.0.18
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/orchestrator.js +3 -3
- package/package.json +1 -1
- package/src/ink/app.mjs +7 -7
- package/src/ink/index.mjs +44 -0
package/orchestrator.js
CHANGED
|
@@ -1949,7 +1949,7 @@ function startQueueWatcher() {
|
|
|
1949
1949
|
log("INFO", WORKSPACE_LANGUAGE === "es"
|
|
1950
1950
|
? `Nueva tarea detectada en QUEUE.md`
|
|
1951
1951
|
: `New task detected in QUEUE.md`);
|
|
1952
|
-
},
|
|
1952
|
+
}, 50);
|
|
1953
1953
|
});
|
|
1954
1954
|
watcher.on('error', () => {});
|
|
1955
1955
|
} catch {}
|
|
@@ -2045,7 +2045,7 @@ function startInboxWatcher() {
|
|
|
2045
2045
|
const watcher = fs.watch(INBOX_FILE, {persistent: false}, (eventType) => {
|
|
2046
2046
|
if (eventType !== 'change') return;
|
|
2047
2047
|
if (_inboxDebounce) clearTimeout(_inboxDebounce);
|
|
2048
|
-
_inboxDebounce = setTimeout(dispatchInboxClaude,
|
|
2048
|
+
_inboxDebounce = setTimeout(dispatchInboxClaude, 100);
|
|
2049
2049
|
});
|
|
2050
2050
|
watcher.on('error', () => {});
|
|
2051
2051
|
} catch {}
|
|
@@ -2118,7 +2118,7 @@ function runAwayModeCheck() {
|
|
|
2118
2118
|
const stateCtx = lines.join('\n');
|
|
2119
2119
|
const monitorPrompt = lang === 'es'
|
|
2120
2120
|
? `Modo Ausencia activo — revisión automática cada 5 minutos.\n\nEstado del orquestador:\n${stateCtx}\n\nInstrucciones:\n1. Lee INBOX.md — si hay análisis completados sin tarea de implementación en QUEUE.md, créala\n2. Lee QUEUE.md — si hay tareas fallidas no reasignadas, reasígnalas al siguiente agente\n3. Si hay tareas pendientes sin asignar a ningún agente (agent = >0 o vacío), asígnalas a un agente idle (Codex u OpenCode)\n4. Si hay agentes idle y tareas pendientes sin procesar, revisa bloqueos y resuélvelos\n5. Si todo avanza, no hagas nada y responde brevemente "TodoOK"\n\nNo hagas commit ni push. No inventes tareas nuevas.`
|
|
2121
|
-
: `Away Mode active — automatic check every 5 minutes.\n\nOrchestrator state:\n${stateCtx}\n\nInstructions:\n1. Read INBOX.md — if there are completed analyses without implementation tasks in QUEUE.md, create them\n2. Read QUEUE.md — if there are failed tasks not reassigned, reassign to next available agent\n3. If there are pending tasks with no agent assigned (agent = >0 or empty), assign them to an idle agent (Codex or OpenCode)\n4. If there are idle agents and pending tasks not being processed, check for blocking issues\n5. If everything is progressing, do nothing and respond briefly "AllGood"\n\nDo not commit or push. Do not invent new tasks.`;
|
|
2121
|
+
: `Away Mode active — automatic check every 5 minutes.\n\nOrchestrator state:\n${stateCtx}\n\nInstructions:\n1. Read INBOX.md — if there are completed analyses without implementation tasks in QUEUE.md, create them\n2. Read QUEUE.md — if there are failed tasks not reassigned, reassign to next available agent\n3. If there are pending tasks with no agent assigned (agent = >0 or empty), assign them to an idle agent (Codex or OpenCode)\n4. If there are idle agents and pending tasks not being processed, check for blocking issues\n5. If everything is progressing, do nothing and respond briefly "AllGood"\n\nDo not commit or push. Do not invent new tasks.`;
|
|
2122
2122
|
|
|
2123
2123
|
const logPath = path.join(LOG_DIR, `away-check-${Date.now()}.log`);
|
|
2124
2124
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@liriraid/agentflow-ai",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.18",
|
|
4
4
|
"description": "Multi-agent workspace orchestrator with TUI. Coordinates AI coding agents over your real frontend and backend projects.",
|
|
5
5
|
"author": "LiriRaid",
|
|
6
6
|
"homepage": "https://github.com/LiriRaid/agentflow-ai#readme",
|
package/src/ink/app.mjs
CHANGED
|
@@ -33,6 +33,7 @@ const TEXT = {
|
|
|
33
33
|
paused: 'Pausado',
|
|
34
34
|
preview: 'Explorando Ink',
|
|
35
35
|
shortcuts: 'Atajos',
|
|
36
|
+
start: 'reanudar',
|
|
36
37
|
pause: 'pausar',
|
|
37
38
|
quit: 'salir y matar agentes',
|
|
38
39
|
summary: 'Resumen',
|
|
@@ -57,6 +58,7 @@ const TEXT = {
|
|
|
57
58
|
paused: 'Paused',
|
|
58
59
|
preview: 'Exploring Ink',
|
|
59
60
|
shortcuts: 'Shortcuts',
|
|
61
|
+
start: 'resume',
|
|
60
62
|
pause: 'pause',
|
|
61
63
|
quit: 'quit and stop agents',
|
|
62
64
|
summary: 'Summary',
|
|
@@ -137,6 +139,7 @@ export function App({snapshot, paused = false, onAction}) {
|
|
|
137
139
|
}
|
|
138
140
|
|
|
139
141
|
const normalized = input.toLowerCase();
|
|
142
|
+
if (normalized === 's') onAction?.('start');
|
|
140
143
|
if (normalized === 'p') onAction?.('pause');
|
|
141
144
|
if (normalized === 'q') onAction?.('quit');
|
|
142
145
|
});
|
|
@@ -160,11 +163,6 @@ export function App({snapshot, paused = false, onAction}) {
|
|
|
160
163
|
`${snapshot.timestamp} | ${snapshot.stateLabel || (paused ? text.paused : text.preview)} | ${text.active.toLowerCase()} ${liveActiveLabel}`,
|
|
161
164
|
heroWidth
|
|
162
165
|
);
|
|
163
|
-
const shortcutRest = truncate(
|
|
164
|
-
`S ${text.start} P ${text.pause} R ${text.reload} Q ${text.quit}`,
|
|
165
|
-
Math.max(0, shortcutWidth - 8)
|
|
166
|
-
);
|
|
167
|
-
|
|
168
166
|
return h(
|
|
169
167
|
Box,
|
|
170
168
|
{flexDirection: 'column', paddingX: 1, paddingY: 0},
|
|
@@ -185,10 +183,12 @@ export function App({snapshot, paused = false, onAction}) {
|
|
|
185
183
|
Box,
|
|
186
184
|
{marginBottom: 1},
|
|
187
185
|
h(Text, {color: COLORS.warning}, `${text.shortcuts}: `),
|
|
186
|
+
h(Text, {bold: true}, 'S'),
|
|
187
|
+
h(Text, {color: COLORS.muted}, ` ${text.start} `),
|
|
188
188
|
h(Text, {bold: true}, 'P'),
|
|
189
|
-
h(Text, {color: COLORS.muted},
|
|
189
|
+
h(Text, {color: COLORS.muted}, ` ${text.pause} `),
|
|
190
190
|
h(Text, {bold: true}, 'Q'),
|
|
191
|
-
h(Text, {color: COLORS.muted},
|
|
191
|
+
h(Text, {color: COLORS.muted}, ` ${text.quit}`)
|
|
192
192
|
),
|
|
193
193
|
h(
|
|
194
194
|
Box,
|
package/src/ink/index.mjs
CHANGED
|
@@ -90,6 +90,10 @@ if (fs.existsSync(CONTROL_FILE)) {
|
|
|
90
90
|
|
|
91
91
|
let inkApp = null;
|
|
92
92
|
let refreshTimer = null;
|
|
93
|
+
let clockInterval = null;
|
|
94
|
+
let stateWatcher = null;
|
|
95
|
+
let stateWatchDebounce = null;
|
|
96
|
+
let lastRenderedSnapshot = null;
|
|
93
97
|
let spawnedEngine = null;
|
|
94
98
|
let localEvents = [];
|
|
95
99
|
let quitRequested = false;
|
|
@@ -323,6 +327,8 @@ function refresh() {
|
|
|
323
327
|
if (isResizing) return;
|
|
324
328
|
|
|
325
329
|
const snapshot = buildSnapshot();
|
|
330
|
+
lastRenderedSnapshot = snapshot;
|
|
331
|
+
ensureStateWatcher();
|
|
326
332
|
if (!inkApp) {
|
|
327
333
|
inkApp = render(React.createElement(App, {snapshot, onAction: requestAction}), {
|
|
328
334
|
exitOnCtrlC: false,
|
|
@@ -371,6 +377,9 @@ function requestAction(action) {
|
|
|
371
377
|
function shutdown() {
|
|
372
378
|
if (refreshTimer) clearTimeout(refreshTimer);
|
|
373
379
|
if (resizeTimer) clearTimeout(resizeTimer);
|
|
380
|
+
if (clockInterval) clearInterval(clockInterval);
|
|
381
|
+
if (stateWatchDebounce) clearTimeout(stateWatchDebounce);
|
|
382
|
+
if (stateWatcher) { try { stateWatcher.close(); } catch {} stateWatcher = null; }
|
|
374
383
|
if (spawnedEngine && !spawnedEngine.killed && quitRequested) {
|
|
375
384
|
try {
|
|
376
385
|
spawnedEngine.kill('SIGTERM');
|
|
@@ -383,6 +392,40 @@ function shutdown() {
|
|
|
383
392
|
try { fs.unlinkSync(STATE_FILE); } catch {}
|
|
384
393
|
}
|
|
385
394
|
|
|
395
|
+
// Reactivo: dispara un refresh inmediato cuando el engine escribe el STATE_FILE.
|
|
396
|
+
// Esto elimina el lag de polling para cambios de cola y estado de agentes.
|
|
397
|
+
function ensureStateWatcher() {
|
|
398
|
+
if (stateWatcher || !fs.existsSync(STATE_FILE)) return;
|
|
399
|
+
try {
|
|
400
|
+
stateWatcher = fs.watch(STATE_FILE, {persistent: false}, () => {
|
|
401
|
+
if (stateWatchDebounce) clearTimeout(stateWatchDebounce);
|
|
402
|
+
stateWatchDebounce = setTimeout(() => {
|
|
403
|
+
if (quitRequested) return;
|
|
404
|
+
if (refreshTimer) { clearTimeout(refreshTimer); refreshTimer = null; }
|
|
405
|
+
refresh();
|
|
406
|
+
scheduleRefresh();
|
|
407
|
+
}, 50);
|
|
408
|
+
});
|
|
409
|
+
stateWatcher.on('error', () => { stateWatcher = null; });
|
|
410
|
+
} catch { stateWatcher = null; }
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// Tick independiente del reloj: actualiza activeLabel y timestamp cada 1s
|
|
414
|
+
// sin leer el STATE_FILE, usando la última snapshot cacheada.
|
|
415
|
+
function startClockTick() {
|
|
416
|
+
clockInterval = setInterval(() => {
|
|
417
|
+
if (!inkApp || !lastRenderedSnapshot?.startedAt) return;
|
|
418
|
+
const activeSeconds = Math.max(0, Math.round((Date.now() - lastRenderedSnapshot.startedAt) / 1000));
|
|
419
|
+
const updated = {
|
|
420
|
+
...lastRenderedSnapshot,
|
|
421
|
+
activeLabel: formatDuration(activeSeconds),
|
|
422
|
+
timestamp: new Date().toLocaleString(getLocale(), {hour12: false})
|
|
423
|
+
};
|
|
424
|
+
lastRenderedSnapshot = updated;
|
|
425
|
+
inkApp.rerender(React.createElement(App, {snapshot: updated, onAction: requestAction}));
|
|
426
|
+
}, 1000);
|
|
427
|
+
}
|
|
428
|
+
|
|
386
429
|
function scheduleRefresh() {
|
|
387
430
|
const engineState = readEngineState();
|
|
388
431
|
const busy = engineState && Object.values(engineState.agents || {}).some(a => a.status === 'busy');
|
|
@@ -398,6 +441,7 @@ function mount() {
|
|
|
398
441
|
ensureEngine();
|
|
399
442
|
refresh();
|
|
400
443
|
scheduleRefresh();
|
|
444
|
+
startClockTick();
|
|
401
445
|
|
|
402
446
|
if (process.stdout.isTTY) {
|
|
403
447
|
process.stdout.on('resize', () => {
|