@fieldwangai/agentflow 0.1.160 → 0.1.162
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/auth.mjs +231 -14
- package/bin/lib/catalog-flows.mjs +2 -0
- package/bin/lib/cli-auth-page.mjs +103 -0
- package/bin/lib/skill-runtime.mjs +6 -0
- package/bin/lib/table.mjs +2 -3
- package/bin/lib/ui-server.mjs +220 -17
- package/bin/lib/workspace-draft.mjs +74 -0
- package/bin/lib/workspace-routes.mjs +297 -4
- package/bin/lib/workspace-server.mjs +6 -0
- package/builtin/nodes/control_while.md +5 -5
- package/builtin/pipelines/new/workspace.flow.js +1 -1
- package/builtin/web-ui/dist/assets/{WorkflowAssistantThread-Bfo9Ythw.js → WorkflowAssistantThread-CTrXOZ00.js} +1 -1
- package/builtin/web-ui/dist/assets/{index-e1omCEau.css → index-5uJFccdX.css} +1 -1
- package/builtin/web-ui/dist/assets/{index-XbeI5foV.js → index-BeUfNQRL.js} +4 -3
- package/builtin/web-ui/dist/index.html +2 -2
- package/package.json +5 -1
- package/skills/agentflow-author-flow/SKILL.md +81 -8
- package/skills/agentflow-author-flow/agents/openai.yaml +3 -3
- package/skills/agentflow-cli/SKILL.md +130 -25
- package/skills/agentflow-cli/agents/openai.yaml +2 -2
- package/skills/agentflow-cli/runtime/bin/lib/skill-runtime.mjs +14765 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/agent_subAgent.md +34 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/control_agent_toBool.md +26 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/control_anyOne.md +19 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/control_cancelled.md +26 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/control_cd_workspace.md +50 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/control_delay.md +31 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/control_end.md +13 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/control_if.md +21 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/control_interval_loop.md +55 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/control_load_mcp.md +24 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/control_load_skills.md +35 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/control_parse_json.md +25 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/control_start.md +13 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/control_subflow_call.md +27 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/control_toBool.md +27 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/control_user_workspace.md +24 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/control_wait_until.md +34 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/control_while.md +124 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/display_ascii.md +24 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/display_chart.md +32 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/display_code.md +39 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/display_html.md +32 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/display_image.md +36 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/display_markdown.md +24 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/display_mermaid.md +24 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/display_react_app.md +53 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/display_table.md +32 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/provide_bool.md +14 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/provide_file.md +14 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/provide_json.md +14 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/provide_password.md +14 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/provide_str.md +14 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/tool_display_share_link.md +47 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/tool_get_env.md +20 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/tool_git_checkout.md +70 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/tool_git_worktree_load.md +73 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/tool_git_worktree_unload.md +52 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/tool_gitlab_create_mr.md +114 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/tool_jenkins_build.md +67 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/tool_load_key.md +26 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/tool_nodejs.md +52 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/tool_print.md +21 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/tool_save_key.md +26 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/tool_set_run_env.md +38 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/tool_user_ask.md +27 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/tool_user_check.md +26 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/tool_wecom_send_app_markdown.md +57 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/tool_wecom_send_group_markdown.md +45 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/workspace_one_click_task.md +44 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/workspace_run.md +16 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/workspace_scheduled_run.md +16 -0
- package/skills/agentflow-cli/runtime/builtin/nodes/workspace_subflow_input.md +14 -0
- package/skills/agentflow-cli/runtime/package.json +6 -0
- package/skills/agentflow-cli/scripts/agentflow-auth-store.mjs +113 -0
- package/skills/agentflow-cli/scripts/agentflow-cli.mjs +393 -28
- package/skills/agentflow-cli/scripts/agentflow-runtime.mjs +11 -4
- package/skills/agentflow-flow-add-instances/SKILL.md +2 -2
- package/skills/agentflow-flow-dsl/SKILL.md +25 -6
- package/skills/agentflow-flow-dsl/agents/openai.yaml +2 -2
- package/skills/agentflow-flow-edit-node-fields/SKILL.md +3 -3
- package/skills/agentflow-flow-recipes/SKILL.md +1 -1
- package/skills/agentflow-flow-recipes/references/recipes.md +1 -2
- package/skills/agentflow-flow-sync-ui/SKILL.md +1 -1
- package/skills/agentflow-node-dsl/SKILL.md +5 -13
- package/skills/agentflow-workspace-markdown/SKILL.md +1 -1
- package/skills/agentflow-workspace-graph/SKILL.md +0 -85
|
@@ -27,16 +27,19 @@ import { NODE_PACKAGE_ENTRY, nodePackageExportsRun, readNodePackageManifest } fr
|
|
|
27
27
|
import { inspectNodePackageDirectory } from "./node-package-archive.mjs";
|
|
28
28
|
import { json, readBody } from "./http-util.mjs";
|
|
29
29
|
import { log } from "./log.mjs";
|
|
30
|
-
import { PACKAGE_ROOT, getAgentflowUserDataRoot } from "./paths.mjs";
|
|
30
|
+
import { PACKAGE_ROOT, PIPELINES_DIR, getAgentflowUserDataRoot, getUserPipelinesRoot } from "./paths.mjs";
|
|
31
|
+
import { validateUserPipelineId } from "./flow-write.mjs";
|
|
31
32
|
import { runLedgerId } from "./run-ledger.mjs";
|
|
32
33
|
import { getTeamById, getTeamForUser } from "./teams.mjs";
|
|
33
34
|
import { readMergedEnvObject, runtimeEnvForUser } from "./user-env.mjs";
|
|
34
|
-
import { acceptWorkspaceCollaborationInvite, addWorkspaceCollaborationMember, ensureWorkspaceCollaboration, getWorkspaceCollaborationForProject, listWorkspaceCollaborationsForUser, removeWorkspaceCollaborationMember, removeWorkspaceCollaborationTeamShare, setWorkspaceCollaborationTeamShare, workspaceCollaborationAccess } from "./workspace-collaboration.mjs";
|
|
35
|
+
import { acceptWorkspaceCollaborationInvite, addWorkspaceCollaborationMember, deleteWorkspaceCollaborationForFlow, ensureWorkspaceCollaboration, getWorkspaceCollaborationForProject, listWorkspaceCollaborationsForUser, removeWorkspaceCollaborationMember, removeWorkspaceCollaborationTeamShare, setWorkspaceCollaborationTeamShare, workspaceCollaborationAccess } from "./workspace-collaboration.mjs";
|
|
35
36
|
import { WorkspaceFlowParseError } from "./workspace-flow-store.mjs";
|
|
36
37
|
import { mergeWorkspaceGraphs, workspaceDesignRevision, workspaceRuntimeRevision } from "./workspace-graph-merge.mjs";
|
|
37
38
|
import { DEFAULT_WORKSPACE_PREVIEW_TTL_MS, createWorkspacePreviewId, normalizeWorkspacePreviewTtlMs, readWorkspacePreviewMetadata, workspaceSharedPreviewFlowDir, writeWorkspacePreviewMetadata } from "./workspace-preview.mjs";
|
|
39
|
+
import { DEFAULT_WORKSPACE_DRAFT_TTL_MS, createWorkspaceDraftId, normalizeWorkspaceDraftTtlMs, readWorkspaceDraftMetadata, workspaceDraftFlowDir, writeWorkspaceDraftMetadata } from "./workspace-draft.mjs";
|
|
38
40
|
import { appendWorkspaceRunLogEvent, createWorkspaceRunLogSession, finishWorkspaceRunLogSession, listWorkspaceRunLogs, readWorkspaceRunLogEvents } from "./workspace-run-logs.mjs";
|
|
39
|
-
import { activeWorkspaceRuns, appendWorkspaceRunFinished, appendWorkspaceRunStarted, hydrateWorkspaceGraphForRuntime, isReadonlyBuiltinFlowSource, isTransientAgentNetworkError, isValidFlowSourceRead, isWorkspaceRunAbortError, listWorkspaceScheduleStatusesForFlow, mergeWorkspacePersistentNodeRefs, mergeWorkspaceRunGraph, normalizeWorkspaceEntry, readWorkspaceConversations, readWorkspaceFiles, readWorkspaceGraph, removeWorkspaceDeferredRun, resolveWorkspaceFilePath, resolveWorkspaceScopeRoot, runWorkspaceGraph, sleepMs, syncWorkspaceSchedulesForGraph, upsertWorkspaceDeferredRun, workspaceActiveRunsForScope, workspaceCollaborationEventKey, workspaceCollaborationSequences, workspaceCollaborationSubscribers, workspaceCollaborationSummaryWithUsers, workspaceDeferredRunsForScope, workspaceDesignPath, workspaceDownloadContentDisposition, workspaceFindActiveRunConflict, workspaceGraphAsSource, workspaceOptimizeRunImplementations, workspaceRepoUrlWithCredential, workspaceRunControl, workspaceRunEntryKey, workspaceRunKey, workspaceRunPlan, workspaceRunPlanNodeIds, workspaceRunTouchedNodeIds, workspaceRuntimeNodeLabel, workspaceScopedUserContext, workspaceSearchGuardrailsBlock, workspaceUnwrapOutputEnvelopeForDisplay, workspacesPath, writeWorkspaceConversations, writeWorkspaceGraph } from "./workspace-server.mjs";
|
|
41
|
+
import { activeWorkspaceRuns, appendWorkspaceRunFinished, appendWorkspaceRunStarted, hydrateWorkspaceGraphForRuntime, isReadonlyBuiltinFlowSource, isTransientAgentNetworkError, isValidFlowSourceRead, isWorkspaceRunAbortError, listWorkspaceScheduleStatusesForFlow, mergeWorkspacePersistentNodeRefs, mergeWorkspaceRunGraph, normalizeWorkspaceEntry, normalizeWorkspaceScheduledRunConfig, readWorkspaceConversations, readWorkspaceFiles, readWorkspaceGraph, removeWorkspaceDeferredRun, resolveWorkspaceFilePath, resolveWorkspaceScopeRoot, runWorkspaceGraph, sleepMs, syncWorkspaceSchedulesForGraph, upsertWorkspaceDeferredRun, workspaceActiveRunsForScope, workspaceCollaborationEventKey, workspaceCollaborationSequences, workspaceCollaborationSubscribers, workspaceCollaborationSummaryWithUsers, workspaceDeferredRunsForScope, workspaceDesignPath, workspaceDownloadContentDisposition, workspaceFindActiveRunConflict, workspaceGraphAsSource, workspaceOptimizeRunImplementations, workspaceRepoUrlWithCredential, workspaceRunControl, workspaceRunEntryKey, workspaceRunKey, workspaceRunPlan, workspaceRunPlanNodeIds, workspaceRunTouchedNodeIds, workspaceRuntimeNodeLabel, workspaceScheduleNextRunAt, workspaceScopedUserContext, workspaceSearchGuardrailsBlock, workspaceUnwrapOutputEnvelopeForDisplay, workspacesPath, writeWorkspaceConversations, writeWorkspaceGraph } from "./workspace-server.mjs";
|
|
42
|
+
import { WORKSPACE_STATE_FILENAME } from "./workspace-state.mjs";
|
|
40
43
|
import { getWorkspaceTree } from "./workspace-tree.mjs";
|
|
41
44
|
import busboy from "busboy";
|
|
42
45
|
import crypto from "crypto";
|
|
@@ -192,6 +195,29 @@ function commitWorkspaceGraph(workspaceRoot, scoped, graph, userCtx) {
|
|
|
192
195
|
};
|
|
193
196
|
}
|
|
194
197
|
|
|
198
|
+
export function workspaceGraphWithScheduleMode(graph, mode = "disabled") {
|
|
199
|
+
const normalizedMode = String(mode || "disabled").trim().toLowerCase();
|
|
200
|
+
if (!["enabled", "disabled", "preserve"].includes(normalizedMode)) {
|
|
201
|
+
throw new Error("scheduleMode must be enabled, disabled, or preserve");
|
|
202
|
+
}
|
|
203
|
+
if (normalizedMode === "preserve") return graph;
|
|
204
|
+
const enabled = normalizedMode === "enabled";
|
|
205
|
+
const instances = { ...(graph?.instances || {}) };
|
|
206
|
+
let scheduleCount = 0;
|
|
207
|
+
for (const [nodeId, instance] of Object.entries(instances)) {
|
|
208
|
+
if (String(instance?.definitionId || "") !== "workspace_scheduled_run") continue;
|
|
209
|
+
scheduleCount += 1;
|
|
210
|
+
const config = normalizeWorkspaceScheduledRunConfig(instance.body || "");
|
|
211
|
+
const nextConfig = { ...config, enabled };
|
|
212
|
+
if (enabled) workspaceScheduleNextRunAt(nextConfig, new Date());
|
|
213
|
+
instances[nodeId] = { ...instance, body: JSON.stringify(nextConfig) };
|
|
214
|
+
}
|
|
215
|
+
if (enabled && scheduleCount === 0) {
|
|
216
|
+
throw new Error("Cannot enable scheduling: the draft has no Scheduled Run node");
|
|
217
|
+
}
|
|
218
|
+
return { ...graph, instances };
|
|
219
|
+
}
|
|
220
|
+
|
|
195
221
|
function missingWorkspaceGraphNodePackages(workspaceRoot, scoped, graph, userCtx) {
|
|
196
222
|
const missing = new Set();
|
|
197
223
|
for (const instance of Object.values(graph?.instances || {})) {
|
|
@@ -1309,6 +1335,185 @@ async function workspaceRoutes(req, res, ctx) {
|
|
|
1309
1335
|
return;
|
|
1310
1336
|
}
|
|
1311
1337
|
|
|
1338
|
+
if (req.method === "POST" && url.pathname === "/api/workspace/draft") {
|
|
1339
|
+
if (!authUser?.userId) {
|
|
1340
|
+
json(res, 401, { error: "Authentication required" });
|
|
1341
|
+
return;
|
|
1342
|
+
}
|
|
1343
|
+
let payload;
|
|
1344
|
+
try {
|
|
1345
|
+
payload = JSON.parse(await readBody(req, 4 * 1024 * 1024));
|
|
1346
|
+
} catch {
|
|
1347
|
+
json(res, 400, { error: "Invalid JSON" });
|
|
1348
|
+
return;
|
|
1349
|
+
}
|
|
1350
|
+
const graph = payload?.graph;
|
|
1351
|
+
if (!graph || typeof graph !== "object" || Array.isArray(graph)) {
|
|
1352
|
+
json(res, 400, { error: "graph must be an object" });
|
|
1353
|
+
return;
|
|
1354
|
+
}
|
|
1355
|
+
const rawRequestedId = String(payload.draftId || "").trim();
|
|
1356
|
+
const flowId = rawRequestedId || createWorkspaceDraftId();
|
|
1357
|
+
const flowDir = workspaceDraftFlowDir(flowId, authUser.userId);
|
|
1358
|
+
if (!flowDir) {
|
|
1359
|
+
json(res, 400, { error: "Invalid draftId" });
|
|
1360
|
+
return;
|
|
1361
|
+
}
|
|
1362
|
+
const existing = readWorkspaceDraftMetadata(flowDir);
|
|
1363
|
+
if (existing && existing.ownerId !== authUser.userId) {
|
|
1364
|
+
json(res, 403, { error: "Draft ownership denied" });
|
|
1365
|
+
return;
|
|
1366
|
+
}
|
|
1367
|
+
if (rawRequestedId && !existing && fs.existsSync(flowDir)) {
|
|
1368
|
+
json(res, 409, { error: "Draft project already exists but is not a draft" });
|
|
1369
|
+
return;
|
|
1370
|
+
}
|
|
1371
|
+
if (existing) {
|
|
1372
|
+
const currentGraph = readWorkspaceGraph(flowDir, root).graph;
|
|
1373
|
+
const currentRevision = workspaceDesignRevision(currentGraph);
|
|
1374
|
+
const baseRevision = String(payload.baseRevision || "").trim();
|
|
1375
|
+
if (!baseRevision) {
|
|
1376
|
+
json(res, 428, {
|
|
1377
|
+
error: "Draft update requires baseRevision",
|
|
1378
|
+
conflict: "missing-base-revision",
|
|
1379
|
+
currentRevision,
|
|
1380
|
+
});
|
|
1381
|
+
return;
|
|
1382
|
+
}
|
|
1383
|
+
if (baseRevision !== currentRevision) {
|
|
1384
|
+
json(res, 409, {
|
|
1385
|
+
error: "Draft 已被更新,请先拉取最新版本后再修改",
|
|
1386
|
+
conflict: "revision-mismatch",
|
|
1387
|
+
expectedRevision: baseRevision,
|
|
1388
|
+
currentRevision,
|
|
1389
|
+
});
|
|
1390
|
+
return;
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
const now = Date.now();
|
|
1394
|
+
const ttlInput = payload.ttlMs != null
|
|
1395
|
+
? Number(payload.ttlMs)
|
|
1396
|
+
: payload.ttlSeconds != null
|
|
1397
|
+
? Number(payload.ttlSeconds) * 1000
|
|
1398
|
+
: DEFAULT_WORKSPACE_DRAFT_TTL_MS;
|
|
1399
|
+
const ttlMs = normalizeWorkspaceDraftTtlMs(ttlInput);
|
|
1400
|
+
const metadata = {
|
|
1401
|
+
version: 1,
|
|
1402
|
+
flowId,
|
|
1403
|
+
ownerId: authUser.userId,
|
|
1404
|
+
title: String(payload.title || "Workspace Draft").trim().slice(0, 200),
|
|
1405
|
+
createdAt: existing?.createdAt || new Date(now).toISOString(),
|
|
1406
|
+
updatedAt: new Date(now).toISOString(),
|
|
1407
|
+
expiresAt: new Date(now + ttlMs).toISOString(),
|
|
1408
|
+
};
|
|
1409
|
+
const created = !fs.existsSync(flowDir);
|
|
1410
|
+
try {
|
|
1411
|
+
fs.mkdirSync(flowDir, { recursive: true });
|
|
1412
|
+
if (payload.resetRuntime !== false) {
|
|
1413
|
+
const statePath = path.join(flowDir, WORKSPACE_STATE_FILENAME);
|
|
1414
|
+
if (fs.existsSync(statePath)) fs.rmSync(statePath);
|
|
1415
|
+
}
|
|
1416
|
+
const written = writeWorkspaceGraph(flowDir, graph, root);
|
|
1417
|
+
writeWorkspaceDraftMetadata(flowDir, metadata);
|
|
1418
|
+
const persisted = readWorkspaceGraph(flowDir, root).graph;
|
|
1419
|
+
const baseUrl = `${url.protocol}//${url.host}`;
|
|
1420
|
+
json(res, 200, {
|
|
1421
|
+
ok: true,
|
|
1422
|
+
flowId,
|
|
1423
|
+
draftId: flowId,
|
|
1424
|
+
flowSource: "user",
|
|
1425
|
+
draft: true,
|
|
1426
|
+
writable: true,
|
|
1427
|
+
runnable: true,
|
|
1428
|
+
schedulesSuppressed: true,
|
|
1429
|
+
revision: workspaceDesignRevision(persisted),
|
|
1430
|
+
format: written.format,
|
|
1431
|
+
expiresAt: metadata.expiresAt,
|
|
1432
|
+
url: `${baseUrl}/workspace?flowId=${encodeURIComponent(flowId)}&flowSource=user`,
|
|
1433
|
+
});
|
|
1434
|
+
} catch (e) {
|
|
1435
|
+
if (created) {
|
|
1436
|
+
try { fs.rmSync(flowDir, { recursive: true, force: true }); } catch (_) {}
|
|
1437
|
+
}
|
|
1438
|
+
json(res, 500, { error: (e && e.message) || String(e) });
|
|
1439
|
+
}
|
|
1440
|
+
return;
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
if (req.method === "POST" && url.pathname === "/api/workspace/draft/publish") {
|
|
1444
|
+
if (!authUser?.userId) {
|
|
1445
|
+
json(res, 401, { error: "Authentication required" });
|
|
1446
|
+
return;
|
|
1447
|
+
}
|
|
1448
|
+
let payload;
|
|
1449
|
+
try {
|
|
1450
|
+
payload = JSON.parse(await readBody(req));
|
|
1451
|
+
} catch {
|
|
1452
|
+
json(res, 400, { error: "Invalid JSON body" });
|
|
1453
|
+
return;
|
|
1454
|
+
}
|
|
1455
|
+
const draftId = String(payload.draftId || "").trim();
|
|
1456
|
+
const draftDir = workspaceDraftFlowDir(draftId, authUser.userId);
|
|
1457
|
+
const draftMetadata = draftDir ? readWorkspaceDraftMetadata(draftDir) : null;
|
|
1458
|
+
if (!draftMetadata || draftMetadata.ownerId !== authUser.userId) {
|
|
1459
|
+
json(res, 404, { error: "Workspace draft not found" });
|
|
1460
|
+
return;
|
|
1461
|
+
}
|
|
1462
|
+
const idCheck = validateUserPipelineId(String(payload.flowId || ""));
|
|
1463
|
+
if (!idCheck.ok) {
|
|
1464
|
+
json(res, 400, { error: idCheck.error });
|
|
1465
|
+
return;
|
|
1466
|
+
}
|
|
1467
|
+
const flowId = idCheck.flowId;
|
|
1468
|
+
const requestedTarget = String(payload.targetSpace || "personal").trim().toLowerCase();
|
|
1469
|
+
const flowSource = requestedTarget === "workspace" || requestedTarget === "team" ? "workspace" : "user";
|
|
1470
|
+
const targetDir = flowSource === "workspace"
|
|
1471
|
+
? path.join(path.resolve(root), PIPELINES_DIR, flowId)
|
|
1472
|
+
: path.join(getUserPipelinesRoot(authUser.userId), flowId);
|
|
1473
|
+
if (fs.existsSync(targetDir)) {
|
|
1474
|
+
json(res, 409, { error: `已存在同名流水线 ${flowId}` });
|
|
1475
|
+
return;
|
|
1476
|
+
}
|
|
1477
|
+
try {
|
|
1478
|
+
const draftGraph = readWorkspaceGraph(draftDir, root).graph;
|
|
1479
|
+
const graph = workspaceGraphWithScheduleMode(draftGraph, payload.scheduleMode || "disabled");
|
|
1480
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
1481
|
+
writeWorkspaceGraph(targetDir, graph, root);
|
|
1482
|
+
let collaborationCreated = false;
|
|
1483
|
+
if (flowSource === "workspace") {
|
|
1484
|
+
ensureWorkspaceCollaboration({ flowId, userId: authUser.userId });
|
|
1485
|
+
collaborationCreated = true;
|
|
1486
|
+
}
|
|
1487
|
+
try {
|
|
1488
|
+
const scoped = resolveWorkspaceScopeRoot(root, { flowId, flowSource }, userCtx);
|
|
1489
|
+
if (scoped.error) throw new Error(scoped.error);
|
|
1490
|
+
const persisted = readWorkspaceGraph(scoped.root, root).graph;
|
|
1491
|
+
const workspaceSchedules = syncWorkspaceSchedulesForGraph(root, scoped, persisted, authUser, userCtx);
|
|
1492
|
+
const baseUrl = `${url.protocol}//${url.host}`;
|
|
1493
|
+
json(res, 200, {
|
|
1494
|
+
ok: true,
|
|
1495
|
+
success: true,
|
|
1496
|
+
action: "created",
|
|
1497
|
+
draftId,
|
|
1498
|
+
flowId,
|
|
1499
|
+
flowSource,
|
|
1500
|
+
targetSpace: requestedTarget === "team" ? "team" : flowSource === "user" ? "personal" : "workspace",
|
|
1501
|
+
scheduleMode: String(payload.scheduleMode || "disabled").toLowerCase(),
|
|
1502
|
+
workspaceSchedules,
|
|
1503
|
+
revision: workspaceDesignRevision(persisted),
|
|
1504
|
+
url: `${baseUrl}/workspace?flowId=${encodeURIComponent(flowId)}&flowSource=${encodeURIComponent(flowSource)}`,
|
|
1505
|
+
});
|
|
1506
|
+
} catch (e) {
|
|
1507
|
+
if (collaborationCreated) deleteWorkspaceCollaborationForFlow(flowId, false);
|
|
1508
|
+
throw e;
|
|
1509
|
+
}
|
|
1510
|
+
} catch (e) {
|
|
1511
|
+
try { fs.rmSync(targetDir, { recursive: true, force: true }); } catch (_) {}
|
|
1512
|
+
json(res, 500, { error: (e && e.message) || String(e) });
|
|
1513
|
+
}
|
|
1514
|
+
return;
|
|
1515
|
+
}
|
|
1516
|
+
|
|
1312
1517
|
if (req.method === "POST" && url.pathname === "/api/workspace/preview") {
|
|
1313
1518
|
if (!authUser?.userId) {
|
|
1314
1519
|
json(res, 401, { error: "Authentication required" });
|
|
@@ -1475,6 +1680,7 @@ async function workspaceRoutes(req, res, ctx) {
|
|
|
1475
1680
|
flowId: scoped.flowId,
|
|
1476
1681
|
flowSource: scoped.flowSource,
|
|
1477
1682
|
archived: scoped.archived,
|
|
1683
|
+
draft: scoped.draft === true,
|
|
1478
1684
|
writable: !(scoped.archived || isReadonlyBuiltinFlowSource(scoped.flowSource))
|
|
1479
1685
|
&& scoped.adminReadonly !== true
|
|
1480
1686
|
&& collaborationAccess.writable !== false,
|
|
@@ -1596,7 +1802,20 @@ async function workspaceRoutes(req, res, ctx) {
|
|
|
1596
1802
|
const graph = mergeWorkspacePersistentNodeRefs(nextGraph, currentGraph);
|
|
1597
1803
|
const committed = commitWorkspaceGraph(root, scoped, graph, userCtx);
|
|
1598
1804
|
const { path: graphPath, revision, runtimeRevision } = committed;
|
|
1599
|
-
|
|
1805
|
+
let workspaceSchedules;
|
|
1806
|
+
try {
|
|
1807
|
+
workspaceSchedules = syncWorkspaceSchedulesForGraph(root, scoped, committed.graph, authUser, userCtx);
|
|
1808
|
+
} catch (scheduleError) {
|
|
1809
|
+
try {
|
|
1810
|
+
const rolledBack = commitWorkspaceGraph(root, scoped, currentStoredGraph, userCtx);
|
|
1811
|
+
syncWorkspaceSchedulesForGraph(root, scoped, rolledBack.graph, authUser, userCtx);
|
|
1812
|
+
} catch (rollbackError) {
|
|
1813
|
+
throw new Error(
|
|
1814
|
+
`Workspace schedule sync failed and graph rollback also failed: ${(scheduleError && scheduleError.message) || String(scheduleError)}; rollback: ${(rollbackError && rollbackError.message) || String(rollbackError)}`,
|
|
1815
|
+
);
|
|
1816
|
+
}
|
|
1817
|
+
throw new Error(`Workspace graph save rolled back: ${(scheduleError && scheduleError.message) || String(scheduleError)}`);
|
|
1818
|
+
}
|
|
1600
1819
|
broadcastWorkspaceCollaborationEvent(
|
|
1601
1820
|
userCtx,
|
|
1602
1821
|
scoped.flowSource,
|
|
@@ -1655,6 +1874,80 @@ async function workspaceRoutes(req, res, ctx) {
|
|
|
1655
1874
|
return;
|
|
1656
1875
|
}
|
|
1657
1876
|
|
|
1877
|
+
if (req.method === "POST" && url.pathname === "/api/workspace/schedule/config") {
|
|
1878
|
+
let payload;
|
|
1879
|
+
try {
|
|
1880
|
+
payload = JSON.parse(await readBody(req));
|
|
1881
|
+
} catch {
|
|
1882
|
+
json(res, 400, { error: "Invalid JSON body" });
|
|
1883
|
+
return;
|
|
1884
|
+
}
|
|
1885
|
+
try {
|
|
1886
|
+
const flowId = String(payload.flowId || "").trim();
|
|
1887
|
+
const flowSource = String(payload.flowSource || "user").trim() || "user";
|
|
1888
|
+
const scheduleNodeId = String(payload.scheduleNodeId || "").trim();
|
|
1889
|
+
if (!flowId || !scheduleNodeId) {
|
|
1890
|
+
json(res, 400, { error: "flowId and scheduleNodeId are required" });
|
|
1891
|
+
return;
|
|
1892
|
+
}
|
|
1893
|
+
const scoped = resolveWorkspaceScopeRoot(root, { flowId, flowSource }, userCtx);
|
|
1894
|
+
if (scoped.error) {
|
|
1895
|
+
json(res, scoped.status || 400, { error: scoped.error });
|
|
1896
|
+
return;
|
|
1897
|
+
}
|
|
1898
|
+
if (
|
|
1899
|
+
scoped.archived
|
|
1900
|
+
|| isReadonlyBuiltinFlowSource(scoped.flowSource)
|
|
1901
|
+
|| scoped.collaborationAccess?.writable === false
|
|
1902
|
+
) {
|
|
1903
|
+
json(res, 403, { error: "Workspace schedule edit permission denied" });
|
|
1904
|
+
return;
|
|
1905
|
+
}
|
|
1906
|
+
const graph = readWorkspaceGraph(scoped.root, root).graph;
|
|
1907
|
+
const instance = graph.instances?.[scheduleNodeId];
|
|
1908
|
+
if (!instance || String(instance.definitionId || "") !== "workspace_scheduled_run") {
|
|
1909
|
+
json(res, 404, { error: "Workspace schedule node not found" });
|
|
1910
|
+
return;
|
|
1911
|
+
}
|
|
1912
|
+
const current = normalizeWorkspaceScheduledRunConfig(instance.body || "");
|
|
1913
|
+
const config = {
|
|
1914
|
+
...current,
|
|
1915
|
+
...(typeof payload.enabled === "boolean" ? { enabled: payload.enabled } : {}),
|
|
1916
|
+
...(payload.cron != null ? { cron: String(payload.cron).trim() } : {}),
|
|
1917
|
+
...(payload.timezone != null ? { timezone: String(payload.timezone).trim() } : {}),
|
|
1918
|
+
...(payload.overlapPolicy != null ? { overlapPolicy: String(payload.overlapPolicy).trim() } : {}),
|
|
1919
|
+
};
|
|
1920
|
+
if (config.overlapPolicy !== "skip") {
|
|
1921
|
+
json(res, 400, { error: "Only overlapPolicy=skip is supported" });
|
|
1922
|
+
return;
|
|
1923
|
+
}
|
|
1924
|
+
if (config.enabled) workspaceScheduleNextRunAt(config, new Date());
|
|
1925
|
+
graph.instances = { ...(graph.instances || {}) };
|
|
1926
|
+
graph.instances[scheduleNodeId] = { ...instance, body: JSON.stringify(config) };
|
|
1927
|
+
const committed = commitWorkspaceGraph(root, scoped, graph, userCtx);
|
|
1928
|
+
const workspaceSchedules = syncWorkspaceSchedulesForGraph(root, scoped, committed.graph, authUser, userCtx);
|
|
1929
|
+
const schedule = workspaceSchedules.find((item) => item.scheduleNodeId === scheduleNodeId) || {
|
|
1930
|
+
flowId,
|
|
1931
|
+
flowSource,
|
|
1932
|
+
scheduleNodeId,
|
|
1933
|
+
...config,
|
|
1934
|
+
suppressed: scoped.draft === true,
|
|
1935
|
+
};
|
|
1936
|
+
json(res, 200, {
|
|
1937
|
+
ok: true,
|
|
1938
|
+
flowId,
|
|
1939
|
+
flowSource,
|
|
1940
|
+
draft: scoped.draft === true,
|
|
1941
|
+
revision: committed.revision,
|
|
1942
|
+
schedule,
|
|
1943
|
+
workspaceSchedules,
|
|
1944
|
+
});
|
|
1945
|
+
} catch (e) {
|
|
1946
|
+
json(res, 400, { error: (e && e.message) || String(e) });
|
|
1947
|
+
}
|
|
1948
|
+
return;
|
|
1949
|
+
}
|
|
1950
|
+
|
|
1658
1951
|
if (req.method === "POST" && url.pathname === "/api/workspace/run/plan") {
|
|
1659
1952
|
let payload;
|
|
1660
1953
|
try {
|
|
@@ -44,6 +44,7 @@ import { FLOW_SOURCE_FILENAME, WORKSPACE_GRAPH_FILENAME, WorkspaceFlowParseError
|
|
|
44
44
|
import { createWorkspaceRunController, terminateWorkspaceChild } from "./workspace-run-controller.mjs";
|
|
45
45
|
import { appendWorkspaceRunLogEvent, createWorkspaceRunLogSession, finishWorkspaceRunLogSession } from "./workspace-run-logs.mjs";
|
|
46
46
|
import { splitWorkspaceGraph } from "./workspace-state.mjs";
|
|
47
|
+
import { isWorkspaceDraftDir } from "./workspace-draft.mjs";
|
|
47
48
|
import { getPipelineFiles } from "./workspace-tree.mjs";
|
|
48
49
|
import { spawn } from "child_process";
|
|
49
50
|
import crypto from "crypto";
|
|
@@ -1491,6 +1492,7 @@ export function resolveWorkspaceScopeRoot(workspaceRoot, params = {}, opts = {})
|
|
|
1491
1492
|
requestedFlowSource: flowSource,
|
|
1492
1493
|
workspaceId: collaboration?.id || workspaceId,
|
|
1493
1494
|
archived,
|
|
1495
|
+
draft: isWorkspaceDraftDir(result.path),
|
|
1494
1496
|
collaboration,
|
|
1495
1497
|
collaborationAccess: workspaceCollaborationAccess(collaboration, opts.userId),
|
|
1496
1498
|
};
|
|
@@ -6726,6 +6728,10 @@ export function syncWorkspaceSchedulesForGraph(root, scoped, graph, authUser, us
|
|
|
6726
6728
|
);
|
|
6727
6729
|
if (sameSharedFlow || key.startsWith(prefix)) delete schedules[key];
|
|
6728
6730
|
}
|
|
6731
|
+
if (isWorkspaceDraftDir(scoped?.root || "")) {
|
|
6732
|
+
writeWorkspaceScheduleRegistry({ version: 1, schedules });
|
|
6733
|
+
return [];
|
|
6734
|
+
}
|
|
6729
6735
|
const instances = graph?.instances && typeof graph.instances === "object" ? graph.instances : {};
|
|
6730
6736
|
for (const [scheduleNodeId, instance] of Object.entries(instances)) {
|
|
6731
6737
|
if (String(instance?.definitionId || "") !== "workspace_scheduled_run") continue;
|
|
@@ -44,24 +44,24 @@ ui:
|
|
|
44
44
|
label: Loop execution
|
|
45
45
|
field: script
|
|
46
46
|
- type: decision
|
|
47
|
-
label: Decision
|
|
47
|
+
label: Decision status
|
|
48
48
|
output: decision
|
|
49
49
|
source: step.stdout.decision
|
|
50
50
|
options:
|
|
51
51
|
- value: continue
|
|
52
|
-
label: continue
|
|
52
|
+
label: continue
|
|
53
53
|
tone: purple
|
|
54
54
|
description: 保存 state 并立即进入下一轮
|
|
55
55
|
- value: wait
|
|
56
|
-
label: wait
|
|
56
|
+
label: wait
|
|
57
57
|
tone: amber
|
|
58
58
|
description: 保存 checkpoint,暂停当前 Run
|
|
59
59
|
- value: done
|
|
60
|
-
label: done
|
|
60
|
+
label: done
|
|
61
61
|
tone: green
|
|
62
62
|
description: 结束循环并继续下游
|
|
63
63
|
- value: fail
|
|
64
|
-
label: fail
|
|
64
|
+
label: fail
|
|
65
65
|
tone: red
|
|
66
66
|
description: 终止并标记节点失败
|
|
67
67
|
- type: metrics
|
|
@@ -10,7 +10,7 @@ const flowId = provide.str("新流程 ID", {
|
|
|
10
10
|
|
|
11
11
|
// 规划和落地都得照 DSL 语法写,技能直接注入,不再单独跑一个收集节点
|
|
12
12
|
const skills = control.loadSkills("加载 DSL 技能", {
|
|
13
|
-
skillKeys: "agentflow-flow-dsl,agentflow-node-reference
|
|
13
|
+
skillKeys: "agentflow-flow-dsl,agentflow-node-reference",
|
|
14
14
|
});
|
|
15
15
|
|
|
16
16
|
// ── ① 规划 ──────────────────────────────────────────────────────────
|