@fieldwangai/agentflow 0.1.32 → 0.1.34
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/bin/lib/catalog-flows.mjs +7 -2
- package/bin/lib/composer-node-schema.mjs +7 -7
- package/bin/lib/composer-planner.mjs +5 -5
- package/bin/lib/git-worktree.mjs +263 -0
- package/bin/lib/gitlab-mr.mjs +174 -0
- package/bin/lib/locales/en.json +27 -7
- package/bin/lib/locales/zh.json +26 -6
- package/bin/lib/marketplace.mjs +106 -2
- package/bin/lib/paths.mjs +5 -1
- package/bin/lib/scheduler.mjs +8 -3
- package/bin/lib/ui-server.mjs +284 -13
- package/bin/pipeline/build-node-prompt.mjs +27 -1
- package/bin/pipeline/pre-process-node.mjs +216 -39
- package/bin/pipeline/validate-flow.mjs +7 -17
- package/builtin/nodes/agent_subAgent.md +2 -0
- package/builtin/nodes/control_agent_toBool.md +4 -0
- package/builtin/nodes/control_cancelled.md +8 -4
- package/builtin/nodes/control_cd_workspace.md +2 -0
- package/builtin/nodes/control_delay.md +9 -0
- package/builtin/nodes/control_toBool.md +6 -2
- package/builtin/nodes/control_user_workspace.md +2 -0
- package/builtin/nodes/control_wait_until.md +9 -0
- package/builtin/nodes/display_html.md +31 -0
- package/builtin/nodes/display_image.md +35 -0
- package/builtin/nodes/provide_bool.md +13 -0
- package/builtin/nodes/provide_file.md +2 -0
- package/builtin/nodes/provide_str.md +2 -0
- package/builtin/nodes/tool_get_env.md +4 -0
- package/builtin/nodes/tool_git_checkout.md +14 -2
- package/builtin/nodes/tool_git_worktree_load.md +59 -0
- package/builtin/nodes/tool_git_worktree_unload.md +51 -0
- package/builtin/nodes/tool_gitlab_create_mr.md +113 -0
- package/builtin/nodes/tool_load_key.md +4 -0
- package/builtin/nodes/tool_nodejs.md +4 -0
- package/builtin/nodes/tool_print.md +2 -0
- package/builtin/nodes/tool_save_key.md +4 -0
- package/builtin/nodes/tool_user_ask.md +3 -1
- package/builtin/nodes/tool_user_check.md +3 -1
- package/builtin/web-ui/dist/assets/index-B1j_UaHw.js +202 -0
- package/builtin/web-ui/dist/assets/index-ChiTnW0H.css +1 -0
- package/builtin/web-ui/dist/index.html +2 -2
- package/package.json +1 -1
- package/builtin/nodes/control_deadline.md +0 -32
- package/builtin/web-ui/dist/assets/index-D0Tkhqr6.css +0 -1
- package/builtin/web-ui/dist/assets/index-DyhW5chp.js +0 -197
|
@@ -41,12 +41,15 @@ import { getRunDir, sanitizeAgentflowUserId } from "../lib/paths.mjs";
|
|
|
41
41
|
import {
|
|
42
42
|
buildSkillsContext,
|
|
43
43
|
buildSkillsContextFromRegistry,
|
|
44
|
+
buildDefaultWorkspaceContext,
|
|
44
45
|
expandRuntimePlaceholders,
|
|
45
46
|
normalizeSkillsContext,
|
|
46
47
|
normalizeWorkspaceContext,
|
|
47
48
|
parseSkillKeyList,
|
|
48
49
|
resolveWorkspaceTarget,
|
|
49
50
|
} from "../lib/runtime-context.mjs";
|
|
51
|
+
import { buildGitContext, inferGitRepoRootFromWorktree, loadGitWorktree, normalizeGitContext, unloadGitWorktree } from "../lib/git-worktree.mjs";
|
|
52
|
+
import { createGitLabMergeRequest } from "../lib/gitlab-mr.mjs";
|
|
50
53
|
|
|
51
54
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
52
55
|
|
|
@@ -264,6 +267,12 @@ function isTruthyInput(value) {
|
|
|
264
267
|
return text === "true" || text === "1" || text === "yes" || text === "y" || text === "on";
|
|
265
268
|
}
|
|
266
269
|
|
|
270
|
+
function resolveMaybeWorkspacePath(raw, workspaceContext, extra = {}) {
|
|
271
|
+
const text = String(raw || "").trim();
|
|
272
|
+
if (!text) return "";
|
|
273
|
+
return resolveWorkspaceTarget(text, workspaceContext, extra);
|
|
274
|
+
}
|
|
275
|
+
|
|
267
276
|
function emitGitCheckoutNode(workspaceRoot, flowName, uuid, instanceId, execId, resultPathRel) {
|
|
268
277
|
const runDir = getRunDir(workspaceRoot, flowName, uuid);
|
|
269
278
|
const { inputs, workspaceContext } = resolveNodeRuntimeContexts(workspaceRoot, flowName, uuid, instanceId);
|
|
@@ -312,6 +321,12 @@ function emitGitCheckoutNode(workspaceRoot, flowName, uuid, instanceId, execId,
|
|
|
312
321
|
|
|
313
322
|
const currentBranch = runGit(["rev-parse", "--abbrev-ref", "HEAD"], targetDir).stdout.trim();
|
|
314
323
|
const commit = runGit(["rev-parse", "HEAD"], targetDir).stdout.trim();
|
|
324
|
+
const gitContext = buildGitContext({
|
|
325
|
+
repoPath: targetDir,
|
|
326
|
+
branch: currentBranch === "HEAD" ? "DETACHED" : currentBranch,
|
|
327
|
+
commit,
|
|
328
|
+
remote: String(inputs.remote || "origin").trim() || "origin",
|
|
329
|
+
});
|
|
315
330
|
const outWorkspaceContext = {
|
|
316
331
|
version: 1,
|
|
317
332
|
label: inputs.label || sanitizeRepoDirName(repoUrl),
|
|
@@ -326,10 +341,131 @@ function emitGitCheckoutNode(workspaceRoot, flowName, uuid, instanceId, execId,
|
|
|
326
341
|
writeOutputSlot(runDir, instanceId, execId, "commit", commit);
|
|
327
342
|
writeOutputSlot(runDir, instanceId, execId, "changed", changed ? "true" : "false");
|
|
328
343
|
writeOutputSlot(runDir, instanceId, execId, "workspaceContext", JSON.stringify(outWorkspaceContext));
|
|
344
|
+
writeOutputSlot(runDir, instanceId, execId, "gitContext", JSON.stringify(gitContext));
|
|
329
345
|
writeResult(workspaceRoot, flowName, uuid, instanceId, { status: "success", message: `git ${action}: ${currentBranch}@${commit.slice(0, 8)}` }, { execId });
|
|
330
346
|
return emitLocalNoopPrompt(workspaceRoot, runDir, instanceId, "git-checkout", `Git checkout completed: ${targetDir}\n`);
|
|
331
347
|
}
|
|
332
348
|
|
|
349
|
+
function requireWorkspaceContextInput(inputs, definitionId) {
|
|
350
|
+
if (!String(inputs?.workspaceContext || "").trim()) {
|
|
351
|
+
throw new Error(`${definitionId}: workspaceContext is required`);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
function emitGitWorktreeLoadNode(workspaceRoot, flowName, uuid, instanceId, execId) {
|
|
356
|
+
const runDir = getRunDir(workspaceRoot, flowName, uuid);
|
|
357
|
+
const { inputs, workspaceContext } = resolveNodeRuntimeContexts(workspaceRoot, flowName, uuid, instanceId);
|
|
358
|
+
requireWorkspaceContextInput(inputs, "tool_git_worktree_load");
|
|
359
|
+
const gitContext = normalizeGitContext(inputs.gitContext);
|
|
360
|
+
const repoPath = resolveMaybeWorkspacePath(inputs.repoPath, workspaceContext) ||
|
|
361
|
+
(gitContext?.repoPath ? path.resolve(gitContext.repoPath) : "");
|
|
362
|
+
if (!repoPath) throw new Error("tool_git_worktree_load: repoPath or gitContext.repoPath is required");
|
|
363
|
+
const branch = String(inputs.branch || "").trim();
|
|
364
|
+
const worktreePath = resolveMaybeWorkspacePath(inputs.worktreePath, workspaceContext, { branch }) ||
|
|
365
|
+
(gitContext?.worktreePath ? path.resolve(gitContext.worktreePath) : "");
|
|
366
|
+
const result = loadGitWorktree({
|
|
367
|
+
repoPath,
|
|
368
|
+
branch,
|
|
369
|
+
worktreePath,
|
|
370
|
+
pipelineWorkspace: workspaceContext.pipelineWorkspace || path.resolve(workspaceRoot),
|
|
371
|
+
});
|
|
372
|
+
const outWorkspaceContext = {
|
|
373
|
+
version: 1,
|
|
374
|
+
label: result.branch === "DETACHED" ? `worktree:${result.commit.slice(0, 8)}` : `worktree:${result.branch}`,
|
|
375
|
+
cwd: result.worktreePath,
|
|
376
|
+
workspaceRoot: result.worktreePath,
|
|
377
|
+
pipelineWorkspace: workspaceContext.pipelineWorkspace || path.resolve(workspaceRoot),
|
|
378
|
+
flowDir: workspaceContext.flowDir,
|
|
379
|
+
previous: workspaceContext,
|
|
380
|
+
};
|
|
381
|
+
const outGitContext = buildGitContext({
|
|
382
|
+
repoPath: result.repoRoot,
|
|
383
|
+
worktreePath: result.worktreePath,
|
|
384
|
+
branch: result.branch,
|
|
385
|
+
commit: result.commit,
|
|
386
|
+
remote: gitContext?.remote || "origin",
|
|
387
|
+
remoteUrl: gitContext?.remoteUrl || "",
|
|
388
|
+
});
|
|
389
|
+
writeOutputSlot(runDir, instanceId, execId, "worktreePath", result.worktreePath);
|
|
390
|
+
writeOutputSlot(runDir, instanceId, execId, "branch", result.branch);
|
|
391
|
+
writeOutputSlot(runDir, instanceId, execId, "commit", result.commit);
|
|
392
|
+
writeOutputSlot(runDir, instanceId, execId, "workspaceContext", JSON.stringify(outWorkspaceContext));
|
|
393
|
+
writeOutputSlot(runDir, instanceId, execId, "gitContext", JSON.stringify(outGitContext));
|
|
394
|
+
writeResult(workspaceRoot, flowName, uuid, instanceId, {
|
|
395
|
+
status: "success",
|
|
396
|
+
message: `worktree loaded: ${result.worktreePath} (${result.branch}@${result.commit.slice(0, 8)})`,
|
|
397
|
+
}, { execId });
|
|
398
|
+
return emitLocalNoopPrompt(workspaceRoot, runDir, instanceId, "git-worktree-load", `Git worktree loaded: ${result.worktreePath}\n`);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
function emitGitWorktreeUnloadNode(workspaceRoot, flowName, uuid, instanceId, execId) {
|
|
402
|
+
const runDir = getRunDir(workspaceRoot, flowName, uuid);
|
|
403
|
+
const { inputs, workspaceContext, flowJson } = resolveNodeRuntimeContexts(workspaceRoot, flowName, uuid, instanceId);
|
|
404
|
+
requireWorkspaceContextInput(inputs, "tool_git_worktree_unload");
|
|
405
|
+
const gitContext = normalizeGitContext(inputs.gitContext);
|
|
406
|
+
const worktreePath = resolveMaybeWorkspacePath(inputs.worktreePath, workspaceContext) ||
|
|
407
|
+
(gitContext?.worktreePath ? path.resolve(gitContext.worktreePath) : "") ||
|
|
408
|
+
(workspaceContext.cwd ? path.resolve(workspaceContext.cwd) : "") ||
|
|
409
|
+
(workspaceContext.workspaceRoot ? path.resolve(workspaceContext.workspaceRoot) : "");
|
|
410
|
+
if (!worktreePath) throw new Error("tool_git_worktree_unload: workspaceContext.cwd or worktreePath is required");
|
|
411
|
+
const repoPath = resolveMaybeWorkspacePath(inputs.repoPath, workspaceContext) ||
|
|
412
|
+
(gitContext?.repoPath ? path.resolve(gitContext.repoPath) : "") ||
|
|
413
|
+
inferGitRepoRootFromWorktree(worktreePath) ||
|
|
414
|
+
(workspaceContext.previous?.cwd ? path.resolve(workspaceContext.previous.cwd) : "") ||
|
|
415
|
+
(workspaceContext.previous?.workspaceRoot ? path.resolve(workspaceContext.previous.workspaceRoot) : "");
|
|
416
|
+
const force = isTruthyInput(inputs.force);
|
|
417
|
+
const prune = String(inputs.prune ?? "true").trim().toLowerCase() !== "false";
|
|
418
|
+
const result = unloadGitWorktree({ repoPath, worktreePath, force, prune });
|
|
419
|
+
const nextWorkspaceContext = workspaceContext.previous
|
|
420
|
+
? normalizeWorkspaceContext(workspaceContext.previous, workspaceRoot, flowName, flowJson)
|
|
421
|
+
: buildDefaultWorkspaceContext(workspaceRoot, flowName, flowJson);
|
|
422
|
+
writeOutputSlot(runDir, instanceId, execId, "removed", "true");
|
|
423
|
+
writeOutputSlot(runDir, instanceId, execId, "message", result.message);
|
|
424
|
+
writeOutputSlot(runDir, instanceId, execId, "workspaceContext", JSON.stringify(nextWorkspaceContext));
|
|
425
|
+
writeResult(workspaceRoot, flowName, uuid, instanceId, {
|
|
426
|
+
status: "success",
|
|
427
|
+
message: result.message,
|
|
428
|
+
}, { execId });
|
|
429
|
+
return emitLocalNoopPrompt(workspaceRoot, runDir, instanceId, "git-worktree-unload", `${result.message}\n`);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
async function emitGitLabCreateMrNode(workspaceRoot, flowName, uuid, instanceId, execId) {
|
|
433
|
+
const runDir = getRunDir(workspaceRoot, flowName, uuid);
|
|
434
|
+
const { inputs, workspaceContext } = resolveNodeRuntimeContexts(workspaceRoot, flowName, uuid, instanceId);
|
|
435
|
+
const repoPath = resolveMaybeWorkspacePath(inputs.repoPath, workspaceContext);
|
|
436
|
+
const result = await createGitLabMergeRequest({
|
|
437
|
+
gitContext: inputs.gitContext,
|
|
438
|
+
workspaceCwd: workspaceContext.cwd,
|
|
439
|
+
repoPath,
|
|
440
|
+
sourceBranch: inputs.sourceBranch,
|
|
441
|
+
targetBranch: inputs.targetBranch,
|
|
442
|
+
title: inputs.title,
|
|
443
|
+
description: inputs.description,
|
|
444
|
+
draft: inputs.draft,
|
|
445
|
+
labels: inputs.labels,
|
|
446
|
+
push: inputs.push,
|
|
447
|
+
remote: inputs.remote,
|
|
448
|
+
tokenEnv: inputs.tokenEnv,
|
|
449
|
+
gitlabApiBase: inputs.gitlabApiBase,
|
|
450
|
+
removeSourceBranch: inputs.removeSourceBranch,
|
|
451
|
+
squash: inputs.squash,
|
|
452
|
+
}, process.env);
|
|
453
|
+
writeOutputSlot(runDir, instanceId, execId, "mrUrl", result.mrUrl);
|
|
454
|
+
writeOutputSlot(runDir, instanceId, execId, "created", result.created ? "true" : "false");
|
|
455
|
+
writeOutputSlot(runDir, instanceId, execId, "mrIid", result.mrIid ?? "");
|
|
456
|
+
writeOutputSlot(runDir, instanceId, execId, "projectId", result.projectId ?? "");
|
|
457
|
+
writeOutputSlot(runDir, instanceId, execId, "sourceBranch", result.sourceBranch ?? "");
|
|
458
|
+
writeOutputSlot(runDir, instanceId, execId, "targetBranch", result.targetBranch ?? "");
|
|
459
|
+
writeOutputSlot(runDir, instanceId, execId, "title", result.title ?? "");
|
|
460
|
+
writeOutputSlot(runDir, instanceId, execId, "message", result.message ?? "");
|
|
461
|
+
writeResult(workspaceRoot, flowName, uuid, instanceId, {
|
|
462
|
+
status: "success",
|
|
463
|
+
message: result.message || result.mrUrl,
|
|
464
|
+
body: result.mrUrl,
|
|
465
|
+
}, { execId });
|
|
466
|
+
return emitLocalNoopPrompt(workspaceRoot, runDir, instanceId, "gitlab-create-mr", `${result.message || "GitLab MR ready"}\n${result.mrUrl}\n`);
|
|
467
|
+
}
|
|
468
|
+
|
|
333
469
|
function emitCdWorkspaceNode(workspaceRoot, flowName, uuid, instanceId, execId) {
|
|
334
470
|
const runDir = getRunDir(workspaceRoot, flowName, uuid);
|
|
335
471
|
const { inputs, workspaceContext } = resolveNodeRuntimeContexts(workspaceRoot, flowName, uuid, instanceId);
|
|
@@ -461,7 +597,7 @@ function emitToolPrintNode(workspaceRoot, flowName, uuid, instanceId, execId) {
|
|
|
461
597
|
const flowJson = readFlowJsonObject(workspaceRoot, flowName, uuid);
|
|
462
598
|
const data = getResolvedValues(workspaceRoot, flowName, uuid, instanceId);
|
|
463
599
|
const inputs = data.ok ? (data.resolvedInputs || {}) : {};
|
|
464
|
-
const skipNames = new Set(["prev", "next", "workspaceContext", "skillsContext", "workspaceRoot", "pipelineWorkspace", "flowName", "runDir", "flowDir", "cwd"]);
|
|
600
|
+
const skipNames = new Set(["prev", "next", "workspaceContext", "gitContext", "skillsContext", "workspaceRoot", "pipelineWorkspace", "flowName", "runDir", "flowDir", "cwd"]);
|
|
465
601
|
|
|
466
602
|
let content = readPrintableValue(inputs.content, runDir);
|
|
467
603
|
if (!content) {
|
|
@@ -504,8 +640,14 @@ function writeWaitState(runDir, state) {
|
|
|
504
640
|
if (parsed && typeof parsed === "object" && Array.isArray(parsed.waits)) registry = parsed;
|
|
505
641
|
} catch (_) {}
|
|
506
642
|
}
|
|
507
|
-
const
|
|
508
|
-
const
|
|
643
|
+
const waitId = String(state.waitId || state.id || `${state.instanceId}:${state.execId || 1}`).trim();
|
|
644
|
+
const waits = registry.waits.filter((w) => {
|
|
645
|
+
if (!w) return false;
|
|
646
|
+
const key = String(w.waitId || w.id || "").trim();
|
|
647
|
+
if (key && key === waitId) return false;
|
|
648
|
+
return !(w.instanceId === state.instanceId && String(w.execId || 1) === String(state.execId || 1));
|
|
649
|
+
});
|
|
650
|
+
const wait = { ...state, waitId, id: state.id || waitId };
|
|
509
651
|
waits.push(wait);
|
|
510
652
|
registry = {
|
|
511
653
|
...registry,
|
|
@@ -519,6 +661,44 @@ function writeWaitState(runDir, state) {
|
|
|
519
661
|
fs.writeFileSync(legacyPath, JSON.stringify(wait, null, 2) + "\n", "utf-8");
|
|
520
662
|
}
|
|
521
663
|
|
|
664
|
+
function buildWaitId(uuid, instanceId, execId, explicit = "") {
|
|
665
|
+
const text = String(explicit || "").trim();
|
|
666
|
+
if (text) return text;
|
|
667
|
+
return `${uuid}:${instanceId}:${execId || 1}`;
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
function readWaitStateById(runDir, waitId) {
|
|
671
|
+
const text = String(waitId || "").trim();
|
|
672
|
+
if (!text) return null;
|
|
673
|
+
const registryPath = path.join(runDir, "wait-states.json");
|
|
674
|
+
if (fs.existsSync(registryPath)) {
|
|
675
|
+
try {
|
|
676
|
+
const registry = JSON.parse(fs.readFileSync(registryPath, "utf-8"));
|
|
677
|
+
const waits = Array.isArray(registry?.waits) ? registry.waits : [];
|
|
678
|
+
const found = waits.find((w) => {
|
|
679
|
+
const key = String(w?.waitId || w?.id || "").trim();
|
|
680
|
+
return key === text;
|
|
681
|
+
});
|
|
682
|
+
if (found) return found;
|
|
683
|
+
} catch (_) {}
|
|
684
|
+
}
|
|
685
|
+
const legacyPath = path.join(runDir, "wait-state.json");
|
|
686
|
+
if (fs.existsSync(legacyPath)) {
|
|
687
|
+
try {
|
|
688
|
+
const state = JSON.parse(fs.readFileSync(legacyPath, "utf-8"));
|
|
689
|
+
const key = String(state?.waitId || state?.id || "").trim();
|
|
690
|
+
if (key === text) return state;
|
|
691
|
+
} catch (_) {}
|
|
692
|
+
}
|
|
693
|
+
return null;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
function readCancelFlagForWait(runDir, waitId) {
|
|
697
|
+
const state = readWaitStateById(runDir, waitId);
|
|
698
|
+
if (state && (state.cancelled === true || state.status === "cancelled" || state.branch === "cancelled")) return true;
|
|
699
|
+
return readCancelFlag(runDir);
|
|
700
|
+
}
|
|
701
|
+
|
|
522
702
|
function readCancelFlag(runDir) {
|
|
523
703
|
for (const name of ["cancelled", "cancelled.json", "wait-cancelled.json"]) {
|
|
524
704
|
const p = path.join(runDir, name);
|
|
@@ -681,7 +861,7 @@ ${directCommand}
|
|
|
681
861
|
return { optionalPromptPath: relativePath.replace(/\\/g, "/"), directCommand };
|
|
682
862
|
}
|
|
683
863
|
|
|
684
|
-
function main() {
|
|
864
|
+
async function main() {
|
|
685
865
|
const args = process.argv.slice(2);
|
|
686
866
|
if (args.length < 4) {
|
|
687
867
|
console.error(
|
|
@@ -834,8 +1014,11 @@ function main() {
|
|
|
834
1014
|
process.exit(1);
|
|
835
1015
|
}
|
|
836
1016
|
|
|
1017
|
+
const waitId = buildWaitId(uuid, instanceId, execId, inputs.waitId || inputs.watchId);
|
|
1018
|
+
writeOutputSlot(runDir, instanceId, execId, "waitId", waitId);
|
|
837
1019
|
writeOutputSlot(runDir, instanceId, execId, "wakeAt", wakeAt);
|
|
838
1020
|
writeWaitState(runDir, {
|
|
1021
|
+
waitId,
|
|
839
1022
|
status: "waiting",
|
|
840
1023
|
reason: definitionId,
|
|
841
1024
|
flowName,
|
|
@@ -861,7 +1044,7 @@ function main() {
|
|
|
861
1044
|
`此节点为 ${definitionId},已写入 wait-state.json,等待 scheduler 在 ${wakeAt} 唤醒。\n`,
|
|
862
1045
|
);
|
|
863
1046
|
writeCacheJsonForNode(workspaceRoot, flowName, uuid, instanceId, execId);
|
|
864
|
-
logToRunTag(workspaceRoot, flowName, uuid, "pre-process", { event: "waiting", instanceId, wakeAt, definitionId });
|
|
1047
|
+
logToRunTag(workspaceRoot, flowName, uuid, "pre-process", { event: "waiting", instanceId, waitId, wakeAt, definitionId });
|
|
865
1048
|
console.log(JSON.stringify({
|
|
866
1049
|
ok: true,
|
|
867
1050
|
promptPath,
|
|
@@ -952,37 +1135,22 @@ function main() {
|
|
|
952
1135
|
return;
|
|
953
1136
|
}
|
|
954
1137
|
|
|
955
|
-
if (definitionId === "
|
|
1138
|
+
if (definitionId === "control_cancelled") {
|
|
956
1139
|
const data = getResolvedValues(workspaceRoot, flowName, uuid, instanceId);
|
|
957
1140
|
if (!data.ok) {
|
|
958
|
-
console.error(JSON.stringify({ ok: false, error:
|
|
1141
|
+
console.error(JSON.stringify({ ok: false, error: "control_cancelled: getResolvedValues failed" }));
|
|
959
1142
|
process.exit(1);
|
|
960
1143
|
}
|
|
961
|
-
|
|
962
|
-
let
|
|
963
|
-
let message = "";
|
|
1144
|
+
let boolValue;
|
|
1145
|
+
let message;
|
|
964
1146
|
try {
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
} else {
|
|
971
|
-
const start = inputs.startAt ? parseDateTime(inputs.startAt, timezone) : new Date();
|
|
972
|
-
deadline = new Date(start.getTime() + parseDurationMs(inputs.duration || ""));
|
|
973
|
-
}
|
|
974
|
-
const deadlineAt = deadline.toISOString();
|
|
975
|
-
boolValue = Date.now() >= deadline.getTime();
|
|
976
|
-
writeOutputSlot(runDir, instanceId, execId, "deadlineAt", deadlineAt);
|
|
977
|
-
writeOutputSlot(runDir, instanceId, execId, "expired", boolValue ? "true" : "false");
|
|
978
|
-
message = boolValue ? `已超过截止时间 ${deadlineAt}` : `未超过截止时间 ${deadlineAt}`;
|
|
979
|
-
} else {
|
|
980
|
-
boolValue = readCancelFlag(runDir);
|
|
981
|
-
writeOutputSlot(runDir, instanceId, execId, "cancelled", boolValue ? "true" : "false");
|
|
982
|
-
message = boolValue ? "已取消" : "未取消";
|
|
983
|
-
}
|
|
1147
|
+
const inputs = data.resolvedInputs || {};
|
|
1148
|
+
const waitId = String(inputs.waitId || inputs.watchId || "").trim();
|
|
1149
|
+
boolValue = waitId ? readCancelFlagForWait(runDir, waitId) : readCancelFlag(runDir);
|
|
1150
|
+
writeOutputSlot(runDir, instanceId, execId, "cancelled", boolValue ? "true" : "false");
|
|
1151
|
+
message = boolValue ? "已取消" : "未取消";
|
|
984
1152
|
} catch (e) {
|
|
985
|
-
console.error(JSON.stringify({ ok: false, error:
|
|
1153
|
+
console.error(JSON.stringify({ ok: false, error: `control_cancelled: ${e.message}` }));
|
|
986
1154
|
process.exit(1);
|
|
987
1155
|
}
|
|
988
1156
|
writeResult(workspaceRoot, flowName, uuid, instanceId, { status: "success", message }, { execId, preserveBody: false });
|
|
@@ -1001,18 +1169,24 @@ function main() {
|
|
|
1001
1169
|
return;
|
|
1002
1170
|
}
|
|
1003
1171
|
|
|
1004
|
-
if (definitionId === "tool_git_checkout" || definitionId === "control_cd_workspace" || definitionId === "control_user_workspace" || definitionId === "control_load_skills" || definitionId === "tool_print") {
|
|
1172
|
+
if (definitionId === "tool_git_checkout" || definitionId === "tool_git_worktree_load" || definitionId === "tool_git_worktree_unload" || definitionId === "tool_gitlab_create_mr" || definitionId === "control_cd_workspace" || definitionId === "control_user_workspace" || definitionId === "control_load_skills" || definitionId === "tool_print") {
|
|
1005
1173
|
try {
|
|
1006
1174
|
const promptPath =
|
|
1007
1175
|
definitionId === "tool_git_checkout"
|
|
1008
1176
|
? emitGitCheckoutNode(workspaceRoot, flowName, uuid, instanceId, execId, resultPathRel)
|
|
1009
|
-
: definitionId === "
|
|
1010
|
-
?
|
|
1011
|
-
: definitionId === "
|
|
1012
|
-
?
|
|
1013
|
-
: definitionId === "
|
|
1014
|
-
?
|
|
1015
|
-
:
|
|
1177
|
+
: definitionId === "tool_git_worktree_load"
|
|
1178
|
+
? emitGitWorktreeLoadNode(workspaceRoot, flowName, uuid, instanceId, execId)
|
|
1179
|
+
: definitionId === "tool_git_worktree_unload"
|
|
1180
|
+
? emitGitWorktreeUnloadNode(workspaceRoot, flowName, uuid, instanceId, execId)
|
|
1181
|
+
: definitionId === "tool_gitlab_create_mr"
|
|
1182
|
+
? await emitGitLabCreateMrNode(workspaceRoot, flowName, uuid, instanceId, execId)
|
|
1183
|
+
: definitionId === "control_cd_workspace"
|
|
1184
|
+
? emitCdWorkspaceNode(workspaceRoot, flowName, uuid, instanceId, execId)
|
|
1185
|
+
: definitionId === "control_user_workspace"
|
|
1186
|
+
? emitUserWorkspaceNode(workspaceRoot, flowName, uuid, instanceId, execId)
|
|
1187
|
+
: definitionId === "control_load_skills"
|
|
1188
|
+
? emitLoadSkillsNode(workspaceRoot, flowName, uuid, instanceId, execId)
|
|
1189
|
+
: emitToolPrintNode(workspaceRoot, flowName, uuid, instanceId, execId);
|
|
1016
1190
|
writeCacheJsonForNode(workspaceRoot, flowName, uuid, instanceId, execId);
|
|
1017
1191
|
logToRunTag(workspaceRoot, flowName, uuid, "pre-process", { event: "runtime-context-node", instanceId, definitionId });
|
|
1018
1192
|
console.log(JSON.stringify({
|
|
@@ -1103,4 +1277,7 @@ function main() {
|
|
|
1103
1277
|
console.log(JSON.stringify(output));
|
|
1104
1278
|
}
|
|
1105
1279
|
|
|
1106
|
-
main()
|
|
1280
|
+
main().catch((e) => {
|
|
1281
|
+
console.error(JSON.stringify({ ok: false, error: e.message || String(e) }));
|
|
1282
|
+
process.exit(1);
|
|
1283
|
+
});
|
|
@@ -493,26 +493,16 @@ function checkFlowCore(nodes, edges, flowDir, nodeIdToSlots, getNodeBody, instan
|
|
|
493
493
|
}
|
|
494
494
|
}
|
|
495
495
|
|
|
496
|
-
// provide_str / provide_file output 类型校验
|
|
497
|
-
if (defId === "provide_str") {
|
|
496
|
+
// provide_str / provide_file / provide_bool output 类型校验
|
|
497
|
+
if (defId === "provide_str" || defId === "provide_file" || defId === "provide_bool") {
|
|
498
498
|
const out = Array.isArray(inst.output) ? inst.output : [];
|
|
499
|
+
const expectedType = defId === "provide_file" ? "file" : defId === "provide_bool" ? "bool" : "text";
|
|
499
500
|
if (out.length !== 1) {
|
|
500
|
-
errors.push(`节点 "${n.id}"
|
|
501
|
+
errors.push(`节点 "${n.id}"(${defId})output 必须仅有 1 个槽位(value:${expectedType}),当前 ${out.length} 个`);
|
|
501
502
|
} else {
|
|
502
503
|
const t0 = normType(out[0] && out[0].type);
|
|
503
|
-
if (t0 !==
|
|
504
|
-
errors.push(`节点 "${n.id}"
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
if (defId === "provide_file") {
|
|
509
|
-
const out = Array.isArray(inst.output) ? inst.output : [];
|
|
510
|
-
if (out.length !== 1) {
|
|
511
|
-
errors.push(`节点 "${n.id}"(provide_file)output 必须仅有 1 个槽位(value:file),当前 ${out.length} 个`);
|
|
512
|
-
} else {
|
|
513
|
-
const t0 = normType(out[0] && out[0].type);
|
|
514
|
-
if (t0 !== "file") {
|
|
515
|
-
errors.push(`节点 "${n.id}"(provide_file)output[0].type 必须为 \`file\`,当前为 \`${t0 || "(空)"}\``);
|
|
504
|
+
if (t0 !== expectedType) {
|
|
505
|
+
errors.push(`节点 "${n.id}"(${defId})output[0].type 必须为 \`${expectedType}\`,当前为 \`${t0 || "(空)"}\``);
|
|
516
506
|
}
|
|
517
507
|
}
|
|
518
508
|
}
|
|
@@ -675,7 +665,7 @@ function computeValidation(loaded, workspaceRoot) {
|
|
|
675
665
|
if (!isNodeEdge) continue;
|
|
676
666
|
if (srcDef.startsWith("provide_") || tgtDef.startsWith("provide_")) {
|
|
677
667
|
validationErrors.push(
|
|
678
|
-
`provide_* 节点不得出现在控制链上(node→node 边):${e.source} ${sh} -> ${e.target} ${th};provide 仅作数据源,请改连下游 text/file 数据槽`
|
|
668
|
+
`provide_* 节点不得出现在控制链上(node→node 边):${e.source} ${sh} -> ${e.target} ${th};provide 仅作数据源,请改连下游 text/file/bool 数据槽`
|
|
679
669
|
);
|
|
680
670
|
}
|
|
681
671
|
}
|
|
@@ -9,6 +9,8 @@ input:
|
|
|
9
9
|
- type: text
|
|
10
10
|
name: value
|
|
11
11
|
default: ""
|
|
12
|
+
required: true
|
|
13
|
+
showOnNode: true
|
|
12
14
|
output:
|
|
13
15
|
- type: node
|
|
14
16
|
name: next
|
|
@@ -16,5 +18,7 @@ output:
|
|
|
16
18
|
- type: bool
|
|
17
19
|
name: prediction
|
|
18
20
|
default: ""
|
|
21
|
+
required: true
|
|
22
|
+
showOnNode: true
|
|
19
23
|
---
|
|
20
24
|
${USER_PROMPT}
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
---
|
|
2
|
-
#
|
|
3
|
-
description: Check whether the current run
|
|
4
|
-
displayName:
|
|
2
|
+
# 内置节点:取消判断
|
|
3
|
+
description: Check whether the current wait/run has been cancelled. Use cancelled output with control_if.
|
|
4
|
+
displayName: Cancel Check
|
|
5
5
|
input:
|
|
6
6
|
- type: node
|
|
7
7
|
name: prev
|
|
8
8
|
default: ""
|
|
9
9
|
- type: text
|
|
10
|
-
name:
|
|
10
|
+
name: waitId
|
|
11
11
|
default: ""
|
|
12
|
+
required: true
|
|
13
|
+
showOnNode: true
|
|
12
14
|
output:
|
|
13
15
|
- type: node
|
|
14
16
|
name: next
|
|
@@ -16,5 +18,7 @@ output:
|
|
|
16
18
|
- type: bool
|
|
17
19
|
name: cancelled
|
|
18
20
|
default: ""
|
|
21
|
+
required: true
|
|
22
|
+
showOnNode: true
|
|
19
23
|
---
|
|
20
24
|
${USER_PROMPT}
|
|
@@ -9,12 +9,21 @@ input:
|
|
|
9
9
|
- type: text
|
|
10
10
|
name: duration
|
|
11
11
|
default: "10m"
|
|
12
|
+
required: true
|
|
13
|
+
showOnNode: true
|
|
12
14
|
output:
|
|
13
15
|
- type: node
|
|
14
16
|
name: next
|
|
15
17
|
default: ""
|
|
18
|
+
- type: text
|
|
19
|
+
name: waitId
|
|
20
|
+
default: ""
|
|
21
|
+
required: true
|
|
22
|
+
showOnNode: true
|
|
16
23
|
- type: text
|
|
17
24
|
name: wakeAt
|
|
18
25
|
default: ""
|
|
26
|
+
required: true
|
|
27
|
+
showOnNode: true
|
|
19
28
|
---
|
|
20
29
|
${USER_PROMPT}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
#
|
|
2
|
+
# 内置节点:代码转布尔(本地脚本执行,★ 可扩展输入)
|
|
3
3
|
description: "Script-based boolean conversion: executes script to produce true/false prediction. Like tool_nodejs but enforces bool output. Must have script field."
|
|
4
|
-
displayName: ToBool
|
|
4
|
+
displayName: Code ToBool
|
|
5
5
|
input:
|
|
6
6
|
- type: node
|
|
7
7
|
name: prev
|
|
@@ -9,6 +9,8 @@ input:
|
|
|
9
9
|
- type: text
|
|
10
10
|
name: value
|
|
11
11
|
default: ""
|
|
12
|
+
required: true
|
|
13
|
+
showOnNode: true
|
|
12
14
|
output:
|
|
13
15
|
- type: node
|
|
14
16
|
name: next
|
|
@@ -16,6 +18,8 @@ output:
|
|
|
16
18
|
- type: bool
|
|
17
19
|
name: prediction
|
|
18
20
|
default: ""
|
|
21
|
+
required: true
|
|
22
|
+
showOnNode: true
|
|
19
23
|
extensible: true
|
|
20
24
|
---
|
|
21
25
|
${USER_PROMPT}
|
|
@@ -9,6 +9,8 @@ input:
|
|
|
9
9
|
- type: text
|
|
10
10
|
name: until
|
|
11
11
|
default: ""
|
|
12
|
+
required: true
|
|
13
|
+
showOnNode: true
|
|
12
14
|
- type: text
|
|
13
15
|
name: timezone
|
|
14
16
|
default: "Asia/Shanghai"
|
|
@@ -16,8 +18,15 @@ output:
|
|
|
16
18
|
- type: node
|
|
17
19
|
name: next
|
|
18
20
|
default: ""
|
|
21
|
+
- type: text
|
|
22
|
+
name: waitId
|
|
23
|
+
default: ""
|
|
24
|
+
required: true
|
|
25
|
+
showOnNode: true
|
|
19
26
|
- type: text
|
|
20
27
|
name: wakeAt
|
|
21
28
|
default: ""
|
|
29
|
+
required: true
|
|
30
|
+
showOnNode: true
|
|
22
31
|
---
|
|
23
32
|
${USER_PROMPT}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
# Built-in node: HTML Display
|
|
3
|
+
description: Display HTML content in workspace canvas; passes HTML downstream as text
|
|
4
|
+
displayName: HTML Display
|
|
5
|
+
input:
|
|
6
|
+
- type: node
|
|
7
|
+
name: prev
|
|
8
|
+
default: ""
|
|
9
|
+
- type: text
|
|
10
|
+
name: content
|
|
11
|
+
default: ""
|
|
12
|
+
required: true
|
|
13
|
+
showOnNode: true
|
|
14
|
+
- type: file
|
|
15
|
+
name: filePath
|
|
16
|
+
default: ""
|
|
17
|
+
showOnNode: false
|
|
18
|
+
- type: text
|
|
19
|
+
name: workspaceContext
|
|
20
|
+
default: ""
|
|
21
|
+
showOnNode: false
|
|
22
|
+
output:
|
|
23
|
+
- type: text
|
|
24
|
+
name: content
|
|
25
|
+
default: ""
|
|
26
|
+
showOnNode: false
|
|
27
|
+
- type: node
|
|
28
|
+
name: next
|
|
29
|
+
default: ""
|
|
30
|
+
---
|
|
31
|
+
${content}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
# Built-in node: Image Display
|
|
3
|
+
description: Display an image URL, data URL, or image path in workspace canvas; passes source downstream as text
|
|
4
|
+
displayName: Image Display
|
|
5
|
+
input:
|
|
6
|
+
- type: node
|
|
7
|
+
name: prev
|
|
8
|
+
default: ""
|
|
9
|
+
- type: text
|
|
10
|
+
name: src
|
|
11
|
+
default: ""
|
|
12
|
+
required: true
|
|
13
|
+
showOnNode: true
|
|
14
|
+
- type: file
|
|
15
|
+
name: filePath
|
|
16
|
+
default: ""
|
|
17
|
+
showOnNode: false
|
|
18
|
+
- type: text
|
|
19
|
+
name: alt
|
|
20
|
+
default: ""
|
|
21
|
+
showOnNode: false
|
|
22
|
+
- type: text
|
|
23
|
+
name: workspaceContext
|
|
24
|
+
default: ""
|
|
25
|
+
showOnNode: false
|
|
26
|
+
output:
|
|
27
|
+
- type: text
|
|
28
|
+
name: src
|
|
29
|
+
default: ""
|
|
30
|
+
showOnNode: false
|
|
31
|
+
- type: node
|
|
32
|
+
name: next
|
|
33
|
+
default: ""
|
|
34
|
+
---
|
|
35
|
+
${src}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
# 内置节点:直接提供布尔值
|
|
3
|
+
description: Provide a boolean value directly, value will be passed to downstream as true or false
|
|
4
|
+
displayName: Boolean
|
|
5
|
+
input: []
|
|
6
|
+
output:
|
|
7
|
+
- type: bool
|
|
8
|
+
name: value
|
|
9
|
+
default: "false"
|
|
10
|
+
required: true
|
|
11
|
+
showOnNode: true
|
|
12
|
+
---
|
|
13
|
+
${USER_PROMPT}
|