loop-task 2.1.3 → 2.1.5
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} +6 -13
- 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 +133 -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
|
@@ -1,224 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useMemo, useCallback, useState, useRef } from "react";
|
|
3
|
-
import { createLoop, updateLoop } from "../daemon.js";
|
|
4
|
-
import { t } from "../../i18n/index.js";
|
|
5
|
-
import { WizardForm } from "./WizardForm.js";
|
|
6
|
-
import { TaskPickerModal } from "./TaskPickerModal.js";
|
|
7
|
-
import { SelectModal, SelectValueField } from "./SelectModal.js";
|
|
8
|
-
import { CodeEditorPreview } from "./CodeEditorPreview.js";
|
|
9
|
-
import { CodeEditorModal } from "./CodeEditorModal.js";
|
|
10
|
-
import { parseDuration } from "../../duration.js";
|
|
11
|
-
import { parseCommandLine, joinCommandLines } from "../../loop-config.js";
|
|
12
|
-
export function CreateView(props) {
|
|
13
|
-
const { mode, editId, initial, selectedTaskId, selectedTaskName, tasks, currentProjectId, onCancel, onDone, onChooseTask, } = props;
|
|
14
|
-
const [taskPickerOpen, setTaskPickerOpen] = useState(false);
|
|
15
|
-
const [openSelect, setOpenSelect] = useState(null);
|
|
16
|
-
const [commandEditorOpen, setCommandEditorOpen] = useState(false);
|
|
17
|
-
const [commandValue, setCommandValue] = useState(initial.command ?? "");
|
|
18
|
-
// renderCustom is invoked on every WizardForm render for every field, so this
|
|
19
|
-
// ref always holds the latest onChange/onAdvance closures per field key —
|
|
20
|
-
// letting the modal (rendered outside WizardForm) drive a field's value.
|
|
21
|
-
const fieldCallbacksRef = useRef({});
|
|
22
|
-
const taskModeInitial = initial.taskMode === "existing" ? "Existing task" : "Inline command";
|
|
23
|
-
// Resolve task display name on edit mode: if the user hasn't picked a task
|
|
24
|
-
// via the picker (selectedTaskName is null), look it up from the tasks list.
|
|
25
|
-
// Always format as "<name> (<short id>)" for consistency.
|
|
26
|
-
const resolvedTaskName = useMemo(() => {
|
|
27
|
-
const tid = selectedTaskId ?? initial.taskId;
|
|
28
|
-
if (!tid)
|
|
29
|
-
return null;
|
|
30
|
-
const displayName = selectedTaskName ?? tasks.find((t) => t.id === tid)?.name;
|
|
31
|
-
if (displayName)
|
|
32
|
-
return `${displayName} (${tid.slice(0, 8)})`;
|
|
33
|
-
return `${tid.slice(0, 8)}`;
|
|
34
|
-
}, [selectedTaskName, selectedTaskId, initial.taskId, tasks]);
|
|
35
|
-
const steps = useMemo(() => {
|
|
36
|
-
const list = [
|
|
37
|
-
{
|
|
38
|
-
key: "interval",
|
|
39
|
-
prompt: t("wizard.intervalPrompt"),
|
|
40
|
-
hint: t("wizard.intervalHint"),
|
|
41
|
-
required: true,
|
|
42
|
-
inputType: "text",
|
|
43
|
-
defaultValue: initial.interval ?? undefined,
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
key: "taskMode",
|
|
47
|
-
prompt: t("wizard.taskModePrompt"),
|
|
48
|
-
hint: t("board.hintTaskMode"),
|
|
49
|
-
required: true,
|
|
50
|
-
defaultValue: taskModeInitial,
|
|
51
|
-
onActivate: () => setOpenSelect("taskMode"),
|
|
52
|
-
renderCustom: ({ value, isActive, onChange, onAdvance }) => {
|
|
53
|
-
fieldCallbacksRef.current.taskMode = { value, onChange, onAdvance };
|
|
54
|
-
return (_jsx(SelectValueField, { label: value || null, placeholder: t("wizard.taskModePrompt"), isActive: isActive }));
|
|
55
|
-
},
|
|
56
|
-
},
|
|
57
|
-
{
|
|
58
|
-
key: "taskId",
|
|
59
|
-
prompt: resolvedTaskName
|
|
60
|
-
? t("board.selectedTask", { name: resolvedTaskName })
|
|
61
|
-
: t("board.chooseTask"),
|
|
62
|
-
hint: t("board.hintTask"),
|
|
63
|
-
required: true,
|
|
64
|
-
inputType: "text",
|
|
65
|
-
defaultValue: selectedTaskId ?? initial.taskId ?? undefined,
|
|
66
|
-
skip: (values) => !values.taskMode?.includes("Existing"),
|
|
67
|
-
onActivate: () => setTaskPickerOpen(true),
|
|
68
|
-
renderCustom: ({ isActive }) => (_jsx(SelectValueField, { label: resolvedTaskName, placeholder: t("board.chooseTask"), isActive: isActive })),
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
key: "command",
|
|
72
|
-
prompt: t("wizard.commandPrompt"),
|
|
73
|
-
hint: t("wizard.commandHint"),
|
|
74
|
-
required: true,
|
|
75
|
-
inputType: "text",
|
|
76
|
-
defaultValue: commandValue || undefined,
|
|
77
|
-
skip: (values) => !!values.taskMode?.includes("Existing"),
|
|
78
|
-
onActivate: () => setCommandEditorOpen(true),
|
|
79
|
-
renderCustom: ({ isActive }) => (_jsx(CodeEditorPreview, { value: commandValue, hint: t("wizard.commandHint"), isActive: isActive, onActivate: () => setCommandEditorOpen(true) })),
|
|
80
|
-
},
|
|
81
|
-
{
|
|
82
|
-
key: "runNow",
|
|
83
|
-
prompt: t("wizard.runNowPrompt"),
|
|
84
|
-
hint: t("board.hintRunNow"),
|
|
85
|
-
required: true,
|
|
86
|
-
defaultValue: initial.runNow === "true" || initial.runNow === "yes"
|
|
87
|
-
? t("wizard.runNowNow")
|
|
88
|
-
: t("wizard.runNowWait"),
|
|
89
|
-
onActivate: () => setOpenSelect("runNow"),
|
|
90
|
-
renderCustom: ({ value, isActive, onChange, onAdvance }) => {
|
|
91
|
-
fieldCallbacksRef.current.runNow = { value, onChange, onAdvance };
|
|
92
|
-
return (_jsx(SelectValueField, { label: value || null, placeholder: t("wizard.runNowPrompt"), isActive: isActive }));
|
|
93
|
-
},
|
|
94
|
-
},
|
|
95
|
-
{
|
|
96
|
-
key: "cwd",
|
|
97
|
-
prompt: t("wizard.cwdPrompt"),
|
|
98
|
-
hint: t("wizard.cwdHint"),
|
|
99
|
-
required: false,
|
|
100
|
-
inputType: "text",
|
|
101
|
-
defaultValue: initial.cwd ?? undefined,
|
|
102
|
-
},
|
|
103
|
-
{
|
|
104
|
-
key: "description",
|
|
105
|
-
prompt: t("wizard.descriptionPrompt"),
|
|
106
|
-
hint: t("wizard.descriptionHint"),
|
|
107
|
-
required: true,
|
|
108
|
-
inputType: "text",
|
|
109
|
-
defaultValue: initial.description ?? undefined,
|
|
110
|
-
},
|
|
111
|
-
{
|
|
112
|
-
key: "maxRuns",
|
|
113
|
-
prompt: t("wizard.maxRunsPrompt"),
|
|
114
|
-
hint: t("wizard.maxRunsHint"),
|
|
115
|
-
required: false,
|
|
116
|
-
inputType: "text",
|
|
117
|
-
defaultValue: initial.maxRuns ?? undefined,
|
|
118
|
-
},
|
|
119
|
-
{
|
|
120
|
-
key: "project",
|
|
121
|
-
prompt: t("wizard.projectPrompt"),
|
|
122
|
-
hint: t("wizard.projectHint"),
|
|
123
|
-
required: false,
|
|
124
|
-
defaultValue: props.projects.find((p) => p.id === (initial.project ?? "default"))?.name ?? props.projects[0]?.name,
|
|
125
|
-
onActivate: () => setOpenSelect("project"),
|
|
126
|
-
renderCustom: ({ value, isActive, onChange, onAdvance }) => {
|
|
127
|
-
fieldCallbacksRef.current.project = { value, onChange, onAdvance };
|
|
128
|
-
return (_jsx(SelectValueField, { label: value || null, placeholder: t("wizard.projectPrompt"), isActive: isActive }));
|
|
129
|
-
},
|
|
130
|
-
},
|
|
131
|
-
];
|
|
132
|
-
return list;
|
|
133
|
-
}, [taskModeInitial, selectedTaskId, resolvedTaskName, initial, commandValue, props.projects]);
|
|
134
|
-
const handleComplete = useCallback((values) => {
|
|
135
|
-
const intervalInput = values.interval ?? "";
|
|
136
|
-
if (!intervalInput.trim())
|
|
137
|
-
return;
|
|
138
|
-
let interval;
|
|
139
|
-
try {
|
|
140
|
-
interval = parseDuration(intervalInput.trim());
|
|
141
|
-
}
|
|
142
|
-
catch {
|
|
143
|
-
return;
|
|
144
|
-
}
|
|
145
|
-
const intervalHuman = intervalInput.trim();
|
|
146
|
-
const isExistingTask = !!values.taskMode?.includes("Existing");
|
|
147
|
-
if (isExistingTask && !selectedTaskId && !values.taskId?.trim())
|
|
148
|
-
return;
|
|
149
|
-
const cmdValue = values.command ?? commandValue;
|
|
150
|
-
if (!isExistingTask && !cmdValue.trim())
|
|
151
|
-
return;
|
|
152
|
-
const cmd = isExistingTask
|
|
153
|
-
? ""
|
|
154
|
-
: joinCommandLines(cmdValue);
|
|
155
|
-
let cmdOnly = "";
|
|
156
|
-
let args = [];
|
|
157
|
-
if (cmd.trim()) {
|
|
158
|
-
try {
|
|
159
|
-
const tokens = parseCommandLine(cmd.trim());
|
|
160
|
-
cmdOnly = tokens[0] ?? "";
|
|
161
|
-
args = tokens.slice(1);
|
|
162
|
-
}
|
|
163
|
-
catch {
|
|
164
|
-
return;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
const runNowValue = values.runNow === t("wizard.runNowNow");
|
|
168
|
-
const projectName = values.project ?? "";
|
|
169
|
-
const project = props.projects.find((p) => p.name === projectName);
|
|
170
|
-
const projectId = project?.id ?? currentProjectId;
|
|
171
|
-
const options = {
|
|
172
|
-
interval,
|
|
173
|
-
taskId: isExistingTask
|
|
174
|
-
? (selectedTaskId ?? values.taskId?.trim() ?? null)
|
|
175
|
-
: null,
|
|
176
|
-
command: cmdOnly,
|
|
177
|
-
commandArgs: args,
|
|
178
|
-
commandRaw: isExistingTask ? undefined : cmdValue,
|
|
179
|
-
cwd: (values.cwd ?? "").trim() || process.cwd(),
|
|
180
|
-
immediate: runNowValue,
|
|
181
|
-
maxRuns: (values.maxRuns ?? "").trim()
|
|
182
|
-
? parseInt(values.maxRuns, 10)
|
|
183
|
-
: null,
|
|
184
|
-
verbose: false,
|
|
185
|
-
description: (values.description ?? "").trim(),
|
|
186
|
-
projectId,
|
|
187
|
-
offset: null,
|
|
188
|
-
};
|
|
189
|
-
const desc = (values.description ?? "").trim() || [cmdOnly, ...args].join(" ").trim();
|
|
190
|
-
if (mode === "edit" && editId) {
|
|
191
|
-
updateLoop(editId, options, intervalHuman)
|
|
192
|
-
.then((id) => onDone(true, id, desc))
|
|
193
|
-
.catch(() => { });
|
|
194
|
-
}
|
|
195
|
-
else {
|
|
196
|
-
createLoop(options, intervalHuman)
|
|
197
|
-
.then((id) => onDone(false, id, desc))
|
|
198
|
-
.catch(() => { });
|
|
199
|
-
}
|
|
200
|
-
}, [selectedTaskId, mode, editId, currentProjectId, onDone, commandValue, props.projects]);
|
|
201
|
-
const selectOptionsFor = useCallback((field) => {
|
|
202
|
-
if (field === "taskMode") {
|
|
203
|
-
return [t("wizard.taskModeInline"), t("wizard.taskModeExisting")].map((v) => ({ value: v, label: v }));
|
|
204
|
-
}
|
|
205
|
-
if (field === "runNow") {
|
|
206
|
-
return [t("wizard.runNowWait"), t("wizard.runNowNow")].map((v) => ({ value: v, label: v }));
|
|
207
|
-
}
|
|
208
|
-
return props.projects.map((p) => ({ value: p.name, label: p.name }));
|
|
209
|
-
}, [props.projects]);
|
|
210
|
-
const selectTitleFor = (field) => field === "taskMode" ? t("wizard.taskModePrompt")
|
|
211
|
-
: field === "runNow" ? t("wizard.runNowPrompt")
|
|
212
|
-
: t("wizard.projectPrompt");
|
|
213
|
-
return (_jsxs(_Fragment, { children: [_jsx(WizardForm, { title: mode === "edit" ? t("wizard.editLoop") : t("wizard.newLoop"), steps: steps, onComplete: handleComplete, onCancel: onCancel, disabled: taskPickerOpen || openSelect !== null || commandEditorOpen }), taskPickerOpen ? (_jsx(TaskPickerModal, { tasks: tasks, onSelect: (task) => {
|
|
214
|
-
onChooseTask({ id: task.id, name: task.name });
|
|
215
|
-
setTaskPickerOpen(false);
|
|
216
|
-
}, onClose: () => setTaskPickerOpen(false) })) : null, openSelect ? (_jsx(SelectModal, { title: selectTitleFor(openSelect), options: selectOptionsFor(openSelect), initialValue: fieldCallbacksRef.current[openSelect]?.value, onSelect: (option) => {
|
|
217
|
-
fieldCallbacksRef.current[openSelect]?.onChange(option.value);
|
|
218
|
-
fieldCallbacksRef.current[openSelect]?.onAdvance();
|
|
219
|
-
setOpenSelect(null);
|
|
220
|
-
}, onClose: () => setOpenSelect(null) })) : null, commandEditorOpen ? (_jsx(CodeEditorModal, { initialValue: commandValue, onSave: (v) => {
|
|
221
|
-
setCommandValue(v);
|
|
222
|
-
setCommandEditorOpen(false);
|
|
223
|
-
}, onCancel: () => setCommandEditorOpen(false) })) : null] }));
|
|
224
|
-
}
|
package/dist/tui/daemon.js
DELETED
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
import { sendRequest, streamRequest } from "../client/ipc.js";
|
|
2
|
-
import { t } from "../i18n/index.js";
|
|
3
|
-
import { LOG_TAIL_DEFAULT } from "../config/constants.js";
|
|
4
|
-
import fs from "node:fs/promises";
|
|
5
|
-
import path from "node:path";
|
|
6
|
-
import { getDataDir } from "../config/paths.js";
|
|
7
|
-
function expectOk(message, type) {
|
|
8
|
-
if (type !== "ok") {
|
|
9
|
-
throw new Error(message);
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
export async function listLoops() {
|
|
13
|
-
const response = await sendRequest({ type: "list" });
|
|
14
|
-
if (response.type !== "ok") {
|
|
15
|
-
throw new Error(response.message);
|
|
16
|
-
}
|
|
17
|
-
return response.data;
|
|
18
|
-
}
|
|
19
|
-
export async function pauseLoop(id) {
|
|
20
|
-
const response = await sendRequest({ type: "pause", payload: { id } });
|
|
21
|
-
expectOk(response.message ?? t("errors.pauseFailed"), response.type);
|
|
22
|
-
}
|
|
23
|
-
export async function resumeLoop(id) {
|
|
24
|
-
const response = await sendRequest({ type: "resume", payload: { id } });
|
|
25
|
-
expectOk(response.message ?? t("errors.resumeFailed"), response.type);
|
|
26
|
-
}
|
|
27
|
-
export async function stopLoop(id) {
|
|
28
|
-
const response = await sendRequest({ type: "stop-loop", payload: { id } });
|
|
29
|
-
expectOk(response.message ?? "Stop failed", response.type);
|
|
30
|
-
}
|
|
31
|
-
export async function playLoop(id) {
|
|
32
|
-
const response = await sendRequest({ type: "play-loop", payload: { id } });
|
|
33
|
-
expectOk(response.message ?? "Play failed", response.type);
|
|
34
|
-
}
|
|
35
|
-
export async function triggerLoop(id) {
|
|
36
|
-
const response = await sendRequest({ type: "trigger", payload: { id } });
|
|
37
|
-
expectOk(response.message ?? t("errors.forceRunFailed"), response.type);
|
|
38
|
-
}
|
|
39
|
-
export async function deleteLoop(id) {
|
|
40
|
-
const response = await sendRequest({ type: "delete", payload: { id } });
|
|
41
|
-
expectOk(response.message ?? t("errors.deleteFailed"), response.type);
|
|
42
|
-
}
|
|
43
|
-
export async function createLoop(options, intervalHuman) {
|
|
44
|
-
const response = await sendRequest({
|
|
45
|
-
type: "start",
|
|
46
|
-
payload: { ...options, intervalHuman },
|
|
47
|
-
});
|
|
48
|
-
if (response.type !== "ok") {
|
|
49
|
-
throw new Error(response.message);
|
|
50
|
-
}
|
|
51
|
-
return response.data.id;
|
|
52
|
-
}
|
|
53
|
-
export async function updateLoop(id, options, intervalHuman) {
|
|
54
|
-
const response = await sendRequest({
|
|
55
|
-
type: "update",
|
|
56
|
-
payload: { id, ...options, intervalHuman },
|
|
57
|
-
});
|
|
58
|
-
if (response.type !== "ok") {
|
|
59
|
-
throw new Error(response.message);
|
|
60
|
-
}
|
|
61
|
-
return response.data.id;
|
|
62
|
-
}
|
|
63
|
-
export function streamLogs(id, onLine, onError) {
|
|
64
|
-
return streamRequest({ type: "logs", payload: { id, follow: true, tail: LOG_TAIL_DEFAULT } }, onLine, () => { }, onError);
|
|
65
|
-
}
|
|
66
|
-
export async function fetchRunLog(id, runNumber) {
|
|
67
|
-
const response = await sendRequest({ type: "run-log", payload: { id, runNumber } });
|
|
68
|
-
if (response.type !== "ok") {
|
|
69
|
-
throw new Error(response.message ?? "Failed to fetch run log");
|
|
70
|
-
}
|
|
71
|
-
return response.data ?? "";
|
|
72
|
-
}
|
|
73
|
-
export function streamRunLog(id, runNumber, onLine, onEnd, onError) {
|
|
74
|
-
return streamRequest({ type: "run-log-stream", payload: { id, runNumber } }, onLine, onEnd, onError);
|
|
75
|
-
}
|
|
76
|
-
export async function listTasks() {
|
|
77
|
-
const response = await sendRequest({ type: "task-list" });
|
|
78
|
-
if (response.type !== "ok") {
|
|
79
|
-
throw new Error(response.message);
|
|
80
|
-
}
|
|
81
|
-
return response.data;
|
|
82
|
-
}
|
|
83
|
-
export async function createTask(payload) {
|
|
84
|
-
const response = await sendRequest({ type: "task-create", payload });
|
|
85
|
-
if (response.type !== "ok") {
|
|
86
|
-
throw new Error(response.message);
|
|
87
|
-
}
|
|
88
|
-
return response.data;
|
|
89
|
-
}
|
|
90
|
-
export async function updateTask(id, payload) {
|
|
91
|
-
const response = await sendRequest({ type: "task-update", payload: { id, ...payload } });
|
|
92
|
-
if (response.type !== "ok") {
|
|
93
|
-
throw new Error(response.message);
|
|
94
|
-
}
|
|
95
|
-
return response.data;
|
|
96
|
-
}
|
|
97
|
-
export async function deleteTask(id) {
|
|
98
|
-
const response = await sendRequest({ type: "task-delete", payload: { id } });
|
|
99
|
-
expectOk(response.message ?? "Task delete failed", response.type);
|
|
100
|
-
}
|
|
101
|
-
export async function listProjects() {
|
|
102
|
-
const response = await sendRequest({ type: "project-list" });
|
|
103
|
-
if (response.type !== "ok")
|
|
104
|
-
throw new Error(response.message);
|
|
105
|
-
return response.data;
|
|
106
|
-
}
|
|
107
|
-
export async function createProject(name, color, directory) {
|
|
108
|
-
const response = await sendRequest({ type: "project-create", payload: { name, color, directory } });
|
|
109
|
-
if (response.type !== "ok")
|
|
110
|
-
throw new Error(response.message);
|
|
111
|
-
return response.data;
|
|
112
|
-
}
|
|
113
|
-
export async function updateProject(id, name, color, directory) {
|
|
114
|
-
const response = await sendRequest({ type: "project-update", payload: { id, name, color, directory } });
|
|
115
|
-
expectOk(response.message ?? t("project.error.updateFailed"), response.type);
|
|
116
|
-
}
|
|
117
|
-
export async function exportConfig() {
|
|
118
|
-
const [loops, tasks, projects] = await Promise.all([
|
|
119
|
-
listLoops().catch(() => []),
|
|
120
|
-
listTasks().catch(() => []),
|
|
121
|
-
listProjects().catch(() => []),
|
|
122
|
-
]);
|
|
123
|
-
const exportData = {
|
|
124
|
-
version: 2,
|
|
125
|
-
exportedAt: new Date().toISOString(),
|
|
126
|
-
loops: loops.map(({ runHistory: _ignored, ...rest }) => rest),
|
|
127
|
-
tasks,
|
|
128
|
-
projects,
|
|
129
|
-
};
|
|
130
|
-
const json = JSON.stringify(exportData, null, 2);
|
|
131
|
-
const dataDir = getDataDir();
|
|
132
|
-
const exportsDir = path.join(dataDir, "exports");
|
|
133
|
-
await fs.mkdir(exportsDir, { recursive: true });
|
|
134
|
-
const ts = new Date().toISOString().replace(/[:.]/g, "-");
|
|
135
|
-
const filePath = path.join(exportsDir, `loop-export-${ts}.json`);
|
|
136
|
-
await fs.writeFile(filePath, json, "utf-8");
|
|
137
|
-
return { json, filePath };
|
|
138
|
-
}
|
|
139
|
-
export async function deleteProject(id) {
|
|
140
|
-
const response = await sendRequest({ type: "project-delete", payload: { id } });
|
|
141
|
-
expectOk(response.message ?? t("project.error.deleteFailed"), response.type);
|
|
142
|
-
}
|
package/dist/tui/format.js
DELETED
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
import { t } from "../i18n/index.js";
|
|
2
|
-
export function unescapeCommand(str) {
|
|
3
|
-
return str
|
|
4
|
-
.replace(/\\\\/g, "\x00")
|
|
5
|
-
.replace(/\\"/g, '"')
|
|
6
|
-
.replace(/\x00/g, "\\");
|
|
7
|
-
}
|
|
8
|
-
export function quoteArg(arg) {
|
|
9
|
-
return /[\s"]/.test(arg) ? `"${arg.replace(/"/g, '\\"')}"` : arg;
|
|
10
|
-
}
|
|
11
|
-
export function commandLine(command, args) {
|
|
12
|
-
return [unescapeCommand(command), ...args.map((a) => quoteArg(unescapeCommand(a)))].join(" ").trim();
|
|
13
|
-
}
|
|
14
|
-
export function formatCmd(command, args, max = 24) {
|
|
15
|
-
const full = commandLine(command, args);
|
|
16
|
-
return full.length > max ? full.slice(0, max - 3) + "..." : full;
|
|
17
|
-
}
|
|
18
|
-
export function describeLoop(loop) {
|
|
19
|
-
return loop.description?.trim() || commandLine(loop.command, loop.commandArgs);
|
|
20
|
-
}
|
|
21
|
-
export function truncate(text, max) {
|
|
22
|
-
return text.length > max ? text.slice(0, max - 3) + "..." : text;
|
|
23
|
-
}
|
|
24
|
-
export function timeAgo(iso) {
|
|
25
|
-
if (!iso)
|
|
26
|
-
return t("format.dash");
|
|
27
|
-
const diff = Date.now() - new Date(iso).getTime();
|
|
28
|
-
const secs = Math.floor(diff / 1000);
|
|
29
|
-
if (secs < 5)
|
|
30
|
-
return t("format.justNow");
|
|
31
|
-
if (secs < 60)
|
|
32
|
-
return t("format.secsAgo", { secs });
|
|
33
|
-
const mins = Math.floor(secs / 60);
|
|
34
|
-
if (mins < 60)
|
|
35
|
-
return t("format.minsAgo", { mins });
|
|
36
|
-
const hrs = Math.floor(mins / 60);
|
|
37
|
-
if (hrs < 24)
|
|
38
|
-
return t("format.hrsAgo", { hrs });
|
|
39
|
-
return t("format.daysAgo", { days: Math.floor(hrs / 24) });
|
|
40
|
-
}
|
|
41
|
-
export function timeUntil(iso) {
|
|
42
|
-
if (!iso)
|
|
43
|
-
return t("format.dash");
|
|
44
|
-
const diff = Math.max(0, new Date(iso).getTime() - Date.now());
|
|
45
|
-
const secs = Math.floor(diff / 1000);
|
|
46
|
-
if (secs < 5)
|
|
47
|
-
return t("format.justNow");
|
|
48
|
-
if (secs < 60)
|
|
49
|
-
return t("format.secsAhead", { secs });
|
|
50
|
-
const mins = Math.floor(secs / 60);
|
|
51
|
-
if (mins < 60)
|
|
52
|
-
return t("format.minsAhead", { mins });
|
|
53
|
-
const hrs = Math.floor(mins / 60);
|
|
54
|
-
if (hrs < 24)
|
|
55
|
-
return t("format.hrsAhead", { hrs });
|
|
56
|
-
return t("format.daysAhead", { days: Math.floor(hrs / 24) });
|
|
57
|
-
}
|
|
58
|
-
const STATUS_COLORS = {
|
|
59
|
-
running: "#4ade80",
|
|
60
|
-
waiting: "#6b7280",
|
|
61
|
-
paused: "#facc15",
|
|
62
|
-
idle: "#fb923c",
|
|
63
|
-
stopped: "#f87171",
|
|
64
|
-
};
|
|
65
|
-
export function statusColor(status) {
|
|
66
|
-
return STATUS_COLORS[status] ?? "#ffffff";
|
|
67
|
-
}
|
|
68
|
-
export function timingLabel(loop) {
|
|
69
|
-
if (loop.status === "paused")
|
|
70
|
-
return t("format.timingPaused");
|
|
71
|
-
if (loop.status === "idle")
|
|
72
|
-
return t("format.timingIdle");
|
|
73
|
-
if (loop.nextRunAt)
|
|
74
|
-
return t("format.timingNext", { timeAgo: timeUntil(loop.nextRunAt) });
|
|
75
|
-
if (loop.lastRunAt)
|
|
76
|
-
return t("format.timingLast", { timeAgo: timeAgo(loop.lastRunAt) });
|
|
77
|
-
return t("format.timingNew");
|
|
78
|
-
}
|
|
79
|
-
export function statusLabel(status) {
|
|
80
|
-
return status === "waiting" ? "waiting" : status;
|
|
81
|
-
}
|
|
82
|
-
export function formatFileSize(bytes) {
|
|
83
|
-
if (bytes < 1024)
|
|
84
|
-
return `${bytes} B`;
|
|
85
|
-
if (bytes < 1024 * 1024)
|
|
86
|
-
return `${Math.round(bytes / 1024)} KB`;
|
|
87
|
-
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
88
|
-
}
|
|
89
|
-
export function formatRunDuration(ms) {
|
|
90
|
-
if (ms < 1000)
|
|
91
|
-
return `${ms}ms`;
|
|
92
|
-
if (ms < 60000)
|
|
93
|
-
return `${(ms / 1000).toFixed(1)}s`;
|
|
94
|
-
return `${Math.floor(ms / 60000)}m${Math.floor((ms % 60000) / 1000)}s`;
|
|
95
|
-
}
|
|
96
|
-
export function formatRunTime(iso) {
|
|
97
|
-
const d = new Date(iso);
|
|
98
|
-
return d.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", second: "2-digit", hour12: false });
|
|
99
|
-
}
|
|
100
|
-
export function formatDate(iso) {
|
|
101
|
-
const d = new Date(iso);
|
|
102
|
-
const date = d.toLocaleDateString("en-US", { month: "short", day: "numeric" });
|
|
103
|
-
const time = d.toLocaleTimeString("en-US", { hour: "2-digit", minute: "2-digit", hour12: false });
|
|
104
|
-
return `${date} ${time}`;
|
|
105
|
-
}
|
|
106
|
-
export function sinceLabel(loop) {
|
|
107
|
-
const ts = loop.sessionStartedAt ?? loop.createdAt;
|
|
108
|
-
if (!ts)
|
|
109
|
-
return t("format.dash");
|
|
110
|
-
return formatDate(ts);
|
|
111
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|