@diff-review-system/drs 4.1.0 → 5.0.0-rc.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/.pi/agents/task/okf-wiki-maintainer.md +72 -0
- package/.pi/workflows/github-pr-fix-review-issues-stacked.yaml +2 -0
- package/.pi/workflows/github-pr-review-post.yaml +29 -19
- package/.pi/workflows/github-pr-review.yaml +23 -0
- package/.pi/workflows/github-pr-update-agents-md-stacked.yaml +2 -0
- package/.pi/workflows/gitlab-mr-fix-review-issues-stacked.yaml +2 -0
- package/.pi/workflows/gitlab-mr-review.yaml +6 -0
- package/.pi/workflows/gitlab-mr-update-agents-md-stacked.yaml +2 -0
- package/.pi/workflows/release-changelog-finalize.yaml +10 -0
- package/.pi/workflows/repository-wiki-check.yaml +40 -0
- package/.pi/workflows/repository-wiki-sync.yaml +187 -0
- package/.wiki-site/.vitepress/config.mts +452 -0
- package/.wiki-site/.vitepress/theme/PageLead.vue +122 -0
- package/.wiki-site/.vitepress/theme/index.ts +12 -0
- package/.wiki-site/.vitepress/theme/style.css +270 -0
- package/README.md +38 -4
- package/dist/cli/index.js +3 -127
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +0 -3
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/run-agent.d.ts +3 -0
- package/dist/cli/run-agent.d.ts.map +1 -1
- package/dist/cli/run-agent.js +56 -5
- package/dist/cli/run-agent.js.map +1 -1
- package/dist/cli/wiki.d.ts +3 -0
- package/dist/cli/wiki.d.ts.map +1 -0
- package/dist/cli/wiki.js +174 -0
- package/dist/cli/wiki.js.map +1 -0
- package/dist/cli/workflow.d.ts +2 -0
- package/dist/cli/workflow.d.ts.map +1 -1
- package/dist/cli/workflow.js +344 -31
- package/dist/cli/workflow.js.map +1 -1
- package/dist/github/client.d.ts.map +1 -1
- package/dist/github/client.js +2 -2
- package/dist/github/client.js.map +1 -1
- package/dist/github/platform-adapter.d.ts.map +1 -1
- package/dist/github/platform-adapter.js +15 -1
- package/dist/github/platform-adapter.js.map +1 -1
- package/dist/gitlab/client.d.ts +4 -0
- package/dist/gitlab/client.d.ts.map +1 -1
- package/dist/gitlab/client.js +17 -1
- package/dist/gitlab/client.js.map +1 -1
- package/dist/gitlab/platform-adapter.d.ts.map +1 -1
- package/dist/gitlab/platform-adapter.js +17 -1
- package/dist/gitlab/platform-adapter.js.map +1 -1
- package/dist/lib/agent-permissions.d.ts +44 -0
- package/dist/lib/agent-permissions.d.ts.map +1 -0
- package/dist/lib/agent-permissions.js +358 -0
- package/dist/lib/agent-permissions.js.map +1 -0
- package/dist/lib/agent-result.d.ts +2 -0
- package/dist/lib/agent-result.d.ts.map +1 -1
- package/dist/lib/comment-poster.d.ts +1 -1
- package/dist/lib/comment-poster.d.ts.map +1 -1
- package/dist/lib/comment-poster.js +38 -16
- package/dist/lib/comment-poster.js.map +1 -1
- package/dist/lib/config.d.ts +6 -1
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +17 -0
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/error-comment-poster.d.ts +1 -1
- package/dist/lib/error-comment-poster.d.ts.map +1 -1
- package/dist/lib/error-comment-poster.js +15 -7
- package/dist/lib/error-comment-poster.js.map +1 -1
- package/dist/lib/okf-wiki.d.ts +65 -0
- package/dist/lib/okf-wiki.d.ts.map +1 -0
- package/dist/lib/okf-wiki.js +647 -0
- package/dist/lib/okf-wiki.js.map +1 -0
- package/dist/lib/platform-client.d.ts +2 -0
- package/dist/lib/platform-client.d.ts.map +1 -1
- package/dist/lib/project-setup.d.ts +0 -3
- package/dist/lib/project-setup.d.ts.map +1 -1
- package/dist/lib/project-setup.js +1 -15
- package/dist/lib/project-setup.js.map +1 -1
- package/dist/lib/review-artifact.d.ts +11 -1
- package/dist/lib/review-artifact.d.ts.map +1 -1
- package/dist/lib/review-artifact.js +202 -0
- package/dist/lib/review-artifact.js.map +1 -1
- package/dist/lib/review-orchestrator.d.ts +6 -1
- package/dist/lib/review-orchestrator.d.ts.map +1 -1
- package/dist/lib/review-orchestrator.js +3 -1
- package/dist/lib/review-orchestrator.js.map +1 -1
- package/dist/lib/wiki-delta.d.ts +71 -0
- package/dist/lib/wiki-delta.d.ts.map +1 -0
- package/dist/lib/wiki-delta.js +524 -0
- package/dist/lib/wiki-delta.js.map +1 -0
- package/dist/lib/wiki-run-summary.d.ts +45 -0
- package/dist/lib/wiki-run-summary.d.ts.map +1 -0
- package/dist/lib/wiki-run-summary.js +168 -0
- package/dist/lib/wiki-run-summary.js.map +1 -0
- package/dist/lib/wiki-search.d.ts +24 -0
- package/dist/lib/wiki-search.d.ts.map +1 -0
- package/dist/lib/wiki-search.js +200 -0
- package/dist/lib/wiki-search.js.map +1 -0
- package/dist/lib/wiki-site-graph.d.ts +33 -0
- package/dist/lib/wiki-site-graph.d.ts.map +1 -0
- package/dist/lib/wiki-site-graph.js +446 -0
- package/dist/lib/wiki-site-graph.js.map +1 -0
- package/dist/lib/wiki-site-safety.d.ts +6 -0
- package/dist/lib/wiki-site-safety.d.ts.map +1 -0
- package/dist/lib/wiki-site-safety.js +83 -0
- package/dist/lib/wiki-site-safety.js.map +1 -0
- package/dist/lib/wiki-site-smoke.d.ts +16 -0
- package/dist/lib/wiki-site-smoke.d.ts.map +1 -0
- package/dist/lib/wiki-site-smoke.js +270 -0
- package/dist/lib/wiki-site-smoke.js.map +1 -0
- package/dist/lib/wiki-site.d.ts +26 -0
- package/dist/lib/wiki-site.d.ts.map +1 -0
- package/dist/lib/wiki-site.js +221 -0
- package/dist/lib/wiki-site.js.map +1 -0
- package/dist/lib/workflow/planning.d.ts +1 -0
- package/dist/lib/workflow/planning.d.ts.map +1 -1
- package/dist/lib/workflow/planning.js +53 -2
- package/dist/lib/workflow/planning.js.map +1 -1
- package/dist/lib/workflow/types.d.ts +5 -0
- package/dist/lib/workflow/types.d.ts.map +1 -1
- package/dist/lib/workflow-artifacts.d.ts.map +1 -1
- package/dist/lib/workflow-artifacts.js +23 -1
- package/dist/lib/workflow-artifacts.js.map +1 -1
- package/dist/pi/sdk.d.ts.map +1 -1
- package/dist/pi/sdk.js +270 -17
- package/dist/pi/sdk.js.map +1 -1
- package/dist/runtime/built-in-paths.d.ts +0 -1
- package/dist/runtime/built-in-paths.d.ts.map +1 -1
- package/dist/runtime/built-in-paths.js +0 -7
- package/dist/runtime/built-in-paths.js.map +1 -1
- package/dist/runtime/client.d.ts +3 -0
- package/dist/runtime/client.d.ts.map +1 -1
- package/dist/runtime/client.js +4 -0
- package/dist/runtime/client.js.map +1 -1
- package/dist/temporal/executor.d.ts.map +1 -1
- package/dist/temporal/executor.js +9 -2
- package/dist/temporal/executor.js.map +1 -1
- package/dist/temporal/retry-policy.d.ts +1 -1
- package/dist/temporal/retry-policy.d.ts.map +1 -1
- package/dist/temporal/retry-policy.js +6 -1
- package/dist/temporal/retry-policy.js.map +1 -1
- package/dist/temporal/workflows.d.ts.map +1 -1
- package/dist/temporal/workflows.js +16 -3
- package/dist/temporal/workflows.js.map +1 -1
- package/package.json +23 -13
- package/dist/lib/skills.d.ts +0 -16
- package/dist/lib/skills.d.ts.map +0 -1
- package/dist/lib/skills.js +0 -140
- package/dist/lib/skills.js.map +0 -1
package/dist/cli/workflow.js
CHANGED
|
@@ -16,17 +16,21 @@ import { resolveCursorFixLinkOptions } from '../lib/cursor-fix-link.js';
|
|
|
16
16
|
import { extractHtmlDocument, parseArtifactOutputPointer, readArtifactOutputPointer, validateHtmlArtifact, } from '../lib/html-artifact.js';
|
|
17
17
|
import { getCanonicalDiffCommand, resolveBaseBranch } from '../lib/repository-validator.js';
|
|
18
18
|
import { formatCodeQualityReport, generateCodeQualityReport } from '../lib/code-quality-report.js';
|
|
19
|
+
import { formatOkfValidationErrors, synchronizeOkfIndexes, validateOkfBundle, } from '../lib/okf-wiki.js';
|
|
20
|
+
import { checkWikiClean, planWikiUpdate, recordWikiState, } from '../lib/wiki-delta.js';
|
|
21
|
+
import { createWikiRunSummary, formatWikiRunSummaryHuman, formatWikiRunSummaryMarkdown, getWikiRunSummary, } from '../lib/wiki-run-summary.js';
|
|
19
22
|
import { createGitHubClient } from '../github/client.js';
|
|
20
23
|
import { GitHubPlatformAdapter } from '../github/platform-adapter.js';
|
|
21
24
|
import { createGitLabClient } from '../gitlab/client.js';
|
|
22
25
|
import { GitLabPlatformAdapter } from '../gitlab/platform-adapter.js';
|
|
23
26
|
import { loadWorkflowArtifact, saveWorkflowArtifact, updateWorkflowArtifact, workflowArtifactExists, } from '../lib/workflow-artifacts.js';
|
|
24
|
-
import { addReviewArtifactFinding, createReviewArtifactPayload, getReviewArtifactStatus, isReviewArtifactPayload, updateReviewArtifactFindings, } from '../lib/review-artifact.js';
|
|
27
|
+
import { addReviewArtifactFinding, createReviewArtifactPayload, getReviewArtifactStatus, isReviewArtifactPayload, reviewArtifactToReviewResult, updateReviewArtifactFindings, } from '../lib/review-artifact.js';
|
|
28
|
+
import { renderAgentPermissions, renderAgentValidation } from '../lib/agent-permissions.js';
|
|
25
29
|
import { runAgent } from './run-agent.js';
|
|
26
30
|
import { TraceCollector } from '../lib/trace-collector.js';
|
|
27
31
|
import { renderTraceHtml } from '../lib/trace-html.js';
|
|
28
32
|
import { buildWorkflowGraph, formatWorkflowGraphMermaid, } from '../lib/workflow/graph.js';
|
|
29
|
-
import { computeActiveWorkflowNodes, createSkippedWorkflowNodeResult, findWorkflowSegmentIndex, getNodeKind, getNodeNeeds, getWorkflowExecutionOrder, getWorkflowExecutionWaves, getWorkflowNodeSkipReason, getWorkflowNodes, hasWorkflowControlNodes, normalizeWorkflowBooleanLike, renderTemplate, runControlWorkflowNode, splitWorkflowSegments, } from '../lib/workflow/planning.js';
|
|
33
|
+
import { computeActiveWorkflowNodes, createSkippedWorkflowNodeResult, findWorkflowSegmentIndex, getNodeKind, getNodeNeeds, getWorkflowExecutionOrder, getWorkflowExecutionWaves, getWorkflowNodeSkipReason, getWorkflowNodes, hasWorkflowControlNodes, isPotentialWorkspaceMutation, normalizeWorkflowBooleanLike, renderTemplate, runControlWorkflowNode, splitWorkflowSegments, } from '../lib/workflow/planning.js';
|
|
30
34
|
import { getWorkflowInputConfigType } from '../lib/workflow/input.js';
|
|
31
35
|
export { compileWorkflowPlan, } from '../lib/workflow/compiled-plan.js';
|
|
32
36
|
function createWorkflowLock() {
|
|
@@ -186,7 +190,7 @@ function hasConfiguredAgentPrompt(config, agentId) {
|
|
|
186
190
|
const runConfig = resolveAgentRunConfig(config, agentId);
|
|
187
191
|
return runConfig.prompt !== undefined || runConfig.promptFile !== undefined;
|
|
188
192
|
}
|
|
189
|
-
function createAgentOptions(prompt, options, workingDir) {
|
|
193
|
+
function createAgentOptions(prompt, options, workingDir, node, context) {
|
|
190
194
|
return {
|
|
191
195
|
prompt,
|
|
192
196
|
jsonOutput: false,
|
|
@@ -196,6 +200,16 @@ function createAgentOptions(prompt, options, workingDir) {
|
|
|
196
200
|
quiet: true,
|
|
197
201
|
allowImplicitStdin: false,
|
|
198
202
|
ignoreConfiguredOutput: true,
|
|
203
|
+
...(node.permissions
|
|
204
|
+
? {
|
|
205
|
+
permissions: renderAgentPermissions(node.permissions, (value) => renderTemplate(value, context)),
|
|
206
|
+
}
|
|
207
|
+
: {}),
|
|
208
|
+
...(node.validation
|
|
209
|
+
? {
|
|
210
|
+
validation: renderAgentValidation(node.validation, (value) => renderTemplate(value, context)),
|
|
211
|
+
}
|
|
212
|
+
: {}),
|
|
199
213
|
};
|
|
200
214
|
}
|
|
201
215
|
async function writeWorkflowFile(workingDir, relativeOutputPath, content) {
|
|
@@ -255,7 +269,7 @@ async function runAgentWorkflowNode(config, nodeId, node, options, workingDir, c
|
|
|
255
269
|
throw new Error(`Workflow agent node "${nodeId}" must define input or configure ` +
|
|
256
270
|
`agents.overrides.${agentId}.run.prompt/promptFile.`);
|
|
257
271
|
}
|
|
258
|
-
const agentOptions = createAgentOptions(prompt, options, workingDir);
|
|
272
|
+
const agentOptions = createAgentOptions(prompt, options, workingDir, node, context);
|
|
259
273
|
if (executionContext?.traceCollector && prompt) {
|
|
260
274
|
agentOptions.traceCollector = executionContext.traceCollector;
|
|
261
275
|
executionContext.traceCollector.setContext(nodeId, agentId, prompt);
|
|
@@ -273,6 +287,8 @@ async function runAgentWorkflowNode(config, nodeId, node, options, workingDir, c
|
|
|
273
287
|
type: 'agent',
|
|
274
288
|
agent: agentId,
|
|
275
289
|
response: result.response,
|
|
290
|
+
usage: result.usage,
|
|
291
|
+
workspaceChanges: result.workspaceChanges,
|
|
276
292
|
output,
|
|
277
293
|
writes,
|
|
278
294
|
};
|
|
@@ -291,7 +307,7 @@ async function runAgentsWorkflowNode(config, nodeId, node, options, workingDir,
|
|
|
291
307
|
`agents.overrides.${missingPromptAgent}.run.prompt/promptFile.`);
|
|
292
308
|
}
|
|
293
309
|
}
|
|
294
|
-
const responses = await Promise.all(agentIds.map((agentId) => runAgent(config, agentId, createAgentOptions(prompt, options, workingDir))));
|
|
310
|
+
const responses = await Promise.all(agentIds.map((agentId) => runAgent(config, agentId, createAgentOptions(prompt, options, workingDir, node, context))));
|
|
295
311
|
const response = responses
|
|
296
312
|
.map((result) => `## ${result.agent}\n\n${result.response.trim()}`.trim())
|
|
297
313
|
.join('\n\n');
|
|
@@ -392,6 +408,27 @@ async function runActionWorkflowNode(config, nodeId, node, options, workingDir,
|
|
|
392
408
|
if (node.action === 'code-quality-report') {
|
|
393
409
|
return runCodeQualityReportWorkflowNode(nodeId, node, workingDir, context);
|
|
394
410
|
}
|
|
411
|
+
if (node.action === 'plan-wiki-update') {
|
|
412
|
+
return runPlanWikiUpdateWorkflowNode(nodeId, node, workingDir, context);
|
|
413
|
+
}
|
|
414
|
+
if (node.action === 'sync-okf-indexes') {
|
|
415
|
+
return runSyncOkfIndexesWorkflowNode(nodeId, node, workingDir, context);
|
|
416
|
+
}
|
|
417
|
+
if (node.action === 'validate-okf-wiki') {
|
|
418
|
+
return runValidateOkfWikiWorkflowNode(nodeId, node, workingDir, context);
|
|
419
|
+
}
|
|
420
|
+
if (node.action === 'summarize-wiki-run') {
|
|
421
|
+
return runSummarizeWikiRunWorkflowNode(nodeId, node, context);
|
|
422
|
+
}
|
|
423
|
+
if (node.action === 'record-wiki-state') {
|
|
424
|
+
return runRecordWikiStateWorkflowNode(nodeId, node, workingDir, context);
|
|
425
|
+
}
|
|
426
|
+
if (node.action === 'check-wiki-state') {
|
|
427
|
+
return runCheckWikiStateWorkflowNode(nodeId, node, workingDir, context);
|
|
428
|
+
}
|
|
429
|
+
if (node.action === 'check-wiki-clean') {
|
|
430
|
+
return runCheckWikiCleanWorkflowNode(nodeId, node, workingDir, context);
|
|
431
|
+
}
|
|
395
432
|
if (node.action === 'post-comment') {
|
|
396
433
|
return runPostCommentWorkflowNode(nodeId, node, options, workingDir, context, executionContext);
|
|
397
434
|
}
|
|
@@ -545,13 +582,45 @@ async function runGitBranchWorkflowNode(nodeId, node, workingDir, context, execu
|
|
|
545
582
|
async function runGitCommitWorkflowNode(nodeId, node, workingDir, context, executionContext) {
|
|
546
583
|
const git = await requireWorkflowGitRepo(nodeId, workingDir, executionContext);
|
|
547
584
|
const message = requireStringActionOption(nodeId, node, 'message', context);
|
|
585
|
+
const useChangeRequestAuthor = getBooleanActionOption(node, 'useChangeRequestAuthor', context);
|
|
586
|
+
let commitGit = git;
|
|
587
|
+
if (useChangeRequestAuthor) {
|
|
588
|
+
const configuredSource = getStringActionOption(node, 'source', context)?.trim();
|
|
589
|
+
const sourceArtifact = configuredSource ?? 'change';
|
|
590
|
+
const source = context.artifacts[sourceArtifact];
|
|
591
|
+
const platform = isReviewSource(source) && typeof source.context.platform === 'string'
|
|
592
|
+
? source.context.platform
|
|
593
|
+
: undefined;
|
|
594
|
+
const pullRequest = isReviewSource(source) && isPullRequest(source.context.pullRequest)
|
|
595
|
+
? source.context.pullRequest
|
|
596
|
+
: undefined;
|
|
597
|
+
const name = pullRequest?.author.trim();
|
|
598
|
+
const email = pullRequest?.authorEmail?.trim();
|
|
599
|
+
if (!isWorkflowPlatform(platform) ||
|
|
600
|
+
!name ||
|
|
601
|
+
name === 'Unknown' ||
|
|
602
|
+
/[\0\r\n]/.test(name) ||
|
|
603
|
+
!email ||
|
|
604
|
+
/[\0\r\n]/.test(email) ||
|
|
605
|
+
!/^[^\s<>@]+@[^\s<>@]+$/.test(email)) {
|
|
606
|
+
throw new Error(`Workflow git-commit node "${nodeId}" with.useChangeRequestAuthor requires source artifact "${sourceArtifact}" to contain a GitHub PR or GitLab MR creator identity.`);
|
|
607
|
+
}
|
|
608
|
+
const environment = Object.fromEntries(Object.entries(process.env).filter((entry) => entry[1] !== undefined));
|
|
609
|
+
commitGit = simpleGit({ baseDir: workingDir }).env({
|
|
610
|
+
...environment,
|
|
611
|
+
GIT_AUTHOR_NAME: name,
|
|
612
|
+
GIT_AUTHOR_EMAIL: email,
|
|
613
|
+
GIT_COMMITTER_NAME: name,
|
|
614
|
+
GIT_COMMITTER_EMAIL: email,
|
|
615
|
+
});
|
|
616
|
+
}
|
|
548
617
|
const paths = hasActionOption(node, 'paths') || hasActionOption(node, 'path')
|
|
549
618
|
? getPathActionOption(nodeId, node, context, workingDir)
|
|
550
619
|
: undefined;
|
|
551
620
|
if (paths) {
|
|
552
621
|
await git.add(paths);
|
|
553
622
|
}
|
|
554
|
-
const commit = paths ? await
|
|
623
|
+
const commit = paths ? await commitGit.commit(message, paths) : await commitGit.commit(message);
|
|
555
624
|
const output = {
|
|
556
625
|
commit: commit.commit,
|
|
557
626
|
message,
|
|
@@ -1124,16 +1193,59 @@ function createPlatformChangeSource(platform, name, projectId, pullRequest, chan
|
|
|
1124
1193
|
workingDir,
|
|
1125
1194
|
};
|
|
1126
1195
|
}
|
|
1196
|
+
function getPatchChangeCounts(patch) {
|
|
1197
|
+
let additions = 0;
|
|
1198
|
+
let deletions = 0;
|
|
1199
|
+
for (const line of patch.split('\n')) {
|
|
1200
|
+
if (line.startsWith('+'))
|
|
1201
|
+
additions += 1;
|
|
1202
|
+
if (line.startsWith('-'))
|
|
1203
|
+
deletions += 1;
|
|
1204
|
+
}
|
|
1205
|
+
return { additions, deletions };
|
|
1206
|
+
}
|
|
1127
1207
|
async function loadGitHubChangeSource(nodeId, node, workingDir, context, executionContext) {
|
|
1128
1208
|
const owner = requireStringActionOption(nodeId, node, 'owner', context);
|
|
1129
1209
|
const repo = requireStringActionOption(nodeId, node, 'repo', context);
|
|
1130
1210
|
const prNumber = requireNumberActionOption(nodeId, node, 'pr', context);
|
|
1131
1211
|
const projectId = `${owner}/${repo}`;
|
|
1132
1212
|
const platformClient = getWorkflowPlatformClient(executionContext, 'github');
|
|
1133
|
-
const
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1213
|
+
const requireCompleteDiff = getBooleanActionOption(node, 'requireCompleteDiff', context);
|
|
1214
|
+
let pullRequest;
|
|
1215
|
+
let changedFiles;
|
|
1216
|
+
if (requireCompleteDiff) {
|
|
1217
|
+
const before = await platformClient.getPullRequest(projectId, prNumber);
|
|
1218
|
+
changedFiles = await platformClient.getChangedFiles(projectId, prNumber);
|
|
1219
|
+
const after = await platformClient.getPullRequest(projectId, prNumber);
|
|
1220
|
+
if (before.headSha !== after.headSha) {
|
|
1221
|
+
throw new Error(`Workflow change-source node "${nodeId}" cannot review an unstable pull request head.`);
|
|
1222
|
+
}
|
|
1223
|
+
const platformData = after.platformData;
|
|
1224
|
+
const expectedFileCount = platformData && typeof platformData === 'object' && 'changed_files' in platformData
|
|
1225
|
+
? platformData.changed_files
|
|
1226
|
+
: undefined;
|
|
1227
|
+
if (typeof expectedFileCount !== 'number' ||
|
|
1228
|
+
!Number.isInteger(expectedFileCount) ||
|
|
1229
|
+
expectedFileCount !== changedFiles.length) {
|
|
1230
|
+
throw new Error(`Workflow change-source node "${nodeId}" did not receive the complete GitHub file list.`);
|
|
1231
|
+
}
|
|
1232
|
+
const incompleteFiles = changedFiles.filter((file) => {
|
|
1233
|
+
if (!file.patch || file.patch.length === 0)
|
|
1234
|
+
return true;
|
|
1235
|
+
const counts = getPatchChangeCounts(file.patch);
|
|
1236
|
+
return counts.additions !== file.additions || counts.deletions !== file.deletions;
|
|
1237
|
+
});
|
|
1238
|
+
if (incompleteFiles.length > 0) {
|
|
1239
|
+
throw new Error(`Workflow change-source node "${nodeId}" did not receive complete patches for: ${incompleteFiles.map((file) => file.filename).join(', ')}`);
|
|
1240
|
+
}
|
|
1241
|
+
pullRequest = after;
|
|
1242
|
+
}
|
|
1243
|
+
else {
|
|
1244
|
+
[pullRequest, changedFiles] = await Promise.all([
|
|
1245
|
+
platformClient.getPullRequest(projectId, prNumber),
|
|
1246
|
+
platformClient.getChangedFiles(projectId, prNumber),
|
|
1247
|
+
]);
|
|
1248
|
+
}
|
|
1137
1249
|
return createPlatformChangeSource('github', `GitHub PR ${projectId}#${prNumber}`, projectId, pullRequest, changedFiles, workingDir);
|
|
1138
1250
|
}
|
|
1139
1251
|
async function loadGitLabChangeSource(nodeId, node, workingDir, context, executionContext) {
|
|
@@ -1556,6 +1668,154 @@ async function runCodeQualityReportWorkflowNode(nodeId, node, workingDir, contex
|
|
|
1556
1668
|
writes: reportPath,
|
|
1557
1669
|
};
|
|
1558
1670
|
}
|
|
1671
|
+
async function runSyncOkfIndexesWorkflowNode(nodeId, node, workingDir, context) {
|
|
1672
|
+
const root = getStringActionOption(node, 'root', context)?.trim() ?? 'wiki';
|
|
1673
|
+
const version = getStringActionOption(node, 'version', context)?.trim() ?? '0.1';
|
|
1674
|
+
const result = await synchronizeOkfIndexes(workingDir, root, version);
|
|
1675
|
+
return {
|
|
1676
|
+
id: nodeId,
|
|
1677
|
+
type: 'action',
|
|
1678
|
+
action: node.action,
|
|
1679
|
+
response: `synchronized ${result.indexes} OKF indexes under ${result.root} (${result.updated} updated)`,
|
|
1680
|
+
output: result,
|
|
1681
|
+
};
|
|
1682
|
+
}
|
|
1683
|
+
function getWikiInstructionsActionOptions(node, context) {
|
|
1684
|
+
const instructionsPath = getStringActionOption(node, 'instructionsPath', context)?.trim();
|
|
1685
|
+
const instructions = getStringActionOption(node, 'instructions', context);
|
|
1686
|
+
return {
|
|
1687
|
+
...(instructionsPath ? { instructionsPath } : {}),
|
|
1688
|
+
...(instructions !== undefined ? { instructions } : {}),
|
|
1689
|
+
};
|
|
1690
|
+
}
|
|
1691
|
+
async function runPlanWikiUpdateWorkflowNode(nodeId, node, workingDir, context) {
|
|
1692
|
+
const root = getStringActionOption(node, 'root', context)?.trim() ?? 'wiki';
|
|
1693
|
+
const statePath = getStringActionOption(node, 'statePath', context)?.trim() ?? '.drs/wiki-state.json';
|
|
1694
|
+
const result = await planWikiUpdate(workingDir, root, statePath, getWikiInstructionsActionOptions(node, context));
|
|
1695
|
+
return {
|
|
1696
|
+
id: nodeId,
|
|
1697
|
+
type: 'action',
|
|
1698
|
+
action: node.action,
|
|
1699
|
+
response: `${result.mode}: ${result.reason}`,
|
|
1700
|
+
output: result,
|
|
1701
|
+
};
|
|
1702
|
+
}
|
|
1703
|
+
async function runValidateOkfWikiWorkflowNode(nodeId, node, workingDir, context) {
|
|
1704
|
+
const root = getStringActionOption(node, 'root', context)?.trim() ?? 'wiki';
|
|
1705
|
+
const version = getStringActionOption(node, 'version', context)?.trim() ?? '0.1';
|
|
1706
|
+
const result = await validateOkfBundle(workingDir, root, version);
|
|
1707
|
+
if (!result.valid) {
|
|
1708
|
+
throw new Error(`Workflow validate-okf-wiki node "${nodeId}" found ${result.errors.length} error(s):\n${formatOkfValidationErrors(result)}`);
|
|
1709
|
+
}
|
|
1710
|
+
return {
|
|
1711
|
+
id: nodeId,
|
|
1712
|
+
type: 'action',
|
|
1713
|
+
action: node.action,
|
|
1714
|
+
response: `validated ${result.concepts} OKF concepts under ${result.root} with ` +
|
|
1715
|
+
`${result.warnings.length} warning(s); graph: ${result.graph.nodeCount} nodes, ` +
|
|
1716
|
+
`${result.graph.directedEdgeCount} directed edges, ` +
|
|
1717
|
+
`${result.graph.orphanConceptCount} orphans, ` +
|
|
1718
|
+
`${result.graph.weaklyConnectedConceptCount} weakly connected`,
|
|
1719
|
+
output: result,
|
|
1720
|
+
};
|
|
1721
|
+
}
|
|
1722
|
+
function runSummarizeWikiRunWorkflowNode(nodeId, node, context) {
|
|
1723
|
+
const planName = getStringActionOption(node, 'plan', context)?.trim() ?? 'wikiDelta';
|
|
1724
|
+
const validationName = getStringActionOption(node, 'validation', context)?.trim() ?? 'wikiValidation';
|
|
1725
|
+
const agentNodeId = getStringActionOption(node, 'agentNode', context)?.trim() ?? 'maintain-wiki';
|
|
1726
|
+
const plan = requireWikiUpdatePlan(context.artifacts[planName], nodeId, planName);
|
|
1727
|
+
const validation = requireWikiValidation(context.artifacts[validationName], nodeId, validationName);
|
|
1728
|
+
const agentNode = context.nodes[agentNodeId];
|
|
1729
|
+
if (plan.shouldRun &&
|
|
1730
|
+
(!agentNode || agentNode.status === 'skipped' || agentNode.type !== 'agent')) {
|
|
1731
|
+
throw new Error(`Workflow summarize-wiki-run node "${nodeId}" expected completed agent node "${agentNodeId}".`);
|
|
1732
|
+
}
|
|
1733
|
+
const startedAt = context.startedAt ? Date.parse(context.startedAt) : Date.now();
|
|
1734
|
+
const summary = createWikiRunSummary({
|
|
1735
|
+
plan,
|
|
1736
|
+
validation,
|
|
1737
|
+
modelInvoked: plan.shouldRun,
|
|
1738
|
+
usage: plan.shouldRun ? agentNode?.usage : undefined,
|
|
1739
|
+
workspaceChanges: plan.shouldRun ? agentNode?.workspaceChanges : undefined,
|
|
1740
|
+
elapsedMs: Date.now() - (Number.isFinite(startedAt) ? startedAt : Date.now()),
|
|
1741
|
+
});
|
|
1742
|
+
const response = formatWikiRunSummaryHuman(summary);
|
|
1743
|
+
return {
|
|
1744
|
+
id: nodeId,
|
|
1745
|
+
type: 'action',
|
|
1746
|
+
action: node.action,
|
|
1747
|
+
response,
|
|
1748
|
+
output: { ...validation, summary, summaryMarkdown: formatWikiRunSummaryMarkdown(summary) },
|
|
1749
|
+
};
|
|
1750
|
+
}
|
|
1751
|
+
function requireWikiUpdatePlan(value, nodeId, artifactName) {
|
|
1752
|
+
if (!isRecord(value) ||
|
|
1753
|
+
!['generate', 'reconcile', 'update', 'noop'].includes(String(value.mode)) ||
|
|
1754
|
+
typeof value.shouldRun !== 'boolean' ||
|
|
1755
|
+
typeof value.changedPathCount !== 'number') {
|
|
1756
|
+
throw new Error(`Workflow summarize-wiki-run node "${nodeId}" needs a wiki update plan artifact at "${artifactName}".`);
|
|
1757
|
+
}
|
|
1758
|
+
return value;
|
|
1759
|
+
}
|
|
1760
|
+
function requireWikiValidation(value, nodeId, artifactName) {
|
|
1761
|
+
if (!isRecord(value) ||
|
|
1762
|
+
typeof value.valid !== 'boolean' ||
|
|
1763
|
+
typeof value.root !== 'string' ||
|
|
1764
|
+
typeof value.concepts !== 'number' ||
|
|
1765
|
+
!Array.isArray(value.errors) ||
|
|
1766
|
+
!Array.isArray(value.warnings) ||
|
|
1767
|
+
!isRecord(value.graph)) {
|
|
1768
|
+
throw new Error(`Workflow summarize-wiki-run node "${nodeId}" needs an OKF validation artifact at "${artifactName}".`);
|
|
1769
|
+
}
|
|
1770
|
+
return value;
|
|
1771
|
+
}
|
|
1772
|
+
function isRecord(value) {
|
|
1773
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
1774
|
+
}
|
|
1775
|
+
async function runRecordWikiStateWorkflowNode(nodeId, node, workingDir, context) {
|
|
1776
|
+
const root = getStringActionOption(node, 'root', context)?.trim() ?? 'wiki';
|
|
1777
|
+
const statePath = getStringActionOption(node, 'statePath', context)?.trim() ?? '.drs/wiki-state.json';
|
|
1778
|
+
const result = await recordWikiState(workingDir, root, statePath, getWikiInstructionsActionOptions(node, context));
|
|
1779
|
+
return {
|
|
1780
|
+
id: nodeId,
|
|
1781
|
+
type: 'action',
|
|
1782
|
+
action: node.action,
|
|
1783
|
+
response: `recorded wiki state for ${result.root} at ${result.gitHead}`,
|
|
1784
|
+
output: result,
|
|
1785
|
+
writes: statePath,
|
|
1786
|
+
};
|
|
1787
|
+
}
|
|
1788
|
+
async function runCheckWikiCleanWorkflowNode(nodeId, node, workingDir, context) {
|
|
1789
|
+
const root = getStringActionOption(node, 'root', context)?.trim() ?? 'wiki';
|
|
1790
|
+
const statePath = getStringActionOption(node, 'statePath', context)?.trim() ?? '.drs/wiki-state.json';
|
|
1791
|
+
const result = await checkWikiClean(workingDir, root, statePath);
|
|
1792
|
+
if (!result.clean) {
|
|
1793
|
+
throw new Error(`Workflow check-wiki-clean node "${nodeId}" found stale wiki output:\n${result.changedPaths.map((filePath) => `- ${filePath}`).join('\n')}`);
|
|
1794
|
+
}
|
|
1795
|
+
return {
|
|
1796
|
+
id: nodeId,
|
|
1797
|
+
type: 'action',
|
|
1798
|
+
action: node.action,
|
|
1799
|
+
response: `wiki bundle ${result.root} and state ${result.statePath} are current`,
|
|
1800
|
+
output: result,
|
|
1801
|
+
};
|
|
1802
|
+
}
|
|
1803
|
+
async function runCheckWikiStateWorkflowNode(nodeId, node, workingDir, context) {
|
|
1804
|
+
const root = getStringActionOption(node, 'root', context)?.trim() ?? 'wiki';
|
|
1805
|
+
const statePath = getStringActionOption(node, 'statePath', context)?.trim() ?? '.drs/wiki-state.json';
|
|
1806
|
+
const result = await planWikiUpdate(workingDir, root, statePath, getWikiInstructionsActionOptions(node, context));
|
|
1807
|
+
if (result.shouldRun) {
|
|
1808
|
+
const paths = result.changedPaths.map((filePath) => `- ${filePath}`).join('\n');
|
|
1809
|
+
throw new Error(`Workflow check-wiki-state node "${nodeId}" found a stale wiki (${result.mode}): ${result.reason}${paths ? `\n${paths}` : ''}`);
|
|
1810
|
+
}
|
|
1811
|
+
return {
|
|
1812
|
+
id: nodeId,
|
|
1813
|
+
type: 'action',
|
|
1814
|
+
action: node.action,
|
|
1815
|
+
response: result.reason,
|
|
1816
|
+
output: result,
|
|
1817
|
+
};
|
|
1818
|
+
}
|
|
1559
1819
|
async function runDescribeWorkflowNode(config, nodeId, node, options, workingDir, context, executionContext) {
|
|
1560
1820
|
const sourceArtifact = getStringActionOption(node, 'source', context) ?? 'change';
|
|
1561
1821
|
const source = context.artifacts[sourceArtifact];
|
|
@@ -1648,15 +1908,35 @@ async function runPostReviewCommentsWorkflowNode(config, nodeId, node, options,
|
|
|
1648
1908
|
const sourceArtifact = getStringActionOption(node, 'source', context) ?? 'change';
|
|
1649
1909
|
const reviewArtifact = getStringActionOption(node, 'review', context) ?? 'review';
|
|
1650
1910
|
const source = context.artifacts[sourceArtifact];
|
|
1651
|
-
const
|
|
1911
|
+
const reviewValue = context.artifacts[reviewArtifact];
|
|
1652
1912
|
if (!isReviewSource(source)) {
|
|
1653
1913
|
throw new Error(`Workflow post-review-comments node "${nodeId}" needs a ReviewSource artifact.`);
|
|
1654
1914
|
}
|
|
1655
|
-
if (!isReviewResult(reviewResult)) {
|
|
1656
|
-
throw new Error(`Workflow post-review-comments node "${nodeId}" needs a ReviewResult artifact.`);
|
|
1657
|
-
}
|
|
1658
1915
|
const target = resolvePostTarget(nodeId, node, context, executionContext, source);
|
|
1659
1916
|
const pullRequest = target.pullRequest;
|
|
1917
|
+
const canonicalArtifactMode = hasActionOption(node, 'expectedHeadSha');
|
|
1918
|
+
const expectedHeadSha = getStringActionOption(node, 'expectedHeadSha', context)?.trim();
|
|
1919
|
+
let reviewResult;
|
|
1920
|
+
if (canonicalArtifactMode) {
|
|
1921
|
+
if (!expectedHeadSha || !pullRequest) {
|
|
1922
|
+
throw new Error(`Workflow post-review-comments node "${nodeId}" needs pull request metadata and a non-empty expectedHeadSha to validate a canonical review artifact.`);
|
|
1923
|
+
}
|
|
1924
|
+
reviewResult = reviewArtifactToReviewResult(reviewValue, {
|
|
1925
|
+
platform: target.platform,
|
|
1926
|
+
projectId: target.projectId,
|
|
1927
|
+
changeKind: target.platform === 'gitlab' ? 'mr' : 'pr',
|
|
1928
|
+
changeNumber: target.prNumber,
|
|
1929
|
+
expectedHeadSha,
|
|
1930
|
+
currentHeadSha: pullRequest.headSha,
|
|
1931
|
+
changedFiles: source.files,
|
|
1932
|
+
});
|
|
1933
|
+
}
|
|
1934
|
+
else if (isReviewResult(reviewValue)) {
|
|
1935
|
+
reviewResult = reviewValue;
|
|
1936
|
+
}
|
|
1937
|
+
else {
|
|
1938
|
+
throw new Error(`Workflow post-review-comments node "${nodeId}" needs a ReviewResult artifact or a canonical review artifact with expectedHeadSha.`);
|
|
1939
|
+
}
|
|
1660
1940
|
const platformData = pullRequest?.platformData;
|
|
1661
1941
|
const lineValidator = createWorkflowLineValidator(target.platform, source);
|
|
1662
1942
|
const createInlinePosition = lineValidator
|
|
@@ -1664,10 +1944,15 @@ async function runPostReviewCommentsWorkflowNode(config, nodeId, node, options,
|
|
|
1664
1944
|
: undefined;
|
|
1665
1945
|
const shouldRemoveErrorComment = !hasActionOption(node, 'removeErrorComment') ||
|
|
1666
1946
|
getBooleanActionOption(node, 'removeErrorComment', context);
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
await
|
|
1947
|
+
const assertCurrentHead = expectedHeadSha
|
|
1948
|
+
? async () => {
|
|
1949
|
+
const latestPullRequest = await target.platformClient.getPullRequest(target.projectId, target.prNumber);
|
|
1950
|
+
if (latestPullRequest.headSha !== expectedHeadSha) {
|
|
1951
|
+
throw new Error('Pull request head changed before review comments could be posted.');
|
|
1952
|
+
}
|
|
1670
1953
|
}
|
|
1954
|
+
: undefined;
|
|
1955
|
+
await withWorkflowConsoleSuppressed(executionContext, options.jsonOutput === true, async () => {
|
|
1671
1956
|
const cursorFixLinks = resolveCursorFixLinkOptions(config, target.projectId, workingDir);
|
|
1672
1957
|
await postReviewComments(target.platformClient, target.projectId, target.prNumber, reviewResult.summary, reviewResult.issues, reviewResult.changeSummary, reviewResult.usage, platformData, lineValidator, createInlinePosition, cursorFixLinks, pullRequest
|
|
1673
1958
|
? {
|
|
@@ -1675,6 +1960,8 @@ async function runPostReviewCommentsWorkflowNode(config, nodeId, node, options,
|
|
|
1675
1960
|
sourceBranch: pullRequest.sourceBranch,
|
|
1676
1961
|
targetBranch: pullRequest.targetBranch,
|
|
1677
1962
|
}
|
|
1963
|
+
: undefined, assertCurrentHead, shouldRemoveErrorComment
|
|
1964
|
+
? () => removeErrorComment(target.platformClient, target.projectId, target.prNumber, assertCurrentHead)
|
|
1678
1965
|
: undefined);
|
|
1679
1966
|
});
|
|
1680
1967
|
return {
|
|
@@ -2004,11 +2291,17 @@ async function runReviewWorkflowNode(config, nodeId, node, options, workingDir,
|
|
|
2004
2291
|
console.warn = () => undefined;
|
|
2005
2292
|
}
|
|
2006
2293
|
try {
|
|
2007
|
-
|
|
2294
|
+
const reviewSource = {
|
|
2008
2295
|
...sourceForReview,
|
|
2009
2296
|
workingDir: sourceForReview.workingDir ?? workingDir,
|
|
2010
2297
|
debug: options.debug,
|
|
2011
2298
|
thinkingLevel: options.thinkingLevel,
|
|
2299
|
+
};
|
|
2300
|
+
if (!node.permissions) {
|
|
2301
|
+
return await executeReview(config, reviewSource);
|
|
2302
|
+
}
|
|
2303
|
+
return await executeReview(config, reviewSource, {
|
|
2304
|
+
permissions: renderAgentPermissions(node.permissions, (value) => renderTemplate(value, context)),
|
|
2012
2305
|
});
|
|
2013
2306
|
}
|
|
2014
2307
|
finally {
|
|
@@ -2214,16 +2507,21 @@ async function runSingleWorkflowNode(config, nodeId, node, options, workingDir,
|
|
|
2214
2507
|
if (getWorkflowNodeSkipReason(node, context)) {
|
|
2215
2508
|
return createSkippedWorkflowNodeResult(nodeId);
|
|
2216
2509
|
}
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2510
|
+
const run = async () => {
|
|
2511
|
+
if (kind === 'agent') {
|
|
2512
|
+
return runAgentWorkflowNode(config, nodeId, node, options, workingDir, context, executionContext);
|
|
2513
|
+
}
|
|
2514
|
+
if (kind === 'agents') {
|
|
2515
|
+
return runAgentsWorkflowNode(config, nodeId, node, options, workingDir, context);
|
|
2516
|
+
}
|
|
2517
|
+
if (kind === 'control') {
|
|
2518
|
+
throw new Error(`Workflow control node "${nodeId}" cannot run in the static DAG executor.`);
|
|
2519
|
+
}
|
|
2520
|
+
return runActionWorkflowNode(config, nodeId, node, options, workingDir, context, executionContext);
|
|
2521
|
+
};
|
|
2522
|
+
return isPotentialWorkspaceMutation(node)
|
|
2523
|
+
? withWorkflowLock(executionContext.locks.workspace, run)
|
|
2524
|
+
: run();
|
|
2227
2525
|
}
|
|
2228
2526
|
/**
|
|
2229
2527
|
* In-process {@link NodeExecutor} that dispatches a single non-control node
|
|
@@ -2256,6 +2554,7 @@ export async function runWorkflowNodeLocally(config, nodeId, node, options, work
|
|
|
2256
2554
|
locks: {
|
|
2257
2555
|
exit: createWorkflowLock(),
|
|
2258
2556
|
console: createWorkflowLock(),
|
|
2557
|
+
workspace: createWorkflowLock(),
|
|
2259
2558
|
},
|
|
2260
2559
|
};
|
|
2261
2560
|
return runSingleWorkflowNode(config, nodeId, node, options, workingDir, context, executionContext);
|
|
@@ -2289,6 +2588,7 @@ export async function runWorkflowFromCompiledPlan(config, plan, options = {}) {
|
|
|
2289
2588
|
return executeWorkflowRun(config, plan.workflowName, plan.nodes, plan.executionOrder, plan.waves, plan.output, false, options);
|
|
2290
2589
|
}
|
|
2291
2590
|
async function executeWorkflowRun(config, workflowName, workflowNodes, executionOrder, executionWaves, workflowOutput, hasControl, options, controlSegments) {
|
|
2591
|
+
const startedAt = new Date().toISOString();
|
|
2292
2592
|
const workflow = config.workflows?.[workflowName];
|
|
2293
2593
|
if (!workflow) {
|
|
2294
2594
|
throw new Error(`Unknown workflow "${workflowName}".`);
|
|
@@ -2298,7 +2598,7 @@ async function executeWorkflowRun(config, workflowName, workflowNodes, execution
|
|
|
2298
2598
|
const nodes = {};
|
|
2299
2599
|
const artifacts = {};
|
|
2300
2600
|
const loop = {};
|
|
2301
|
-
const context = { inputs, nodes, artifacts, loop };
|
|
2601
|
+
const context = { startedAt, inputs, nodes, artifacts, loop };
|
|
2302
2602
|
const executionContext = {
|
|
2303
2603
|
gitClients: new Map(),
|
|
2304
2604
|
platformClients: {},
|
|
@@ -2306,6 +2606,7 @@ async function executeWorkflowRun(config, workflowName, workflowNodes, execution
|
|
|
2306
2606
|
locks: {
|
|
2307
2607
|
exit: createWorkflowLock(),
|
|
2308
2608
|
console: createWorkflowLock(),
|
|
2609
|
+
workspace: createWorkflowLock(),
|
|
2309
2610
|
},
|
|
2310
2611
|
};
|
|
2311
2612
|
const nodeExecutor = new LocalNodeExecutor(config, options, workingDir, executionContext);
|
|
@@ -2397,8 +2698,14 @@ async function executeWorkflowRun(config, workflowName, workflowNodes, execution
|
|
|
2397
2698
|
if (options.jsonOutput) {
|
|
2398
2699
|
console.log(formatWorkflowJson(result));
|
|
2399
2700
|
}
|
|
2400
|
-
else
|
|
2401
|
-
|
|
2701
|
+
else {
|
|
2702
|
+
const wikiSummary = getWikiRunSummary(result.output);
|
|
2703
|
+
if (wikiSummary) {
|
|
2704
|
+
console.log(`\n${formatWikiRunSummaryHuman(wikiSummary)}`);
|
|
2705
|
+
}
|
|
2706
|
+
else if (typeof result.output === 'string' && result.output.trim()) {
|
|
2707
|
+
console.log(`\n${result.output}`);
|
|
2708
|
+
}
|
|
2402
2709
|
}
|
|
2403
2710
|
return result;
|
|
2404
2711
|
}
|
|
@@ -2500,6 +2807,8 @@ function buildWorkflowDetail(name, workflow, workingDir) {
|
|
|
2500
2807
|
output: node.output,
|
|
2501
2808
|
writes: node.writes,
|
|
2502
2809
|
json: node.json,
|
|
2810
|
+
permissions: node.permissions,
|
|
2811
|
+
validation: node.validation,
|
|
2503
2812
|
routes: getWorkflowNodeRoutes(node),
|
|
2504
2813
|
})),
|
|
2505
2814
|
graph,
|
|
@@ -2587,6 +2896,10 @@ export function showWorkflow(config, workflowName, options = {}) {
|
|
|
2587
2896
|
console.log(` output: ${node.output}`);
|
|
2588
2897
|
if (node.writes)
|
|
2589
2898
|
console.log(` writes: ${node.writes}`);
|
|
2899
|
+
if (node.permissions)
|
|
2900
|
+
console.log(` permissions: ${JSON.stringify(node.permissions)}`);
|
|
2901
|
+
if (node.validation)
|
|
2902
|
+
console.log(` validation: ${JSON.stringify(node.validation)}`);
|
|
2590
2903
|
if (node.input)
|
|
2591
2904
|
console.log(` input: ${node.input.split('\n')[0]}`);
|
|
2592
2905
|
if (node.with && Object.keys(node.with).length > 0) {
|