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.
Files changed (148) hide show
  1. package/dist/app/App.js +119 -0
  2. package/dist/{tui → app}/index.js +4 -5
  3. package/dist/app/providers/index.js +1 -0
  4. package/dist/cli/import-writer.js +1 -1
  5. package/dist/cli.js +9 -9
  6. package/dist/client/cli-format.js +7 -0
  7. package/dist/client/commands.js +2 -164
  8. package/dist/client/ipc.js +3 -3
  9. package/dist/client/project-commands.js +159 -0
  10. package/dist/core/{command-runner.js → command/command-runner.js} +3 -3
  11. package/dist/core/{foreground-loop.js → foreground/index.js} +4 -4
  12. package/dist/core/{log-rotator.js → logging/log-rotator.js} +1 -1
  13. package/dist/core/loop/chain-executor.js +67 -0
  14. package/dist/core/loop/delay-utils.js +65 -0
  15. package/dist/core/loop/loop-controller.js +191 -0
  16. package/dist/core/loop/loop-runner.js +132 -0
  17. package/dist/core/loop/run-executor.js +90 -0
  18. package/dist/core/loop/types.js +1 -0
  19. package/dist/daemon/daemon-log.js +1 -1
  20. package/dist/daemon/http/helpers.js +81 -0
  21. package/dist/daemon/http/openapi.js +92 -0
  22. package/dist/daemon/http/route-loops.js +218 -0
  23. package/dist/daemon/http/route-misc.js +36 -0
  24. package/dist/daemon/http/route-projects.js +55 -0
  25. package/dist/daemon/http/route-tasks.js +53 -0
  26. package/dist/daemon/http/routes.js +17 -0
  27. package/dist/daemon/http/server.js +64 -0
  28. package/dist/daemon/http/sse.js +36 -0
  29. package/dist/daemon/index.js +7 -9
  30. package/dist/{ipc/handlers → daemon/ipc}/logs-stream.js +1 -1
  31. package/dist/daemon/managers/loop-entry.js +24 -0
  32. package/dist/daemon/{manager.js → managers/loop-manager.js} +32 -152
  33. package/dist/daemon/managers/loop-options.js +16 -0
  34. package/dist/daemon/managers/loop-serialization.js +43 -0
  35. package/dist/daemon/{projects.js → managers/project-manager.js} +6 -4
  36. package/dist/daemon/{task-manager.js → managers/task-manager.js} +5 -3
  37. package/dist/daemon/server/handlers/index.js +86 -0
  38. package/dist/daemon/server/handlers/log-handlers.js +130 -0
  39. package/dist/daemon/server/handlers/loop-handlers.js +54 -0
  40. package/dist/daemon/server/handlers/project-handlers.js +37 -0
  41. package/dist/daemon/server/handlers/task-handlers.js +20 -0
  42. package/dist/daemon/server/index.js +113 -0
  43. package/dist/daemon/{spawner.js → spawner/index.js} +4 -4
  44. package/dist/daemon/{state.js → state/index.js} +4 -4
  45. package/dist/daemon/{file-watcher.js → watcher/index.js} +8 -6
  46. package/dist/duration.js +1 -1
  47. package/dist/{tui/state.js → entities/loops/filters.js} +0 -76
  48. package/dist/entities/loops/index.js +1 -0
  49. package/dist/entities/projects/filters.js +69 -0
  50. package/dist/entities/projects/index.js +1 -0
  51. package/dist/entities/tasks/filters.js +41 -0
  52. package/dist/entities/tasks/index.js +1 -0
  53. package/dist/{tui/components → features/chain-editor}/ChainEditor.js +7 -5
  54. package/dist/features/code-editor/CodeEditorModal.js +84 -0
  55. package/dist/{tui/components → features/code-editor}/CodeEditorPreview.js +3 -3
  56. package/dist/features/code-editor/useEditorKeyboard.js +147 -0
  57. package/dist/features/code-editor/useModalDimensions.js +9 -0
  58. package/dist/{tui → features/commands}/commands.js +2 -2
  59. package/dist/features/commands/useCommandHandlers.js +147 -0
  60. package/dist/features/commands/useContextualActions.js +75 -0
  61. package/dist/features/commands/useGlobalShortcuts.js +152 -0
  62. package/dist/features/forms/FormRouter.js +44 -0
  63. package/dist/{tui/components → features/overlays}/ContextHelpModal.js +3 -3
  64. package/dist/{tui/components → features/overlays}/ExportModal.js +3 -3
  65. package/dist/{tui/components → features/overlays}/HelpGuideModal.js +3 -3
  66. package/dist/{tui/components → features/overlays}/HelpModal.js +3 -3
  67. package/dist/features/overlays/OverlayStack.js +9 -0
  68. package/dist/{tui/components → features/overlays}/ProjectsModal.js +2 -2
  69. package/dist/{tui/components → features/overlays}/TaskPickerModal.js +2 -2
  70. package/dist/{tui/components → features/overlays}/WelcomeScreen.js +5 -3
  71. package/dist/features/overlays/useOverlayStack.js +51 -0
  72. package/dist/features/state/useAppState.js +150 -0
  73. package/dist/logger.js +0 -1
  74. package/dist/loop-config.js +9 -4
  75. package/dist/{config → shared/config}/constants.js +1 -1
  76. package/dist/shared/container/index.js +22 -0
  77. package/dist/{tui → shared}/hooks/useBreakpoint.js +1 -1
  78. package/dist/shared/hooks/useInject.js +6 -0
  79. package/dist/{tui → shared}/hooks/useLogStream.js +7 -5
  80. package/dist/{hooks → shared/hooks}/useLoopFormValidation.js +2 -2
  81. package/dist/{tui → shared}/hooks/useLoopPolling.js +6 -4
  82. package/dist/shared/{useUndoRedo.js → hooks/useUndoRedo.js} +2 -4
  83. package/dist/shared/providers/InversifyProvider.js +6 -0
  84. package/dist/shared/services/export-service.js +43 -0
  85. package/dist/shared/services/log-service.js +28 -0
  86. package/dist/shared/services/loop-service.js +78 -0
  87. package/dist/shared/services/project-service.js +39 -0
  88. package/dist/shared/services/task-service.js +41 -0
  89. package/dist/shared/services/types.js +7 -0
  90. package/dist/{tui/components → shared/ui}/Button.js +1 -1
  91. package/dist/{tui/components → shared/ui}/DebugPanel.js +1 -1
  92. package/dist/{tui/components → shared/ui}/FocusableButton.js +1 -1
  93. package/dist/{tui/components → shared/ui}/FocusableInput.js +1 -1
  94. package/dist/{tui/components → shared/ui}/FocusableList.js +1 -1
  95. package/dist/{tui/components → shared/ui}/Modal.js +1 -1
  96. package/dist/{tui/components → shared/ui}/SelectModal.js +2 -2
  97. package/dist/{tui/components → shared/ui}/Toast.js +1 -1
  98. package/dist/shared/ui/state.js +9 -0
  99. package/dist/{tui → shared}/utils/paste.js +2 -2
  100. package/dist/{tui → shared}/utils/validation.js +1 -1
  101. package/dist/widgets/command-input/CommandDropdown.js +69 -0
  102. package/dist/widgets/command-input/CommandInput.js +129 -0
  103. package/dist/widgets/command-input/HintBar.js +9 -0
  104. package/dist/widgets/command-input/InputModes.js +68 -0
  105. package/dist/{tui/components → widgets/commands-browser}/CommandsBrowserModal.js +3 -3
  106. package/dist/{tui/components → widgets/header}/Header.js +3 -3
  107. package/dist/{tui/components → widgets/header}/TabBar.js +1 -1
  108. package/dist/{tui/components → widgets/left-panel}/LeftPanel.js +3 -3
  109. package/dist/{tui/components → widgets/left-panel}/Navigator.js +3 -3
  110. package/dist/{tui/components → widgets/left-panel}/ProjectsPage.js +9 -34
  111. package/dist/widgets/left-panel/ProjectsPageParts.js +34 -0
  112. package/dist/{tui/components → widgets/left-panel}/TaskBrowser.js +4 -4
  113. package/dist/{tui/components → widgets/log-modal}/LogModal.js +8 -6
  114. package/dist/widgets/loop-form/CreateForm.js +75 -0
  115. package/dist/widgets/loop-form/TextField.js +9 -0
  116. package/dist/{tui/components → widgets/loop-form}/WizardForm.js +5 -10
  117. package/dist/widgets/loop-form/useCreateSteps.js +107 -0
  118. package/dist/widgets/loop-form/useHandleComplete.js +74 -0
  119. package/dist/{tui/components → widgets/project-form}/ProjectForm.js +9 -7
  120. package/dist/{tui/components → widgets/right-panel}/Inspector.js +4 -4
  121. package/dist/{tui/components → widgets/right-panel}/RightPanel.js +4 -4
  122. package/dist/{tui/components → widgets/right-panel}/RunHistory.js +52 -4
  123. package/dist/{tui/components → widgets/task-form}/TaskForm.js +11 -9
  124. package/package.json +2 -1
  125. package/dist/core/loop-controller.js +0 -511
  126. package/dist/daemon/http-server.js +0 -599
  127. package/dist/daemon/server.js +0 -374
  128. package/dist/tui/App.js +0 -680
  129. package/dist/tui/components/CodeEditorModal.js +0 -244
  130. package/dist/tui/components/CommandInput.js +0 -273
  131. package/dist/tui/components/CreateForm.js +0 -224
  132. package/dist/tui/daemon.js +0 -142
  133. package/dist/tui/format.js +0 -111
  134. /package/dist/{tui/router.js → app/router/index.js} +0 -0
  135. /package/dist/{tui → app}/types.js +0 -0
  136. /package/dist/core/{resolve-cwd.js → command/resolve-cwd.js} +0 -0
  137. /package/dist/core/{context-parser.js → context/context-parser.js} +0 -0
  138. /package/dist/core/{template.js → context/template.js} +0 -0
  139. /package/dist/core/{log-parser.js → logging/log-parser.js} +0 -0
  140. /package/dist/core/{scheduling.js → scheduling/index.js} +0 -0
  141. /package/dist/{ipc → daemon/ipc}/send.js +0 -0
  142. /package/dist/{config → shared/config}/paths.js +0 -0
  143. /package/dist/{i18n → shared/i18n}/en.json +0 -0
  144. /package/dist/{i18n → shared/i18n}/index.js +0 -0
  145. /package/dist/{board → shared/ui}/format.js +0 -0
  146. /package/dist/{tui → shared/ui}/hooks/useHoverState.js +0 -0
  147. /package/dist/{tui → shared/ui}/theme.js +0 -0
  148. /package/dist/{tui → shared}/utils/syntax.js +0 -0
@@ -1,374 +0,0 @@
1
- import net from "node:net";
2
- import fs from "node:fs";
3
- import { getSocketPath, removeSocketFile } from "./state.js";
4
- import { t } from "../i18n/index.js";
5
- import { LOG_TAIL_DEFAULT } from "../config/constants.js";
6
- import { send } from "../ipc/send.js";
7
- import { tail } from "../shared/tail.js";
8
- import { streamLogFollow } from "../ipc/handlers/logs-stream.js";
9
- export class IpcServer {
10
- server;
11
- manager;
12
- taskManager;
13
- socketPath;
14
- clients = new Set();
15
- subscribers = new Set();
16
- constructor(manager, taskManager) {
17
- this.manager = manager;
18
- this.taskManager = taskManager;
19
- this.socketPath = getSocketPath();
20
- this.server = net.createServer((socket) => this.handleConnection(socket));
21
- }
22
- async listen() {
23
- removeSocketFile();
24
- return new Promise((resolve, reject) => {
25
- this.server.on("error", reject);
26
- this.server.listen(this.socketPath, () => {
27
- this.server.removeListener("error", reject);
28
- resolve();
29
- });
30
- });
31
- }
32
- async close() {
33
- for (const client of this.clients) {
34
- client.destroy();
35
- }
36
- this.clients.clear();
37
- this.subscribers.clear();
38
- return new Promise((resolve) => {
39
- this.server.close(() => {
40
- removeSocketFile();
41
- resolve();
42
- });
43
- });
44
- }
45
- pushEvent(event, data) {
46
- for (const socket of this.subscribers) {
47
- try {
48
- send(socket, { type: "event", event, data });
49
- }
50
- catch {
51
- this.subscribers.delete(socket);
52
- }
53
- }
54
- }
55
- handleConnection(socket) {
56
- this.clients.add(socket);
57
- socket.on("close", () => {
58
- this.clients.delete(socket);
59
- this.subscribers.delete(socket);
60
- });
61
- let buffer = "";
62
- socket.on("data", (chunk) => {
63
- buffer += chunk.toString();
64
- const lines = buffer.split("\n");
65
- buffer = lines.pop() ?? "";
66
- for (const line of lines) {
67
- if (line.trim()) {
68
- try {
69
- const request = JSON.parse(line);
70
- this.handleRequest(request, socket);
71
- }
72
- catch {
73
- send(socket, { type: "error", message: t("errors.invalidJson") });
74
- }
75
- }
76
- }
77
- });
78
- socket.on("close", () => {
79
- this.clients.delete(socket);
80
- });
81
- socket.on("error", () => {
82
- this.clients.delete(socket);
83
- });
84
- }
85
- respondOk(socket, ok, id, data) {
86
- if (!ok) {
87
- send(socket, { type: "error", message: t("errors.loopNotFound", { id }) });
88
- }
89
- else if (data !== undefined) {
90
- send(socket, { type: "ok", data });
91
- }
92
- else {
93
- send(socket, { type: "ok" });
94
- }
95
- }
96
- async handleRequest(request, socket) {
97
- switch (request.type) {
98
- case "start": {
99
- const { intervalHuman, ...options } = request.payload;
100
- const id = this.manager.start(options, intervalHuman);
101
- send(socket, { type: "ok", data: { id } });
102
- break;
103
- }
104
- case "update": {
105
- const { id, intervalHuman, ...options } = request.payload;
106
- const ok = await this.manager.update(id, options, intervalHuman);
107
- this.respondOk(socket, ok, id, ok ? { id } : undefined);
108
- break;
109
- }
110
- case "list": {
111
- send(socket, { type: "ok", data: this.manager.list() });
112
- break;
113
- }
114
- case "status": {
115
- const meta = this.manager.status(request.payload.id);
116
- this.respondOk(socket, meta !== null, request.payload.id, meta ?? undefined);
117
- break;
118
- }
119
- case "pause": {
120
- this.respondOk(socket, this.manager.pause(request.payload.id), request.payload.id);
121
- break;
122
- }
123
- case "resume": {
124
- this.respondOk(socket, this.manager.resume(request.payload.id), request.payload.id);
125
- break;
126
- }
127
- case "stop-loop": {
128
- this.respondOk(socket, this.manager.stopLoop(request.payload.id), request.payload.id);
129
- break;
130
- }
131
- case "stop-all": {
132
- const count = this.manager.stopAllLoops();
133
- send(socket, { type: "ok", data: count });
134
- break;
135
- }
136
- case "play-loop": {
137
- if (this.manager.isMaxRunsBlocked(request.payload.id)) {
138
- send(socket, { type: "error", message: t("errors.maxRunsReached") });
139
- break;
140
- }
141
- this.respondOk(socket, this.manager.playLoop(request.payload.id), request.payload.id);
142
- break;
143
- }
144
- case "trigger": {
145
- if (this.manager.isMaxRunsBlocked(request.payload.id)) {
146
- send(socket, { type: "error", message: t("errors.maxRunsReached") });
147
- break;
148
- }
149
- if (this.manager.isRunning(request.payload.id)) {
150
- send(socket, { type: "error", message: t("errors.triggerWhileRunning") });
151
- break;
152
- }
153
- this.respondOk(socket, this.manager.trigger(request.payload.id), request.payload.id);
154
- break;
155
- }
156
- case "delete": {
157
- const ok = await this.manager.delete(request.payload.id);
158
- this.respondOk(socket, ok, request.payload.id);
159
- break;
160
- }
161
- case "run-log": {
162
- this.handleRunLog(request, socket);
163
- break;
164
- }
165
- case "run-log-stream": {
166
- this.handleRunLogStream(request, socket);
167
- break;
168
- }
169
- case "attach":
170
- case "logs": {
171
- this.handleLogs(request, socket);
172
- break;
173
- }
174
- case "task-create": {
175
- const task = this.taskManager.create(request.payload);
176
- send(socket, { type: "ok", data: task });
177
- break;
178
- }
179
- case "task-update": {
180
- const updated = this.taskManager.update(request.payload.id, request.payload);
181
- this.respondOk(socket, updated !== null, request.payload.id, updated ?? undefined);
182
- break;
183
- }
184
- case "task-list": {
185
- send(socket, { type: "ok", data: this.taskManager.list() });
186
- break;
187
- }
188
- case "task-get": {
189
- const task = this.taskManager.get(request.payload.id);
190
- this.respondOk(socket, task !== null, request.payload.id, task ?? undefined);
191
- break;
192
- }
193
- case "task-delete": {
194
- const ok = this.taskManager.delete(request.payload.id);
195
- this.respondOk(socket, ok, request.payload.id);
196
- break;
197
- }
198
- case "project-list": {
199
- send(socket, { type: "ok", data: this.manager.listProjects() });
200
- break;
201
- }
202
- case "project-create": {
203
- const { name, color, directory } = request.payload;
204
- if (!name || !name.trim()) {
205
- send(socket, { type: "error", message: t("project.error.nameRequired") });
206
- break;
207
- }
208
- const project = this.manager.createProject(name.trim(), color, directory);
209
- send(socket, { type: "ok", data: project });
210
- break;
211
- }
212
- case "project-update": {
213
- const { id, name, color, directory } = request.payload;
214
- if (!name || !name.trim()) {
215
- send(socket, { type: "error", message: t("project.error.nameEmpty") });
216
- break;
217
- }
218
- try {
219
- this.manager.updateProject(id, name.trim(), color, directory);
220
- send(socket, { type: "ok" });
221
- }
222
- catch (err) {
223
- send(socket, { type: "error", message: err instanceof Error ? err.message : String(err) });
224
- }
225
- break;
226
- }
227
- case "project-delete": {
228
- try {
229
- this.manager.deleteProject(request.payload.id);
230
- send(socket, { type: "ok" });
231
- }
232
- catch (err) {
233
- send(socket, { type: "error", message: err instanceof Error ? err.message : String(err) });
234
- }
235
- break;
236
- }
237
- case "subscribe": {
238
- this.subscribers.add(socket);
239
- send(socket, { type: "ok" });
240
- break;
241
- }
242
- case "shutdown": {
243
- send(socket, { type: "ok" });
244
- await this.manager.shutdown();
245
- await this.close();
246
- process.exit(0);
247
- }
248
- }
249
- }
250
- handleRunLog(request, socket) {
251
- const { id, runNumber } = request.payload;
252
- const logPath = this.manager.getLogPath(id);
253
- if (!logPath || !fs.existsSync(logPath)) {
254
- send(socket, { type: "ok", data: "" });
255
- return;
256
- }
257
- const meta = this.manager.status(id);
258
- if (!meta) {
259
- send(socket, { type: "ok", data: "" });
260
- return;
261
- }
262
- const records = meta.runHistory
263
- .filter((r) => r.runNumber === runNumber)
264
- .sort((a, b) => a.logOffset - b.logOffset);
265
- if (records.length === 0) {
266
- send(socket, { type: "ok", data: "" });
267
- return;
268
- }
269
- const buffer = fs.readFileSync(logPath);
270
- const allSorted = meta.runHistory.slice().sort((a, b) => a.logOffset - b.logOffset);
271
- const parts = records.map((record) => {
272
- const start = record.logOffset;
273
- const idx = allSorted.indexOf(record);
274
- const end = idx < allSorted.length - 1 ? allSorted[idx + 1].logOffset : buffer.length;
275
- return buffer.toString("utf-8", start, end);
276
- });
277
- send(socket, { type: "ok", data: parts.join("") });
278
- }
279
- handleRunLogStream(request, socket) {
280
- const { id, runNumber } = request.payload;
281
- const meta = this.manager.status(id);
282
- if (!meta) {
283
- send(socket, { type: "error", message: t("errors.loopNotFound", { id }) });
284
- return;
285
- }
286
- const records = meta.runHistory
287
- .filter((r) => r.runNumber === runNumber)
288
- .sort((a, b) => a.logOffset - b.logOffset);
289
- if (records.length === 0) {
290
- send(socket, { type: "ok", data: "" });
291
- return;
292
- }
293
- const logPath = this.manager.getLogPath(id);
294
- if (!logPath || !fs.existsSync(logPath)) {
295
- send(socket, { type: "ok", data: "" });
296
- return;
297
- }
298
- const firstOffset = records[0].logOffset;
299
- const stat = fs.statSync(logPath);
300
- if (stat.size > firstOffset) {
301
- const fd = fs.openSync(logPath, "r");
302
- const buf = Buffer.alloc(stat.size - firstOffset);
303
- fs.readSync(fd, buf, 0, buf.length, firstOffset);
304
- fs.closeSync(fd);
305
- for (const line of buf.toString().split("\n")) {
306
- if (line) {
307
- send(socket, { type: "data", line });
308
- }
309
- }
310
- }
311
- const allCompleted = records.every((r) => r.status === "completed");
312
- if (allCompleted) {
313
- send(socket, { type: "end" });
314
- return;
315
- }
316
- let fileSize = stat.size;
317
- const watcher = fs.watch(logPath, (eventType) => {
318
- if (eventType === "rename" && !fs.existsSync(logPath)) {
319
- watcher.close();
320
- send(socket, { type: "end" });
321
- return;
322
- }
323
- if (eventType === "change") {
324
- try {
325
- const s = fs.statSync(logPath);
326
- if (s.size > fileSize) {
327
- const fd = fs.openSync(logPath, "r");
328
- const buf = Buffer.alloc(s.size - fileSize);
329
- fs.readSync(fd, buf, 0, buf.length, fileSize);
330
- fs.closeSync(fd);
331
- fileSize = s.size;
332
- for (const line of buf.toString().split("\n")) {
333
- if (line) {
334
- send(socket, { type: "data", line });
335
- }
336
- }
337
- }
338
- }
339
- catch {
340
- watcher.close();
341
- send(socket, { type: "end" });
342
- }
343
- }
344
- });
345
- socket.on("close", () => watcher.close());
346
- socket.on("error", () => watcher.close());
347
- }
348
- handleLogs(request, socket) {
349
- const id = request.payload.id;
350
- const logPath = this.manager.getLogPath(id);
351
- if (!logPath) {
352
- send(socket, { type: "error", message: t("errors.loopNotFound", { id }) });
353
- return;
354
- }
355
- if (!fs.existsSync(logPath)) {
356
- if (request.type === "logs") {
357
- send(socket, { type: "ok", data: "" });
358
- }
359
- else {
360
- send(socket, { type: "error", message: t("errors.noLogsYet") });
361
- }
362
- return;
363
- }
364
- const follow = request.type === "attach" || (request.type === "logs" && request.payload.follow);
365
- const tailCount = request.type === "logs" ? (request.payload.tail ?? LOG_TAIL_DEFAULT) : 0;
366
- if (follow) {
367
- streamLogFollow(logPath, socket, tailCount);
368
- }
369
- else {
370
- const content = fs.readFileSync(logPath, "utf-8");
371
- send(socket, { type: "ok", data: tail(content, tailCount).join("\n") });
372
- }
373
- }
374
- }