cueclaw 0.2.0 → 0.2.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/dist/{app-XQRFUTEX.js → app-PUHQN5BJ.js} +259 -261
- package/dist/cli.js +2 -2
- package/package.json +1 -1
|
@@ -268,8 +268,12 @@ var KeyPriority = {
|
|
|
268
268
|
var KeypressContext = createContext(null);
|
|
269
269
|
function KeypressProvider({ children }) {
|
|
270
270
|
const handlersRef = useRef([]);
|
|
271
|
+
const sortHandlers = () => {
|
|
272
|
+
handlersRef.current.sort((a, b) => b.priority - a.priority);
|
|
273
|
+
};
|
|
271
274
|
const register = useCallback((entry) => {
|
|
272
275
|
handlersRef.current = [...handlersRef.current.filter((h) => h.id !== entry.id), entry];
|
|
276
|
+
sortHandlers();
|
|
273
277
|
}, []);
|
|
274
278
|
const unregister = useCallback((id) => {
|
|
275
279
|
handlersRef.current = handlersRef.current.filter((h) => h.id !== id);
|
|
@@ -280,8 +284,8 @@ function KeypressProvider({ children }) {
|
|
|
280
284
|
);
|
|
281
285
|
}, []);
|
|
282
286
|
useInput((input, key) => {
|
|
283
|
-
const
|
|
284
|
-
|
|
287
|
+
for (const entry of handlersRef.current) {
|
|
288
|
+
if (!entry.isActive) continue;
|
|
285
289
|
const consumed = entry.handler(input, key);
|
|
286
290
|
if (consumed === true) break;
|
|
287
291
|
}
|
|
@@ -369,7 +373,7 @@ function DialogManager({ children }) {
|
|
|
369
373
|
}
|
|
370
374
|
|
|
371
375
|
// src/tui/app-provider.tsx
|
|
372
|
-
import { useReducer, useCallback as useCallback7, useMemo
|
|
376
|
+
import { useReducer, useCallback as useCallback7, useMemo, useState as useState4, useEffect as useEffect3, useRef as useRef6 } from "react";
|
|
373
377
|
import { useApp } from "ink";
|
|
374
378
|
|
|
375
379
|
// src/tui/ui-state-context.ts
|
|
@@ -510,9 +514,13 @@ async function stopDaemonBridge(bridge) {
|
|
|
510
514
|
function stopExternalDaemon() {
|
|
511
515
|
const pid = readPidFile();
|
|
512
516
|
if (pid && isProcessAlive(pid)) {
|
|
513
|
-
|
|
517
|
+
try {
|
|
518
|
+
process.kill(pid, "SIGTERM");
|
|
519
|
+
logger.info({ pid }, "Stopped external daemon");
|
|
520
|
+
} catch (err) {
|
|
521
|
+
logger.warn({ err, pid }, "Failed to stop external daemon");
|
|
522
|
+
}
|
|
514
523
|
removePidFile();
|
|
515
|
-
logger.info({ pid }, "Stopped external daemon");
|
|
516
524
|
}
|
|
517
525
|
}
|
|
518
526
|
|
|
@@ -528,7 +536,9 @@ function useDaemonBridge(config, db, cwd, dispatch) {
|
|
|
528
536
|
dispatch({ type: "ADD_MESSAGE", message: { type: "system", text: "Starting daemon..." } });
|
|
529
537
|
initDaemonBridge(db, config, cwd, { skipBots: !hasConfiguredBots }).then((bridge) => {
|
|
530
538
|
if (cancelled) {
|
|
531
|
-
stopDaemonBridge(bridge)
|
|
539
|
+
stopDaemonBridge(bridge).catch((err) => {
|
|
540
|
+
logger.error({ err }, "Failed to stop daemon bridge after cancellation");
|
|
541
|
+
});
|
|
532
542
|
return;
|
|
533
543
|
}
|
|
534
544
|
bridgeRef.current = bridge;
|
|
@@ -563,7 +573,9 @@ function useDaemonBridge(config, db, cwd, dispatch) {
|
|
|
563
573
|
return () => {
|
|
564
574
|
cancelled = true;
|
|
565
575
|
if (bridgeRef.current) {
|
|
566
|
-
stopDaemonBridge(bridgeRef.current)
|
|
576
|
+
stopDaemonBridge(bridgeRef.current).catch((err) => {
|
|
577
|
+
logger.error({ err }, "Failed to stop daemon bridge during cleanup");
|
|
578
|
+
});
|
|
567
579
|
bridgeRef.current = null;
|
|
568
580
|
}
|
|
569
581
|
};
|
|
@@ -708,7 +720,7 @@ Guidelines:
|
|
|
708
720
|
}
|
|
709
721
|
|
|
710
722
|
// src/tui/hooks/use-planner-session.ts
|
|
711
|
-
function usePlannerSession(config, dispatch
|
|
723
|
+
function usePlannerSession(config, dispatch) {
|
|
712
724
|
const plannerSessionRef = useRef3(null);
|
|
713
725
|
const handleUserMessage = useCallback3(async (text) => {
|
|
714
726
|
if (!config) return;
|
|
@@ -717,28 +729,25 @@ function usePlannerSession(config, dispatch, streamingText) {
|
|
|
717
729
|
dispatch({ type: "SET_STREAMING_TEXT", text: "" });
|
|
718
730
|
try {
|
|
719
731
|
let result;
|
|
732
|
+
let accumulated = "";
|
|
733
|
+
const onToken = (token) => {
|
|
734
|
+
accumulated += token;
|
|
735
|
+
dispatch({ type: "SET_STREAMING_TEXT", text: accumulated });
|
|
736
|
+
};
|
|
720
737
|
const tuiContext = { channel: "tui" };
|
|
721
738
|
if (plannerSessionRef.current && plannerSessionRef.current.status === "conversing") {
|
|
722
739
|
result = await continuePlannerSession(
|
|
723
740
|
plannerSessionRef.current,
|
|
724
741
|
text,
|
|
725
742
|
config,
|
|
726
|
-
{
|
|
727
|
-
onToken: (token) => {
|
|
728
|
-
dispatch({ type: "SET_STREAMING_TEXT", text: (streamingText || "") + token });
|
|
729
|
-
}
|
|
730
|
-
},
|
|
743
|
+
{ onToken },
|
|
731
744
|
tuiContext
|
|
732
745
|
);
|
|
733
746
|
} else {
|
|
734
747
|
result = await startPlannerSession(
|
|
735
748
|
text,
|
|
736
749
|
config,
|
|
737
|
-
{
|
|
738
|
-
onToken: (token) => {
|
|
739
|
-
dispatch({ type: "SET_STREAMING_TEXT", text: (streamingText || "") + token });
|
|
740
|
-
}
|
|
741
|
-
},
|
|
750
|
+
{ onToken },
|
|
742
751
|
tuiContext
|
|
743
752
|
);
|
|
744
753
|
}
|
|
@@ -774,7 +783,7 @@ function usePlannerSession(config, dispatch, streamingText) {
|
|
|
774
783
|
plannerSessionRef.current = null;
|
|
775
784
|
logger.error({ err }, "Planner session failed");
|
|
776
785
|
}
|
|
777
|
-
}, [config
|
|
786
|
+
}, [config]);
|
|
778
787
|
const handleCancelGeneration = useCallback3(() => {
|
|
779
788
|
if (plannerSessionRef.current) {
|
|
780
789
|
cancelPlannerSession(plannerSessionRef.current);
|
|
@@ -801,6 +810,8 @@ function useWorkflowExecution(workflow, db, cwd, bridgeRef, plannerSessionRef, d
|
|
|
801
810
|
upsertWorkflow(db, confirmed);
|
|
802
811
|
} catch (err) {
|
|
803
812
|
logger.error({ err }, "Failed to persist workflow");
|
|
813
|
+
dispatch({ type: "ADD_MESSAGE", message: { type: "error", text: `Failed to save workflow: ${err instanceof Error ? err.message : String(err)}` } });
|
|
814
|
+
return;
|
|
804
815
|
}
|
|
805
816
|
const controller = new AbortController();
|
|
806
817
|
abortRef.current = controller;
|
|
@@ -873,7 +884,11 @@ ${failedSteps.join("\n")}` : "";
|
|
|
873
884
|
const handleCancel = useCallback4(() => {
|
|
874
885
|
if (workflow) {
|
|
875
886
|
const rejected = rejectPlan(workflow);
|
|
876
|
-
|
|
887
|
+
try {
|
|
888
|
+
updateWorkflowPhase(db, workflow.id, rejected.phase);
|
|
889
|
+
} catch (err) {
|
|
890
|
+
logger.error({ err }, "Failed to update workflow phase");
|
|
891
|
+
}
|
|
877
892
|
}
|
|
878
893
|
if (plannerSessionRef.current) {
|
|
879
894
|
plannerSessionRef.current = null;
|
|
@@ -1122,7 +1137,7 @@ function useGlobalKeypress({
|
|
|
1122
1137
|
}
|
|
1123
1138
|
|
|
1124
1139
|
// src/tui/hooks/use-command-dispatch.ts
|
|
1125
|
-
import React4, { useCallback as useCallback6,
|
|
1140
|
+
import React4, { useCallback as useCallback6, useRef as useRef5 } from "react";
|
|
1126
1141
|
|
|
1127
1142
|
// src/tui/commands/registry.ts
|
|
1128
1143
|
var commands = [];
|
|
@@ -1485,7 +1500,8 @@ function useCommandDispatch({
|
|
|
1485
1500
|
showDialog,
|
|
1486
1501
|
dismissDialog
|
|
1487
1502
|
}) {
|
|
1488
|
-
const
|
|
1503
|
+
const commandCtxRef = useRef5(null);
|
|
1504
|
+
commandCtxRef.current = {
|
|
1489
1505
|
db,
|
|
1490
1506
|
config,
|
|
1491
1507
|
cwd,
|
|
@@ -1494,13 +1510,12 @@ function useCommandDispatch({
|
|
|
1494
1510
|
clearMessages: () => dispatch({ type: "SET_MESSAGES", messages: [] }),
|
|
1495
1511
|
setConfig,
|
|
1496
1512
|
setThemeVersion
|
|
1497
|
-
}
|
|
1513
|
+
};
|
|
1498
1514
|
const handleSlashCommand = useCallback6(async (text) => {
|
|
1499
1515
|
if (!config) return false;
|
|
1500
1516
|
const parsed = parseSlashCommand(text);
|
|
1501
1517
|
if (!parsed) return false;
|
|
1502
|
-
commandCtx
|
|
1503
|
-
commandCtx.config = config;
|
|
1518
|
+
const commandCtx = commandCtxRef.current;
|
|
1504
1519
|
dispatch({ type: "ADD_MESSAGE", message: { type: "user", text } });
|
|
1505
1520
|
if (parsed.name === "cancel") {
|
|
1506
1521
|
if (plannerSessionRef.current) {
|
|
@@ -1589,8 +1604,8 @@ function useCommandDispatch({
|
|
|
1589
1604
|
dispatch({ type: "ADD_MESSAGE", message: { type: "assistant", text: `Unknown command: /${parsed.name}. Type /help for available commands.` } });
|
|
1590
1605
|
}
|
|
1591
1606
|
return true;
|
|
1592
|
-
}, [config, db,
|
|
1593
|
-
return { handleSlashCommand
|
|
1607
|
+
}, [config, db, exit, showDialog, dismissDialog]);
|
|
1608
|
+
return { handleSlashCommand };
|
|
1594
1609
|
}
|
|
1595
1610
|
|
|
1596
1611
|
// src/tui/app-provider.tsx
|
|
@@ -1619,8 +1634,10 @@ function appReducer(state, action) {
|
|
|
1619
1634
|
return { ...state, streamingText: action.text };
|
|
1620
1635
|
case "UPDATE_STEP":
|
|
1621
1636
|
return { ...state, stepProgress: new Map(state.stepProgress).set(action.stepId, action.progress) };
|
|
1622
|
-
case "ADD_OUTPUT":
|
|
1623
|
-
|
|
1637
|
+
case "ADD_OUTPUT": {
|
|
1638
|
+
const next = [...state.executionOutput, action.line];
|
|
1639
|
+
return { ...state, executionOutput: next.length > 200 ? next.slice(-200) : next };
|
|
1640
|
+
}
|
|
1624
1641
|
default:
|
|
1625
1642
|
return state;
|
|
1626
1643
|
}
|
|
@@ -1628,7 +1645,7 @@ function appReducer(state, action) {
|
|
|
1628
1645
|
function AppProvider({ cwd, skipOnboarding, children }) {
|
|
1629
1646
|
const { exit } = useApp();
|
|
1630
1647
|
const { showDialog, dismissDialog } = useDialog();
|
|
1631
|
-
const validation =
|
|
1648
|
+
const validation = useMemo(() => validateConfig(), []);
|
|
1632
1649
|
const needsSetup = !skipOnboarding && !validation.valid;
|
|
1633
1650
|
const [config, setConfig] = useState4(() => {
|
|
1634
1651
|
if (needsSetup) return null;
|
|
@@ -1638,7 +1655,7 @@ function AppProvider({ cwd, skipOnboarding, children }) {
|
|
|
1638
1655
|
return null;
|
|
1639
1656
|
}
|
|
1640
1657
|
});
|
|
1641
|
-
const db =
|
|
1658
|
+
const db = useMemo(() => initDb(), []);
|
|
1642
1659
|
const [themeVersion, setThemeVersion] = useState4(0);
|
|
1643
1660
|
const initialState = {
|
|
1644
1661
|
view: needsSetup ? "onboarding" : "chat",
|
|
@@ -1653,7 +1670,16 @@ function AppProvider({ cwd, skipOnboarding, children }) {
|
|
|
1653
1670
|
detailRuns: [],
|
|
1654
1671
|
detailStepRuns: []
|
|
1655
1672
|
};
|
|
1656
|
-
const
|
|
1673
|
+
const msgIdRef = useRef6(0);
|
|
1674
|
+
const [state, rawDispatch] = useReducer(appReducer, initialState);
|
|
1675
|
+
const dispatch = useCallback7((action) => {
|
|
1676
|
+
if (action.type === "ADD_MESSAGE") {
|
|
1677
|
+
msgIdRef.current += 1;
|
|
1678
|
+
rawDispatch({ type: "ADD_MESSAGE", message: { ...action.message, id: msgIdRef.current } });
|
|
1679
|
+
} else {
|
|
1680
|
+
rawDispatch(action);
|
|
1681
|
+
}
|
|
1682
|
+
}, []);
|
|
1657
1683
|
useEffect3(() => {
|
|
1658
1684
|
markSessionStart();
|
|
1659
1685
|
}, []);
|
|
@@ -1663,7 +1689,7 @@ function AppProvider({ cwd, skipOnboarding, children }) {
|
|
|
1663
1689
|
});
|
|
1664
1690
|
}, []);
|
|
1665
1691
|
const { bridgeRef, daemonStatus } = useDaemonBridge(config, db, cwd, dispatch);
|
|
1666
|
-
const planner = usePlannerSession(config, dispatch
|
|
1692
|
+
const planner = usePlannerSession(config, dispatch);
|
|
1667
1693
|
const execution = useWorkflowExecution(
|
|
1668
1694
|
state.workflow,
|
|
1669
1695
|
db,
|
|
@@ -1774,7 +1800,7 @@ function AppProvider({ cwd, skipOnboarding, children }) {
|
|
|
1774
1800
|
execution.handleExecutionBack();
|
|
1775
1801
|
}
|
|
1776
1802
|
}, [state.previousView, state.workflow, db, execution.handleExecutionBack]);
|
|
1777
|
-
const uiState =
|
|
1803
|
+
const uiState = useMemo(() => ({
|
|
1778
1804
|
view: state.view,
|
|
1779
1805
|
messages: state.messages,
|
|
1780
1806
|
workflow: state.workflow,
|
|
@@ -1794,7 +1820,7 @@ function AppProvider({ cwd, skipOnboarding, children }) {
|
|
|
1794
1820
|
detailRuns: state.detailRuns,
|
|
1795
1821
|
detailStepRuns: state.detailStepRuns
|
|
1796
1822
|
}), [state, daemonStatus, execution.isExecuting, config, cwd, footerExtra, footerHints, planner.isConversing, themeVersion]);
|
|
1797
|
-
const uiActions =
|
|
1823
|
+
const uiActions = useMemo(() => ({
|
|
1798
1824
|
handleChatSubmit,
|
|
1799
1825
|
handleCancelGeneration: planner.handleCancelGeneration,
|
|
1800
1826
|
handleConfirm: execution.handleConfirm,
|
|
@@ -1815,7 +1841,7 @@ function AppProvider({ cwd, skipOnboarding, children }) {
|
|
|
1815
1841
|
}
|
|
1816
1842
|
|
|
1817
1843
|
// src/tui/app-layout.tsx
|
|
1818
|
-
import { useMemo as
|
|
1844
|
+
import { useMemo as useMemo5 } from "react";
|
|
1819
1845
|
import { Box as Box21, Static, useStdout as useStdout5 } from "ink";
|
|
1820
1846
|
|
|
1821
1847
|
// src/tui/banner.tsx
|
|
@@ -1881,18 +1907,18 @@ function lerpGradient(stops, t) {
|
|
|
1881
1907
|
import { Box as Box15 } from "ink";
|
|
1882
1908
|
|
|
1883
1909
|
// src/tui/main-content.tsx
|
|
1884
|
-
import { useState as useState6, useMemo as
|
|
1910
|
+
import { useState as useState6, useMemo as useMemo2, useCallback as useCallback9 } from "react";
|
|
1885
1911
|
import { Box as Box13, Text as Text13, useStdout as useStdout2 } from "ink";
|
|
1886
1912
|
|
|
1887
1913
|
// src/tui/thinking-indicator.tsx
|
|
1888
|
-
import { useState as useState5, useEffect as useEffect4, useCallback as useCallback8, useRef as
|
|
1914
|
+
import { useState as useState5, useEffect as useEffect4, useCallback as useCallback8, useRef as useRef7, memo as memo2 } from "react";
|
|
1889
1915
|
import { Box as Box4, Text as Text4 } from "ink";
|
|
1890
1916
|
import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1891
1917
|
var DOTS = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
1892
1918
|
var ThinkingIndicator = memo2(function ThinkingIndicator2({ onCancel }) {
|
|
1893
1919
|
const [elapsed, setElapsed] = useState5(0);
|
|
1894
1920
|
const [frame, setFrame] = useState5(0);
|
|
1895
|
-
const startRef =
|
|
1921
|
+
const startRef = useRef7(Date.now());
|
|
1896
1922
|
useEffect4(() => {
|
|
1897
1923
|
const timer = setInterval(() => {
|
|
1898
1924
|
setElapsed(Math.floor((Date.now() - startRef.current) / 1e3));
|
|
@@ -1908,8 +1934,7 @@ var ThinkingIndicator = memo2(function ThinkingIndicator2({ onCancel }) {
|
|
|
1908
1934
|
return false;
|
|
1909
1935
|
}, [onCancel]));
|
|
1910
1936
|
const gradient = theme.ui.gradient;
|
|
1911
|
-
const
|
|
1912
|
-
const gradientIdx = Math.floor(cyclePos * gradient.length) % gradient.length;
|
|
1937
|
+
const gradientIdx = frame % gradient.length;
|
|
1913
1938
|
const spinnerColor = gradient[gradientIdx] ?? theme.text.accent;
|
|
1914
1939
|
const cancelHint = onCancel ? " (esc to cancel)" : "";
|
|
1915
1940
|
return /* @__PURE__ */ jsxs4(Box4, { paddingX: 1, children: [
|
|
@@ -2048,12 +2073,6 @@ function MainContent() {
|
|
|
2048
2073
|
const { stdout } = useStdout2();
|
|
2049
2074
|
const rows = stdout?.rows ?? 24;
|
|
2050
2075
|
const [scrollOffset, setScrollOffset] = useState6(0);
|
|
2051
|
-
const prevMessageCountRef = useRef6(messages.length);
|
|
2052
|
-
useEffect5(() => {
|
|
2053
|
-
if (messages.length > prevMessageCountRef.current && scrollOffset === 0) {
|
|
2054
|
-
}
|
|
2055
|
-
prevMessageCountRef.current = messages.length;
|
|
2056
|
-
}, [messages.length, scrollOffset]);
|
|
2057
2076
|
const pageSize = Math.max(1, Math.floor(rows / 2));
|
|
2058
2077
|
useKeypress("chat-scroll", KeyPriority.Normal, useCallback9((input, key) => {
|
|
2059
2078
|
if (keyBindings.scrollUp(input, key)) {
|
|
@@ -2066,7 +2085,7 @@ function MainContent() {
|
|
|
2066
2085
|
}
|
|
2067
2086
|
return false;
|
|
2068
2087
|
}, [pageSize, messages.length]));
|
|
2069
|
-
const visibleMessages =
|
|
2088
|
+
const visibleMessages = useMemo2(() => {
|
|
2070
2089
|
if (scrollOffset === 0) return messages;
|
|
2071
2090
|
const end = messages.length - scrollOffset;
|
|
2072
2091
|
return messages.slice(0, Math.max(0, end));
|
|
@@ -2081,7 +2100,7 @@ function MainContent() {
|
|
|
2081
2100
|
" (Ctrl+P/Ctrl+N)"
|
|
2082
2101
|
] }) }),
|
|
2083
2102
|
/* @__PURE__ */ jsxs12(Box13, { flexDirection: "column", flexGrow: 1, marginTop: hiddenAbove > 0 ? 0 : 1, children: [
|
|
2084
|
-
visibleMessages.map((msg
|
|
2103
|
+
visibleMessages.map((msg) => /* @__PURE__ */ jsx16(Box13, { marginBottom: 1, flexDirection: "column", children: /* @__PURE__ */ jsx16(MessageDisplay, { message: msg }) }, msg.id)),
|
|
2085
2104
|
streamingText && /* @__PURE__ */ jsxs12(Box13, { marginBottom: 1, paddingX: 1, children: [
|
|
2086
2105
|
/* @__PURE__ */ jsx16(Box13, { width: 2, children: /* @__PURE__ */ jsx16(Text13, { color: theme.text.accent, children: "\u2726 " }) }),
|
|
2087
2106
|
/* @__PURE__ */ jsx16(Box13, { flexShrink: 1, children: /* @__PURE__ */ jsx16(Text13, { color: theme.text.primary, children: streamingText }) })
|
|
@@ -2092,11 +2111,11 @@ function MainContent() {
|
|
|
2092
2111
|
}
|
|
2093
2112
|
|
|
2094
2113
|
// src/tui/composer.tsx
|
|
2095
|
-
import { useState as useState7, useMemo as
|
|
2114
|
+
import { useState as useState7, useMemo as useMemo3, useCallback as useCallback12 } from "react";
|
|
2096
2115
|
import { Box as Box14, Text as Text15, useStdout as useStdout3 } from "ink";
|
|
2097
2116
|
|
|
2098
2117
|
// src/tui/resettable-input.tsx
|
|
2099
|
-
import { useReducer as useReducer2, useEffect as
|
|
2118
|
+
import { useReducer as useReducer2, useEffect as useEffect5, useRef as useRef8, useCallback as useCallback10 } from "react";
|
|
2100
2119
|
import { Text as Text14 } from "ink";
|
|
2101
2120
|
import { jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
2102
2121
|
function inputReducer(state, action) {
|
|
@@ -2122,13 +2141,13 @@ function inputReducer(state, action) {
|
|
|
2122
2141
|
}
|
|
2123
2142
|
function ResettableInput({ placeholder, onSubmit, onChange, isDisabled, onUpArrow, onDownArrow }) {
|
|
2124
2143
|
const [state, dispatch] = useReducer2(inputReducer, { value: "", prevValue: "", cursor: 0 });
|
|
2125
|
-
const submitRef =
|
|
2126
|
-
|
|
2144
|
+
const submitRef = useRef8(null);
|
|
2145
|
+
useEffect5(() => {
|
|
2127
2146
|
if (state.value !== state.prevValue) {
|
|
2128
2147
|
onChange?.(state.value);
|
|
2129
2148
|
}
|
|
2130
2149
|
}, [state.value, state.prevValue, onChange]);
|
|
2131
|
-
|
|
2150
|
+
useEffect5(() => {
|
|
2132
2151
|
if (submitRef.current !== null) {
|
|
2133
2152
|
const value = submitRef.current;
|
|
2134
2153
|
submitRef.current = null;
|
|
@@ -2136,7 +2155,7 @@ function ResettableInput({ placeholder, onSubmit, onChange, isDisabled, onUpArro
|
|
|
2136
2155
|
}
|
|
2137
2156
|
});
|
|
2138
2157
|
useKeypress("resettable-input", KeyPriority.Normal, useCallback10((input, key) => {
|
|
2139
|
-
if (key.ctrl && input === "c" || key.tab || key.shift && key.tab) return false;
|
|
2158
|
+
if (key.ctrl && input === "c" || key.tab || key.shift && key.tab || key.escape) return false;
|
|
2140
2159
|
if (key.upArrow) {
|
|
2141
2160
|
if (onUpArrow) {
|
|
2142
2161
|
const entry = onUpArrow(state.value);
|
|
@@ -2188,11 +2207,11 @@ function ResettableInput({ placeholder, onSubmit, onChange, isDisabled, onUpArro
|
|
|
2188
2207
|
}
|
|
2189
2208
|
|
|
2190
2209
|
// src/tui/use-input-history.ts
|
|
2191
|
-
import { useRef as
|
|
2210
|
+
import { useRef as useRef9, useCallback as useCallback11 } from "react";
|
|
2192
2211
|
function useInputHistory() {
|
|
2193
|
-
const historyRef =
|
|
2194
|
-
const indexRef =
|
|
2195
|
-
const draftRef =
|
|
2212
|
+
const historyRef = useRef9([]);
|
|
2213
|
+
const indexRef = useRef9(-1);
|
|
2214
|
+
const draftRef = useRef9("");
|
|
2196
2215
|
const up = useCallback11((currentValue) => {
|
|
2197
2216
|
const history = historyRef.current;
|
|
2198
2217
|
if (history.length === 0) return void 0;
|
|
@@ -2259,8 +2278,8 @@ function Composer() {
|
|
|
2259
2278
|
const cols = stdout?.columns ?? 80;
|
|
2260
2279
|
const history = useInputHistory();
|
|
2261
2280
|
const [currentInput, setCurrentInput] = useState7("");
|
|
2262
|
-
const allCommands =
|
|
2263
|
-
const matchingCommands =
|
|
2281
|
+
const allCommands = useMemo3(() => getCommands(), []);
|
|
2282
|
+
const matchingCommands = useMemo3(() => {
|
|
2264
2283
|
if (!currentInput.startsWith("/")) return [];
|
|
2265
2284
|
const prefix = currentInput.toLowerCase();
|
|
2266
2285
|
return allCommands.filter((c) => {
|
|
@@ -2269,6 +2288,18 @@ function Composer() {
|
|
|
2269
2288
|
});
|
|
2270
2289
|
}, [currentInput, allCommands]);
|
|
2271
2290
|
const showCommandHints = currentInput.startsWith("/") && matchingCommands.length > 0 && currentInput !== "/" + matchingCommands[0]?.name;
|
|
2291
|
+
const handleInputChange = useCallback12((value) => {
|
|
2292
|
+
setCurrentInput(value);
|
|
2293
|
+
history.resetBrowsing();
|
|
2294
|
+
}, [history]);
|
|
2295
|
+
const handleInputSubmit = useCallback12((value) => {
|
|
2296
|
+
const trimmed = value.trim();
|
|
2297
|
+
if (trimmed) {
|
|
2298
|
+
history.push(trimmed);
|
|
2299
|
+
setCurrentInput("");
|
|
2300
|
+
handleChatSubmit(trimmed);
|
|
2301
|
+
}
|
|
2302
|
+
}, [history, handleChatSubmit]);
|
|
2272
2303
|
const mode = modeLabel({ isExecuting, isGenerating, isConversing });
|
|
2273
2304
|
const daemon = daemonLabel(daemonStatus);
|
|
2274
2305
|
return /* @__PURE__ */ jsxs14(Fragment3, { children: [
|
|
@@ -2311,18 +2342,8 @@ function Composer() {
|
|
|
2311
2342
|
ResettableInput,
|
|
2312
2343
|
{
|
|
2313
2344
|
placeholder: "Describe a workflow or type /help",
|
|
2314
|
-
onChange:
|
|
2315
|
-
|
|
2316
|
-
history.resetBrowsing();
|
|
2317
|
-
},
|
|
2318
|
-
onSubmit: (value) => {
|
|
2319
|
-
const trimmed = value.trim();
|
|
2320
|
-
if (trimmed) {
|
|
2321
|
-
history.push(trimmed);
|
|
2322
|
-
setCurrentInput("");
|
|
2323
|
-
handleChatSubmit(trimmed);
|
|
2324
|
-
}
|
|
2325
|
-
},
|
|
2345
|
+
onChange: handleInputChange,
|
|
2346
|
+
onSubmit: handleInputSubmit,
|
|
2326
2347
|
onUpArrow: history.up,
|
|
2327
2348
|
onDownArrow: history.down,
|
|
2328
2349
|
isDisabled: isGenerating
|
|
@@ -2344,11 +2365,11 @@ function Chat() {
|
|
|
2344
2365
|
}
|
|
2345
2366
|
|
|
2346
2367
|
// src/tui/plan-view.tsx
|
|
2347
|
-
import { useCallback as
|
|
2368
|
+
import { useCallback as useCallback13, memo as memo10 } from "react";
|
|
2348
2369
|
import { Box as Box16, Text as Text16 } from "ink";
|
|
2349
2370
|
import { jsx as jsx20, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
2350
2371
|
function PlanView({ workflow, onConfirm, onModify, onCancel }) {
|
|
2351
|
-
useKeypress("plan-view-actions", KeyPriority.Normal,
|
|
2372
|
+
useKeypress("plan-view-actions", KeyPriority.Normal, useCallback13((input, key) => {
|
|
2352
2373
|
if (keyBindings.confirmPlan(input, key)) {
|
|
2353
2374
|
onConfirm();
|
|
2354
2375
|
return true;
|
|
@@ -2411,13 +2432,90 @@ var StepLine = memo10(function StepLine2({ step, index }) {
|
|
|
2411
2432
|
});
|
|
2412
2433
|
|
|
2413
2434
|
// src/tui/execution-view.tsx
|
|
2414
|
-
import { useCallback as
|
|
2435
|
+
import { useCallback as useCallback14 } from "react";
|
|
2415
2436
|
import { Box as Box17, Text as Text17 } from "ink";
|
|
2437
|
+
|
|
2438
|
+
// src/tui/format-utils.ts
|
|
2439
|
+
function stepStatusIcon(status) {
|
|
2440
|
+
switch (status) {
|
|
2441
|
+
case "succeeded":
|
|
2442
|
+
return "\u2713";
|
|
2443
|
+
case "running":
|
|
2444
|
+
return "\u22B7";
|
|
2445
|
+
case "failed":
|
|
2446
|
+
return "\u2717";
|
|
2447
|
+
case "skipped":
|
|
2448
|
+
return "\u25CB";
|
|
2449
|
+
default:
|
|
2450
|
+
return "\u25CB";
|
|
2451
|
+
}
|
|
2452
|
+
}
|
|
2453
|
+
function stepStatusColor(status) {
|
|
2454
|
+
switch (status) {
|
|
2455
|
+
case "succeeded":
|
|
2456
|
+
return theme.status.success;
|
|
2457
|
+
case "running":
|
|
2458
|
+
return theme.status.warning;
|
|
2459
|
+
case "failed":
|
|
2460
|
+
return theme.status.error;
|
|
2461
|
+
case "skipped":
|
|
2462
|
+
return theme.status.muted;
|
|
2463
|
+
default:
|
|
2464
|
+
return theme.status.muted;
|
|
2465
|
+
}
|
|
2466
|
+
}
|
|
2467
|
+
function phaseColor2(phase) {
|
|
2468
|
+
switch (phase) {
|
|
2469
|
+
case "executing":
|
|
2470
|
+
return theme.status.warning;
|
|
2471
|
+
case "active":
|
|
2472
|
+
return theme.status.success;
|
|
2473
|
+
case "completed":
|
|
2474
|
+
return theme.status.success;
|
|
2475
|
+
case "failed":
|
|
2476
|
+
return theme.status.error;
|
|
2477
|
+
case "paused":
|
|
2478
|
+
return theme.status.muted;
|
|
2479
|
+
default:
|
|
2480
|
+
return theme.text.primary;
|
|
2481
|
+
}
|
|
2482
|
+
}
|
|
2483
|
+
function runStatusColor(status) {
|
|
2484
|
+
switch (status) {
|
|
2485
|
+
case "completed":
|
|
2486
|
+
return theme.status.success;
|
|
2487
|
+
case "running":
|
|
2488
|
+
return theme.status.warning;
|
|
2489
|
+
case "failed":
|
|
2490
|
+
return theme.status.error;
|
|
2491
|
+
default:
|
|
2492
|
+
return theme.text.primary;
|
|
2493
|
+
}
|
|
2494
|
+
}
|
|
2495
|
+
function formatDuration2(ms) {
|
|
2496
|
+
if (ms < 1e3) return `${ms}ms`;
|
|
2497
|
+
if (ms < 6e4) return `${Math.round(ms / 1e3)}s`;
|
|
2498
|
+
return `${Math.round(ms / 6e4)}m`;
|
|
2499
|
+
}
|
|
2500
|
+
function formatTrigger(trigger) {
|
|
2501
|
+
switch (trigger.type) {
|
|
2502
|
+
case "poll":
|
|
2503
|
+
return `poll every ${trigger.interval_seconds}s (${trigger.diff_mode})`;
|
|
2504
|
+
case "cron":
|
|
2505
|
+
return `cron: ${trigger.expression}${trigger.timezone ? ` (${trigger.timezone})` : ""}`;
|
|
2506
|
+
case "manual":
|
|
2507
|
+
return "manual";
|
|
2508
|
+
default:
|
|
2509
|
+
return trigger.type;
|
|
2510
|
+
}
|
|
2511
|
+
}
|
|
2512
|
+
|
|
2513
|
+
// src/tui/execution-view.tsx
|
|
2416
2514
|
import { jsx as jsx21, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
2417
2515
|
function ExecutionView({ workflow, stepProgress, output, onBack, onAbort }) {
|
|
2418
2516
|
const { isExecuting } = useUIState();
|
|
2419
2517
|
const isRunning = Array.from(stepProgress.values()).some((s) => s.status === "running");
|
|
2420
|
-
useKeypress("execution-view-actions", KeyPriority.Normal,
|
|
2518
|
+
useKeypress("execution-view-actions", KeyPriority.Normal, useCallback14((input, key) => {
|
|
2421
2519
|
if (isExecuting && onAbort && keyBindings.abortExec(input, key)) {
|
|
2422
2520
|
onAbort();
|
|
2423
2521
|
return true;
|
|
@@ -2445,9 +2543,9 @@ function ExecutionView({ workflow, stepProgress, output, onBack, onAbort }) {
|
|
|
2445
2543
|
workflow.steps.map((step, i) => {
|
|
2446
2544
|
const progress = stepProgress.get(step.id);
|
|
2447
2545
|
const status = progress?.status ?? "pending";
|
|
2448
|
-
const icon =
|
|
2546
|
+
const icon = stepStatusIcon(status);
|
|
2449
2547
|
const durationText = progress?.duration ? ` (${formatDuration2(progress.duration)})` : "";
|
|
2450
|
-
return /* @__PURE__ */ jsx21(Box17, { children: /* @__PURE__ */ jsxs17(Text17, { color:
|
|
2548
|
+
return /* @__PURE__ */ jsx21(Box17, { children: /* @__PURE__ */ jsxs17(Text17, { color: stepStatusColor(status), children: [
|
|
2451
2549
|
icon,
|
|
2452
2550
|
" ",
|
|
2453
2551
|
i + 1,
|
|
@@ -2464,47 +2562,20 @@ function ExecutionView({ workflow, stepProgress, output, onBack, onAbort }) {
|
|
|
2464
2562
|
/* @__PURE__ */ jsx21(Box17, { marginTop: 1, children: /* @__PURE__ */ jsx21(Text17, { color: theme.ui.comment, children: isExecuting ? "Press [X] to cancel" : "Press Enter, Q, or Esc to return to chat" }) })
|
|
2465
2563
|
] });
|
|
2466
2564
|
}
|
|
2467
|
-
function statusIcon(status) {
|
|
2468
|
-
switch (status) {
|
|
2469
|
-
case "succeeded":
|
|
2470
|
-
return "\u2713";
|
|
2471
|
-
case "running":
|
|
2472
|
-
return "\u22B7";
|
|
2473
|
-
case "failed":
|
|
2474
|
-
return "\u2717";
|
|
2475
|
-
case "skipped":
|
|
2476
|
-
return "\u25CB";
|
|
2477
|
-
default:
|
|
2478
|
-
return "\u25CB";
|
|
2479
|
-
}
|
|
2480
|
-
}
|
|
2481
|
-
function statusColor(status) {
|
|
2482
|
-
switch (status) {
|
|
2483
|
-
case "succeeded":
|
|
2484
|
-
return theme.status.success;
|
|
2485
|
-
case "running":
|
|
2486
|
-
return theme.status.warning;
|
|
2487
|
-
case "failed":
|
|
2488
|
-
return theme.status.error;
|
|
2489
|
-
case "skipped":
|
|
2490
|
-
return theme.status.muted;
|
|
2491
|
-
default:
|
|
2492
|
-
return theme.status.muted;
|
|
2493
|
-
}
|
|
2494
|
-
}
|
|
2495
|
-
function formatDuration2(ms) {
|
|
2496
|
-
if (ms < 1e3) return `${ms}ms`;
|
|
2497
|
-
return `${Math.round(ms / 1e3)}s`;
|
|
2498
|
-
}
|
|
2499
2565
|
|
|
2500
2566
|
// src/tui/workflow-detail-view.tsx
|
|
2501
|
-
import { useState as useState8, useCallback as
|
|
2567
|
+
import { useState as useState8, useCallback as useCallback15, useEffect as useEffect6 } from "react";
|
|
2502
2568
|
import { Box as Box18, Text as Text18 } from "ink";
|
|
2503
2569
|
import { Fragment as Fragment4, jsx as jsx22, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
2504
2570
|
function WorkflowDetailView({ workflow, runs, latestStepRuns, onBack, onSelectRun, onStop }) {
|
|
2505
2571
|
const [selectedRunIndex, setSelectedRunIndex] = useState8(0);
|
|
2506
2572
|
const displayRuns = runs.slice(0, 5);
|
|
2507
|
-
|
|
2573
|
+
useEffect6(() => {
|
|
2574
|
+
if (selectedRunIndex >= displayRuns.length && displayRuns.length > 0) {
|
|
2575
|
+
setSelectedRunIndex(displayRuns.length - 1);
|
|
2576
|
+
}
|
|
2577
|
+
}, [displayRuns.length, selectedRunIndex]);
|
|
2578
|
+
useKeypress("detail-view-actions", KeyPriority.Normal, useCallback15((input, key) => {
|
|
2508
2579
|
if (keyBindings.escape(input, key) || keyBindings.quit(input, key)) {
|
|
2509
2580
|
onBack();
|
|
2510
2581
|
return true;
|
|
@@ -2566,8 +2637,8 @@ function WorkflowDetailView({ workflow, runs, latestStepRuns, onBack, onSelectRu
|
|
|
2566
2637
|
workflow.steps.map((step, i) => {
|
|
2567
2638
|
const deps = step.depends_on.length > 0 ? ` \u2192 depends on: ${step.depends_on.join(", ")}` : "";
|
|
2568
2639
|
const stepRun = latestStepRuns.find((sr) => sr.step_id === step.id);
|
|
2569
|
-
const icon = stepRun ?
|
|
2570
|
-
const iconColor = stepRun ?
|
|
2640
|
+
const icon = stepRun ? stepStatusIcon(stepRun.status) : "\u25CB";
|
|
2641
|
+
const iconColor = stepRun ? stepStatusColor(stepRun.status) : theme.status.muted;
|
|
2571
2642
|
return /* @__PURE__ */ jsxs18(Box18, { children: [
|
|
2572
2643
|
/* @__PURE__ */ jsxs18(Text18, { color: iconColor, children: [
|
|
2573
2644
|
icon,
|
|
@@ -2593,7 +2664,7 @@ function WorkflowDetailView({ workflow, runs, latestStepRuns, onBack, onSelectRu
|
|
|
2593
2664
|
displayRuns.map((run, i) => /* @__PURE__ */ jsx22(Box18, { children: /* @__PURE__ */ jsxs18(Text18, { inverse: i === selectedRunIndex, children: [
|
|
2594
2665
|
/* @__PURE__ */ jsx22(Text18, { color: runStatusColor(run.status), children: run.status.padEnd(14) }),
|
|
2595
2666
|
/* @__PURE__ */ jsx22(Text18, { children: run.started_at.slice(0, 22).padEnd(24) }),
|
|
2596
|
-
/* @__PURE__ */ jsx22(Text18, { children: (run.duration_ms != null ?
|
|
2667
|
+
/* @__PURE__ */ jsx22(Text18, { children: (run.duration_ms != null ? formatDuration2(run.duration_ms) : "\u2014").padEnd(12) }),
|
|
2597
2668
|
/* @__PURE__ */ jsx22(Text18, { color: theme.status.error, children: run.error ? run.error.slice(0, 40) : "" })
|
|
2598
2669
|
] }) }, run.id))
|
|
2599
2670
|
] })
|
|
@@ -2605,80 +2676,9 @@ function WorkflowDetailView({ workflow, runs, latestStepRuns, onBack, onSelectRu
|
|
|
2605
2676
|
] }) })
|
|
2606
2677
|
] });
|
|
2607
2678
|
}
|
|
2608
|
-
function phaseColor2(phase) {
|
|
2609
|
-
switch (phase) {
|
|
2610
|
-
case "executing":
|
|
2611
|
-
return theme.status.warning;
|
|
2612
|
-
case "active":
|
|
2613
|
-
return theme.status.success;
|
|
2614
|
-
case "completed":
|
|
2615
|
-
return theme.status.success;
|
|
2616
|
-
case "failed":
|
|
2617
|
-
return theme.status.error;
|
|
2618
|
-
case "paused":
|
|
2619
|
-
return theme.status.muted;
|
|
2620
|
-
default:
|
|
2621
|
-
return theme.text.primary;
|
|
2622
|
-
}
|
|
2623
|
-
}
|
|
2624
|
-
function runStatusColor(status) {
|
|
2625
|
-
switch (status) {
|
|
2626
|
-
case "completed":
|
|
2627
|
-
return theme.status.success;
|
|
2628
|
-
case "running":
|
|
2629
|
-
return theme.status.warning;
|
|
2630
|
-
case "failed":
|
|
2631
|
-
return theme.status.error;
|
|
2632
|
-
default:
|
|
2633
|
-
return theme.text.primary;
|
|
2634
|
-
}
|
|
2635
|
-
}
|
|
2636
|
-
function statusIcon2(status) {
|
|
2637
|
-
switch (status) {
|
|
2638
|
-
case "succeeded":
|
|
2639
|
-
return "\u2713";
|
|
2640
|
-
case "running":
|
|
2641
|
-
return "\u22B7";
|
|
2642
|
-
case "failed":
|
|
2643
|
-
return "\u2717";
|
|
2644
|
-
case "skipped":
|
|
2645
|
-
return "\u25CB";
|
|
2646
|
-
default:
|
|
2647
|
-
return "\u25CB";
|
|
2648
|
-
}
|
|
2649
|
-
}
|
|
2650
|
-
function statusColor2(status) {
|
|
2651
|
-
switch (status) {
|
|
2652
|
-
case "succeeded":
|
|
2653
|
-
return theme.status.success;
|
|
2654
|
-
case "running":
|
|
2655
|
-
return theme.status.warning;
|
|
2656
|
-
case "failed":
|
|
2657
|
-
return theme.status.error;
|
|
2658
|
-
case "skipped":
|
|
2659
|
-
return theme.status.muted;
|
|
2660
|
-
default:
|
|
2661
|
-
return theme.status.muted;
|
|
2662
|
-
}
|
|
2663
|
-
}
|
|
2664
|
-
function formatTrigger(trigger) {
|
|
2665
|
-
switch (trigger.type) {
|
|
2666
|
-
case "poll":
|
|
2667
|
-
return `poll every ${trigger.interval_seconds}s (${trigger.diff_mode})`;
|
|
2668
|
-
case "cron":
|
|
2669
|
-
return `cron: ${trigger.expression}${trigger.timezone ? ` (${trigger.timezone})` : ""}`;
|
|
2670
|
-
case "manual":
|
|
2671
|
-
return "manual";
|
|
2672
|
-
}
|
|
2673
|
-
}
|
|
2674
|
-
function formatDuration3(ms) {
|
|
2675
|
-
if (ms < 1e3) return `${ms}ms`;
|
|
2676
|
-
if (ms < 6e4) return `${Math.round(ms / 1e3)}s`;
|
|
2677
|
-
return `${Math.round(ms / 6e4)}m`;
|
|
2678
|
-
}
|
|
2679
2679
|
|
|
2680
2680
|
// src/tui/onboarding.tsx
|
|
2681
|
-
import { useState as useState9, useCallback as
|
|
2681
|
+
import { useState as useState9, useCallback as useCallback16, useMemo as useMemo4, useRef as useRef10, useEffect as useEffect7 } from "react";
|
|
2682
2682
|
import { Box as Box19, Text as Text19, useStdout as useStdout4 } from "ink";
|
|
2683
2683
|
import { TextInput, PasswordInput, ConfirmInput, Spinner, StatusMessage } from "@inkjs/ui";
|
|
2684
2684
|
import { jsx as jsx23, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
@@ -2686,11 +2686,23 @@ function maskKey(key) {
|
|
|
2686
2686
|
if (key.length <= 8) return "****";
|
|
2687
2687
|
return key.slice(0, 4) + "****" + key.slice(-4);
|
|
2688
2688
|
}
|
|
2689
|
+
function StepLayout({ children, input }) {
|
|
2690
|
+
return /* @__PURE__ */ jsxs19(Box19, { flexDirection: "column", flexGrow: 1, children: [
|
|
2691
|
+
/* @__PURE__ */ jsx23(Box19, { flexDirection: "column", flexGrow: 1, children }),
|
|
2692
|
+
/* @__PURE__ */ jsx23(Box19, { marginTop: 1, children: input })
|
|
2693
|
+
] });
|
|
2694
|
+
}
|
|
2689
2695
|
function Onboarding({ onComplete, onCancel, issues }) {
|
|
2690
2696
|
const { stdout } = useStdout4();
|
|
2691
2697
|
const cols = stdout?.columns ?? 80;
|
|
2692
|
-
const existing =
|
|
2693
|
-
const
|
|
2698
|
+
const existing = useMemo4(() => loadExistingConfig(), []);
|
|
2699
|
+
const completeTimerRef = useRef10(null);
|
|
2700
|
+
useEffect7(() => {
|
|
2701
|
+
return () => {
|
|
2702
|
+
if (completeTimerRef.current) clearTimeout(completeTimerRef.current);
|
|
2703
|
+
};
|
|
2704
|
+
}, []);
|
|
2705
|
+
const initialStep = useMemo4(() => {
|
|
2694
2706
|
if (!issues || issues.length === 0) return "welcome";
|
|
2695
2707
|
const errorFields = new Set(issues.filter((i) => i.severity === "error").map((i) => i.field));
|
|
2696
2708
|
if (errorFields.size === 1 && errorFields.has("claude.api_key")) {
|
|
@@ -2707,62 +2719,46 @@ function Onboarding({ onComplete, onCancel, issues }) {
|
|
|
2707
2719
|
telegramToken: existing.telegramToken ?? "",
|
|
2708
2720
|
whatsappEnabled: existing.whatsappEnabled ?? false
|
|
2709
2721
|
});
|
|
2710
|
-
const env = checkEnvironment();
|
|
2711
|
-
useKeypress("onboarding-cancel", KeyPriority.Normal,
|
|
2722
|
+
const env = useMemo4(() => checkEnvironment(), []);
|
|
2723
|
+
useKeypress("onboarding-cancel", KeyPriority.Normal, useCallback16((input, key) => {
|
|
2712
2724
|
if (onCancel && keyBindings.escape(input, key)) {
|
|
2725
|
+
if (completeTimerRef.current) clearTimeout(completeTimerRef.current);
|
|
2713
2726
|
onCancel();
|
|
2714
2727
|
return true;
|
|
2715
2728
|
}
|
|
2716
2729
|
return false;
|
|
2717
2730
|
}, [onCancel]));
|
|
2718
|
-
const gotoApiKey =
|
|
2731
|
+
const gotoApiKey = useCallback16(() => {
|
|
2719
2732
|
setStep(existing.apiKey ? "api_key_existing" : "api_key");
|
|
2720
2733
|
}, [existing]);
|
|
2721
|
-
const gotoBaseUrl =
|
|
2734
|
+
const gotoBaseUrl = useCallback16(() => {
|
|
2722
2735
|
if (!isDev) return;
|
|
2723
2736
|
setStep(existing.baseUrl ? "base_url_existing" : "base_url");
|
|
2724
2737
|
}, [existing]);
|
|
2725
|
-
const
|
|
2738
|
+
const gotoTelegram = useCallback16(() => {
|
|
2739
|
+
setStep(existing.telegramEnabled !== void 0 ? "telegram_existing" : "telegram");
|
|
2740
|
+
}, [existing]);
|
|
2741
|
+
const gotoContainer = useCallback16(() => {
|
|
2726
2742
|
if (!(env.docker && env.dockerRunning)) {
|
|
2727
2743
|
gotoTelegram();
|
|
2728
2744
|
return;
|
|
2729
2745
|
}
|
|
2730
2746
|
setStep(existing.containerEnabled !== void 0 ? "container_existing" : "container");
|
|
2731
|
-
}, [env, existing]);
|
|
2732
|
-
const
|
|
2733
|
-
setStep(existing.telegramEnabled !== void 0 ? "telegram_existing" : "telegram");
|
|
2734
|
-
}, [existing]);
|
|
2735
|
-
const gotoTelegramToken = useCallback15(() => {
|
|
2747
|
+
}, [env, existing, gotoTelegram]);
|
|
2748
|
+
const gotoTelegramToken = useCallback16(() => {
|
|
2736
2749
|
setStep(existing.telegramToken ? "telegram_token_existing" : "telegram_token");
|
|
2737
2750
|
}, [existing]);
|
|
2738
|
-
const gotoWhatsApp =
|
|
2751
|
+
const gotoWhatsApp = useCallback16(() => {
|
|
2739
2752
|
setStep(existing.whatsappEnabled !== void 0 ? "whatsapp_existing" : "whatsapp");
|
|
2740
2753
|
}, [existing]);
|
|
2741
|
-
const afterValidation =
|
|
2754
|
+
const afterValidation = useCallback16(() => {
|
|
2742
2755
|
if (env.docker && env.dockerRunning) {
|
|
2743
2756
|
gotoContainer();
|
|
2744
2757
|
} else {
|
|
2745
2758
|
gotoTelegram();
|
|
2746
2759
|
}
|
|
2747
2760
|
}, [env, gotoContainer, gotoTelegram]);
|
|
2748
|
-
const
|
|
2749
|
-
const key = value.trim();
|
|
2750
|
-
if (!key) return;
|
|
2751
|
-
setState((s) => ({ ...s, apiKey: key }));
|
|
2752
|
-
if (isDev) {
|
|
2753
|
-
gotoBaseUrl();
|
|
2754
|
-
} else {
|
|
2755
|
-
setStep("validating");
|
|
2756
|
-
doValidation(key, "");
|
|
2757
|
-
}
|
|
2758
|
-
}, [gotoBaseUrl]);
|
|
2759
|
-
const handleBaseUrlSubmit = useCallback15((value) => {
|
|
2760
|
-
const url = value.trim();
|
|
2761
|
-
setState((s) => ({ ...s, baseUrl: url }));
|
|
2762
|
-
setStep("validating");
|
|
2763
|
-
doValidation(state.apiKey, url);
|
|
2764
|
-
}, [state.apiKey]);
|
|
2765
|
-
const doValidation = useCallback15(async (apiKey, baseUrl) => {
|
|
2761
|
+
const doValidation = useCallback16(async (apiKey, baseUrl) => {
|
|
2766
2762
|
const prevKey = process.env["ANTHROPIC_API_KEY"];
|
|
2767
2763
|
const prevUrl = process.env["ANTHROPIC_BASE_URL"];
|
|
2768
2764
|
process.env["ANTHROPIC_API_KEY"] = apiKey;
|
|
@@ -2791,35 +2787,44 @@ function Onboarding({ onComplete, onCancel, issues }) {
|
|
|
2791
2787
|
else delete process.env["ANTHROPIC_BASE_URL"];
|
|
2792
2788
|
}
|
|
2793
2789
|
}, [afterValidation]);
|
|
2794
|
-
const
|
|
2790
|
+
const handleApiKeySubmit = useCallback16((value) => {
|
|
2791
|
+
const key = value.trim();
|
|
2792
|
+
if (!key) return;
|
|
2793
|
+
setState((s) => ({ ...s, apiKey: key }));
|
|
2794
|
+
if (isDev) {
|
|
2795
|
+
gotoBaseUrl();
|
|
2796
|
+
} else {
|
|
2797
|
+
setStep("validating");
|
|
2798
|
+
doValidation(key, "");
|
|
2799
|
+
}
|
|
2800
|
+
}, [gotoBaseUrl, doValidation]);
|
|
2801
|
+
const handleBaseUrlSubmit = useCallback16((value) => {
|
|
2802
|
+
const url = value.trim();
|
|
2803
|
+
setState((s) => ({ ...s, baseUrl: url }));
|
|
2804
|
+
setStep("validating");
|
|
2805
|
+
doValidation(state.apiKey, url);
|
|
2806
|
+
}, [state.apiKey, doValidation]);
|
|
2807
|
+
const handleContainerYes = useCallback16(() => {
|
|
2795
2808
|
setState((s) => ({ ...s, containerEnabled: true }));
|
|
2796
2809
|
gotoTelegram();
|
|
2797
2810
|
}, [gotoTelegram]);
|
|
2798
|
-
const handleContainerNo =
|
|
2811
|
+
const handleContainerNo = useCallback16(() => {
|
|
2799
2812
|
setState((s) => ({ ...s, containerEnabled: false }));
|
|
2800
2813
|
gotoTelegram();
|
|
2801
2814
|
}, [gotoTelegram]);
|
|
2802
|
-
const handleTelegramYes =
|
|
2815
|
+
const handleTelegramYes = useCallback16(() => {
|
|
2803
2816
|
setState((s) => ({ ...s, telegramEnabled: true }));
|
|
2804
2817
|
gotoTelegramToken();
|
|
2805
2818
|
}, [gotoTelegramToken]);
|
|
2806
|
-
const handleTelegramNo =
|
|
2819
|
+
const handleTelegramNo = useCallback16(() => {
|
|
2807
2820
|
setState((s) => ({ ...s, telegramEnabled: false }));
|
|
2808
2821
|
gotoWhatsApp();
|
|
2809
2822
|
}, [gotoWhatsApp]);
|
|
2810
|
-
const handleTelegramTokenSubmit =
|
|
2823
|
+
const handleTelegramTokenSubmit = useCallback16((value) => {
|
|
2811
2824
|
setState((s) => ({ ...s, telegramToken: value.trim() }));
|
|
2812
2825
|
gotoWhatsApp();
|
|
2813
2826
|
}, [gotoWhatsApp]);
|
|
2814
|
-
const
|
|
2815
|
-
const next = { ...state, whatsappEnabled: enabled };
|
|
2816
|
-
setState((s) => ({ ...s, whatsappEnabled: enabled }));
|
|
2817
|
-
setStep("saving");
|
|
2818
|
-
doSaveConfig(next);
|
|
2819
|
-
}, [state]);
|
|
2820
|
-
const handleWhatsAppYes = useCallback15(() => finishWithWhatsApp(true), [finishWithWhatsApp]);
|
|
2821
|
-
const handleWhatsAppNo = useCallback15(() => finishWithWhatsApp(false), [finishWithWhatsApp]);
|
|
2822
|
-
const doSaveConfig = useCallback15((finalState) => {
|
|
2827
|
+
const doSaveConfig = useCallback16((finalState) => {
|
|
2823
2828
|
if (isDev) {
|
|
2824
2829
|
writeEnvVar("ANTHROPIC_API_KEY", finalState.apiKey);
|
|
2825
2830
|
if (finalState.baseUrl) {
|
|
@@ -2848,12 +2853,16 @@ function Onboarding({ onComplete, onCancel, issues }) {
|
|
|
2848
2853
|
}
|
|
2849
2854
|
const config = loadConfig();
|
|
2850
2855
|
setStep("done");
|
|
2851
|
-
setTimeout(() => onComplete(config), 1500);
|
|
2856
|
+
completeTimerRef.current = setTimeout(() => onComplete(config), 1500);
|
|
2852
2857
|
}, [onComplete]);
|
|
2853
|
-
const
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2858
|
+
const finishWithWhatsApp = useCallback16((enabled) => {
|
|
2859
|
+
const next = { ...state, whatsappEnabled: enabled };
|
|
2860
|
+
setState((s) => ({ ...s, whatsappEnabled: enabled }));
|
|
2861
|
+
setStep("saving");
|
|
2862
|
+
doSaveConfig(next);
|
|
2863
|
+
}, [state, doSaveConfig]);
|
|
2864
|
+
const handleWhatsAppYes = useCallback16(() => finishWithWhatsApp(true), [finishWithWhatsApp]);
|
|
2865
|
+
const handleWhatsAppNo = useCallback16(() => finishWithWhatsApp(false), [finishWithWhatsApp]);
|
|
2857
2866
|
return /* @__PURE__ */ jsxs19(Box19, { flexDirection: "column", paddingX: 1, flexGrow: 1, children: [
|
|
2858
2867
|
onCancel && /* @__PURE__ */ jsx23(Box19, { justifyContent: "flex-end", children: /* @__PURE__ */ jsx23(Text19, { dimColor: true, children: "Press Esc to cancel" }) }),
|
|
2859
2868
|
step === "welcome" && /* @__PURE__ */ jsxs19(
|
|
@@ -3106,19 +3115,24 @@ function Onboarding({ onComplete, onCancel, issues }) {
|
|
|
3106
3115
|
}
|
|
3107
3116
|
|
|
3108
3117
|
// src/tui/status.tsx
|
|
3109
|
-
import { useState as useState10, useCallback as
|
|
3118
|
+
import { useState as useState10, useCallback as useCallback17, useEffect as useEffect8 } from "react";
|
|
3110
3119
|
import { Box as Box20, Text as Text20 } from "ink";
|
|
3111
3120
|
import { Fragment as Fragment5, jsx as jsx24, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
3112
3121
|
function Status({ workflows, onSelect, onBack, onStop, onDelete }) {
|
|
3113
3122
|
const [selectedIndex, setSelectedIndex] = useState10(0);
|
|
3114
3123
|
const [confirm, setConfirm] = useState10(null);
|
|
3115
3124
|
const [message, setMessage] = useState10(null);
|
|
3116
|
-
|
|
3125
|
+
useEffect8(() => {
|
|
3126
|
+
if (selectedIndex >= workflows.length && workflows.length > 0) {
|
|
3127
|
+
setSelectedIndex(workflows.length - 1);
|
|
3128
|
+
}
|
|
3129
|
+
}, [workflows.length, selectedIndex]);
|
|
3130
|
+
useEffect8(() => {
|
|
3117
3131
|
if (!message) return;
|
|
3118
3132
|
const timer = setTimeout(() => setMessage(null), 3e3);
|
|
3119
3133
|
return () => clearTimeout(timer);
|
|
3120
3134
|
}, [message]);
|
|
3121
|
-
useKeypress("status-view", KeyPriority.Normal,
|
|
3135
|
+
useKeypress("status-view", KeyPriority.Normal, useCallback17((input, key) => {
|
|
3122
3136
|
if (confirm) {
|
|
3123
3137
|
if (keyBindings.confirmYes(input, key)) {
|
|
3124
3138
|
const wf = workflows.find((w) => w.id === confirm.workflowId);
|
|
@@ -3167,22 +3181,6 @@ function Status({ workflows, onSelect, onBack, onStop, onDelete }) {
|
|
|
3167
3181
|
}
|
|
3168
3182
|
return false;
|
|
3169
3183
|
}, [confirm, message, selectedIndex, workflows, onSelect, onBack, onStop, onDelete]));
|
|
3170
|
-
const phaseColor3 = (phase) => {
|
|
3171
|
-
switch (phase) {
|
|
3172
|
-
case "executing":
|
|
3173
|
-
return theme.status.warning;
|
|
3174
|
-
case "active":
|
|
3175
|
-
return theme.status.success;
|
|
3176
|
-
case "completed":
|
|
3177
|
-
return theme.status.success;
|
|
3178
|
-
case "failed":
|
|
3179
|
-
return theme.status.error;
|
|
3180
|
-
case "paused":
|
|
3181
|
-
return theme.status.muted;
|
|
3182
|
-
default:
|
|
3183
|
-
return theme.text.primary;
|
|
3184
|
-
}
|
|
3185
|
-
};
|
|
3186
3184
|
return /* @__PURE__ */ jsxs20(Box20, { flexDirection: "column", paddingX: 1, flexGrow: 1, children: [
|
|
3187
3185
|
/* @__PURE__ */ jsxs20(Box20, { flexDirection: "column", flexGrow: 1, children: [
|
|
3188
3186
|
/* @__PURE__ */ jsx24(Text20, { bold: true, color: theme.border.accent, children: "Workflows" }),
|
|
@@ -3196,7 +3194,7 @@ function Status({ workflows, onSelect, onBack, onStop, onDelete }) {
|
|
|
3196
3194
|
workflows.map((wf, i) => /* @__PURE__ */ jsx24(Box20, { children: /* @__PURE__ */ jsxs20(Text20, { inverse: i === selectedIndex, children: [
|
|
3197
3195
|
/* @__PURE__ */ jsx24(Text20, { children: wf.id.slice(0, 12).padEnd(14) }),
|
|
3198
3196
|
/* @__PURE__ */ jsx24(Text20, { children: wf.name.slice(0, 26).padEnd(28) }),
|
|
3199
|
-
/* @__PURE__ */ jsx24(Text20, { color:
|
|
3197
|
+
/* @__PURE__ */ jsx24(Text20, { color: phaseColor2(wf.phase), children: wf.phase })
|
|
3200
3198
|
] }) }, wf.id))
|
|
3201
3199
|
] })
|
|
3202
3200
|
] }),
|
|
@@ -3221,10 +3219,10 @@ function AppLayout({ cwd }) {
|
|
|
3221
3219
|
const rows = stdout?.rows ?? 24;
|
|
3222
3220
|
const displayPath = cwd ? cwd.replace(process.env["HOME"] ?? "", "~") : "";
|
|
3223
3221
|
const versionLabel = appVersion === "dev" ? "dev" : `v${appVersion}`;
|
|
3224
|
-
const configIssues =
|
|
3222
|
+
const configIssues = useMemo5(() => {
|
|
3225
3223
|
const validation = validateConfig();
|
|
3226
3224
|
return validation.issues.filter((i) => i.severity === "error");
|
|
3227
|
-
}, []);
|
|
3225
|
+
}, [config]);
|
|
3228
3226
|
return /* @__PURE__ */ jsxs21(Box21, { flexDirection: "column", height: rows, children: [
|
|
3229
3227
|
/* @__PURE__ */ jsx25(Static, { items: view !== "onboarding" ? ["banner"] : [], children: (item) => /* @__PURE__ */ jsx25(
|
|
3230
3228
|
Banner,
|
package/dist/cli.js
CHANGED
|
@@ -508,7 +508,7 @@ program.command("tui").description("Start interactive TUI").option("--skip-onboa
|
|
|
508
508
|
enableTuiLogging();
|
|
509
509
|
const React = await import("react");
|
|
510
510
|
const { render } = await import("ink");
|
|
511
|
-
const { App } = await import("./app-
|
|
511
|
+
const { App } = await import("./app-PUHQN5BJ.js");
|
|
512
512
|
render(React.createElement(App, { cwd: process.cwd(), skipOnboarding: opts.skipOnboarding }), { exitOnCtrlC: false });
|
|
513
513
|
} catch (err) {
|
|
514
514
|
logger.error({ err }, "Failed to start TUI");
|
|
@@ -522,7 +522,7 @@ program.option("--skip-onboarding", "Skip first-run onboarding wizard").action(a
|
|
|
522
522
|
enableTuiLogging();
|
|
523
523
|
const React = await import("react");
|
|
524
524
|
const { render } = await import("ink");
|
|
525
|
-
const { App } = await import("./app-
|
|
525
|
+
const { App } = await import("./app-PUHQN5BJ.js");
|
|
526
526
|
render(React.createElement(App, { cwd: process.cwd(), skipOnboarding }), { exitOnCtrlC: false });
|
|
527
527
|
} catch (err) {
|
|
528
528
|
logger.error({ err }, "Failed to start TUI");
|