loop-task 2.1.4 → 2.1.7
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/App.js +119 -0
- package/dist/{tui → app}/index.js +4 -5
- package/dist/app/providers/index.js +1 -0
- package/dist/cli/import-writer.js +1 -1
- package/dist/cli.js +9 -9
- package/dist/client/cli-format.js +7 -0
- package/dist/client/commands.js +2 -164
- package/dist/client/ipc.js +3 -3
- package/dist/client/project-commands.js +159 -0
- package/dist/core/{command-runner.js → command/command-runner.js} +3 -3
- package/dist/core/{foreground-loop.js → foreground/index.js} +4 -4
- package/dist/core/{log-rotator.js → logging/log-rotator.js} +1 -1
- package/dist/core/loop/chain-executor.js +67 -0
- package/dist/core/loop/delay-utils.js +65 -0
- package/dist/core/loop/loop-controller.js +191 -0
- package/dist/core/loop/loop-runner.js +132 -0
- package/dist/core/loop/run-executor.js +90 -0
- package/dist/core/loop/types.js +1 -0
- package/dist/daemon/daemon-log.js +1 -1
- package/dist/daemon/http/helpers.js +81 -0
- package/dist/daemon/http/openapi.js +92 -0
- package/dist/daemon/http/route-loops.js +218 -0
- package/dist/daemon/http/route-misc.js +36 -0
- package/dist/daemon/http/route-projects.js +55 -0
- package/dist/daemon/http/route-tasks.js +53 -0
- package/dist/daemon/http/routes.js +17 -0
- package/dist/daemon/http/server.js +64 -0
- package/dist/daemon/http/sse.js +36 -0
- package/dist/daemon/index.js +7 -9
- package/dist/{ipc/handlers → daemon/ipc}/logs-stream.js +1 -1
- package/dist/daemon/managers/loop-entry.js +24 -0
- package/dist/daemon/{manager.js → managers/loop-manager.js} +32 -152
- package/dist/daemon/managers/loop-options.js +16 -0
- package/dist/daemon/managers/loop-serialization.js +43 -0
- package/dist/daemon/{projects.js → managers/project-manager.js} +6 -4
- package/dist/daemon/{task-manager.js → managers/task-manager.js} +5 -3
- package/dist/daemon/server/handlers/index.js +86 -0
- package/dist/daemon/server/handlers/log-handlers.js +130 -0
- package/dist/daemon/server/handlers/loop-handlers.js +54 -0
- package/dist/daemon/server/handlers/project-handlers.js +37 -0
- package/dist/daemon/server/handlers/task-handlers.js +20 -0
- package/dist/daemon/server/index.js +113 -0
- package/dist/daemon/{spawner.js → spawner/index.js} +4 -4
- package/dist/daemon/{state.js → state/index.js} +4 -4
- package/dist/daemon/{file-watcher.js → watcher/index.js} +8 -6
- package/dist/duration.js +1 -1
- package/dist/{tui/state.js → entities/loops/filters.js} +0 -76
- package/dist/entities/loops/index.js +1 -0
- package/dist/entities/projects/filters.js +69 -0
- package/dist/entities/projects/index.js +1 -0
- package/dist/entities/tasks/filters.js +41 -0
- package/dist/entities/tasks/index.js +1 -0
- package/dist/{tui/components → features/chain-editor}/ChainEditor.js +7 -5
- package/dist/features/code-editor/CodeEditorModal.js +84 -0
- package/dist/{tui/components → features/code-editor}/CodeEditorPreview.js +3 -3
- package/dist/features/code-editor/useEditorKeyboard.js +147 -0
- package/dist/features/code-editor/useModalDimensions.js +9 -0
- package/dist/{tui → features/commands}/commands.js +2 -2
- package/dist/features/commands/useCommandHandlers.js +147 -0
- package/dist/features/commands/useContextualActions.js +75 -0
- package/dist/features/commands/useGlobalShortcuts.js +152 -0
- package/dist/features/forms/FormRouter.js +44 -0
- package/dist/{tui/components → features/overlays}/ContextHelpModal.js +3 -3
- package/dist/{tui/components → features/overlays}/ExportModal.js +3 -3
- package/dist/{tui/components → features/overlays}/HelpGuideModal.js +3 -3
- package/dist/{tui/components → features/overlays}/HelpModal.js +3 -3
- package/dist/features/overlays/OverlayStack.js +9 -0
- package/dist/{tui/components → features/overlays}/ProjectsModal.js +2 -2
- package/dist/{tui/components → features/overlays}/TaskPickerModal.js +2 -2
- package/dist/{tui/components → features/overlays}/WelcomeScreen.js +5 -3
- package/dist/features/overlays/useOverlayStack.js +51 -0
- package/dist/features/state/useAppState.js +150 -0
- package/dist/logger.js +0 -1
- package/dist/loop-config.js +9 -4
- package/dist/{config → shared/config}/constants.js +1 -1
- package/dist/shared/container/index.js +22 -0
- package/dist/{tui → shared}/hooks/useBreakpoint.js +1 -1
- package/dist/shared/hooks/useInject.js +6 -0
- package/dist/{tui → shared}/hooks/useLogStream.js +7 -5
- package/dist/{hooks → shared/hooks}/useLoopFormValidation.js +2 -2
- package/dist/{tui → shared}/hooks/useLoopPolling.js +6 -4
- package/dist/shared/{useUndoRedo.js → hooks/useUndoRedo.js} +2 -4
- package/dist/shared/providers/InversifyProvider.js +6 -0
- package/dist/shared/services/export-service.js +43 -0
- package/dist/shared/services/log-service.js +28 -0
- package/dist/shared/services/loop-service.js +78 -0
- package/dist/shared/services/project-service.js +39 -0
- package/dist/shared/services/task-service.js +41 -0
- package/dist/shared/services/types.js +7 -0
- package/dist/{tui/components → shared/ui}/Button.js +1 -1
- package/dist/{tui/components → shared/ui}/DebugPanel.js +1 -1
- package/dist/{tui/components → shared/ui}/FocusableButton.js +1 -1
- package/dist/{tui/components → shared/ui}/FocusableInput.js +1 -1
- package/dist/{tui/components → shared/ui}/FocusableList.js +1 -1
- package/dist/{tui/components → shared/ui}/Modal.js +1 -1
- package/dist/{tui/components → shared/ui}/SelectModal.js +2 -2
- package/dist/{tui/components → shared/ui}/Toast.js +1 -1
- package/dist/shared/ui/state.js +9 -0
- package/dist/{tui → shared}/utils/paste.js +2 -2
- package/dist/{tui → shared}/utils/validation.js +1 -1
- package/dist/widgets/command-input/CommandDropdown.js +69 -0
- package/dist/widgets/command-input/CommandInput.js +129 -0
- package/dist/widgets/command-input/HintBar.js +9 -0
- package/dist/widgets/command-input/InputModes.js +68 -0
- package/dist/{tui/components → widgets/commands-browser}/CommandsBrowserModal.js +3 -3
- package/dist/{tui/components → widgets/header}/Header.js +3 -3
- package/dist/{tui/components → widgets/header}/TabBar.js +1 -1
- package/dist/{tui/components → widgets/left-panel}/LeftPanel.js +3 -3
- package/dist/{tui/components → widgets/left-panel}/Navigator.js +3 -3
- package/dist/{tui/components → widgets/left-panel}/ProjectsPage.js +9 -34
- package/dist/widgets/left-panel/ProjectsPageParts.js +34 -0
- package/dist/{tui/components → widgets/left-panel}/TaskBrowser.js +4 -4
- package/dist/{tui/components → widgets/log-modal}/LogModal.js +8 -6
- package/dist/widgets/loop-form/CreateForm.js +75 -0
- package/dist/widgets/loop-form/TextField.js +9 -0
- package/dist/{tui/components → widgets/loop-form}/WizardForm.js +5 -10
- package/dist/widgets/loop-form/useCreateSteps.js +107 -0
- package/dist/widgets/loop-form/useHandleComplete.js +74 -0
- package/dist/{tui/components → widgets/project-form}/ProjectForm.js +9 -7
- package/dist/{tui/components → widgets/right-panel}/Inspector.js +4 -4
- package/dist/{tui/components → widgets/right-panel}/RightPanel.js +4 -4
- package/dist/{tui/components → widgets/right-panel}/RunHistory.js +52 -4
- package/dist/{tui/components → widgets/task-form}/TaskForm.js +11 -9
- package/package.json +2 -1
- package/dist/core/loop-controller.js +0 -511
- package/dist/daemon/http-server.js +0 -599
- package/dist/daemon/server.js +0 -374
- package/dist/tui/App.js +0 -680
- package/dist/tui/components/CodeEditorModal.js +0 -244
- package/dist/tui/components/CommandInput.js +0 -273
- package/dist/tui/components/CreateForm.js +0 -224
- package/dist/tui/daemon.js +0 -142
- package/dist/tui/format.js +0 -111
- /package/dist/{tui/router.js → app/router/index.js} +0 -0
- /package/dist/{tui → app}/types.js +0 -0
- /package/dist/core/{resolve-cwd.js → command/resolve-cwd.js} +0 -0
- /package/dist/core/{context-parser.js → context/context-parser.js} +0 -0
- /package/dist/core/{template.js → context/template.js} +0 -0
- /package/dist/core/{log-parser.js → logging/log-parser.js} +0 -0
- /package/dist/core/{scheduling.js → scheduling/index.js} +0 -0
- /package/dist/{ipc → daemon/ipc}/send.js +0 -0
- /package/dist/{config → shared/config}/paths.js +0 -0
- /package/dist/{i18n → shared/i18n}/en.json +0 -0
- /package/dist/{i18n → shared/i18n}/index.js +0 -0
- /package/dist/{board → shared/ui}/format.js +0 -0
- /package/dist/{tui → shared/ui}/hooks/useHoverState.js +0 -0
- /package/dist/{tui → shared/ui}/theme.js +0 -0
- /package/dist/{tui → shared}/utils/syntax.js +0 -0
package/dist/app/App.js
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useMemo, useCallback } from "react";
|
|
3
|
+
import { Box, useApp } from "ink";
|
|
4
|
+
import { useLoopPolling } from "../shared/hooks/useLoopPolling.js";
|
|
5
|
+
import { useLogStream } from "../shared/hooks/useLogStream.js";
|
|
6
|
+
import { useBreakpoint } from "../shared/hooks/useBreakpoint.js";
|
|
7
|
+
import { useToasts } from "../shared/ui/Toast.js";
|
|
8
|
+
import { Header } from "../widgets/header/Header.js";
|
|
9
|
+
import { LeftPanel } from "../widgets/left-panel/LeftPanel.js";
|
|
10
|
+
import { RightPanel } from "../widgets/right-panel/RightPanel.js";
|
|
11
|
+
import { CommandInput } from "../widgets/command-input/CommandInput.js";
|
|
12
|
+
import { DebugPanel } from "../shared/ui/DebugPanel.js";
|
|
13
|
+
import { useInject } from "../shared/hooks/useInject.js";
|
|
14
|
+
import { TYPES } from "../shared/services/types.js";
|
|
15
|
+
import { cycleSortMode, cycleStatusFilter } from "../entities/loops/filters.js";
|
|
16
|
+
import { t } from "../shared/i18n/index.js";
|
|
17
|
+
import { darkTheme as theme } from "../shared/ui/theme.js";
|
|
18
|
+
import { useRouter } from "./router/index.js";
|
|
19
|
+
import { useAppState } from "../features/state/useAppState.js";
|
|
20
|
+
import { useCommandHandlers } from "../features/commands/useCommandHandlers.js";
|
|
21
|
+
import { useContextualActions } from "../features/commands/useContextualActions.js";
|
|
22
|
+
import { useOverlayStack } from "../features/overlays/useOverlayStack.js";
|
|
23
|
+
import { useGlobalShortcuts } from "../features/commands/useGlobalShortcuts.js";
|
|
24
|
+
import { FormRouter } from "../features/forms/FormRouter.js";
|
|
25
|
+
import { OverlayStack } from "../features/overlays/OverlayStack.js";
|
|
26
|
+
function viewKey(view, editTarget, editTask) {
|
|
27
|
+
if (view === "create")
|
|
28
|
+
return `${view}:${editTarget?.id ?? "new"}`;
|
|
29
|
+
if (view === "task-edit")
|
|
30
|
+
return `${view}:${editTask?.id ?? "new"}`;
|
|
31
|
+
return view;
|
|
32
|
+
}
|
|
33
|
+
function isBoardView(view) {
|
|
34
|
+
return view === "board";
|
|
35
|
+
}
|
|
36
|
+
export function App(props) {
|
|
37
|
+
const { onQuit } = props;
|
|
38
|
+
const { exit } = useApp();
|
|
39
|
+
const loopService = useInject(TYPES.LoopService);
|
|
40
|
+
const taskService = useInject(TYPES.TaskService);
|
|
41
|
+
const projectService = useInject(TYPES.ProjectService);
|
|
42
|
+
const logService = useInject(TYPES.LogService);
|
|
43
|
+
const exportService = useInject(TYPES.ExportService);
|
|
44
|
+
const { loops, daemonStatus, refresh } = useLoopPolling();
|
|
45
|
+
const { view, push, pop } = useRouter("board");
|
|
46
|
+
const { toasts, push: pushToast } = useToasts();
|
|
47
|
+
const breakpoint = useBreakpoint();
|
|
48
|
+
const s = useAppState(loops, pushToast, refresh, loopService, taskService, projectService, logService, view, push, pop);
|
|
49
|
+
useLogStream(s.selectedId, view, (error) => pushToast("error", error.message));
|
|
50
|
+
const { handleCommand } = useCommandHandlers({
|
|
51
|
+
activeTab: s.activeTab, selected: s.selected, selectedTask: s.selectedTask, selectedProjectEntity: s.selectedProjectEntity,
|
|
52
|
+
tasks: s.tasks, projects: s.projects, currentProjectId: s.currentProjectId, filters: s.filters, projectFilters: s.projectFilters,
|
|
53
|
+
setCloneMode: s.setCloneMode, setEditTarget: s.setEditTarget, setPendingTaskSelection: s.setPendingTaskSelection,
|
|
54
|
+
setEditTask: s.setEditTask, setEditProject: s.setEditProject, setActiveTab: s.setActiveTab,
|
|
55
|
+
setConfirmState: s.setConfirmState, setCommandsBrowserOpen: s.setCommandsBrowserOpen, setSearchValue: s.setSearchValue, setSearchState: s.setSearchState,
|
|
56
|
+
setFilters: s.setFilters, setSort: s.setSort, setCurrentProjectId: s.setCurrentProjectId,
|
|
57
|
+
setProjectFilters: s.setProjectFilters, setProjectSelectedIndex: s.setProjectSelectedIndex,
|
|
58
|
+
setDebugMode: s.setDebugMode, setExportModal: s.setExportModal,
|
|
59
|
+
push, pop, refresh, refreshTasks: s.refreshTasks, refreshProjects: s.refreshProjects, pushToast,
|
|
60
|
+
loopService, taskService, projectService, exportService, logService,
|
|
61
|
+
runAction: s.runAction, handleOpenRunLog: s.handleOpenRunLog,
|
|
62
|
+
});
|
|
63
|
+
const { handleContextualCopy, triggerContextualAction } = useContextualActions({
|
|
64
|
+
activeTab: s.activeTab, focusedPanel: s.focusedPanel, selected: s.selected, selectedTask: s.selectedTask, selectedProjectEntity: s.selectedProjectEntity,
|
|
65
|
+
tasks: s.tasks, push, setCloneMode: s.setCloneMode, setEditTarget: s.setEditTarget, setPendingTaskSelection: s.setPendingTaskSelection,
|
|
66
|
+
handleCommand, handleOpenRunLog: s.handleOpenRunLog, pushToast, isBoardView,
|
|
67
|
+
view, logModalRun: s.logModalRun, commandsBrowserOpen: s.commandsBrowserOpen, confirmState: s.confirmState, searchState: s.searchState,
|
|
68
|
+
setChordState: s.setChordState, chordState: s.chordState,
|
|
69
|
+
});
|
|
70
|
+
const { popLayer, anyModalOpen, commandInputDisabled, inputOwner } = useOverlayStack({
|
|
71
|
+
confirmState: s.confirmState, setConfirmState: s.setConfirmState, searchState: s.searchState, setSearchState: s.setSearchState, setSearchValue: s.setSearchValue,
|
|
72
|
+
logModalRun: s.logModalRun, setLogModalRun: s.setLogModalRun, logModalLoopId: s.logModalLoopId, setLogModalLoopId: s.setLogModalLoopId,
|
|
73
|
+
commandsBrowserOpen: s.commandsBrowserOpen, setCommandsBrowserOpen: s.setCommandsBrowserOpen, exportModal: s.exportModal, setExportModal: s.setExportModal,
|
|
74
|
+
contextHelpOpen: s.contextHelpOpen, setContextHelpOpen: s.setContextHelpOpen, view, pop, onQuit, exit,
|
|
75
|
+
commandBarHasText: s.commandBarHasText, commandBarDropdownOpen: s.commandBarDropdownOpen,
|
|
76
|
+
});
|
|
77
|
+
useGlobalShortcuts({
|
|
78
|
+
activeTab: s.activeTab, focusedPanel: s.focusedPanel, setFocusedPanel: s.setFocusedPanel, setActiveTab: s.setActiveTab,
|
|
79
|
+
view, handleCommand, triggerContextualAction, popLayer,
|
|
80
|
+
anyModalOpen, debugMode: s.debugMode, setDebugEntries: s.setDebugEntries, inputOwner,
|
|
81
|
+
confirmState: s.confirmState, searchState: s.searchState, logModalRun: s.logModalRun, commandsBrowserOpen: s.commandsBrowserOpen, exportModal: s.exportModal,
|
|
82
|
+
contextHelpOpen: s.contextHelpOpen, setContextHelpOpen: s.setContextHelpOpen, onQuit, exit, setConfirmState: s.setConfirmState,
|
|
83
|
+
setLogModalRun: s.setLogModalRun, setLogModalLoopId: s.setLogModalLoopId, setCommandsBrowserOpen: s.setCommandsBrowserOpen,
|
|
84
|
+
chordState: s.chordState, setChordState: s.setChordState,
|
|
85
|
+
});
|
|
86
|
+
const handleConfirmYes = useCallback(() => { if (s.confirmState) {
|
|
87
|
+
s.confirmState.onConfirm();
|
|
88
|
+
s.setConfirmState(null);
|
|
89
|
+
} }, [s.confirmState]);
|
|
90
|
+
const handleConfirmCancel = useCallback(() => { s.setConfirmState(null); }, []);
|
|
91
|
+
const cancelCreate = () => { s.setEditTarget(null); s.setCloneMode(false); s.setPendingTaskSelection(null); pop(); };
|
|
92
|
+
const cancelTask = () => { s.setEditTask(null); pop(); };
|
|
93
|
+
const cancelProject = () => { s.setEditProject(null); pop(); };
|
|
94
|
+
const handleChooseTask = (task) => { s.setPendingTaskSelection(task); };
|
|
95
|
+
const onCreateDone = (updated, _id, desc) => {
|
|
96
|
+
s.setEditTarget(null);
|
|
97
|
+
s.setCloneMode(false);
|
|
98
|
+
s.setPendingTaskSelection(null);
|
|
99
|
+
pop();
|
|
100
|
+
pushToast("success", updated ? t("board.toastUpdated", { desc }) : t("board.toastStarted", { desc }));
|
|
101
|
+
void refresh();
|
|
102
|
+
};
|
|
103
|
+
const onTaskDone = (updated, id) => {
|
|
104
|
+
s.setEditTask(null);
|
|
105
|
+
void s.refreshTasks();
|
|
106
|
+
pop();
|
|
107
|
+
pushToast("success", updated ? t("board.toastTaskUpdated", { id }) : t("board.toastTaskCreated", { id }));
|
|
108
|
+
};
|
|
109
|
+
const onProjectDone = (updated, name) => {
|
|
110
|
+
s.setEditProject(null);
|
|
111
|
+
void s.refreshProjects();
|
|
112
|
+
pop();
|
|
113
|
+
pushToast("success", updated ? t("project.toastUpdated", { name }) : t("project.toastCreated", { name }));
|
|
114
|
+
};
|
|
115
|
+
const commandContext = useMemo(() => ({ activeTab: s.activeTab, selectedLoop: s.selected, selectedTask: s.selectedTask, selectedProject: s.selectedProjectEntity }), [s.activeTab, s.selected, s.selectedTask]);
|
|
116
|
+
return (_jsxs(Box, { flexDirection: "column", width: "100%", height: process.stdout.rows || 24, backgroundColor: theme.bg.base, children: [_jsx(Header, { daemonStatus: daemonStatus, counts: s.counts, activeTab: s.activeTab, onTabChange: s.setActiveTab, tabCounts: s.tabCounts }), _jsx(Box, { flexGrow: 1, children: isBoardView(view) ? (_jsxs(Box, { flexDirection: breakpoint === "narrow" ? "column" : "row", flexGrow: 1, children: [_jsx(LeftPanel, { isFocused: s.focusedPanel === "left" && !anyModalOpen, navActive: inputOwner === "panel", activeTab: s.activeTab, query: s.leftPanelQuery, loops: s.visible, selectedIndex: s.clampedIndex, filters: s.filters, sort: s.sort, breakpoint: breakpoint, projects: s.projects, onSelect: (i) => s.setSelectedIndex(i), onActivate: (i) => { s.setSelectedIndex(i); }, tasks: s.filteredTasks, taskSelectedIndex: s.taskClampedIndex, onTaskSelect: (i) => s.setTaskSelectedIndex(i), onTaskActivate: (i) => { s.setTaskSelectedIndex(i); s.setEditTask(s.filteredTasks[i] ?? null); push("task-edit"); }, onStatusCycle: () => s.setFilters((prev) => ({ ...prev, status: cycleStatusFilter(prev.status) })), onSortCycle: () => s.setSort(cycleSortMode(s.sort)), onSelectProject: () => s.setActiveTab("projects"), currentProjectName: s.currentProjectId === "all" ? t("project.showAll") : (s.projects.find(p => p.id === s.currentProjectId)?.name ?? "Default"), projectFilters: s.projectFilters, projectSelectedIndex: s.projectClampedIndex, onProjectSelect: (i) => s.setProjectSelectedIndex(i), onProjectActivate: (i) => { s.setProjectSelectedIndex(i); }, projectLoops: loops }), _jsx(RightPanel, { isFocused: s.focusedPanel === "right" && !anyModalOpen, navActive: inputOwner === "panel", activeTab: s.activeTab, loop: s.selected, selectedRunIndex: s.selectedRunIndex, onSelectRun: (i) => s.setSelectedRunIndex(i), onOpenRun: s.handleOpenRunLog, selectedTask: s.selectedTask, allTasks: s.tasks, selectedProject: s.selectedProjectEntity, projectLoopCount: s.projectLoopCount, projects: s.projects, onProjectEdit: () => { if (s.selectedProjectEntity && !s.selectedProjectEntity.isSystem)
|
|
117
|
+
handleCommand("edit"); }, onProjectDelete: () => { if (s.selectedProjectEntity && !s.selectedProjectEntity.isSystem)
|
|
118
|
+
handleCommand("delete"); } }), s.debugMode ? _jsx(DebugPanel, { entries: s.debugEntries }) : null] })) : (_jsx(FormRouter, { view: view, editTarget: s.editTarget, cloneMode: s.cloneMode, editTask: s.editTask, editProject: s.editProject, pendingTaskSelection: s.pendingTaskSelection, tasks: s.tasks, projects: s.projects, currentProjectId: s.currentProjectId, cancelCreate: cancelCreate, onCreateDone: onCreateDone, handleChooseTask: handleChooseTask, cancelTask: cancelTask, onTaskDone: onTaskDone, cancelProject: cancelProject, onProjectDone: onProjectDone })) }, viewKey(view, s.editTarget, s.editTask)), isBoardView(view) ? (_jsx(CommandInput, { context: commandContext, onCommand: handleCommand, confirmState: s.confirmState, searchState: s.searchState, searchValue: s.searchValue, onSearchChange: s.handleSearchChange, onSearchSubmit: s.handleSearchSubmit, onSearchCancel: s.handleSearchCancel, onConfirmYes: handleConfirmYes, onConfirmCancel: handleConfirmCancel, onCopy: handleContextualCopy, onPanelAction: triggerContextualAction, disabled: commandInputDisabled, navOwner: inputOwner, onInputStateChange: (hasText, dropdownOpen) => { s.setCommandBarHasText(hasText); s.setCommandBarDropdownOpen(dropdownOpen); } })) : null, _jsx(OverlayStack, { commandsBrowserOpen: s.commandsBrowserOpen, commandContext: commandContext, onCommandsBrowserClose: () => s.setCommandsBrowserOpen(false), onCommandsBrowserExecute: (v) => { s.setCommandsBrowserOpen(false); handleCommand(v); }, contextHelpOpen: s.contextHelpOpen, onContextHelpClose: () => s.setContextHelpOpen(false), exportModal: s.exportModal, onExportModalClose: () => s.setExportModal(null), onExportCopy: () => pushToast("success", t("board.toastCopied")), logModalRun: s.logModalRun, logModalLoopId: s.logModalLoopId, logModalLines: s.logModalLines, logModalLoading: s.logModalLoading, onLogModalClose: () => { s.setLogModalRun(null); s.setLogModalLoopId(null); }, onLogCopy: () => pushToast("success", t("board.toastCopied")), toasts: toasts })] }));
|
|
119
|
+
}
|
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
import { render } from "ink";
|
|
2
2
|
import React from "react";
|
|
3
3
|
import { App } from "./App.js";
|
|
4
|
-
import { BRACKETED_PASTE_ENABLE, BRACKETED_PASTE_DISABLE } from "../config/constants.js";
|
|
4
|
+
import { BRACKETED_PASTE_ENABLE, BRACKETED_PASTE_DISABLE } from "../shared/config/constants.js";
|
|
5
|
+
import { InversifyProvider } from "../shared/providers/InversifyProvider.js";
|
|
5
6
|
export async function launchBoard() {
|
|
6
|
-
// Enable bracketed paste so a native paste (Ctrl+Shift+V / Cmd+V / right-click)
|
|
7
|
-
// arrives as one delimited chunk instead of a stream of keypresses.
|
|
8
7
|
process.stdout.write(BRACKETED_PASTE_ENABLE);
|
|
9
8
|
const disableBracketedPaste = () => process.stdout.write(BRACKETED_PASTE_DISABLE);
|
|
10
|
-
const instance = render(React.createElement(App, {
|
|
9
|
+
const instance = render(React.createElement(InversifyProvider, null, React.createElement(App, {
|
|
11
10
|
onQuit: () => {
|
|
12
11
|
disableBracketedPaste();
|
|
13
12
|
instance.unmount();
|
|
14
13
|
}
|
|
15
|
-
}));
|
|
14
|
+
})));
|
|
16
15
|
process.on("exit", disableBracketedPaste);
|
|
17
16
|
process.on("uncaughtException", (error) => {
|
|
18
17
|
console.error("Uncaught exception:", error);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { InversifyProvider, InversifyContext } from "../../shared/providers/InversifyProvider.js";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { writeFileAtomic } from "../shared/fs-utils.js";
|
|
4
|
-
import { getDataDir } from "../config/paths.js";
|
|
4
|
+
import { getDataDir } from "../shared/config/paths.js";
|
|
5
5
|
const STORE_FILES = ["loops.json", "tasks.json", "projects.json"];
|
|
6
6
|
function backupFilePath(filePath) {
|
|
7
7
|
return `${filePath}.bak`;
|
package/dist/cli.js
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
import { Command } from "commander";
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
4
|
import { Logger } from "./logger.js";
|
|
5
|
-
import { runLoop } from "./core/foreground
|
|
5
|
+
import { runLoop } from "./core/foreground/index.js";
|
|
6
6
|
import { buildLoopOptions } from "./loop-config.js";
|
|
7
7
|
import { parseDuration } from "./duration.js";
|
|
8
8
|
import { startLoop } from "./client/commands.js";
|
|
9
|
-
import { listProjectsCli, createProjectCli, renameProjectCli, setProjectColorCli, deleteProjectCli, resolveProjectId, } from "./client/commands.js";
|
|
10
|
-
import { t } from "./i18n/index.js";
|
|
11
|
-
import { HTTP_API_PORT, HTTP_API_HOST } from "./config/constants.js";
|
|
9
|
+
import { listProjectsCli, createProjectCli, renameProjectCli, setProjectColorCli, deleteProjectCli, resolveProjectId, } from "./client/project-commands.js";
|
|
10
|
+
import { t } from "./shared/i18n/index.js";
|
|
11
|
+
import { HTTP_API_PORT, HTTP_API_HOST } from "./shared/config/constants.js";
|
|
12
12
|
const require = createRequire(import.meta.url);
|
|
13
13
|
const packageJson = require("../package.json");
|
|
14
14
|
const program = new Command();
|
|
@@ -20,7 +20,7 @@ program
|
|
|
20
20
|
.command("start")
|
|
21
21
|
.description(t("cli.startDescription"))
|
|
22
22
|
.action(async () => {
|
|
23
|
-
const { ensureDaemon } = await import("./daemon/spawner.js");
|
|
23
|
+
const { ensureDaemon } = await import("./daemon/spawner/index.js");
|
|
24
24
|
ensureDaemon();
|
|
25
25
|
console.log(t("cli.daemonStarted"));
|
|
26
26
|
});
|
|
@@ -54,8 +54,8 @@ program
|
|
|
54
54
|
.command("restart")
|
|
55
55
|
.description("Kill the daemon and all running loops, then restart fresh")
|
|
56
56
|
.action(async () => {
|
|
57
|
-
const { stopDaemon, ensureDaemon } = await import("./daemon/spawner.js");
|
|
58
|
-
const { readDaemonPid, removeDaemonPid, removeDaemonSignature } = await import("./daemon/state.js");
|
|
57
|
+
const { stopDaemon, ensureDaemon } = await import("./daemon/spawner/index.js");
|
|
58
|
+
const { readDaemonPid, removeDaemonPid, removeDaemonSignature } = await import("./daemon/state/index.js");
|
|
59
59
|
const pid = readDaemonPid();
|
|
60
60
|
if (pid !== null) {
|
|
61
61
|
console.log("Stopping daemon...");
|
|
@@ -170,7 +170,7 @@ projectCmd
|
|
|
170
170
|
await deleteProjectCli(idOrName);
|
|
171
171
|
});
|
|
172
172
|
program.action(async () => {
|
|
173
|
-
const { launchBoard } = await import("./
|
|
173
|
+
const { launchBoard } = await import("./app/index.js");
|
|
174
174
|
await launchBoard();
|
|
175
175
|
});
|
|
176
176
|
program
|
|
@@ -190,7 +190,7 @@ program
|
|
|
190
190
|
}
|
|
191
191
|
else {
|
|
192
192
|
for (const loop of loops) {
|
|
193
|
-
const { describeLoop, statusLabel } = await import("./
|
|
193
|
+
const { describeLoop, statusLabel } = await import("./shared/ui/format.js");
|
|
194
194
|
console.log(`${loop.id} ${statusLabel(loop.status)} ${describeLoop(loop)}`);
|
|
195
195
|
}
|
|
196
196
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export function formatCmd(command, args) {
|
|
2
|
+
const full = `${command} ${args.join(" ")}`.trim();
|
|
3
|
+
return full.length > 30 ? full.slice(0, 27) + "..." : full;
|
|
4
|
+
}
|
|
5
|
+
export function pad(str, width) {
|
|
6
|
+
return str.length >= width ? str + " " : str + " ".repeat(width - str.length);
|
|
7
|
+
}
|
package/dist/client/commands.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { formatDuration } from "../duration.js";
|
|
2
|
-
import {
|
|
3
|
-
import { t } from "../i18n/index.js";
|
|
2
|
+
import { t } from "../shared/i18n/index.js";
|
|
4
3
|
import { sendRequest, streamRequest } from "./ipc.js";
|
|
4
|
+
import { formatCmd, pad } from "./cli-format.js";
|
|
5
5
|
export async function createBackgroundLoop(options, intervalHuman) {
|
|
6
6
|
const response = await sendRequest({
|
|
7
7
|
type: "start",
|
|
@@ -153,165 +153,3 @@ export async function attachLoop(id) {
|
|
|
153
153
|
process.exit(0);
|
|
154
154
|
});
|
|
155
155
|
}
|
|
156
|
-
function formatCmd(command, args) {
|
|
157
|
-
const full = `${command} ${args.join(" ")}`.trim();
|
|
158
|
-
return full.length > 30 ? full.slice(0, 27) + "..." : full;
|
|
159
|
-
}
|
|
160
|
-
function pad(str, width) {
|
|
161
|
-
return str.length >= width ? str + " " : str + " ".repeat(width - str.length);
|
|
162
|
-
}
|
|
163
|
-
async function fetchProjects() {
|
|
164
|
-
const response = await sendRequest({ type: "project-list" });
|
|
165
|
-
if (response.type !== "ok") {
|
|
166
|
-
throw new Error(response.message);
|
|
167
|
-
}
|
|
168
|
-
return response.data;
|
|
169
|
-
}
|
|
170
|
-
export function resolveColor(input) {
|
|
171
|
-
if (PROJECT_COLORS[input])
|
|
172
|
-
return PROJECT_COLORS[input];
|
|
173
|
-
if (/^#[0-9a-fA-F]{6}$/.test(input))
|
|
174
|
-
return input;
|
|
175
|
-
throw new Error(t("cli.projectInvalidColor", {
|
|
176
|
-
color: input,
|
|
177
|
-
valid: Object.keys(PROJECT_COLORS).join(", "),
|
|
178
|
-
}));
|
|
179
|
-
}
|
|
180
|
-
export async function resolveProjectId(idOrName) {
|
|
181
|
-
const projects = await fetchProjects();
|
|
182
|
-
const byId = projects.find((p) => p.id === idOrName);
|
|
183
|
-
if (byId)
|
|
184
|
-
return byId.id;
|
|
185
|
-
const matches = projects.filter((p) => p.name.toLowerCase() === idOrName.toLowerCase());
|
|
186
|
-
if (matches.length === 1)
|
|
187
|
-
return matches[0].id;
|
|
188
|
-
if (matches.length === 0)
|
|
189
|
-
throw new Error(t("cli.projectNotFound", { name: idOrName }));
|
|
190
|
-
throw new Error(t("cli.projectAmbiguous", { name: idOrName }));
|
|
191
|
-
}
|
|
192
|
-
export async function listProjectsCli() {
|
|
193
|
-
try {
|
|
194
|
-
const projects = await fetchProjects();
|
|
195
|
-
if (projects.length === 0) {
|
|
196
|
-
console.log(t("cli.noProjects"));
|
|
197
|
-
process.exit(0);
|
|
198
|
-
}
|
|
199
|
-
const loopResponse = await sendRequest({ type: "list" });
|
|
200
|
-
const loops = loopResponse.type === "ok" ? loopResponse.data : [];
|
|
201
|
-
const counts = new Map();
|
|
202
|
-
for (const loop of loops) {
|
|
203
|
-
counts.set(loop.projectId, (counts.get(loop.projectId) ?? 0) + 1);
|
|
204
|
-
}
|
|
205
|
-
const nameW = Math.max(t("cli.projectHeaderName").length, ...projects.map((p) => p.name.length)) + 2;
|
|
206
|
-
const idW = Math.max(t("cli.projectHeaderId").length, ...projects.map((p) => p.id.length)) + 2;
|
|
207
|
-
const colorW = Math.max(t("cli.projectHeaderColor").length, ...projects.map((p) => p.color.length)) + 2;
|
|
208
|
-
const loopsW = t("cli.projectHeaderLoops").length + 2;
|
|
209
|
-
const header = pad(t("cli.projectHeaderName"), nameW) +
|
|
210
|
-
pad(t("cli.projectHeaderId"), idW) +
|
|
211
|
-
pad(t("cli.projectHeaderColor"), colorW) +
|
|
212
|
-
pad(t("cli.projectHeaderLoops"), loopsW) +
|
|
213
|
-
t("cli.projectHeaderSystem");
|
|
214
|
-
console.log(header);
|
|
215
|
-
console.log("-".repeat(header.length));
|
|
216
|
-
for (const project of projects) {
|
|
217
|
-
console.log(pad(project.name, nameW) +
|
|
218
|
-
pad(project.id, idW) +
|
|
219
|
-
pad(project.color, colorW) +
|
|
220
|
-
pad(String(counts.get(project.id) ?? 0), loopsW) +
|
|
221
|
-
(project.isSystem ? "yes" : ""));
|
|
222
|
-
}
|
|
223
|
-
process.exit(0);
|
|
224
|
-
}
|
|
225
|
-
catch (error) {
|
|
226
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
227
|
-
console.error(t("cli.error", { message }));
|
|
228
|
-
process.exit(1);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
export async function createProjectCli(name, colorInput) {
|
|
232
|
-
try {
|
|
233
|
-
const color = colorInput ? resolveColor(colorInput) : PROJECT_COLORS.cyan;
|
|
234
|
-
const response = await sendRequest({
|
|
235
|
-
type: "project-create",
|
|
236
|
-
payload: { name, color },
|
|
237
|
-
});
|
|
238
|
-
if (response.type !== "ok") {
|
|
239
|
-
throw new Error(response.message);
|
|
240
|
-
}
|
|
241
|
-
const id = response.data.id;
|
|
242
|
-
console.log(t("cli.projectCreated", { name, id }));
|
|
243
|
-
process.exit(0);
|
|
244
|
-
}
|
|
245
|
-
catch (error) {
|
|
246
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
247
|
-
console.error(t("cli.error", { message }));
|
|
248
|
-
process.exit(1);
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
export async function renameProjectCli(idOrName, newName) {
|
|
252
|
-
try {
|
|
253
|
-
const id = await resolveProjectId(idOrName);
|
|
254
|
-
const response = await sendRequest({
|
|
255
|
-
type: "project-update",
|
|
256
|
-
payload: { id, name: newName },
|
|
257
|
-
});
|
|
258
|
-
if (response.type !== "ok") {
|
|
259
|
-
throw new Error(response.message);
|
|
260
|
-
}
|
|
261
|
-
console.log(t("cli.projectRenamed", { name: newName }));
|
|
262
|
-
process.exit(0);
|
|
263
|
-
}
|
|
264
|
-
catch (error) {
|
|
265
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
266
|
-
console.error(t("cli.error", { message }));
|
|
267
|
-
process.exit(1);
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
export async function setProjectColorCli(idOrName, colorInput) {
|
|
271
|
-
try {
|
|
272
|
-
const projects = await fetchProjects();
|
|
273
|
-
const byId = projects.find((p) => p.id === idOrName);
|
|
274
|
-
const matches = byId
|
|
275
|
-
? [byId]
|
|
276
|
-
: projects.filter((p) => p.name.toLowerCase() === idOrName.toLowerCase());
|
|
277
|
-
if (matches.length === 0)
|
|
278
|
-
throw new Error(t("cli.projectNotFound", { name: idOrName }));
|
|
279
|
-
if (matches.length > 1)
|
|
280
|
-
throw new Error(t("cli.projectAmbiguous", { name: idOrName }));
|
|
281
|
-
const project = matches[0];
|
|
282
|
-
const color = resolveColor(colorInput);
|
|
283
|
-
const response = await sendRequest({
|
|
284
|
-
type: "project-update",
|
|
285
|
-
payload: { id: project.id, name: project.name, color },
|
|
286
|
-
});
|
|
287
|
-
if (response.type !== "ok") {
|
|
288
|
-
throw new Error(response.message);
|
|
289
|
-
}
|
|
290
|
-
console.log(t("cli.projectColorSet", { name: project.name, color }));
|
|
291
|
-
process.exit(0);
|
|
292
|
-
}
|
|
293
|
-
catch (error) {
|
|
294
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
295
|
-
console.error(t("cli.error", { message }));
|
|
296
|
-
process.exit(1);
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
export async function deleteProjectCli(idOrName) {
|
|
300
|
-
try {
|
|
301
|
-
const id = await resolveProjectId(idOrName);
|
|
302
|
-
const response = await sendRequest({
|
|
303
|
-
type: "project-delete",
|
|
304
|
-
payload: { id },
|
|
305
|
-
});
|
|
306
|
-
if (response.type !== "ok") {
|
|
307
|
-
throw new Error(response.message);
|
|
308
|
-
}
|
|
309
|
-
console.log(t("cli.projectDeleted", { name: idOrName }));
|
|
310
|
-
process.exit(0);
|
|
311
|
-
}
|
|
312
|
-
catch (error) {
|
|
313
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
314
|
-
console.error(t("cli.error", { message }));
|
|
315
|
-
process.exit(1);
|
|
316
|
-
}
|
|
317
|
-
}
|
package/dist/client/ipc.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import net from "node:net";
|
|
2
|
-
import { ensureDaemon, getSocket } from "../daemon/spawner.js";
|
|
3
|
-
import { t } from "../i18n/index.js";
|
|
4
|
-
import { IPC_TIMEOUT_MS } from "../config/constants.js";
|
|
2
|
+
import { ensureDaemon, getSocket } from "../daemon/spawner/index.js";
|
|
3
|
+
import { t } from "../shared/i18n/index.js";
|
|
4
|
+
import { IPC_TIMEOUT_MS } from "../shared/config/constants.js";
|
|
5
5
|
export async function sendRequest(request) {
|
|
6
6
|
ensureDaemon();
|
|
7
7
|
const socketPath = getSocket();
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { PROJECT_COLORS } from "../shared/config/constants.js";
|
|
2
|
+
import { t } from "../shared/i18n/index.js";
|
|
3
|
+
import { sendRequest } from "./ipc.js";
|
|
4
|
+
import { pad } from "./cli-format.js";
|
|
5
|
+
async function fetchProjects() {
|
|
6
|
+
const response = await sendRequest({ type: "project-list" });
|
|
7
|
+
if (response.type !== "ok") {
|
|
8
|
+
throw new Error(response.message);
|
|
9
|
+
}
|
|
10
|
+
return response.data;
|
|
11
|
+
}
|
|
12
|
+
export function resolveColor(input) {
|
|
13
|
+
if (PROJECT_COLORS[input])
|
|
14
|
+
return PROJECT_COLORS[input];
|
|
15
|
+
if (/^#[0-9a-fA-F]{6}$/.test(input))
|
|
16
|
+
return input;
|
|
17
|
+
throw new Error(t("cli.projectInvalidColor", {
|
|
18
|
+
color: input,
|
|
19
|
+
valid: Object.keys(PROJECT_COLORS).join(", "),
|
|
20
|
+
}));
|
|
21
|
+
}
|
|
22
|
+
export async function resolveProjectId(idOrName) {
|
|
23
|
+
const projects = await fetchProjects();
|
|
24
|
+
const byId = projects.find((p) => p.id === idOrName);
|
|
25
|
+
if (byId)
|
|
26
|
+
return byId.id;
|
|
27
|
+
const matches = projects.filter((p) => p.name.toLowerCase() === idOrName.toLowerCase());
|
|
28
|
+
if (matches.length === 1)
|
|
29
|
+
return matches[0].id;
|
|
30
|
+
if (matches.length === 0)
|
|
31
|
+
throw new Error(t("cli.projectNotFound", { name: idOrName }));
|
|
32
|
+
throw new Error(t("cli.projectAmbiguous", { name: idOrName }));
|
|
33
|
+
}
|
|
34
|
+
export async function listProjectsCli() {
|
|
35
|
+
try {
|
|
36
|
+
const projects = await fetchProjects();
|
|
37
|
+
if (projects.length === 0) {
|
|
38
|
+
console.log(t("cli.noProjects"));
|
|
39
|
+
process.exit(0);
|
|
40
|
+
}
|
|
41
|
+
const loopResponse = await sendRequest({ type: "list" });
|
|
42
|
+
const loops = loopResponse.type === "ok" ? loopResponse.data : [];
|
|
43
|
+
const counts = new Map();
|
|
44
|
+
for (const loop of loops) {
|
|
45
|
+
counts.set(loop.projectId, (counts.get(loop.projectId) ?? 0) + 1);
|
|
46
|
+
}
|
|
47
|
+
const nameW = Math.max(t("cli.projectHeaderName").length, ...projects.map((p) => p.name.length)) + 2;
|
|
48
|
+
const idW = Math.max(t("cli.projectHeaderId").length, ...projects.map((p) => p.id.length)) + 2;
|
|
49
|
+
const colorW = Math.max(t("cli.projectHeaderColor").length, ...projects.map((p) => p.color.length)) + 2;
|
|
50
|
+
const loopsW = t("cli.projectHeaderLoops").length + 2;
|
|
51
|
+
const header = pad(t("cli.projectHeaderName"), nameW) +
|
|
52
|
+
pad(t("cli.projectHeaderId"), idW) +
|
|
53
|
+
pad(t("cli.projectHeaderColor"), colorW) +
|
|
54
|
+
pad(t("cli.projectHeaderLoops"), loopsW) +
|
|
55
|
+
t("cli.projectHeaderSystem");
|
|
56
|
+
console.log(header);
|
|
57
|
+
console.log("-".repeat(header.length));
|
|
58
|
+
for (const project of projects) {
|
|
59
|
+
console.log(pad(project.name, nameW) +
|
|
60
|
+
pad(project.id, idW) +
|
|
61
|
+
pad(project.color, colorW) +
|
|
62
|
+
pad(String(counts.get(project.id) ?? 0), loopsW) +
|
|
63
|
+
(project.isSystem ? "yes" : ""));
|
|
64
|
+
}
|
|
65
|
+
process.exit(0);
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
69
|
+
console.error(t("cli.error", { message }));
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
export async function createProjectCli(name, colorInput) {
|
|
74
|
+
try {
|
|
75
|
+
const color = colorInput ? resolveColor(colorInput) : PROJECT_COLORS.cyan;
|
|
76
|
+
const response = await sendRequest({
|
|
77
|
+
type: "project-create",
|
|
78
|
+
payload: { name, color },
|
|
79
|
+
});
|
|
80
|
+
if (response.type !== "ok") {
|
|
81
|
+
throw new Error(response.message);
|
|
82
|
+
}
|
|
83
|
+
const id = response.data.id;
|
|
84
|
+
console.log(t("cli.projectCreated", { name, id }));
|
|
85
|
+
process.exit(0);
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
89
|
+
console.error(t("cli.error", { message }));
|
|
90
|
+
process.exit(1);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
export async function renameProjectCli(idOrName, newName) {
|
|
94
|
+
try {
|
|
95
|
+
const id = await resolveProjectId(idOrName);
|
|
96
|
+
const response = await sendRequest({
|
|
97
|
+
type: "project-update",
|
|
98
|
+
payload: { id, name: newName },
|
|
99
|
+
});
|
|
100
|
+
if (response.type !== "ok") {
|
|
101
|
+
throw new Error(response.message);
|
|
102
|
+
}
|
|
103
|
+
console.log(t("cli.projectRenamed", { name: newName }));
|
|
104
|
+
process.exit(0);
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
108
|
+
console.error(t("cli.error", { message }));
|
|
109
|
+
process.exit(1);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
export async function setProjectColorCli(idOrName, colorInput) {
|
|
113
|
+
try {
|
|
114
|
+
const projects = await fetchProjects();
|
|
115
|
+
const byId = projects.find((p) => p.id === idOrName);
|
|
116
|
+
const matches = byId
|
|
117
|
+
? [byId]
|
|
118
|
+
: projects.filter((p) => p.name.toLowerCase() === idOrName.toLowerCase());
|
|
119
|
+
if (matches.length === 0)
|
|
120
|
+
throw new Error(t("cli.projectNotFound", { name: idOrName }));
|
|
121
|
+
if (matches.length > 1)
|
|
122
|
+
throw new Error(t("cli.projectAmbiguous", { name: idOrName }));
|
|
123
|
+
const project = matches[0];
|
|
124
|
+
const color = resolveColor(colorInput);
|
|
125
|
+
const response = await sendRequest({
|
|
126
|
+
type: "project-update",
|
|
127
|
+
payload: { id: project.id, name: project.name, color },
|
|
128
|
+
});
|
|
129
|
+
if (response.type !== "ok") {
|
|
130
|
+
throw new Error(response.message);
|
|
131
|
+
}
|
|
132
|
+
console.log(t("cli.projectColorSet", { name: project.name, color }));
|
|
133
|
+
process.exit(0);
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
137
|
+
console.error(t("cli.error", { message }));
|
|
138
|
+
process.exit(1);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
export async function deleteProjectCli(idOrName) {
|
|
142
|
+
try {
|
|
143
|
+
const id = await resolveProjectId(idOrName);
|
|
144
|
+
const response = await sendRequest({
|
|
145
|
+
type: "project-delete",
|
|
146
|
+
payload: { id },
|
|
147
|
+
});
|
|
148
|
+
if (response.type !== "ok") {
|
|
149
|
+
throw new Error(response.message);
|
|
150
|
+
}
|
|
151
|
+
console.log(t("cli.projectDeleted", { name: idOrName }));
|
|
152
|
+
process.exit(0);
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
156
|
+
console.error(t("cli.error", { message }));
|
|
157
|
+
process.exit(1);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import { execa } from "execa";
|
|
3
|
-
import { formatDuration } from "
|
|
4
|
-
import { t } from "
|
|
5
|
-
import { MAX_CONTEXT_STDOUT_BYTES } from "
|
|
3
|
+
import { formatDuration } from "../../duration.js";
|
|
4
|
+
import { t } from "../../shared/i18n/index.js";
|
|
5
|
+
import { MAX_CONTEXT_STDOUT_BYTES } from "../../shared/config/constants.js";
|
|
6
6
|
function quoteArg(arg) {
|
|
7
7
|
if (arg.length === 0)
|
|
8
8
|
return "''";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { formatDuration } from "
|
|
2
|
-
import { t } from "
|
|
3
|
-
import { sleep } from "
|
|
4
|
-
import { executeCommandForeground } from "
|
|
1
|
+
import { formatDuration } from "../../duration.js";
|
|
2
|
+
import { t } from "../../shared/i18n/index.js";
|
|
3
|
+
import { sleep } from "../../shared/sleep.js";
|
|
4
|
+
import { executeCommandForeground } from "../command/command-runner.js";
|
|
5
5
|
export async function runLoop(options, logger, signal) {
|
|
6
6
|
const state = {
|
|
7
7
|
running: false,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
|
-
import { MAX_LOG_BYTES, MAX_LOG_GENERATIONS } from "
|
|
2
|
+
import { MAX_LOG_BYTES, MAX_LOG_GENERATIONS } from "../../shared/config/constants.js";
|
|
3
3
|
export function rotateLogIfNeeded(logPath, currentStream) {
|
|
4
4
|
if (!fs.existsSync(logPath)) {
|
|
5
5
|
return currentStream;
|