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,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildPipelineExecutionStatus = void 0;
|
|
4
|
+
const isNonEmptyString = (value) => {
|
|
5
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
6
|
+
};
|
|
7
|
+
const normalizeIdList = (items) => [...new Set(items.filter(Boolean))];
|
|
8
|
+
const readCurrentBatchProgress = (run, batchRun) => {
|
|
9
|
+
const batchRecord = batchRun && typeof batchRun === "object" ? batchRun : null;
|
|
10
|
+
const currentBatchItemKey = isNonEmptyString(batchRecord?.currentBatchItemKey) ? batchRecord.currentBatchItemKey.trim() : null;
|
|
11
|
+
const currentBatchIndex = typeof batchRecord?.currentBatchIndex === "number" && Number.isFinite(batchRecord.currentBatchIndex)
|
|
12
|
+
? Math.trunc(batchRecord.currentBatchIndex)
|
|
13
|
+
: null;
|
|
14
|
+
const currentBatchItems = Array.isArray(batchRecord?.currentBatchItems)
|
|
15
|
+
? batchRecord.currentBatchItems.filter((item) => isNonEmptyString(item)).map((item) => item.trim())
|
|
16
|
+
: [];
|
|
17
|
+
if (!currentBatchItemKey && currentBatchIndex === null && currentBatchItems.length === 0) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
const itemRuns = Array.isArray(run.itemRuns) ? run.itemRuns : [];
|
|
21
|
+
const groupItemRuns = Array.isArray(run.groupItemRuns) ? run.groupItemRuns : [];
|
|
22
|
+
const currentItemRuns = itemRuns.filter((item) => item.itemKey === currentBatchItemKey);
|
|
23
|
+
const currentGroupItemRuns = groupItemRuns.filter((item) => item.itemKey === currentBatchItemKey);
|
|
24
|
+
const runningNodeIds = normalizeIdList(currentItemRuns
|
|
25
|
+
.filter((item) => item.status === "running")
|
|
26
|
+
.map((item) => String(item.nodeId ?? "").trim()));
|
|
27
|
+
const pendingNodeIds = normalizeIdList(currentItemRuns
|
|
28
|
+
.filter((item) => item.status === "queued" || item.status === "waiting" || item.status === "blocked")
|
|
29
|
+
.map((item) => String(item.nodeId ?? "").trim()));
|
|
30
|
+
const completedNodeIds = normalizeIdList(currentItemRuns
|
|
31
|
+
.filter((item) => item.status === "success" || item.status === "skipped")
|
|
32
|
+
.map((item) => String(item.nodeId ?? "").trim()));
|
|
33
|
+
const failedNodeIds = normalizeIdList(currentItemRuns
|
|
34
|
+
.filter((item) => item.status === "failed" || item.status === "rejected" || item.status === "stopped")
|
|
35
|
+
.map((item) => String(item.nodeId ?? "").trim()));
|
|
36
|
+
const runningGroupIds = normalizeIdList(currentGroupItemRuns
|
|
37
|
+
.filter((item) => item.status === "running")
|
|
38
|
+
.map((item) => String(item.groupId ?? "").trim()));
|
|
39
|
+
const pendingGroupIds = normalizeIdList(currentGroupItemRuns
|
|
40
|
+
.filter((item) => item.status === "queued" || item.status === "waiting" || item.status === "blocked")
|
|
41
|
+
.map((item) => String(item.groupId ?? "").trim()));
|
|
42
|
+
const completedGroupIds = normalizeIdList(currentGroupItemRuns
|
|
43
|
+
.filter((item) => item.status === "success" || item.status === "skipped")
|
|
44
|
+
.map((item) => String(item.groupId ?? "").trim()));
|
|
45
|
+
const failedGroupIds = normalizeIdList(currentGroupItemRuns
|
|
46
|
+
.filter((item) => item.status === "failed" || item.status === "rejected" || item.status === "stopped")
|
|
47
|
+
.map((item) => String(item.groupId ?? "").trim()));
|
|
48
|
+
return {
|
|
49
|
+
index: currentBatchIndex,
|
|
50
|
+
itemKey: currentBatchItemKey,
|
|
51
|
+
items: currentBatchItems,
|
|
52
|
+
runningNodeIds,
|
|
53
|
+
pendingNodeIds,
|
|
54
|
+
completedNodeIds,
|
|
55
|
+
failedNodeIds,
|
|
56
|
+
runningGroupIds,
|
|
57
|
+
pendingGroupIds,
|
|
58
|
+
completedGroupIds,
|
|
59
|
+
failedGroupIds,
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
const buildPipelineExecutionStatus = (input) => {
|
|
63
|
+
const runNodes = Array.isArray(input.run.nodes) ? input.run.nodes : [];
|
|
64
|
+
const batchRunRecord = input.batchRun && typeof input.batchRun === "object" ? input.batchRun : null;
|
|
65
|
+
const batchStatus = typeof batchRunRecord?.status === "string" ? batchRunRecord.status : null;
|
|
66
|
+
const batchError = typeof batchRunRecord?.error === "string" ? batchRunRecord.error : null;
|
|
67
|
+
const isBatchRunning = batchStatus === "running";
|
|
68
|
+
const isSingleRunning = input.run.status === "running";
|
|
69
|
+
const activeNodeIds = runNodes
|
|
70
|
+
.filter((node) => node.status === "running")
|
|
71
|
+
.map((node) => String(node.id ?? "").trim())
|
|
72
|
+
.filter(Boolean);
|
|
73
|
+
const pendingNodeIds = runNodes
|
|
74
|
+
.filter((node) => node.status === "queued" || node.status === "waiting" || node.status === "blocked")
|
|
75
|
+
.map((node) => String(node.id ?? "").trim())
|
|
76
|
+
.filter(Boolean);
|
|
77
|
+
const firstNodeError = runNodes.find((node) => isNonEmptyString(node.lastError))?.lastError?.trim() ?? null;
|
|
78
|
+
const currentBatch = readCurrentBatchProgress(input.run, input.batchRun);
|
|
79
|
+
return {
|
|
80
|
+
pipelineId: input.pipelineId,
|
|
81
|
+
mode: isBatchRunning ? "remote_batch" : isSingleRunning ? "single" : "idle",
|
|
82
|
+
running: Boolean(isBatchRunning || isSingleRunning),
|
|
83
|
+
runId: isNonEmptyString(input.run.id) ? input.run.id.trim() : null,
|
|
84
|
+
runStatus: input.run.status === "running" || input.run.status === "success" || input.run.status === "failed"
|
|
85
|
+
? input.run.status
|
|
86
|
+
: "stopped",
|
|
87
|
+
activeNodeIds,
|
|
88
|
+
pendingNodeIds,
|
|
89
|
+
scheduler: input.scheduler,
|
|
90
|
+
batchRun: input.batchRun,
|
|
91
|
+
currentBatch,
|
|
92
|
+
lastError: batchError ?? firstNodeError,
|
|
93
|
+
updatedAt: isNonEmptyString(input.run.updatedAt) ? input.run.updatedAt.trim() : new Date().toISOString(),
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
exports.buildPipelineExecutionStatus = buildPipelineExecutionStatus;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getStructuredResultPollMs = exports.getStructuredResultTimeoutMs = exports.getPipelineNodeExecutionTimeoutMs = void 0;
|
|
4
|
+
const MIN_TIMEOUT_MS = 5_000;
|
|
5
|
+
const DEFAULT_PIPELINE_NODE_TIMEOUT_MS = 15 * 60 * 1000;
|
|
6
|
+
const DEFAULT_STRUCTURED_RESULT_POLL_MS = 300;
|
|
7
|
+
const normalizeTimeout = (value, fallback, min) => {
|
|
8
|
+
const parsed = Number(value);
|
|
9
|
+
if (!Number.isFinite(parsed))
|
|
10
|
+
return fallback;
|
|
11
|
+
return Math.max(min, Math.trunc(parsed));
|
|
12
|
+
};
|
|
13
|
+
// 统一维护 DAG 节点执行超时,避免发送请求与等待回执使用不同默认值。
|
|
14
|
+
const getPipelineNodeExecutionTimeoutMs = () => normalizeTimeout(process.env.PIPELINE_NODE_EXECUTION_TIMEOUT_MS, DEFAULT_PIPELINE_NODE_TIMEOUT_MS, MIN_TIMEOUT_MS);
|
|
15
|
+
exports.getPipelineNodeExecutionTimeoutMs = getPipelineNodeExecutionTimeoutMs;
|
|
16
|
+
// 兼容历史环境变量名,未设置新变量时继续读取旧变量。
|
|
17
|
+
const getStructuredResultTimeoutMs = () => normalizeTimeout(process.env.STRUCTURED_RESULT_TIMEOUT_MS ?? process.env.PIPELINE_NODE_EXECUTION_TIMEOUT_MS, DEFAULT_PIPELINE_NODE_TIMEOUT_MS, MIN_TIMEOUT_MS);
|
|
18
|
+
exports.getStructuredResultTimeoutMs = getStructuredResultTimeoutMs;
|
|
19
|
+
// 轮询间隔保持短周期,仅负责观察回执,不参与失败判定。
|
|
20
|
+
const getStructuredResultPollMs = () => normalizeTimeout(process.env.STRUCTURED_RESULT_POLL_MS, DEFAULT_STRUCTURED_RESULT_POLL_MS, 100);
|
|
21
|
+
exports.getStructuredResultPollMs = getStructuredResultPollMs;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.matchIdentity = exports.formatIdentity = exports.toRunIdentity = exports.buildItemKeyFromKeywords = exports.buildRequestId = exports.buildDerivedRouteItemKey = exports.buildBatchItemKey = exports.buildBatchRunId = exports.buildRunId = exports.randomUUID = void 0;
|
|
4
|
+
const node_crypto_1 = require("node:crypto");
|
|
5
|
+
Object.defineProperty(exports, "randomUUID", { enumerable: true, get: function () { return node_crypto_1.randomUUID; } });
|
|
6
|
+
const buildRunId = () => `run-${Date.now()}`;
|
|
7
|
+
exports.buildRunId = buildRunId;
|
|
8
|
+
const buildBatchRunId = (pipelineId) => `batch-${pipelineId}-${Date.now()}`;
|
|
9
|
+
exports.buildBatchRunId = buildBatchRunId;
|
|
10
|
+
const buildBatchItemKey = (batchIndex) => `batch-${batchIndex}`;
|
|
11
|
+
exports.buildBatchItemKey = buildBatchItemKey;
|
|
12
|
+
const buildDerivedRouteItemKey = (itemKey, nodeId, route) => `${itemKey}::${nodeId}:${route}`;
|
|
13
|
+
exports.buildDerivedRouteItemKey = buildDerivedRouteItemKey;
|
|
14
|
+
const buildRequestId = (nodeId) => `node-${nodeId}-${(0, node_crypto_1.randomUUID)()}`;
|
|
15
|
+
exports.buildRequestId = buildRequestId;
|
|
16
|
+
const buildItemKeyFromKeywords = (keywords) => [...new Set(keywords.map((item) => item.trim()).filter(Boolean))];
|
|
17
|
+
exports.buildItemKeyFromKeywords = buildItemKeyFromKeywords;
|
|
18
|
+
/** 从 Run 和 pipelineId 构造身份快照 */
|
|
19
|
+
const toRunIdentity = (pipelineId, run) => ({
|
|
20
|
+
pipelineId,
|
|
21
|
+
runId: run.id,
|
|
22
|
+
batchRunId: null,
|
|
23
|
+
});
|
|
24
|
+
exports.toRunIdentity = toRunIdentity;
|
|
25
|
+
/** 格式化身份为可读字符串 */
|
|
26
|
+
const formatIdentity = (id) => `pipeline=${id.pipelineId} run=${id.runId}${id.batchRunId ? ` batch=${id.batchRunId}` : ""}`;
|
|
27
|
+
exports.formatIdentity = formatIdentity;
|
|
28
|
+
/** 校验身份匹配:pipelineId 必须相等,runId 必须相等,batchRunId 任一方为 null 时放行 */
|
|
29
|
+
const matchIdentity = (target, candidate) => target.pipelineId === candidate.pipelineId &&
|
|
30
|
+
target.runId === candidate.runId &&
|
|
31
|
+
(target.batchRunId === null || candidate.batchRunId === null || target.batchRunId === candidate.batchRunId);
|
|
32
|
+
exports.matchIdentity = matchIdentity;
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createItemBatchController = exports.normalizePoolItems = void 0;
|
|
4
|
+
const DEFAULT_BATCH_SIZE = 10;
|
|
5
|
+
const clampBatchSize = (value) => {
|
|
6
|
+
if (typeof value !== "number" || !Number.isFinite(value))
|
|
7
|
+
return DEFAULT_BATCH_SIZE;
|
|
8
|
+
return Math.min(200, Math.max(1, Math.trunc(value)));
|
|
9
|
+
};
|
|
10
|
+
const clampStartIndex = (value, total) => {
|
|
11
|
+
if (typeof value !== "number" || !Number.isFinite(value))
|
|
12
|
+
return 0;
|
|
13
|
+
return Math.min(Math.max(0, Math.trunc(value)), Math.max(0, total));
|
|
14
|
+
};
|
|
15
|
+
const createIdleSnapshot = () => ({
|
|
16
|
+
status: "idle",
|
|
17
|
+
batchSize: DEFAULT_BATCH_SIZE,
|
|
18
|
+
totalItems: 0,
|
|
19
|
+
totalBatches: 0,
|
|
20
|
+
processedItems: 0,
|
|
21
|
+
processedBatches: 0,
|
|
22
|
+
nextBatchIndex: 1,
|
|
23
|
+
currentBatchIndex: null,
|
|
24
|
+
currentBatchItemKey: null,
|
|
25
|
+
currentBatchItems: [],
|
|
26
|
+
startedAt: null,
|
|
27
|
+
finishedAt: null,
|
|
28
|
+
lastBatchItems: [],
|
|
29
|
+
error: null,
|
|
30
|
+
stopRequested: false,
|
|
31
|
+
batchRunId: null,
|
|
32
|
+
});
|
|
33
|
+
const cloneSnapshot = (snapshot) => ({
|
|
34
|
+
...snapshot,
|
|
35
|
+
currentBatchItems: [...snapshot.currentBatchItems],
|
|
36
|
+
lastBatchItems: [...snapshot.lastBatchItems],
|
|
37
|
+
});
|
|
38
|
+
const normalizePoolItems = (value) => {
|
|
39
|
+
const rawList = [];
|
|
40
|
+
if (Array.isArray(value)) {
|
|
41
|
+
for (const entry of value) {
|
|
42
|
+
if (typeof entry === "string")
|
|
43
|
+
rawList.push(entry);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
else if (typeof value === "string") {
|
|
47
|
+
// 支持逗号与换行混输,方便直接粘贴关键词池。
|
|
48
|
+
rawList.push(...value.split(/[\n,]/g));
|
|
49
|
+
}
|
|
50
|
+
const unique = new Set();
|
|
51
|
+
for (const item of rawList) {
|
|
52
|
+
const normalized = item.trim();
|
|
53
|
+
if (!normalized)
|
|
54
|
+
continue;
|
|
55
|
+
unique.add(normalized);
|
|
56
|
+
}
|
|
57
|
+
return [...unique];
|
|
58
|
+
};
|
|
59
|
+
exports.normalizePoolItems = normalizePoolItems;
|
|
60
|
+
const identity_1 = require("./identity");
|
|
61
|
+
const createItemBatchController = (deps) => {
|
|
62
|
+
let snapshot = createIdleSnapshot();
|
|
63
|
+
let runToken = 0;
|
|
64
|
+
let hasBatchErrors = false;
|
|
65
|
+
const runLoop = async (token, queue) => {
|
|
66
|
+
while (queue.length > 0) {
|
|
67
|
+
if (token !== runToken)
|
|
68
|
+
return;
|
|
69
|
+
if (snapshot.stopRequested) {
|
|
70
|
+
snapshot = {
|
|
71
|
+
...snapshot,
|
|
72
|
+
status: "stopped",
|
|
73
|
+
finishedAt: new Date().toISOString(),
|
|
74
|
+
};
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const currentBatchItems = queue.splice(0, snapshot.batchSize);
|
|
78
|
+
const currentBatchIndex = snapshot.processedBatches + 1;
|
|
79
|
+
snapshot = {
|
|
80
|
+
...snapshot,
|
|
81
|
+
currentBatchIndex,
|
|
82
|
+
currentBatchItemKey: `batch-${currentBatchIndex}`,
|
|
83
|
+
currentBatchItems: [...currentBatchItems],
|
|
84
|
+
lastBatchItems: [...currentBatchItems],
|
|
85
|
+
};
|
|
86
|
+
let result;
|
|
87
|
+
try {
|
|
88
|
+
result = await deps.executeBatch({
|
|
89
|
+
batchItems: currentBatchItems,
|
|
90
|
+
batchIndex: currentBatchIndex,
|
|
91
|
+
totalBatches: snapshot.totalBatches,
|
|
92
|
+
totalItems: snapshot.totalItems,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
// 批跑控制器运行在 HTTP 请求之外;这里若让异常逃逸,快照会永久停留在 running,
|
|
97
|
+
// 后续 start() 都会误判为已有批跑进行中。必须在控制器内兜底收口状态。
|
|
98
|
+
if (token !== runToken)
|
|
99
|
+
return;
|
|
100
|
+
hasBatchErrors = true;
|
|
101
|
+
snapshot = {
|
|
102
|
+
...snapshot,
|
|
103
|
+
status: "failed",
|
|
104
|
+
currentBatchIndex: null,
|
|
105
|
+
currentBatchItemKey: null,
|
|
106
|
+
currentBatchItems: [],
|
|
107
|
+
error: error instanceof Error ? error.message : String(error),
|
|
108
|
+
finishedAt: new Date().toISOString(),
|
|
109
|
+
};
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
if (token !== runToken)
|
|
113
|
+
return;
|
|
114
|
+
snapshot = {
|
|
115
|
+
...snapshot,
|
|
116
|
+
processedBatches: snapshot.processedBatches + 1,
|
|
117
|
+
processedItems: snapshot.processedItems + currentBatchItems.length,
|
|
118
|
+
nextBatchIndex: snapshot.processedBatches + 2,
|
|
119
|
+
currentBatchIndex: null,
|
|
120
|
+
currentBatchItemKey: null,
|
|
121
|
+
currentBatchItems: [],
|
|
122
|
+
};
|
|
123
|
+
if (!result.ok) {
|
|
124
|
+
hasBatchErrors = true;
|
|
125
|
+
snapshot = {
|
|
126
|
+
...snapshot,
|
|
127
|
+
error: result.error ?? "batch_execute_failed",
|
|
128
|
+
};
|
|
129
|
+
if (result.hardStop) {
|
|
130
|
+
snapshot = {
|
|
131
|
+
...snapshot,
|
|
132
|
+
status: "failed",
|
|
133
|
+
currentBatchIndex: null,
|
|
134
|
+
currentBatchItemKey: null,
|
|
135
|
+
currentBatchItems: [],
|
|
136
|
+
finishedAt: new Date().toISOString(),
|
|
137
|
+
};
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
if (token !== runToken)
|
|
143
|
+
return;
|
|
144
|
+
snapshot = {
|
|
145
|
+
...snapshot,
|
|
146
|
+
status: hasBatchErrors ? "failed" : "completed",
|
|
147
|
+
currentBatchIndex: null,
|
|
148
|
+
currentBatchItemKey: null,
|
|
149
|
+
currentBatchItems: [],
|
|
150
|
+
finishedAt: new Date().toISOString(),
|
|
151
|
+
nextBatchIndex: snapshot.processedBatches + 1,
|
|
152
|
+
};
|
|
153
|
+
};
|
|
154
|
+
const start = (items, batchSize, options) => {
|
|
155
|
+
if (snapshot.status === "running") {
|
|
156
|
+
return { ok: false, error: "batch_run_in_progress", snapshot: cloneSnapshot(snapshot) };
|
|
157
|
+
}
|
|
158
|
+
if (items.length === 0) {
|
|
159
|
+
return { ok: false, error: "batch_items_empty", snapshot: cloneSnapshot(snapshot) };
|
|
160
|
+
}
|
|
161
|
+
const normalizedBatchSize = clampBatchSize(batchSize);
|
|
162
|
+
const totalBatches = Math.ceil(items.length / normalizedBatchSize);
|
|
163
|
+
const startIndex = clampStartIndex(options?.startIndex, items.length);
|
|
164
|
+
const queuedItems = items.slice(startIndex);
|
|
165
|
+
const processedBatches = Math.floor(startIndex / normalizedBatchSize);
|
|
166
|
+
runToken += 1;
|
|
167
|
+
hasBatchErrors = false;
|
|
168
|
+
const batchRunId = (0, identity_1.buildBatchRunId)(deps.pipelineId);
|
|
169
|
+
snapshot = {
|
|
170
|
+
status: "running",
|
|
171
|
+
batchSize: normalizedBatchSize,
|
|
172
|
+
totalItems: items.length,
|
|
173
|
+
totalBatches,
|
|
174
|
+
processedItems: startIndex,
|
|
175
|
+
processedBatches,
|
|
176
|
+
nextBatchIndex: processedBatches + 1,
|
|
177
|
+
currentBatchIndex: null,
|
|
178
|
+
currentBatchItemKey: null,
|
|
179
|
+
currentBatchItems: [],
|
|
180
|
+
startedAt: new Date().toISOString(),
|
|
181
|
+
finishedAt: null,
|
|
182
|
+
lastBatchItems: [],
|
|
183
|
+
error: null,
|
|
184
|
+
stopRequested: false,
|
|
185
|
+
batchRunId,
|
|
186
|
+
};
|
|
187
|
+
// 异步后台运行,避免占住 HTTP 请求。
|
|
188
|
+
void runLoop(runToken, [...queuedItems]);
|
|
189
|
+
return { ok: true, snapshot: cloneSnapshot(snapshot) };
|
|
190
|
+
};
|
|
191
|
+
const stop = () => {
|
|
192
|
+
if (snapshot.status !== "running") {
|
|
193
|
+
return { ok: false, error: "batch_run_not_running", snapshot: cloneSnapshot(snapshot) };
|
|
194
|
+
}
|
|
195
|
+
// 仅请求停止,当前批次跑完后会安全退出。
|
|
196
|
+
snapshot = {
|
|
197
|
+
...snapshot,
|
|
198
|
+
stopRequested: true,
|
|
199
|
+
};
|
|
200
|
+
return { ok: true, snapshot: cloneSnapshot(snapshot) };
|
|
201
|
+
};
|
|
202
|
+
const cancel = () => {
|
|
203
|
+
if (snapshot.status !== "running") {
|
|
204
|
+
return { ok: false, error: "batch_run_not_running", snapshot: cloneSnapshot(snapshot) };
|
|
205
|
+
}
|
|
206
|
+
// 插件关闭后不应继续保留运行中的批跑控制器;切换 runToken 让旧循环结果失效。
|
|
207
|
+
runToken += 1;
|
|
208
|
+
snapshot = {
|
|
209
|
+
...snapshot,
|
|
210
|
+
status: "stopped",
|
|
211
|
+
currentBatchIndex: null,
|
|
212
|
+
currentBatchItemKey: null,
|
|
213
|
+
currentBatchItems: [],
|
|
214
|
+
stopRequested: false,
|
|
215
|
+
finishedAt: new Date().toISOString(),
|
|
216
|
+
};
|
|
217
|
+
return { ok: true, snapshot: cloneSnapshot(snapshot) };
|
|
218
|
+
};
|
|
219
|
+
const getSnapshot = () => cloneSnapshot(snapshot);
|
|
220
|
+
return {
|
|
221
|
+
start,
|
|
222
|
+
stop,
|
|
223
|
+
cancel,
|
|
224
|
+
getSnapshot,
|
|
225
|
+
};
|
|
226
|
+
};
|
|
227
|
+
exports.createItemBatchController = createItemBatchController;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolvePipelineOutput = exports.resolveOutputNodeId = void 0;
|
|
4
|
+
const promises_1 = require("node:fs/promises");
|
|
5
|
+
const pipeline_output_1 = require("../types/pipeline-output");
|
|
6
|
+
const resolveOutputNodeId = (workflow) => {
|
|
7
|
+
const output = workflow.output ?? { mode: "mainline_last", nodeId: null };
|
|
8
|
+
if (output.mode === "explicit") {
|
|
9
|
+
return output.nodeId;
|
|
10
|
+
}
|
|
11
|
+
// mainline_last: derive unique mainline sink
|
|
12
|
+
const mainlineNodeIds = new Set(workflow.nodes
|
|
13
|
+
.filter((n) => n.enabled && n.lane === "main" && !n.branchScopeId && !n.routeSourceNodeId && !n.routeValue)
|
|
14
|
+
.map((n) => n.id));
|
|
15
|
+
const hasMainlineDownstream = new Set();
|
|
16
|
+
for (const edge of workflow.edges) {
|
|
17
|
+
if (edge.when !== null)
|
|
18
|
+
continue;
|
|
19
|
+
if (mainlineNodeIds.has(edge.from) && mainlineNodeIds.has(edge.to)) {
|
|
20
|
+
hasMainlineDownstream.add(edge.from);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
const sinkNodes = [...mainlineNodeIds].filter((id) => !hasMainlineDownstream.has(id));
|
|
24
|
+
return sinkNodes.length === 1 ? sinkNodes[0] : null;
|
|
25
|
+
};
|
|
26
|
+
exports.resolveOutputNodeId = resolveOutputNodeId;
|
|
27
|
+
const findOutputArtifact = (run, outputNodeId, itemKey) => {
|
|
28
|
+
if (itemKey) {
|
|
29
|
+
const itemRun = (run.itemRuns ?? []).find((item) => item.nodeId === outputNodeId && item.itemKey === itemKey);
|
|
30
|
+
if (itemRun && itemRun.artifacts.length > 0) {
|
|
31
|
+
return itemRun.artifacts[itemRun.artifacts.length - 1];
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const nodeRun = run.nodes.find((n) => n.id === outputNodeId);
|
|
35
|
+
if (nodeRun && nodeRun.artifacts.length > 0) {
|
|
36
|
+
return nodeRun.artifacts[nodeRun.artifacts.length - 1];
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
};
|
|
40
|
+
const resolvePipelineOutput = async (workflow, run, artifactDir, pipelineId, batchRunId, itemKey) => {
|
|
41
|
+
const outputNodeId = (0, exports.resolveOutputNodeId)(workflow);
|
|
42
|
+
if (!outputNodeId)
|
|
43
|
+
return null;
|
|
44
|
+
const artifact = findOutputArtifact(run, outputNodeId, itemKey);
|
|
45
|
+
if (!artifact)
|
|
46
|
+
return null;
|
|
47
|
+
// Verify artifact file exists and hash matches
|
|
48
|
+
try {
|
|
49
|
+
await (0, promises_1.stat)(artifact.path);
|
|
50
|
+
// Read to verify hash — the resolver validates integrity
|
|
51
|
+
const raw = await (0, promises_1.readFile)(artifact.path, "utf8");
|
|
52
|
+
const expectedHashPrefix = `sha256:`;
|
|
53
|
+
const hashAlgo = artifact.hash.startsWith(expectedHashPrefix) ? "sha256" : null;
|
|
54
|
+
if (!hashAlgo)
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
const relativePath = artifact.path.startsWith(artifactDir)
|
|
61
|
+
? artifact.path.slice(artifactDir.length).replace(/^[/\\]/, "").replaceAll("\\", "/")
|
|
62
|
+
: artifact.path.replaceAll("\\", "/");
|
|
63
|
+
const artifactRef = {
|
|
64
|
+
pipelineId,
|
|
65
|
+
runId: run.id,
|
|
66
|
+
batchRunId,
|
|
67
|
+
nodeId: outputNodeId,
|
|
68
|
+
itemKey: itemKey ?? null,
|
|
69
|
+
relativePath,
|
|
70
|
+
absolutePath: artifact.path,
|
|
71
|
+
type: artifact.type,
|
|
72
|
+
schemaVersion: artifact.schemaVersion,
|
|
73
|
+
name: artifact.name,
|
|
74
|
+
hash: artifact.hash,
|
|
75
|
+
createdAt: artifact.createdAt,
|
|
76
|
+
};
|
|
77
|
+
const outputId = (0, pipeline_output_1.buildOutputId)(pipelineId, run.id, batchRunId, itemKey ?? null, outputNodeId, artifact.id, artifact.hash);
|
|
78
|
+
return {
|
|
79
|
+
schemaVersion: 1,
|
|
80
|
+
outputId,
|
|
81
|
+
pipelineId,
|
|
82
|
+
runId: run.id,
|
|
83
|
+
batchRunId,
|
|
84
|
+
itemKey: itemKey ?? null,
|
|
85
|
+
outputNodeId,
|
|
86
|
+
artifactId: artifact.id,
|
|
87
|
+
artifactRef,
|
|
88
|
+
producedAt: new Date().toISOString(),
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
exports.resolvePipelineOutput = resolvePipelineOutput;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createPipelineOutputStore = void 0;
|
|
4
|
+
const promises_1 = require("node:fs/promises");
|
|
5
|
+
const data_dir_1 = require("../../app/data-dir");
|
|
6
|
+
const getIndexPath = (pipelineId) => (0, data_dir_1.resolveTaskMeldDataPath)("pipelines", pipelineId, "outputs", "index.jsonl");
|
|
7
|
+
const ensureDir = async (pipelineId) => {
|
|
8
|
+
await (0, promises_1.mkdir)((0, data_dir_1.resolveTaskMeldDataPath)("pipelines", pipelineId, "outputs"), { recursive: true });
|
|
9
|
+
};
|
|
10
|
+
const createPipelineOutputStore = (pipelineId) => {
|
|
11
|
+
const indexPath = getIndexPath(pipelineId);
|
|
12
|
+
const loadAll = async () => {
|
|
13
|
+
try {
|
|
14
|
+
const raw = await (0, promises_1.readFile)(indexPath, "utf8");
|
|
15
|
+
const lines = raw.trim().split("\n").filter(Boolean);
|
|
16
|
+
return lines
|
|
17
|
+
.map((line) => {
|
|
18
|
+
try {
|
|
19
|
+
return JSON.parse(line);
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
})
|
|
25
|
+
.filter((o) => o !== null && o.schemaVersion === 1);
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return [];
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
const store = {
|
|
32
|
+
append: async (output) => {
|
|
33
|
+
// Dedup: check if output already exists
|
|
34
|
+
const existing = await store.has(output.outputId);
|
|
35
|
+
if (existing)
|
|
36
|
+
return false;
|
|
37
|
+
await ensureDir(pipelineId);
|
|
38
|
+
const line = JSON.stringify(output) + "\n";
|
|
39
|
+
await (0, promises_1.appendFile)(indexPath, line, "utf8");
|
|
40
|
+
return true;
|
|
41
|
+
},
|
|
42
|
+
list: async () => {
|
|
43
|
+
return loadAll();
|
|
44
|
+
},
|
|
45
|
+
getByRunId: async (runId) => {
|
|
46
|
+
const all = await loadAll();
|
|
47
|
+
return all.find((o) => o.runId === runId) ?? null;
|
|
48
|
+
},
|
|
49
|
+
getById: async (outputId) => {
|
|
50
|
+
const all = await loadAll();
|
|
51
|
+
return all.find((o) => o.outputId === outputId) ?? null;
|
|
52
|
+
},
|
|
53
|
+
has: async (outputId) => {
|
|
54
|
+
const all = await loadAll();
|
|
55
|
+
return all.some((o) => o.outputId === outputId);
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
return store;
|
|
59
|
+
};
|
|
60
|
+
exports.createPipelineOutputStore = createPipelineOutputStore;
|