taskmeld 0.1.1
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/LICENSE +18 -0
- package/README.md +172 -0
- package/README.zh-CN.md +172 -0
- package/dist/src/app/app-context-env.js +51 -0
- package/dist/src/app/create-app-context.js +127 -0
- package/dist/src/app/data-dir.js +29 -0
- package/dist/src/app/pipeline-config.js +105 -0
- package/dist/src/app/pipeline-plugin-config.js +2 -0
- package/dist/src/app/pipeline-registry.js +502 -0
- package/dist/src/app/pipeline-runtime.js +202 -0
- package/dist/src/app/runtime-store.js +151 -0
- package/dist/src/app/user-config.js +37 -0
- package/dist/src/artifacts/artifact-cleanup.js +192 -0
- package/dist/src/artifacts/artifact-index.js +262 -0
- package/dist/src/artifacts/artifact-rebuilder.js +120 -0
- package/dist/src/artifacts/storage-service.js +371 -0
- package/dist/src/cli/bootstrap.js +226 -0
- package/dist/src/cli/commands/agent.js +126 -0
- package/dist/src/cli/commands/artifact.js +175 -0
- package/dist/src/cli/commands/init.js +150 -0
- package/dist/src/cli/commands/pipeline/errors.js +37 -0
- package/dist/src/cli/commands/pipeline/result.js +179 -0
- package/dist/src/cli/commands/pipeline/selector.js +51 -0
- package/dist/src/cli/commands/pipeline/types.js +2 -0
- package/dist/src/cli/commands/pipeline/watch.js +67 -0
- package/dist/src/cli/commands/pipeline.js +339 -0
- package/dist/src/cli/commands/scheduler.js +81 -0
- package/dist/src/cli/commands/server.js +70 -0
- package/dist/src/cli/commands/system.js +21 -0
- package/dist/src/cli/errors.js +71 -0
- package/dist/src/cli/help.js +184 -0
- package/dist/src/cli/index.js +65 -0
- package/dist/src/cli/output.js +19 -0
- package/dist/src/cli/renderers/engine/json.js +67 -0
- package/dist/src/cli/renderers/engine/markdown.js +95 -0
- package/dist/src/cli/renderers/engine/types.js +2 -0
- package/dist/src/cli/renderers/engine/utils.js +32 -0
- package/dist/src/cli/renderers/index.js +27 -0
- package/dist/src/cli/renderers/specs/agent.js +78 -0
- package/dist/src/cli/renderers/specs/artifact.js +32 -0
- package/dist/src/cli/renderers/specs/index.js +36 -0
- package/dist/src/cli/renderers/specs/init.js +25 -0
- package/dist/src/cli/renderers/specs/pipeline.js +561 -0
- package/dist/src/cli/renderers/specs/scheduler.js +46 -0
- package/dist/src/cli/renderers/specs/server.js +38 -0
- package/dist/src/cli/renderers/specs/system.js +36 -0
- package/dist/src/cli/router.js +199 -0
- package/dist/src/cli/server-runtime-client.js +780 -0
- package/dist/src/cli/types.js +2 -0
- package/dist/src/gateway/frame-sanitizer.js +78 -0
- package/dist/src/gateway/gateway-client.js +462 -0
- package/dist/src/gateway/index.js +18 -0
- package/dist/src/gateway/types.js +2 -0
- package/dist/src/index.js +123 -0
- package/dist/src/logs/run-log-reader.js +141 -0
- package/dist/src/logs/run-log-service.js +42 -0
- package/dist/src/logs/run-log-types.js +2 -0
- package/dist/src/pipeline/agent-activity.js +191 -0
- package/dist/src/pipeline/artifact-storage.js +208 -0
- package/dist/src/pipeline/diagnostics/dependency-diagnostic.js +105 -0
- package/dist/src/pipeline/diagnostics/index.js +6 -0
- package/dist/src/pipeline/dispatch/pipeline-inbound-queue.js +215 -0
- package/dist/src/pipeline/dispatch/pipeline-link-dispatcher.js +66 -0
- package/dist/src/pipeline/dispatch/pipeline-link-store.js +94 -0
- package/dist/src/pipeline/dispatch/pipeline-queue-drainer.js +71 -0
- package/dist/src/pipeline/execution/dependency-check.js +52 -0
- package/dist/src/pipeline/execution/execution-result.js +2 -0
- package/dist/src/pipeline/execution/group-item-executor.js +128 -0
- package/dist/src/pipeline/execution/index.js +5 -0
- package/dist/src/pipeline/execution/node-item-executor.js +58 -0
- package/dist/src/pipeline/execution/node-runner.js +159 -0
- package/dist/src/pipeline/execution/readiness-state.js +10 -0
- package/dist/src/pipeline/execution/reject-handler.js +94 -0
- package/dist/src/pipeline/execution/rejected-artifact-archiver.js +45 -0
- package/dist/src/pipeline/execution/route-item-manager.js +253 -0
- package/dist/src/pipeline/execution/run-abort-controller.js +66 -0
- package/dist/src/pipeline/execution/run-state-helpers.js +257 -0
- package/dist/src/pipeline/execution/service.js +165 -0
- package/dist/src/pipeline/execution/session-registry.js +96 -0
- package/dist/src/pipeline/execution/structured-node-runner.js +411 -0
- package/dist/src/pipeline/execution-status.js +96 -0
- package/dist/src/pipeline/execution-timeout.js +21 -0
- package/dist/src/pipeline/identity/index.js +32 -0
- package/dist/src/pipeline/identity/types.js +2 -0
- package/dist/src/pipeline/item-batch-controller.js +227 -0
- package/dist/src/pipeline/output/pipeline-output-resolver.js +91 -0
- package/dist/src/pipeline/output/pipeline-output-store.js +60 -0
- package/dist/src/pipeline/runtime-model.js +173 -0
- package/dist/src/pipeline/scheduler/dependency-state.js +144 -0
- package/dist/src/pipeline/scheduler-service.js +314 -0
- package/dist/src/pipeline/state/group-item-state.js +50 -0
- package/dist/src/pipeline/state/group-run-state.js +41 -0
- package/dist/src/pipeline/state/index.js +20 -0
- package/dist/src/pipeline/state/node-item-state.js +67 -0
- package/dist/src/pipeline/state/node-run-state.js +51 -0
- package/dist/src/pipeline/state/types.js +2 -0
- package/dist/src/pipeline/state-machine.js +101 -0
- package/dist/src/pipeline/structured-output/contract.js +133 -0
- package/dist/src/pipeline/structured-output/index.js +22 -0
- package/dist/src/pipeline/structured-output/parser.js +214 -0
- package/dist/src/pipeline/structured-output/prompt.js +290 -0
- package/dist/src/pipeline/structured-output/waiter.js +139 -0
- package/dist/src/pipeline/template.js +135 -0
- package/dist/src/pipeline/timeline-log-store.js +57 -0
- package/dist/src/pipeline/tool-activity.js +94 -0
- package/dist/src/pipeline/types/pipeline-link.js +7 -0
- package/dist/src/pipeline/types/pipeline-output.js +11 -0
- package/dist/src/pipeline/types/workflow.js +2 -0
- package/dist/src/pipeline/workflow/branch-rules.js +74 -0
- package/dist/src/pipeline/workflow/defaults.js +48 -0
- package/dist/src/pipeline/workflow/io.js +89 -0
- package/dist/src/pipeline/workflow/normalize.js +347 -0
- package/dist/src/pipeline/workflow/routes.js +16 -0
- package/dist/src/pipeline/workflow/template-mapper.js +113 -0
- package/dist/src/pipeline/workflow/validate.js +312 -0
- package/dist/src/pipeline/workflow-graph.js +165 -0
- package/dist/src/server/api-handler.js +163 -0
- package/dist/src/server/http-utils.js +34 -0
- package/dist/src/server/middleware.js +61 -0
- package/dist/src/server/router.js +105 -0
- package/dist/src/server/routes/agents.js +189 -0
- package/dist/src/server/routes/artifacts.js +163 -0
- package/dist/src/server/routes/gateway.js +18 -0
- package/dist/src/server/routes/health.js +16 -0
- package/dist/src/server/routes/logs.js +73 -0
- package/dist/src/server/routes/pipeline-batch.js +163 -0
- package/dist/src/server/routes/pipeline-diagnostics.js +33 -0
- package/dist/src/server/routes/pipeline-identity.js +24 -0
- package/dist/src/server/routes/pipeline-links.js +117 -0
- package/dist/src/server/routes/pipeline-outputs.js +27 -0
- package/dist/src/server/routes/pipeline-queue.js +62 -0
- package/dist/src/server/routes/pipeline-runtime.js +162 -0
- package/dist/src/server/routes/pipeline-scheduler.js +69 -0
- package/dist/src/server/routes/pipeline-workflow.js +180 -0
- package/dist/src/server/routes/pipelines.js +96 -0
- package/dist/src/server/routes/sessions.js +244 -0
- package/dist/src/server/routes/timeline.js +14 -0
- package/dist/src/server/serve-static.js +42 -0
- package/dist/src/server/types.js +2 -0
- package/dist/src/services/agent-service.js +79 -0
- package/dist/src/services/artifact-service.js +74 -0
- package/dist/src/services/gateway-read-helpers.js +10 -0
- package/dist/src/services/index.js +23 -0
- package/dist/src/services/pipeline-service.js +529 -0
- package/dist/src/services/pipeline-status.js +93 -0
- package/dist/src/services/read-services.js +60 -0
- package/dist/src/services/scheduler-service.js +37 -0
- package/dist/src/services/session-service.js +227 -0
- package/dist/src/services/system-service.js +26 -0
- package/dist/src/transport/ws-broker.js +48 -0
- package/dist/src/utils/array.js +17 -0
- package/dist/src/utils/guards.js +5 -0
- package/dist/src/utils/session.js +60 -0
- package/dist/src/version.js +5 -0
- package/package.json +61 -0
- package/web/dist/assets/index-CWnfhkn-.js +65 -0
- package/web/dist/assets/index-gZ0xOfSO.css +1 -0
- package/web/dist/assets/jetbrains-mono-cyrillic-400-normal-BEIGL1Tu.woff2 +0 -0
- package/web/dist/assets/jetbrains-mono-cyrillic-400-normal-ugxPyKxw.woff +0 -0
- package/web/dist/assets/jetbrains-mono-cyrillic-500-normal-DJqRU3vO.woff +0 -0
- package/web/dist/assets/jetbrains-mono-cyrillic-500-normal-DmUKJPL_.woff2 +0 -0
- package/web/dist/assets/jetbrains-mono-cyrillic-700-normal-BWTpRfYl.woff2 +0 -0
- package/web/dist/assets/jetbrains-mono-cyrillic-700-normal-CEoEElIJ.woff +0 -0
- package/web/dist/assets/jetbrains-mono-greek-400-normal-B9oWc5Lo.woff +0 -0
- package/web/dist/assets/jetbrains-mono-greek-400-normal-C190GLew.woff2 +0 -0
- package/web/dist/assets/jetbrains-mono-greek-500-normal-D7SFKleX.woff +0 -0
- package/web/dist/assets/jetbrains-mono-greek-500-normal-JpySY46c.woff2 +0 -0
- package/web/dist/assets/jetbrains-mono-greek-700-normal-C6CZE3T8.woff2 +0 -0
- package/web/dist/assets/jetbrains-mono-greek-700-normal-DEigVDxa.woff +0 -0
- package/web/dist/assets/jetbrains-mono-latin-400-normal-6-qcROiO.woff +0 -0
- package/web/dist/assets/jetbrains-mono-latin-400-normal-V6pRDFza.woff2 +0 -0
- package/web/dist/assets/jetbrains-mono-latin-500-normal-BWZEU5yA.woff2 +0 -0
- package/web/dist/assets/jetbrains-mono-latin-500-normal-CJOVTJB7.woff +0 -0
- package/web/dist/assets/jetbrains-mono-latin-700-normal-BYuf6tUa.woff2 +0 -0
- package/web/dist/assets/jetbrains-mono-latin-700-normal-D3wTyLJW.woff +0 -0
- package/web/dist/assets/jetbrains-mono-latin-ext-400-normal-Bc8Ftmh3.woff2 +0 -0
- package/web/dist/assets/jetbrains-mono-latin-ext-400-normal-fXTG6kC5.woff +0 -0
- package/web/dist/assets/jetbrains-mono-latin-ext-500-normal-Cut-4mMH.woff2 +0 -0
- package/web/dist/assets/jetbrains-mono-latin-ext-500-normal-ckzbgY84.woff +0 -0
- package/web/dist/assets/jetbrains-mono-latin-ext-700-normal-CZipNAKV.woff2 +0 -0
- package/web/dist/assets/jetbrains-mono-latin-ext-700-normal-CxPITLHs.woff +0 -0
- package/web/dist/assets/jetbrains-mono-vietnamese-400-normal-CqNFfHCs.woff +0 -0
- package/web/dist/assets/jetbrains-mono-vietnamese-500-normal-DNRqzVM1.woff +0 -0
- package/web/dist/assets/jetbrains-mono-vietnamese-700-normal-BDLVIk2r.woff +0 -0
- package/web/dist/assets/space-grotesk-latin-400-normal-BnQMeOim.woff +0 -0
- package/web/dist/assets/space-grotesk-latin-400-normal-CJ-V5oYT.woff2 +0 -0
- package/web/dist/assets/space-grotesk-latin-500-normal-CNSSEhBt.woff +0 -0
- package/web/dist/assets/space-grotesk-latin-500-normal-lFbtlQH6.woff2 +0 -0
- package/web/dist/assets/space-grotesk-latin-700-normal-CwsQ-cCU.woff +0 -0
- package/web/dist/assets/space-grotesk-latin-700-normal-RjhwGPKo.woff2 +0 -0
- package/web/dist/assets/space-grotesk-latin-ext-400-normal-CfP_5XZW.woff2 +0 -0
- package/web/dist/assets/space-grotesk-latin-ext-400-normal-DRPE3kg4.woff +0 -0
- package/web/dist/assets/space-grotesk-latin-ext-500-normal-3dgZTiw9.woff +0 -0
- package/web/dist/assets/space-grotesk-latin-ext-500-normal-DUe3BAxM.woff2 +0 -0
- package/web/dist/assets/space-grotesk-latin-ext-700-normal-BQnZhY3m.woff2 +0 -0
- package/web/dist/assets/space-grotesk-latin-ext-700-normal-HVCqSBdx.woff +0 -0
- package/web/dist/assets/space-grotesk-vietnamese-400-normal-B7xT_GF5.woff2 +0 -0
- package/web/dist/assets/space-grotesk-vietnamese-400-normal-BIWiOVfw.woff +0 -0
- package/web/dist/assets/space-grotesk-vietnamese-500-normal-BTqKIpxg.woff +0 -0
- package/web/dist/assets/space-grotesk-vietnamese-500-normal-BmEvtly_.woff2 +0 -0
- package/web/dist/assets/space-grotesk-vietnamese-700-normal-DMty7AZE.woff2 +0 -0
- package/web/dist/assets/space-grotesk-vietnamese-700-normal-Duxec5Rn.woff +0 -0
- package/web/dist/favicon.svg +10 -0
- package/web/dist/index.html +14 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createPipelineRuntime = void 0;
|
|
4
|
+
const http_utils_1 = require("../server/http-utils");
|
|
5
|
+
const template_1 = require("../pipeline/template");
|
|
6
|
+
const execution_timeout_1 = require("../pipeline/execution-timeout");
|
|
7
|
+
const runtime_model_1 = require("../pipeline/runtime-model");
|
|
8
|
+
const array_1 = require("../utils/array");
|
|
9
|
+
const workflow_graph_1 = require("../pipeline/workflow-graph");
|
|
10
|
+
const runtime_store_1 = require("./runtime-store");
|
|
11
|
+
const execution_1 = require("../pipeline/execution");
|
|
12
|
+
const scheduler_service_1 = require("../pipeline/scheduler-service");
|
|
13
|
+
const createPipelineRuntime = (options) => {
|
|
14
|
+
const graph = (0, workflow_graph_1.createWorkflowGraph)((0, template_1.loadWorkflowDefinitionWithStorage)({ workflowFilePath: options.workflowFilePath }), (0, template_1.loadPipelineTemplateWithStorage)({ workflowFilePath: options.workflowFilePath }));
|
|
15
|
+
const initialRun = (0, runtime_model_1.seedRunWithItems)(graph.getTemplateNodes(), options.defaultItemKeys);
|
|
16
|
+
let schedulerStateAccessor = () => ({ enabled: graph.getWorkflow().scheduler.enabled, mode: graph.getWorkflow().scheduler.mode });
|
|
17
|
+
let batchRunStateGetter = () => null;
|
|
18
|
+
const runtimeStore = (0, runtime_store_1.createRuntimeStore)({
|
|
19
|
+
graph,
|
|
20
|
+
defaultItemKeys: options.defaultItemKeys,
|
|
21
|
+
runStateFile: options.runStateFile,
|
|
22
|
+
initialRun,
|
|
23
|
+
getSchedulerState: () => schedulerStateAccessor(),
|
|
24
|
+
getBatchRunState: () => batchRunStateGetter(),
|
|
25
|
+
});
|
|
26
|
+
let getBatchRunId = () => null;
|
|
27
|
+
const executionService = (0, execution_1.createExecutionService)({
|
|
28
|
+
client: options.client,
|
|
29
|
+
runtimeStore,
|
|
30
|
+
graph,
|
|
31
|
+
artifactDir: options.artifactDir,
|
|
32
|
+
pipelineId: options.pipelineId,
|
|
33
|
+
pipelineNodeExecutionTimeoutMs: (0, execution_timeout_1.getPipelineNodeExecutionTimeoutMs)(),
|
|
34
|
+
defaultItemKeys: options.defaultItemKeys,
|
|
35
|
+
getBatchRunId: () => getBatchRunId(),
|
|
36
|
+
});
|
|
37
|
+
const schedulerService = (0, scheduler_service_1.createSchedulerService)({
|
|
38
|
+
pipelineId: options.pipelineId,
|
|
39
|
+
runtimeStore,
|
|
40
|
+
graph,
|
|
41
|
+
defaultItemKeys: options.defaultItemKeys,
|
|
42
|
+
executionService,
|
|
43
|
+
onRunCompleted: options.onRunCompleted,
|
|
44
|
+
});
|
|
45
|
+
getBatchRunId = () => schedulerService.getBatchRunState().batchRunId;
|
|
46
|
+
schedulerStateAccessor = schedulerService.getSchedulerState;
|
|
47
|
+
batchRunStateGetter = () => schedulerService.getBatchRunState();
|
|
48
|
+
const sendJson = (res, code, data) => (0, http_utils_1.sendJson)(res, code, data, options.webOrigin);
|
|
49
|
+
const setRun = (nextRun) => {
|
|
50
|
+
runtimeStore.setRun(nextRun);
|
|
51
|
+
graph.syncRunGroupsFromWorkflow(nextRun);
|
|
52
|
+
if (!nextRun.itemRuns || nextRun.itemRuns.length === 0) {
|
|
53
|
+
runtimeStore.setRun((0, runtime_model_1.seedRunWithItems)(graph.getTemplateNodes(), options.defaultItemKeys));
|
|
54
|
+
graph.syncRunGroupsFromWorkflow(runtimeStore.getRun());
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
const setTemplateNodes = (nextNodes) => {
|
|
58
|
+
graph.setTemplateNodes(nextNodes);
|
|
59
|
+
};
|
|
60
|
+
const setWorkflow = (nextWorkflow) => {
|
|
61
|
+
graph.setWorkflow(nextWorkflow);
|
|
62
|
+
schedulerService.syncSchedulerStateFromWorkflow();
|
|
63
|
+
graph.syncRunGroupsFromWorkflow(runtimeStore.getRun());
|
|
64
|
+
};
|
|
65
|
+
const onGatewayStatus = (status) => {
|
|
66
|
+
runtimeStore.setLatestStatus(status);
|
|
67
|
+
runtimeStore.pushTimeline(`网关状态: ${status.status}`, status.status.includes("failed") ? "error" : "info");
|
|
68
|
+
runtimeStore.broadcast({
|
|
69
|
+
type: "gateway.status",
|
|
70
|
+
payload: status,
|
|
71
|
+
});
|
|
72
|
+
};
|
|
73
|
+
const onGatewayFrame = (frame) => {
|
|
74
|
+
const isHealthEvent = frame.type === "event" && frame.event === "health";
|
|
75
|
+
const isTickEvent = frame.type === "event" && frame.event === "tick";
|
|
76
|
+
const isSilentEvent = isHealthEvent || isTickEvent;
|
|
77
|
+
runtimeStore.setLastFrame(frame);
|
|
78
|
+
if (frame.type === "event" && !isSilentEvent) {
|
|
79
|
+
runtimeStore.pushTimeline(`事件: ${frame.event}`, "info", {
|
|
80
|
+
type: "event",
|
|
81
|
+
event: frame.event,
|
|
82
|
+
seq: frame.seq ?? null,
|
|
83
|
+
stateVersion: frame.stateVersion ?? null,
|
|
84
|
+
payload: frame.payload ?? null,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
// health / tick 都是高频事件,只保留为 lastFrame,不推送到 timeline / ws,
|
|
88
|
+
// 避免前端与日志被刷屏,同时不影响依赖最后一帧做诊断的调试能力。
|
|
89
|
+
if (!isSilentEvent) {
|
|
90
|
+
runtimeStore.broadcast({
|
|
91
|
+
type: "gateway.frame",
|
|
92
|
+
payload: frame,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
const onGatewayRawFrame = (rawFrame) => {
|
|
97
|
+
executionService.onGatewayFrame(rawFrame);
|
|
98
|
+
};
|
|
99
|
+
const onGatewayError = (error) => {
|
|
100
|
+
runtimeStore.pushTimeline(`网关错误: ${String(error)}`, "error");
|
|
101
|
+
runtimeStore.broadcast({
|
|
102
|
+
type: "gateway.error",
|
|
103
|
+
payload: { message: String(error) },
|
|
104
|
+
});
|
|
105
|
+
};
|
|
106
|
+
const onGatewayReady = (hello) => {
|
|
107
|
+
runtimeStore.setLatestHello(hello);
|
|
108
|
+
runtimeStore.pushTimeline("网关握手完成");
|
|
109
|
+
runtimeStore.emitPipeline();
|
|
110
|
+
runtimeStore.broadcast({
|
|
111
|
+
type: "gateway.ready",
|
|
112
|
+
payload: hello,
|
|
113
|
+
});
|
|
114
|
+
};
|
|
115
|
+
const getBootstrapPayload = () => {
|
|
116
|
+
const run = runtimeStore.getRun();
|
|
117
|
+
const nodesWithWorkflowMeta = graph.getNodesWithWorkflowMeta(run.nodes);
|
|
118
|
+
return {
|
|
119
|
+
status: runtimeStore.getLatestStatus() ?? options.client.getStatus(),
|
|
120
|
+
timeline: runtimeStore.getTimeline(),
|
|
121
|
+
run: { ...run, nodes: nodesWithWorkflowMeta },
|
|
122
|
+
pipeline: nodesWithWorkflowMeta,
|
|
123
|
+
runId: run.id,
|
|
124
|
+
hello: runtimeStore.getLatestHello(),
|
|
125
|
+
scheduler: schedulerService.getSchedulerState(),
|
|
126
|
+
batchRunState: schedulerService.getBatchRunState(),
|
|
127
|
+
};
|
|
128
|
+
};
|
|
129
|
+
const hasActiveSession = (sessionKey) => {
|
|
130
|
+
return executionService.hasActiveSession?.(sessionKey) ?? false;
|
|
131
|
+
};
|
|
132
|
+
const initialize = async () => {
|
|
133
|
+
await runtimeStore.restorePersistedRunState();
|
|
134
|
+
};
|
|
135
|
+
return {
|
|
136
|
+
initialize,
|
|
137
|
+
dispose: () => executionService.dispose(),
|
|
138
|
+
sendJson,
|
|
139
|
+
getBootstrapPayload,
|
|
140
|
+
onGatewayStatus,
|
|
141
|
+
onGatewayFrame,
|
|
142
|
+
onGatewayRawFrame,
|
|
143
|
+
hasActiveSession,
|
|
144
|
+
onGatewayError,
|
|
145
|
+
onGatewayReady,
|
|
146
|
+
runtime: {
|
|
147
|
+
setBroadcast: runtimeStore.setBroadcast,
|
|
148
|
+
pushTimeline: runtimeStore.pushTimeline,
|
|
149
|
+
emitPipeline: runtimeStore.emitPipeline,
|
|
150
|
+
getRun: runtimeStore.getRun,
|
|
151
|
+
setRun,
|
|
152
|
+
seedRun: runtimeStore.seedRun,
|
|
153
|
+
getTimeline: runtimeStore.getTimeline,
|
|
154
|
+
touchRun: runtime_model_1.touchRun,
|
|
155
|
+
},
|
|
156
|
+
gateway: {
|
|
157
|
+
client: options.client,
|
|
158
|
+
getLatestStatus: runtimeStore.getLatestStatus,
|
|
159
|
+
getLatestHello: runtimeStore.getLatestHello,
|
|
160
|
+
getLastFrame: runtimeStore.getLastFrame,
|
|
161
|
+
refreshSessionsFromGateway: executionService.refreshSessionsFromGateway,
|
|
162
|
+
getSessionCache: executionService.getSessionCache,
|
|
163
|
+
getExecutorSessionByAgentId: executionService.getExecutorSessionByAgentId,
|
|
164
|
+
pickArray: array_1.pickArray,
|
|
165
|
+
},
|
|
166
|
+
workflow: {
|
|
167
|
+
getTemplateNodes: graph.getTemplateNodes,
|
|
168
|
+
setTemplateNodes,
|
|
169
|
+
getWorkflow: graph.getWorkflow,
|
|
170
|
+
setWorkflow,
|
|
171
|
+
mergeTemplateNodesIntoWorkflow: template_1.mergeTemplateNodesIntoWorkflow,
|
|
172
|
+
getWorkflowNodeById: graph.getWorkflowNodeById,
|
|
173
|
+
getIncomingEdges: graph.getIncomingEdges,
|
|
174
|
+
isCrossBranchEdge: graph.isCrossBranchEdge,
|
|
175
|
+
isGroupId: graph.isGroupId,
|
|
176
|
+
isWorkflowNodeEnabled: graph.isWorkflowNodeEnabled,
|
|
177
|
+
pipelineId: options.pipelineId,
|
|
178
|
+
},
|
|
179
|
+
pipeline: {
|
|
180
|
+
getItemRuns: () => runtimeStore.getRun().itemRuns ?? [],
|
|
181
|
+
executeNode: executionService.executeNode,
|
|
182
|
+
retryNodeExecution: schedulerService.retryNodeExecution,
|
|
183
|
+
drainPipeline: schedulerService.drainPipeline,
|
|
184
|
+
getSchedulerState: schedulerService.getSchedulerState,
|
|
185
|
+
setSchedulerEnabled: schedulerService.setSchedulerEnabled,
|
|
186
|
+
setSchedulerMode: schedulerService.setSchedulerMode,
|
|
187
|
+
getBatchRunState: schedulerService.getBatchRunState,
|
|
188
|
+
startBatchRun: schedulerService.startBatchRun,
|
|
189
|
+
stopBatchRun: schedulerService.stopBatchRun,
|
|
190
|
+
cancelBatchRun: schedulerService.cancelBatchRun,
|
|
191
|
+
abortRunControllers: executionService.abortRunControllers,
|
|
192
|
+
getOrCreateDrainSignal: executionService.getOrCreateDrainSignal,
|
|
193
|
+
},
|
|
194
|
+
output: {
|
|
195
|
+
list: options.outputStore.list.bind(options.outputStore),
|
|
196
|
+
getByRunId: options.outputStore.getByRunId.bind(options.outputStore),
|
|
197
|
+
getById: options.outputStore.getById.bind(options.outputStore),
|
|
198
|
+
append: options.outputStore.append.bind(options.outputStore),
|
|
199
|
+
},
|
|
200
|
+
};
|
|
201
|
+
};
|
|
202
|
+
exports.createPipelineRuntime = createPipelineRuntime;
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createRuntimeStore = void 0;
|
|
4
|
+
const promises_1 = require("node:fs/promises");
|
|
5
|
+
const frame_sanitizer_1 = require("../gateway/frame-sanitizer");
|
|
6
|
+
const runtime_model_1 = require("../pipeline/runtime-model");
|
|
7
|
+
const timeline_log_store_1 = require("../pipeline/timeline-log-store");
|
|
8
|
+
const data_dir_1 = require("./data-dir");
|
|
9
|
+
const createRuntimeStore = (options) => {
|
|
10
|
+
const timeline = [];
|
|
11
|
+
let run = options.initialRun;
|
|
12
|
+
let latestStatus = null;
|
|
13
|
+
let latestHello = null;
|
|
14
|
+
let lastFrame = null;
|
|
15
|
+
let broadcast = () => { };
|
|
16
|
+
let persistRunStateInFlight = null;
|
|
17
|
+
const timelineLogStore = (0, timeline_log_store_1.createTimelineLogStore)({
|
|
18
|
+
rootDir: (0, data_dir_1.resolveTaskMeldDataPath)("logs", "runs"),
|
|
19
|
+
});
|
|
20
|
+
const persistRunState = async () => {
|
|
21
|
+
if (persistRunStateInFlight)
|
|
22
|
+
return persistRunStateInFlight;
|
|
23
|
+
persistRunStateInFlight = (async () => {
|
|
24
|
+
try {
|
|
25
|
+
await (0, promises_1.mkdir)((0, data_dir_1.resolveTaskMeldDataPath)(), { recursive: true });
|
|
26
|
+
await (0, promises_1.writeFile)(options.runStateFile, JSON.stringify({
|
|
27
|
+
savedAt: new Date().toISOString(),
|
|
28
|
+
workflowVersion: options.graph.getWorkflow().version,
|
|
29
|
+
run,
|
|
30
|
+
}, null, 2), "utf8");
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
// Persistence failures should not break pipeline execution.
|
|
34
|
+
pushTimeline(`持久化运行状态失败: ${error instanceof Error ? error.message : String(error)}`, "warn");
|
|
35
|
+
}
|
|
36
|
+
})().finally(() => {
|
|
37
|
+
persistRunStateInFlight = null;
|
|
38
|
+
});
|
|
39
|
+
return persistRunStateInFlight;
|
|
40
|
+
};
|
|
41
|
+
const tryLoadPersistedRunState = async () => {
|
|
42
|
+
try {
|
|
43
|
+
const raw = await (0, promises_1.readFile)(options.runStateFile, "utf8");
|
|
44
|
+
const parsed = JSON.parse(raw);
|
|
45
|
+
if (!parsed || typeof parsed !== "object")
|
|
46
|
+
return null;
|
|
47
|
+
const payload = parsed;
|
|
48
|
+
const savedRun = payload.run;
|
|
49
|
+
if (!savedRun || !Array.isArray(savedRun.nodes))
|
|
50
|
+
return null;
|
|
51
|
+
if (!Array.isArray(savedRun.itemRuns))
|
|
52
|
+
return null;
|
|
53
|
+
if (!Array.isArray(savedRun.groups))
|
|
54
|
+
savedRun.groups = [];
|
|
55
|
+
if (!Array.isArray(savedRun.groupItemRuns))
|
|
56
|
+
savedRun.groupItemRuns = [];
|
|
57
|
+
if (!savedRun.input)
|
|
58
|
+
savedRun.input = { trigger: "manual" };
|
|
59
|
+
if (savedRun.output === undefined)
|
|
60
|
+
savedRun.output = null;
|
|
61
|
+
const nodeIds = new Set(savedRun.nodes.map((node) => node.id));
|
|
62
|
+
const templateNodes = options.graph.getTemplateNodes();
|
|
63
|
+
if (nodeIds.size !== templateNodes.length)
|
|
64
|
+
return null;
|
|
65
|
+
for (const node of templateNodes) {
|
|
66
|
+
if (!nodeIds.has(node.id))
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
return savedRun;
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
pushTimeline(`加载持久化运行状态失败: ${error instanceof Error ? error.message : String(error)}`, "warn");
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
const pushTimeline = (text, level = "info", detail) => {
|
|
77
|
+
const safeDetail = detail !== undefined ? (0, frame_sanitizer_1.sanitizeDiagnosticPayload)(detail) : undefined;
|
|
78
|
+
const item = (0, runtime_model_1.addTimeline)(timeline, text, level, safeDetail);
|
|
79
|
+
void timelineLogStore.appendTimeline(run.id, item);
|
|
80
|
+
broadcast({
|
|
81
|
+
type: "timeline.updated",
|
|
82
|
+
payload: { item },
|
|
83
|
+
});
|
|
84
|
+
};
|
|
85
|
+
const emitPipeline = () => {
|
|
86
|
+
options.graph.syncRunGroupsFromWorkflow(run);
|
|
87
|
+
(0, runtime_model_1.syncRunNodeStatusFromItemRuns)(run);
|
|
88
|
+
(0, runtime_model_1.touchRun)(run);
|
|
89
|
+
const nodesWithWorkflowMeta = options.graph.getNodesWithWorkflowMeta(run.nodes);
|
|
90
|
+
broadcast({
|
|
91
|
+
type: "pipeline.updated",
|
|
92
|
+
payload: {
|
|
93
|
+
run: { ...run, nodes: nodesWithWorkflowMeta },
|
|
94
|
+
runId: run.id,
|
|
95
|
+
nodes: nodesWithWorkflowMeta,
|
|
96
|
+
scheduler: { ...options.getSchedulerState() },
|
|
97
|
+
...(options.getBatchRunState ? { batchRunState: options.getBatchRunState() } : {}),
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
void persistRunState();
|
|
101
|
+
};
|
|
102
|
+
const restorePersistedRunState = async () => {
|
|
103
|
+
const persisted = await tryLoadPersistedRunState();
|
|
104
|
+
if (!persisted)
|
|
105
|
+
return;
|
|
106
|
+
run = persisted;
|
|
107
|
+
options.graph.syncRunGroupsFromWorkflow(run);
|
|
108
|
+
(0, runtime_model_1.syncRunNodeStatusFromItemRuns)(run);
|
|
109
|
+
(0, runtime_model_1.touchRun)(run);
|
|
110
|
+
pushTimeline(`已恢复上次运行状态: ${run.id}`);
|
|
111
|
+
emitPipeline();
|
|
112
|
+
};
|
|
113
|
+
const bootstrapRun = () => {
|
|
114
|
+
if (run.itemRuns && run.itemRuns.length > 0)
|
|
115
|
+
return;
|
|
116
|
+
run = (0, runtime_model_1.seedRunWithItems)(options.graph.getTemplateNodes(), options.defaultItemKeys);
|
|
117
|
+
options.graph.syncRunGroupsFromWorkflow(run);
|
|
118
|
+
};
|
|
119
|
+
return {
|
|
120
|
+
getRun: () => run,
|
|
121
|
+
setRun: (nextRun) => {
|
|
122
|
+
run = nextRun;
|
|
123
|
+
},
|
|
124
|
+
bootstrapRun,
|
|
125
|
+
seedRun: (nodes = options.graph.getTemplateNodes(), itemKeys = options.defaultItemKeys) => (0, runtime_model_1.seedRunWithItems)(nodes, itemKeys),
|
|
126
|
+
getTimeline: () => timeline,
|
|
127
|
+
pushTimeline,
|
|
128
|
+
emitPipeline,
|
|
129
|
+
restorePersistedRunState,
|
|
130
|
+
persistRunState,
|
|
131
|
+
setBroadcast: (nextBroadcast) => {
|
|
132
|
+
broadcast = nextBroadcast;
|
|
133
|
+
},
|
|
134
|
+
broadcast: (payload) => {
|
|
135
|
+
broadcast(payload);
|
|
136
|
+
},
|
|
137
|
+
getLatestStatus: () => latestStatus,
|
|
138
|
+
setLatestStatus: (status) => {
|
|
139
|
+
latestStatus = status;
|
|
140
|
+
},
|
|
141
|
+
getLatestHello: () => latestHello,
|
|
142
|
+
setLatestHello: (hello) => {
|
|
143
|
+
latestHello = hello;
|
|
144
|
+
},
|
|
145
|
+
getLastFrame: () => lastFrame,
|
|
146
|
+
setLastFrame: (frame) => {
|
|
147
|
+
lastFrame = frame;
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
};
|
|
151
|
+
exports.createRuntimeStore = createRuntimeStore;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveGatewayConfig = exports.writeUserConfig = exports.readUserConfig = void 0;
|
|
4
|
+
const node_os_1 = require("node:os");
|
|
5
|
+
const node_path_1 = require("node:path");
|
|
6
|
+
const promises_1 = require("node:fs/promises");
|
|
7
|
+
const userConfigDir = (0, node_path_1.join)((0, node_os_1.homedir)(), ".taskmeld");
|
|
8
|
+
const userConfigPath = (0, node_path_1.join)(userConfigDir, "config.json");
|
|
9
|
+
const readUserConfig = async () => {
|
|
10
|
+
try {
|
|
11
|
+
const raw = await (0, promises_1.readFile)(userConfigPath, "utf8");
|
|
12
|
+
const parsed = JSON.parse(raw);
|
|
13
|
+
return {
|
|
14
|
+
gatewayUrl: typeof parsed.gatewayUrl === "string" ? parsed.gatewayUrl.trim() : undefined,
|
|
15
|
+
gatewayToken: typeof parsed.gatewayToken === "string" ? parsed.gatewayToken.trim() : undefined,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return {};
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
exports.readUserConfig = readUserConfig;
|
|
23
|
+
const writeUserConfig = async (config) => {
|
|
24
|
+
await (0, promises_1.mkdir)(userConfigDir, { recursive: true });
|
|
25
|
+
const existing = await (0, exports.readUserConfig)();
|
|
26
|
+
const merged = { ...existing, ...config };
|
|
27
|
+
await (0, promises_1.writeFile)(userConfigPath, JSON.stringify(merged, null, 2) + "\n", "utf8");
|
|
28
|
+
};
|
|
29
|
+
exports.writeUserConfig = writeUserConfig;
|
|
30
|
+
const resolveGatewayConfig = async () => {
|
|
31
|
+
const userConfig = await (0, exports.readUserConfig)();
|
|
32
|
+
return {
|
|
33
|
+
url: process.env.OPENCLAW_GATEWAY_URL?.trim() || userConfig.gatewayUrl || null,
|
|
34
|
+
token: process.env.OPENCLAW_GATEWAY_TOKEN?.trim() || userConfig.gatewayToken || null,
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
exports.resolveGatewayConfig = resolveGatewayConfig;
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.cleanupTempFiles = exports.cleanupEmptyDirs = exports.executeCleanup = exports.planCleanup = void 0;
|
|
4
|
+
const promises_1 = require("node:fs/promises");
|
|
5
|
+
const node_path_1 = require("node:path");
|
|
6
|
+
const storage_service_1 = require("./storage-service");
|
|
7
|
+
const artifact_index_1 = require("./artifact-index");
|
|
8
|
+
const DEFAULT_RETENTION_DAYS = {
|
|
9
|
+
success: 30,
|
|
10
|
+
failed: 90,
|
|
11
|
+
rejected: 90,
|
|
12
|
+
};
|
|
13
|
+
const ONE_DAY_MS = 24 * 60 * 60 * 1000;
|
|
14
|
+
/**
|
|
15
|
+
* 生成清理计划,不删除任何文件。
|
|
16
|
+
* 默认只清理 success 状态,可通过 options 指定其他状态。
|
|
17
|
+
*/
|
|
18
|
+
const planCleanup = async (definition, options = {}) => {
|
|
19
|
+
const statuses = options.statuses?.length ? options.statuses : ["success"];
|
|
20
|
+
const safeStatuses = statuses.filter((s) => s !== "unknown");
|
|
21
|
+
const statusFilter = new Set(safeStatuses);
|
|
22
|
+
const kindFilter = options.kinds?.length ? new Set(options.kinds) : null;
|
|
23
|
+
const olderThanDays = options.olderThanDays ?? Math.max(...safeStatuses.map((s) => DEFAULT_RETENTION_DAYS[s] ?? 90));
|
|
24
|
+
const cutoffMs = Date.now() - olderThanDays * ONE_DAY_MS;
|
|
25
|
+
// 优先从索引获取文件列表,索引缺失时回退扫描
|
|
26
|
+
let items = await (0, artifact_index_1.readIndexRecords)(definition.artifactDir);
|
|
27
|
+
if (items.length === 0) {
|
|
28
|
+
const scanned = await (0, storage_service_1.scanStoredArtifacts)([definition]);
|
|
29
|
+
items = scanned.map((item) => ({
|
|
30
|
+
schemaVersion: 1,
|
|
31
|
+
artifactId: "",
|
|
32
|
+
pipelineId: item.pipelineId,
|
|
33
|
+
status: item.status,
|
|
34
|
+
kind: "artifact",
|
|
35
|
+
dateBucket: item.dateBucket,
|
|
36
|
+
runId: item.runId,
|
|
37
|
+
batchRunId: null,
|
|
38
|
+
nodeId: item.nodeId,
|
|
39
|
+
groupId: null,
|
|
40
|
+
itemKey: null,
|
|
41
|
+
requestId: null,
|
|
42
|
+
type: "unknown",
|
|
43
|
+
artifactSchemaVersion: 1,
|
|
44
|
+
name: "",
|
|
45
|
+
relativePath: item.relativePath,
|
|
46
|
+
sizeBytes: item.sizeBytes,
|
|
47
|
+
hash: "",
|
|
48
|
+
createdAt: item.updatedAt,
|
|
49
|
+
updatedAt: item.updatedAt,
|
|
50
|
+
}));
|
|
51
|
+
}
|
|
52
|
+
const files = [];
|
|
53
|
+
let totalSizeBytes = 0;
|
|
54
|
+
let oldestDate = null;
|
|
55
|
+
let newestDate = null;
|
|
56
|
+
for (const item of items) {
|
|
57
|
+
if (!statusFilter.has(item.status))
|
|
58
|
+
continue;
|
|
59
|
+
if (kindFilter && !kindFilter.has(item.kind))
|
|
60
|
+
continue;
|
|
61
|
+
const itemMs = Date.parse(item.updatedAt);
|
|
62
|
+
if (!Number.isFinite(itemMs) || itemMs > cutoffMs)
|
|
63
|
+
continue;
|
|
64
|
+
if (options.maxSizeBytes != null && totalSizeBytes + item.sizeBytes > options.maxSizeBytes)
|
|
65
|
+
break;
|
|
66
|
+
files.push({
|
|
67
|
+
relativePath: item.relativePath,
|
|
68
|
+
sizeBytes: item.sizeBytes,
|
|
69
|
+
hash: item.hash,
|
|
70
|
+
});
|
|
71
|
+
totalSizeBytes += item.sizeBytes;
|
|
72
|
+
const dateBucket = item.dateBucket;
|
|
73
|
+
if (dateBucket && (oldestDate === null || dateBucket < oldestDate))
|
|
74
|
+
oldestDate = dateBucket;
|
|
75
|
+
if (dateBucket && (newestDate === null || dateBucket > newestDate))
|
|
76
|
+
newestDate = dateBucket;
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
pipelineId: definition.id,
|
|
80
|
+
files,
|
|
81
|
+
totalSizeBytes,
|
|
82
|
+
oldestDate,
|
|
83
|
+
newestDate,
|
|
84
|
+
};
|
|
85
|
+
};
|
|
86
|
+
exports.planCleanup = planCleanup;
|
|
87
|
+
/**
|
|
88
|
+
* 执行清理:删除文件 + 清理空目录 + 清理临时文件 + 重建索引。
|
|
89
|
+
*/
|
|
90
|
+
const executeCleanup = async (definition, plan) => {
|
|
91
|
+
const warnings = [];
|
|
92
|
+
let deleted = 0;
|
|
93
|
+
let failed = 0;
|
|
94
|
+
for (const file of plan.files) {
|
|
95
|
+
const rootAbs = (0, node_path_1.resolve)(definition.artifactDir);
|
|
96
|
+
const absPath = (0, node_path_1.resolve)(rootAbs, file.relativePath);
|
|
97
|
+
// 路径穿越保护:目标必须位于当前 pipeline 的 artifactDir 内
|
|
98
|
+
if (absPath !== rootAbs && !absPath.startsWith(`${rootAbs}${node_path_1.sep}`)) {
|
|
99
|
+
failed += 1;
|
|
100
|
+
warnings.push(`拒绝删除越界文件 (${definition.id}:${file.relativePath})`);
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
try {
|
|
104
|
+
await (0, promises_1.rm)(absPath);
|
|
105
|
+
deleted += 1;
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
failed += 1;
|
|
109
|
+
warnings.push(`删除失败 (${definition.id}:${file.relativePath}): ${error instanceof Error ? error.message : String(error)}`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
// 清理空目录
|
|
113
|
+
await (0, exports.cleanupEmptyDirs)(definition.artifactDir);
|
|
114
|
+
// 清理临时文件
|
|
115
|
+
const tmpResult = await (0, exports.cleanupTempFiles)(definition.artifactDir);
|
|
116
|
+
warnings.push(...tmpResult.warnings);
|
|
117
|
+
// 重建索引以移除已删文件
|
|
118
|
+
try {
|
|
119
|
+
const { rebuildArtifactIndex } = await import("./artifact-index.js");
|
|
120
|
+
await rebuildArtifactIndex(definition, (d) => (0, storage_service_1.scanStoredArtifacts)([d]));
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
warnings.push(`删除后自动重建索引失败 (${definition.id}),请手动执行 rebuild-index`);
|
|
124
|
+
}
|
|
125
|
+
return { deleted, failed, warnings };
|
|
126
|
+
};
|
|
127
|
+
exports.executeCleanup = executeCleanup;
|
|
128
|
+
/**
|
|
129
|
+
* 递归清理空目录。保留 artifactDir 根目录本身。
|
|
130
|
+
*/
|
|
131
|
+
const cleanupEmptyDirs = async (artifactDir) => {
|
|
132
|
+
const warnings = [];
|
|
133
|
+
let removed = 0;
|
|
134
|
+
const walkAndRemove = async (dirPath) => {
|
|
135
|
+
try {
|
|
136
|
+
const entries = await (0, promises_1.readdir)(dirPath, { withFileTypes: true });
|
|
137
|
+
for (const entry of entries) {
|
|
138
|
+
if (entry.isDirectory()) {
|
|
139
|
+
await walkAndRemove(`${dirPath}${node_path_1.sep}${entry.name}`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
// 不删除 artifacts 根目录本身
|
|
143
|
+
const isRoot = dirPath === artifactDir || dirPath.endsWith(`${node_path_1.sep}artifacts`) && dirPath.replace(/\\/g, "/").endsWith("/artifacts");
|
|
144
|
+
if (isRoot)
|
|
145
|
+
return;
|
|
146
|
+
const after = await (0, promises_1.readdir)(dirPath);
|
|
147
|
+
if (after.length === 0) {
|
|
148
|
+
await (0, promises_1.rmdir)(dirPath);
|
|
149
|
+
removed += 1;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
// 目录不存在或无权访问
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
await walkAndRemove(artifactDir);
|
|
157
|
+
return { removed, warnings };
|
|
158
|
+
};
|
|
159
|
+
exports.cleanupEmptyDirs = cleanupEmptyDirs;
|
|
160
|
+
/**
|
|
161
|
+
* 清理 persistArtifactFile 遗留的 .tmp-*.json 临时文件(原子写入失败残留)。
|
|
162
|
+
*/
|
|
163
|
+
const cleanupTempFiles = async (artifactDir) => {
|
|
164
|
+
const warnings = [];
|
|
165
|
+
let cleaned = 0;
|
|
166
|
+
const walkAndClean = async (dirPath) => {
|
|
167
|
+
try {
|
|
168
|
+
const entries = await (0, promises_1.readdir)(dirPath, { withFileTypes: true });
|
|
169
|
+
for (const entry of entries) {
|
|
170
|
+
const subPath = `${dirPath}${node_path_1.sep}${entry.name}`;
|
|
171
|
+
if (entry.isDirectory()) {
|
|
172
|
+
await walkAndClean(subPath);
|
|
173
|
+
}
|
|
174
|
+
else if (entry.isFile() && entry.name.startsWith(".tmp-") && entry.name.endsWith(".json")) {
|
|
175
|
+
try {
|
|
176
|
+
await (0, promises_1.rm)(subPath);
|
|
177
|
+
cleaned += 1;
|
|
178
|
+
}
|
|
179
|
+
catch (error) {
|
|
180
|
+
warnings.push(`清理临时文件失败: ${error instanceof Error ? error.message : String(error)}`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
catch {
|
|
186
|
+
// 目录不存在
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
await walkAndClean(artifactDir);
|
|
190
|
+
return { cleaned, warnings };
|
|
191
|
+
};
|
|
192
|
+
exports.cleanupTempFiles = cleanupTempFiles;
|