@exaudeus/workrail 3.13.0 → 3.15.0
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/dist/application/services/validation-engine.js +4 -9
- package/dist/application/services/workflow-compiler.js +4 -6
- package/dist/console/assets/index-BZYIjrzJ.js +28 -0
- package/dist/console/assets/index-OLCKbDdm.css +1 -0
- package/dist/console/index.html +2 -2
- package/dist/engine/engine-factory.js +2 -2
- package/dist/engine/types.d.ts +1 -1
- package/dist/manifest.json +63 -63
- package/dist/mcp/handlers/shared/request-workflow-reader.d.ts +5 -0
- package/dist/mcp/handlers/shared/request-workflow-reader.js +47 -2
- package/dist/mcp/handlers/v2-advance-core/assessment-consequences.d.ts +1 -1
- package/dist/mcp/handlers/v2-advance-core/assessment-consequences.js +4 -5
- package/dist/mcp/handlers/v2-advance-core/index.js +1 -1
- package/dist/mcp/handlers/v2-advance-core/outcome-blocked.js +1 -1
- package/dist/mcp/handlers/v2-execution/start.d.ts +1 -0
- package/dist/mcp/handlers/v2-execution/start.js +20 -1
- package/dist/mcp/handlers/v2-workflow.d.ts +23 -0
- package/dist/mcp/handlers/v2-workflow.js +177 -10
- package/dist/mcp/output-schemas.d.ts +202 -8
- package/dist/mcp/output-schemas.js +38 -11
- package/dist/mcp/server.js +48 -1
- package/dist/mcp/tool-descriptions.js +17 -9
- package/dist/mcp/v2/tools.d.ts +6 -0
- package/dist/mcp/v2/tools.js +2 -0
- package/dist/mcp/workflow-protocol-contracts.js +5 -1
- package/dist/types/workflow-definition.d.ts +2 -2
- package/dist/v2/infra/local/workspace-anchor/index.js +4 -1
- package/dist/v2/usecases/console-routes.js +49 -1
- package/dist/v2/usecases/console-service.d.ts +1 -0
- package/dist/v2/usecases/console-service.js +4 -1
- package/dist/v2/usecases/console-types.d.ts +12 -0
- package/dist/v2/usecases/worktree-service.js +55 -7
- package/package.json +3 -2
- package/spec/authoring-spec.json +91 -3
- package/spec/workflow-tags.json +132 -0
- package/spec/workflow.schema.json +411 -97
- package/workflows/adaptive-ticket-creation.json +40 -22
- package/workflows/architecture-scalability-audit.json +65 -31
- package/workflows/bug-investigation.agentic.v2.json +36 -14
- package/workflows/coding-task-workflow-agentic.json +50 -38
- package/workflows/coding-task-workflow-agentic.lean.v2.json +124 -37
- package/workflows/coding-task-workflow-agentic.v2.json +90 -30
- package/workflows/cross-platform-code-conversion.v2.json +168 -48
- package/workflows/document-creation-workflow.json +47 -17
- package/workflows/documentation-update-workflow.json +8 -8
- package/workflows/intelligent-test-case-generation.json +2 -2
- package/workflows/learner-centered-course-workflow.json +267 -267
- package/workflows/mr-review-workflow.agentic.v2.json +81 -14
- package/workflows/personal-learning-materials-creation-branched.json +175 -175
- package/workflows/presentation-creation.json +159 -159
- package/workflows/production-readiness-audit.json +54 -15
- package/workflows/relocation-workflow-us.json +44 -35
- package/workflows/routines/tension-driven-design.json +1 -1
- package/workflows/scoped-documentation-workflow.json +25 -25
- package/workflows/test-artifact-loop-control.json +1 -2
- package/workflows/ui-ux-design-workflow.json +327 -0
- package/workflows/workflow-diagnose-environment.json +1 -1
- package/workflows/workflow-for-workflows.json +507 -484
- package/workflows/workflow-for-workflows.v2.json +90 -18
- package/workflows/wr.discovery.json +112 -30
- package/dist/console/assets/index-DW78t31j.css +0 -1
- package/dist/console/assets/index-EsSXrC_a.js +0 -28
|
@@ -3,9 +3,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.shouldShowStaleness = shouldShowStaleness;
|
|
7
|
+
exports.buildTagSummary = buildTagSummary;
|
|
8
|
+
exports.computeWorkflowStaleness = computeWorkflowStaleness;
|
|
6
9
|
exports.handleV2ListWorkflows = handleV2ListWorkflows;
|
|
7
10
|
exports.handleV2InspectWorkflow = handleV2InspectWorkflow;
|
|
8
11
|
const path_1 = __importDefault(require("path"));
|
|
12
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
13
|
const neverthrow_1 = require("neverthrow");
|
|
10
14
|
const types_js_1 = require("../types.js");
|
|
11
15
|
const error_mapper_js_1 = require("../error-mapper.js");
|
|
@@ -14,6 +18,84 @@ const output_schemas_js_1 = require("../output-schemas.js");
|
|
|
14
18
|
const v1_to_v2_shim_js_1 = require("../../v2/read-only/v1-to-v2-shim.js");
|
|
15
19
|
const hashing_js_1 = require("../../v2/durable-core/canonical/hashing.js");
|
|
16
20
|
const TIMEOUT_MS = 30000;
|
|
21
|
+
function readCurrentSpecVersion() {
|
|
22
|
+
try {
|
|
23
|
+
const specPath = path_1.default.resolve(__dirname, '../../../spec/authoring-spec.json');
|
|
24
|
+
const raw = fs_1.default.readFileSync(specPath, 'utf-8');
|
|
25
|
+
const parsed = JSON.parse(raw);
|
|
26
|
+
if (typeof parsed === 'object' && parsed !== null && 'version' in parsed) {
|
|
27
|
+
const v = parsed['version'];
|
|
28
|
+
if (typeof v === 'number' && Number.isInteger(v) && v >= 1)
|
|
29
|
+
return v;
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
const CURRENT_SPEC_VERSION = readCurrentSpecVersion();
|
|
38
|
+
const DEV_STALENESS = process.env['WORKRAIL_DEV_STALENESS'] === '1';
|
|
39
|
+
function shouldShowStaleness(category, devMode = DEV_STALENESS) {
|
|
40
|
+
if (devMode)
|
|
41
|
+
return true;
|
|
42
|
+
return category === 'personal' || category === 'rooted_sharing' || category === 'external';
|
|
43
|
+
}
|
|
44
|
+
function readWorkflowTags() {
|
|
45
|
+
try {
|
|
46
|
+
const tagsPath = path_1.default.resolve(__dirname, '../../../spec/workflow-tags.json');
|
|
47
|
+
const raw = fs_1.default.readFileSync(tagsPath, 'utf-8');
|
|
48
|
+
return JSON.parse(raw);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const WORKFLOW_TAGS = readWorkflowTags();
|
|
55
|
+
function buildTagSummary(tagsFile, compiledWorkflowIds) {
|
|
56
|
+
const idSet = new Set(compiledWorkflowIds);
|
|
57
|
+
return tagsFile.tags.map((tag) => {
|
|
58
|
+
const count = Object.entries(tagsFile.workflows)
|
|
59
|
+
.filter(([wid, meta]) => !meta.hidden && idSet.has(wid) && meta.tags.includes(tag.id))
|
|
60
|
+
.length;
|
|
61
|
+
return {
|
|
62
|
+
id: tag.id,
|
|
63
|
+
displayName: tag.displayName,
|
|
64
|
+
count,
|
|
65
|
+
when: [...tag.when],
|
|
66
|
+
examples: [...tag.examples],
|
|
67
|
+
};
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
function filterByTags(tagsFile, compiledWorkflowIds, requestedTags) {
|
|
71
|
+
const tagSet = new Set(requestedTags);
|
|
72
|
+
const matching = new Set(Object.entries(tagsFile.workflows)
|
|
73
|
+
.filter(([, meta]) => !meta.hidden && meta.tags.some((t) => tagSet.has(t)))
|
|
74
|
+
.map(([wid]) => wid));
|
|
75
|
+
return compiledWorkflowIds.filter((id) => matching.has(id));
|
|
76
|
+
}
|
|
77
|
+
function computeWorkflowStaleness(stamp, currentVersion) {
|
|
78
|
+
if (currentVersion === null)
|
|
79
|
+
return undefined;
|
|
80
|
+
if (stamp === undefined) {
|
|
81
|
+
return {
|
|
82
|
+
level: 'possible',
|
|
83
|
+
reason: 'This workflow has not been validated against the authoring spec via workflow-for-workflows.',
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
if (stamp === currentVersion) {
|
|
87
|
+
return {
|
|
88
|
+
level: 'none',
|
|
89
|
+
reason: `Workflow validated against current authoring spec (v${currentVersion}).`,
|
|
90
|
+
specVersionAtLastReview: stamp,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
level: 'likely',
|
|
95
|
+
reason: `Authoring spec updated from v${stamp} to v${currentVersion} since this workflow was last reviewed.`,
|
|
96
|
+
specVersionAtLastReview: stamp,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
17
99
|
const with_timeout_js_1 = require("./shared/with-timeout.js");
|
|
18
100
|
const request_workflow_reader_js_1 = require("./shared/request-workflow-reader.js");
|
|
19
101
|
const remembered_roots_js_1 = require("./shared/remembered-roots.js");
|
|
@@ -42,10 +124,17 @@ async function handleV2ListWorkflows(input, ctx) {
|
|
|
42
124
|
workspacePath: input.workspacePath,
|
|
43
125
|
resolvedRootUris: guard.ctx.v2.resolvedRootUris,
|
|
44
126
|
rememberedRootsStore: guard.ctx.v2.rememberedRootsStore,
|
|
127
|
+
managedSourceStore: guard.ctx.v2.managedSourceStore,
|
|
45
128
|
})
|
|
46
|
-
: { reader: ctx.workflowService, stalePaths: [] };
|
|
129
|
+
: { reader: ctx.workflowService, stalePaths: [], managedSourceRecords: [], staleManagedRecords: [], managedStoreError: undefined };
|
|
47
130
|
const workflowReader = readerResult.reader;
|
|
48
131
|
const stalePaths = readerResult.stalePaths;
|
|
132
|
+
const managedSourceRecords = readerResult.managedSourceRecords;
|
|
133
|
+
const staleManagedRecords = readerResult.staleManagedRecords;
|
|
134
|
+
const managedStoreError = readerResult.managedStoreError;
|
|
135
|
+
const warnings = managedStoreError
|
|
136
|
+
? [`Managed workflow source store was temporarily unavailable (${managedStoreError}). Managed sources were not loaded.`]
|
|
137
|
+
: undefined;
|
|
49
138
|
return neverthrow_1.ResultAsync.fromPromise((0, with_timeout_js_1.withTimeout)(workflowReader.listWorkflowSummaries(), TIMEOUT_MS, 'list_workflows'), (err) => (0, error_mapper_js_1.mapUnknownErrorToToolError)(err))
|
|
50
139
|
.andThen((summaries) => neverthrow_1.ResultAsync.combine(summaries.map((s) => neverthrow_1.ResultAsync.fromPromise(buildV2WorkflowListItem({
|
|
51
140
|
summary: s,
|
|
@@ -55,25 +144,62 @@ async function handleV2ListWorkflows(input, ctx) {
|
|
|
55
144
|
pinnedStore,
|
|
56
145
|
}), (err) => (0, error_mapper_js_1.mapUnknownErrorToToolError)(err)))))
|
|
57
146
|
.andThen((compiled) => {
|
|
147
|
+
const sortedIds = compiled.map((w) => w.workflowId).sort((a, b) => a.localeCompare(b));
|
|
148
|
+
const sortedCompiled = [...compiled].sort((a, b) => a.workflowId.localeCompare(b.workflowId));
|
|
149
|
+
const tagFilteredCompiled = (() => {
|
|
150
|
+
if (input.includeSources)
|
|
151
|
+
return sortedCompiled;
|
|
152
|
+
if (!WORKFLOW_TAGS)
|
|
153
|
+
return sortedCompiled;
|
|
154
|
+
if (input.tags && input.tags.length > 0) {
|
|
155
|
+
const filteredIds = new Set(filterByTags(WORKFLOW_TAGS, sortedIds, input.tags));
|
|
156
|
+
return sortedCompiled.filter((w) => filteredIds.has(w.workflowId));
|
|
157
|
+
}
|
|
158
|
+
return [];
|
|
159
|
+
})();
|
|
160
|
+
const tagSummaryEntry = (() => {
|
|
161
|
+
if (input.includeSources)
|
|
162
|
+
return undefined;
|
|
163
|
+
if (!WORKFLOW_TAGS)
|
|
164
|
+
return undefined;
|
|
165
|
+
if (input.tags && input.tags.length > 0)
|
|
166
|
+
return undefined;
|
|
167
|
+
return buildTagSummary(WORKFLOW_TAGS, sortedIds);
|
|
168
|
+
})();
|
|
169
|
+
const nextStepHint = tagSummaryEntry
|
|
170
|
+
? 'Pick a tag from tagSummary that fits the user\'s goal, then call list_workflows with tags=["<tagId>"]. ' +
|
|
171
|
+
'If a workflow ID in examples[] already matches, call start_workflow directly — no second list call needed. ' +
|
|
172
|
+
'If multiple tags could apply, pick the most specific one.'
|
|
173
|
+
: undefined;
|
|
58
174
|
if (!input.includeSources) {
|
|
175
|
+
const includeStaleRoots = !tagSummaryEntry && stalePaths.length > 0;
|
|
59
176
|
const payload = output_schemas_js_1.V2WorkflowListOutputSchema.parse({
|
|
60
|
-
workflows:
|
|
61
|
-
...(
|
|
177
|
+
workflows: tagFilteredCompiled,
|
|
178
|
+
...(tagSummaryEntry ? { tagSummary: tagSummaryEntry } : {}),
|
|
179
|
+
...(nextStepHint ? { _nextStep: nextStepHint } : {}),
|
|
180
|
+
...(includeStaleRoots ? { staleRoots: [...stalePaths] } : {}),
|
|
181
|
+
...(warnings ? { warnings } : {}),
|
|
62
182
|
});
|
|
63
183
|
return (0, neverthrow_1.okAsync)((0, types_js_1.success)(payload));
|
|
64
184
|
}
|
|
65
185
|
if (!(0, workflow_source_visibility_js_1.isCompositeWorkflowReader)(workflowReader)) {
|
|
66
186
|
const payload = output_schemas_js_1.V2WorkflowListOutputSchema.parse({
|
|
67
|
-
workflows:
|
|
187
|
+
workflows: tagFilteredCompiled,
|
|
188
|
+
...(tagSummaryEntry ? { tagSummary: tagSummaryEntry } : {}),
|
|
189
|
+
...(nextStepHint ? { _nextStep: nextStepHint } : {}),
|
|
68
190
|
...(stalePaths.length > 0 ? { staleRoots: [...stalePaths] } : {}),
|
|
191
|
+
...(warnings ? { warnings } : {}),
|
|
69
192
|
sources: [],
|
|
70
193
|
});
|
|
71
194
|
return (0, neverthrow_1.okAsync)((0, types_js_1.success)(payload));
|
|
72
195
|
}
|
|
73
|
-
return neverthrow_1.ResultAsync.fromPromise((0, with_timeout_js_1.withTimeout)(buildSourceCatalog(workflowReader, rememberedRootRecords), TIMEOUT_MS, 'list_workflow_sources'), (err) => (0, error_mapper_js_1.mapUnknownErrorToToolError)(err)).map((sources) => {
|
|
196
|
+
return neverthrow_1.ResultAsync.fromPromise((0, with_timeout_js_1.withTimeout)(buildSourceCatalog(workflowReader, rememberedRootRecords, managedSourceRecords, staleManagedRecords), TIMEOUT_MS, 'list_workflow_sources'), (err) => (0, error_mapper_js_1.mapUnknownErrorToToolError)(err)).map((sources) => {
|
|
74
197
|
const payload = output_schemas_js_1.V2WorkflowListOutputSchema.parse({
|
|
75
|
-
workflows:
|
|
198
|
+
workflows: tagFilteredCompiled,
|
|
199
|
+
...(tagSummaryEntry ? { tagSummary: tagSummaryEntry } : {}),
|
|
200
|
+
...(nextStepHint ? { _nextStep: nextStepHint } : {}),
|
|
76
201
|
...(stalePaths.length > 0 ? { staleRoots: [...stalePaths] } : {}),
|
|
202
|
+
...(warnings ? { warnings } : {}),
|
|
77
203
|
sources,
|
|
78
204
|
});
|
|
79
205
|
return (0, types_js_1.success)(payload);
|
|
@@ -102,10 +228,14 @@ async function handleV2InspectWorkflow(input, ctx) {
|
|
|
102
228
|
workspacePath: input.workspacePath,
|
|
103
229
|
resolvedRootUris: guard.ctx.v2.resolvedRootUris,
|
|
104
230
|
rememberedRootsStore: guard.ctx.v2.rememberedRootsStore,
|
|
231
|
+
managedSourceStore: guard.ctx.v2.managedSourceStore,
|
|
105
232
|
})
|
|
106
|
-
: { reader: ctx.workflowService, stalePaths: [] };
|
|
233
|
+
: { reader: ctx.workflowService, stalePaths: [], managedSourceRecords: [], staleManagedRecords: [], managedStoreError: undefined };
|
|
107
234
|
const workflowReader = readerResult.reader;
|
|
108
235
|
const stalePaths = readerResult.stalePaths;
|
|
236
|
+
const inspectWarnings = readerResult.managedStoreError
|
|
237
|
+
? [`Managed workflow source store was temporarily unavailable (${readerResult.managedStoreError}). Managed sources were not loaded.`]
|
|
238
|
+
: undefined;
|
|
109
239
|
return neverthrow_1.ResultAsync.fromPromise((0, with_timeout_js_1.withTimeout)(workflowReader.getWorkflowById(input.workflowId), TIMEOUT_MS, 'inspect_workflow'), (err) => (0, error_mapper_js_1.mapUnknownErrorToToolError)(err))
|
|
110
240
|
.andThen((workflow) => {
|
|
111
241
|
if (!workflow) {
|
|
@@ -141,7 +271,14 @@ async function handleV2InspectWorkflow(input, ctx) {
|
|
|
141
271
|
compiled: body,
|
|
142
272
|
...(visibility ? { visibility } : {}),
|
|
143
273
|
...(stalePaths.length > 0 ? { staleRoots: [...stalePaths] } : {}),
|
|
274
|
+
...(inspectWarnings ? { warnings: inspectWarnings } : {}),
|
|
144
275
|
...(references != null && references.length > 0 ? { references } : {}),
|
|
276
|
+
...(() => {
|
|
277
|
+
const staleness = shouldShowStaleness(visibility?.category)
|
|
278
|
+
? computeWorkflowStaleness(workflow.definition.validatedAgainstSpecVersion, CURRENT_SPEC_VERSION)
|
|
279
|
+
: undefined;
|
|
280
|
+
return staleness !== undefined ? { staleness } : {};
|
|
281
|
+
})(),
|
|
145
282
|
});
|
|
146
283
|
return (0, neverthrow_1.okAsync)((0, types_js_1.success)(payload));
|
|
147
284
|
}));
|
|
@@ -199,6 +336,9 @@ async function buildV2WorkflowListItem(options) {
|
|
|
199
336
|
};
|
|
200
337
|
}
|
|
201
338
|
}
|
|
339
|
+
const staleness = shouldShowStaleness(visibility?.category)
|
|
340
|
+
? computeWorkflowStaleness(workflow.definition.validatedAgainstSpecVersion, CURRENT_SPEC_VERSION)
|
|
341
|
+
: undefined;
|
|
202
342
|
return {
|
|
203
343
|
workflowId: summary.id,
|
|
204
344
|
name: summary.name,
|
|
@@ -207,9 +347,10 @@ async function buildV2WorkflowListItem(options) {
|
|
|
207
347
|
workflowHash: hash,
|
|
208
348
|
kind: 'workflow',
|
|
209
349
|
visibility,
|
|
350
|
+
...(staleness !== undefined ? { staleness } : {}),
|
|
210
351
|
};
|
|
211
352
|
}
|
|
212
|
-
async function buildSourceCatalog(workflowReader, rememberedRootRecords) {
|
|
353
|
+
async function buildSourceCatalog(workflowReader, rememberedRootRecords, managedSourceRecords, staleManagedRecords) {
|
|
213
354
|
const instances = workflowReader.getStorageInstances();
|
|
214
355
|
const seenIds = new Set();
|
|
215
356
|
const sourceEntryDataReversed = [];
|
|
@@ -223,10 +364,22 @@ async function buildSourceCatalog(workflowReader, rememberedRootRecords) {
|
|
|
223
364
|
sourceEntryDataReversed.push({ source: instance.source, allIds, effectiveIds });
|
|
224
365
|
}
|
|
225
366
|
const sourceEntryData = sourceEntryDataReversed.reverse();
|
|
226
|
-
|
|
367
|
+
const activeEntries = sourceEntryData.map((data) => deriveSourceCatalogEntry({ ...data, rememberedRootRecords, managedSourceRecords, sourceEntryData }));
|
|
368
|
+
const staleEntries = staleManagedRecords.map((record) => ({
|
|
369
|
+
sourceKey: `custom:${record.path}`,
|
|
370
|
+
category: 'managed',
|
|
371
|
+
source: { kind: 'custom', displayName: path_1.default.basename(record.path) },
|
|
372
|
+
sourceMode: 'live_directory',
|
|
373
|
+
effectiveWorkflowCount: 0,
|
|
374
|
+
totalWorkflowCount: 0,
|
|
375
|
+
shadowedWorkflowCount: 0,
|
|
376
|
+
managed: { addedAtMs: record.addedAtMs },
|
|
377
|
+
stale: true,
|
|
378
|
+
}));
|
|
379
|
+
return [...activeEntries, ...staleEntries];
|
|
227
380
|
}
|
|
228
381
|
function deriveSourceCatalogEntry(options) {
|
|
229
|
-
const { source, allIds, effectiveIds, rememberedRootRecords, sourceEntryData } = options;
|
|
382
|
+
const { source, allIds, effectiveIds, rememberedRootRecords, managedSourceRecords, sourceEntryData } = options;
|
|
230
383
|
const total = allIds.length;
|
|
231
384
|
const effective = effectiveIds.length;
|
|
232
385
|
const shadowed = total - effective;
|
|
@@ -252,6 +405,20 @@ function deriveSourceCatalogEntry(options) {
|
|
|
252
405
|
}
|
|
253
406
|
case 'custom': {
|
|
254
407
|
const rootedSharing = deriveRootedSharingForPath(source.directoryPath, rememberedRootRecords);
|
|
408
|
+
const managedRecord = managedSourceRecords.find((r) => path_1.default.resolve(r.path) === path_1.default.resolve(source.directoryPath));
|
|
409
|
+
if (managedRecord) {
|
|
410
|
+
return {
|
|
411
|
+
sourceKey,
|
|
412
|
+
category: 'managed',
|
|
413
|
+
source: { kind: source.kind, displayName },
|
|
414
|
+
sourceMode: 'live_directory',
|
|
415
|
+
effectiveWorkflowCount: effective,
|
|
416
|
+
totalWorkflowCount: total,
|
|
417
|
+
shadowedWorkflowCount: shadowed,
|
|
418
|
+
managed: { addedAtMs: managedRecord.addedAtMs },
|
|
419
|
+
...(rootedSharing ? { rootedSharing } : {}),
|
|
420
|
+
};
|
|
421
|
+
}
|
|
255
422
|
const category = rootedSharing ? 'rooted_sharing' : 'external';
|
|
256
423
|
const sourceMode = rootedSharing ? 'rooted_sharing' : 'live_directory';
|
|
257
424
|
return { sourceKey, category, source: { kind: source.kind, displayName }, sourceMode, effectiveWorkflowCount: effective, totalWorkflowCount: total, shadowedWorkflowCount: shadowed, ...(rootedSharing ? { rootedSharing } : {}) };
|