@mesh-tech/mesh-cli 0.17.0 → 0.18.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/dist/bin/mesh.js CHANGED
@@ -809,8 +809,8 @@ var init_seed = __esm({
809
809
  // libs/mesh-cli/src/commands/local/helpers.ts
810
810
  import * as net from "net";
811
811
  async function upsertLocalSecret(secretId, value, client) {
812
- const { SecretsManagerClient: SecretsManagerClient9, CreateSecretCommand: CreateSecretCommand4, PutSecretValueCommand: PutSecretValueCommand4 } = await import("@aws-sdk/client-secrets-manager");
813
- const sm = client ?? new SecretsManagerClient9(LOCAL_AWS_CONFIG);
812
+ const { SecretsManagerClient: SecretsManagerClient10, CreateSecretCommand: CreateSecretCommand4, PutSecretValueCommand: PutSecretValueCommand4 } = await import("@aws-sdk/client-secrets-manager");
813
+ const sm = client ?? new SecretsManagerClient10(LOCAL_AWS_CONFIG);
814
814
  const secretString = JSON.stringify(value);
815
815
  try {
816
816
  await sm.send(new CreateSecretCommand4({ Name: secretId, SecretString: secretString }));
@@ -6316,12 +6316,12 @@ async function fetchRemoteExternalCredentials(tenant, env, external, profile) {
6316
6316
  const region = process.env.MESH_PLATFORM_REGION ?? process.env.AWS_REGION ?? "us-east-2";
6317
6317
  const fallbackProfile = !process.env.AWS_ACCESS_KEY_ID && !process.env.AWS_PROFILE ? process.env.MESH_AWS_PROFILE ?? profile : void 0;
6318
6318
  try {
6319
- const { SecretsManagerClient: SecretsManagerClient9, GetSecretValueCommand: GetSecretValueCommand9 } = await import("@aws-sdk/client-secrets-manager");
6319
+ const { SecretsManagerClient: SecretsManagerClient10, GetSecretValueCommand: GetSecretValueCommand10 } = await import("@aws-sdk/client-secrets-manager");
6320
6320
  if (fallbackProfile) process.env.AWS_PROFILE = fallbackProfile;
6321
- const sm = new SecretsManagerClient9({ region });
6321
+ const sm = new SecretsManagerClient10({ region });
6322
6322
  let res;
6323
6323
  try {
6324
- res = await sm.send(new GetSecretValueCommand9({ SecretId: secretId }));
6324
+ res = await sm.send(new GetSecretValueCommand10({ SecretId: secretId }));
6325
6325
  } finally {
6326
6326
  if (fallbackProfile) delete process.env.AWS_PROFILE;
6327
6327
  }
@@ -6976,13 +6976,13 @@ async function writeRegistryParams(cliClientId, awsConfig) {
6976
6976
  }
6977
6977
  async function ensureOpsHubAdmin(pat, awsConfig) {
6978
6978
  const {
6979
- SecretsManagerClient: SecretsManagerClient9,
6980
- GetSecretValueCommand: GetSecretValueCommand9,
6979
+ SecretsManagerClient: SecretsManagerClient10,
6980
+ GetSecretValueCommand: GetSecretValueCommand10,
6981
6981
  CreateSecretCommand: CreateSecretCommand4,
6982
6982
  PutSecretValueCommand: PutSecretValueCommand4
6983
6983
  } = await import("@aws-sdk/client-secrets-manager");
6984
- const sm = new SecretsManagerClient9(awsConfig);
6985
- const existing = await sm.send(new GetSecretValueCommand9({ SecretId: OPS_HUB_SECRET_ID })).catch(() => null);
6984
+ const sm = new SecretsManagerClient10(awsConfig);
6985
+ const existing = await sm.send(new GetSecretValueCommand10({ SecretId: OPS_HUB_SECRET_ID })).catch(() => null);
6986
6986
  if (existing?.SecretString) {
6987
6987
  logSuccess("Hub admin key already provisioned (Zitadel writes enabled)");
6988
6988
  return;
@@ -7242,9 +7242,9 @@ async function ensureM2mCaller(pat, tenant, app, orgId, projectId, roles) {
7242
7242
  return false;
7243
7243
  }
7244
7244
  async function authSecretExists(tenant, app, service) {
7245
- const { SecretsManagerClient: SecretsManagerClient9, GetSecretValueCommand: GetSecretValueCommand9 } = await import("@aws-sdk/client-secrets-manager");
7246
- const sm = new SecretsManagerClient9(LOCAL_AWS_CONFIG);
7247
- return sm.send(new GetSecretValueCommand9({ SecretId: authSecretPath(tenant, app, service) })).then(
7245
+ const { SecretsManagerClient: SecretsManagerClient10, GetSecretValueCommand: GetSecretValueCommand10 } = await import("@aws-sdk/client-secrets-manager");
7246
+ const sm = new SecretsManagerClient10(LOCAL_AWS_CONFIG);
7247
+ return sm.send(new GetSecretValueCommand10({ SecretId: authSecretPath(tenant, app, service) })).then(
7248
7248
  () => true,
7249
7249
  () => false
7250
7250
  );
@@ -17500,25 +17500,63 @@ var init_exec2 = __esm({
17500
17500
  }
17501
17501
  });
17502
17502
 
17503
+ // libs/secrets/src/index.ts
17504
+ import {
17505
+ SecretsManagerClient as SecretsManagerClient5,
17506
+ GetSecretValueCommand as GetSecretValueCommand5
17507
+ } from "@aws-sdk/client-secrets-manager";
17508
+ function instanceIndexSecretId(secretPrefix) {
17509
+ return `${secretPrefix}/${INSTANCE_INDEX_SUFFIX}`;
17510
+ }
17511
+ function isInstanceKey(key) {
17512
+ return key.length > 0 && !key.includes("/") && !key.startsWith(".") && !key.endsWith(".config");
17513
+ }
17514
+ function instanceKeyFromSecretName(secretPrefix, secretName) {
17515
+ if (!secretName.startsWith(`${secretPrefix}/`)) return null;
17516
+ const key = secretName.slice(secretPrefix.length + 1);
17517
+ return isInstanceKey(key) ? key : null;
17518
+ }
17519
+ function parseInstanceIndex(secretString) {
17520
+ let parsed;
17521
+ try {
17522
+ parsed = JSON.parse(secretString);
17523
+ } catch {
17524
+ parsed = void 0;
17525
+ }
17526
+ const instances = parsed?.instances;
17527
+ if (!Array.isArray(instances) || !instances.every((k) => typeof k === "string")) {
17528
+ throw new Error(
17529
+ `Malformed instance index (expected {"instances": string[]}). Rebuild it with: mesh secrets reindex external/<name>`
17530
+ );
17531
+ }
17532
+ return instances.filter(isInstanceKey);
17533
+ }
17534
+ function buildInstanceIndexValue(keys) {
17535
+ const instances = [...new Set([...keys].filter(isInstanceKey))].sort();
17536
+ const index = { version: 1, instances };
17537
+ return JSON.stringify(index);
17538
+ }
17539
+ var INSTANCE_INDEX_SUFFIX;
17540
+ var init_src3 = __esm({
17541
+ "libs/secrets/src/index.ts"() {
17542
+ "use strict";
17543
+ INSTANCE_INDEX_SUFFIX = ".index";
17544
+ }
17545
+ });
17546
+
17503
17547
  // libs/mesh-cli/src/commands/secrets/set.ts
17504
17548
  import {
17505
17549
  SSMClient as SSMClient2,
17506
17550
  GetParametersByPathCommand
17507
17551
  } from "@aws-sdk/client-ssm";
17508
17552
  import {
17509
- SecretsManagerClient as SecretsManagerClient5,
17510
- GetSecretValueCommand as GetSecretValueCommand5,
17553
+ SecretsManagerClient as SecretsManagerClient6,
17554
+ GetSecretValueCommand as GetSecretValueCommand6,
17511
17555
  PutSecretValueCommand,
17512
17556
  CreateSecretCommand
17513
17557
  } from "@aws-sdk/client-secrets-manager";
17514
17558
  import input from "@inquirer/input";
17515
17559
  import password from "@inquirer/password";
17516
- import {
17517
- buildInstanceIndexValue,
17518
- instanceIndexSecretId,
17519
- isInstanceKey,
17520
- parseInstanceIndex
17521
- } from "@mesh-tech/secrets";
17522
17560
  async function ensureAwsCredentialsForStack(stack) {
17523
17561
  if (process.env.AWS_ACCESS_KEY_ID || process.env.AWS_SESSION_TOKEN) return;
17524
17562
  if (!stack) return;
@@ -17658,7 +17696,7 @@ async function setCommand(servicePath, opts) {
17658
17696
  const region = opts.region ?? process.env.AWS_REGION ?? "us-east-2";
17659
17697
  await ensureAwsCredentialsForStack(stackOpt ?? context.stage);
17660
17698
  const ssmClient = new SSMClient2({ region });
17661
- const smClient = new SecretsManagerClient5({ region });
17699
+ const smClient = new SecretsManagerClient6({ region });
17662
17700
  const services = await discoverExternalServices(
17663
17701
  ssmClient,
17664
17702
  context.tenant,
@@ -17712,7 +17750,7 @@ Usage: mesh secrets set external/<name>`);
17712
17750
  let existing = {};
17713
17751
  try {
17714
17752
  const response = await smClient.send(
17715
- new GetSecretValueCommand5({ SecretId: secretId })
17753
+ new GetSecretValueCommand6({ SecretId: secretId })
17716
17754
  );
17717
17755
  if (response.SecretString) {
17718
17756
  existing = JSON.parse(response.SecretString);
@@ -17824,7 +17862,7 @@ async function addKeyToInstanceIndex(client, secretPrefix, key, serviceName) {
17824
17862
  try {
17825
17863
  let keys = [];
17826
17864
  try {
17827
- const res = await client.send(new GetSecretValueCommand5({ SecretId: indexId }));
17865
+ const res = await client.send(new GetSecretValueCommand6({ SecretId: indexId }));
17828
17866
  keys = parseInstanceIndex(res.SecretString ?? "");
17829
17867
  } catch (err) {
17830
17868
  if (!isResourceNotFound(err)) throw err;
@@ -17891,6 +17929,7 @@ async function writeConfigMirror(client, secretId, schema, merged, meta) {
17891
17929
  var init_set = __esm({
17892
17930
  "libs/mesh-cli/src/commands/secrets/set.ts"() {
17893
17931
  "use strict";
17932
+ init_src3();
17894
17933
  init_context();
17895
17934
  init_log();
17896
17935
  init_stack_flag();
@@ -17902,18 +17941,12 @@ var init_set = __esm({
17902
17941
  // libs/mesh-cli/src/commands/secrets/reindex.ts
17903
17942
  import { SSMClient as SSMClient3 } from "@aws-sdk/client-ssm";
17904
17943
  import {
17905
- SecretsManagerClient as SecretsManagerClient6,
17944
+ SecretsManagerClient as SecretsManagerClient7,
17906
17945
  ListSecretsCommand,
17907
- GetSecretValueCommand as GetSecretValueCommand6,
17946
+ GetSecretValueCommand as GetSecretValueCommand7,
17908
17947
  PutSecretValueCommand as PutSecretValueCommand2,
17909
17948
  CreateSecretCommand as CreateSecretCommand2
17910
17949
  } from "@aws-sdk/client-secrets-manager";
17911
- import {
17912
- buildInstanceIndexValue as buildInstanceIndexValue2,
17913
- instanceIndexSecretId as instanceIndexSecretId2,
17914
- instanceKeyFromSecretName,
17915
- parseInstanceIndex as parseInstanceIndex2
17916
- } from "@mesh-tech/secrets";
17917
17950
  async function scanInstanceKeys(client, secretPrefix) {
17918
17951
  const keys = [];
17919
17952
  let nextToken;
@@ -17941,7 +17974,7 @@ async function reindexCommand(servicePath, opts) {
17941
17974
  const region = opts.region ?? process.env.AWS_REGION ?? "us-east-2";
17942
17975
  await ensureAwsCredentialsForStack(stackOpt ?? context.stage);
17943
17976
  const ssmClient = new SSMClient3({ region });
17944
- const smClient = new SecretsManagerClient6({ region });
17977
+ const smClient = new SecretsManagerClient7({ region });
17945
17978
  const services = await discoverExternalServices(
17946
17979
  ssmClient,
17947
17980
  context.tenant,
@@ -17970,15 +18003,15 @@ Usage: mesh secrets reindex external/<name>`);
17970
18003
  process.exit(1);
17971
18004
  }
17972
18005
  const secretPrefix = svc.meta.secretPrefix;
17973
- const indexId = instanceIndexSecretId2(secretPrefix);
18006
+ const indexId = instanceIndexSecretId(secretPrefix);
17974
18007
  const actual = [...new Set(await scanInstanceKeys(smClient, secretPrefix))].sort();
17975
18008
  let listed = [];
17976
18009
  let indexExists = true;
17977
18010
  let malformed = false;
17978
18011
  try {
17979
- const res = await smClient.send(new GetSecretValueCommand6({ SecretId: indexId }));
18012
+ const res = await smClient.send(new GetSecretValueCommand7({ SecretId: indexId }));
17980
18013
  try {
17981
- listed = parseInstanceIndex2(res.SecretString ?? "");
18014
+ listed = parseInstanceIndex(res.SecretString ?? "");
17982
18015
  } catch {
17983
18016
  malformed = true;
17984
18017
  logInfo(`Existing index at ${indexId} is malformed \u2014 rebuilding from scratch.`);
@@ -18005,8 +18038,8 @@ Usage: mesh secrets reindex external/<name>`);
18005
18038
  logSuccess(`Index rebuilt: ${indexId} now lists ${actual.length} instance(s).`);
18006
18039
  }
18007
18040
  async function writeInstanceIndex(client, secretPrefix, serviceName, keys) {
18008
- const indexId = instanceIndexSecretId2(secretPrefix);
18009
- const secretString = buildInstanceIndexValue2(keys);
18041
+ const indexId = instanceIndexSecretId(secretPrefix);
18042
+ const secretString = buildInstanceIndexValue(keys);
18010
18043
  try {
18011
18044
  await client.send(
18012
18045
  new PutSecretValueCommand2({ SecretId: indexId, SecretString: secretString })
@@ -18032,6 +18065,7 @@ async function writeInstanceIndex(client, secretPrefix, serviceName, keys) {
18032
18065
  var init_reindex = __esm({
18033
18066
  "libs/mesh-cli/src/commands/secrets/reindex.ts"() {
18034
18067
  "use strict";
18068
+ init_src3();
18035
18069
  init_context();
18036
18070
  init_log();
18037
18071
  init_stack_flag();
@@ -18045,8 +18079,8 @@ import {
18045
18079
  GetParametersByPathCommand as GetParametersByPathCommand2
18046
18080
  } from "@aws-sdk/client-ssm";
18047
18081
  import {
18048
- SecretsManagerClient as SecretsManagerClient7,
18049
- GetSecretValueCommand as GetSecretValueCommand7,
18082
+ SecretsManagerClient as SecretsManagerClient8,
18083
+ GetSecretValueCommand as GetSecretValueCommand8,
18050
18084
  PutSecretValueCommand as PutSecretValueCommand3,
18051
18085
  CreateSecretCommand as CreateSecretCommand3,
18052
18086
  ListSecretsCommand as ListSecretsCommand2
@@ -18135,7 +18169,7 @@ function isResourceNotFound2(err) {
18135
18169
  }
18136
18170
  async function readSecret(client, secretId) {
18137
18171
  try {
18138
- const res = await client.send(new GetSecretValueCommand7({ SecretId: secretId }));
18172
+ const res = await client.send(new GetSecretValueCommand8({ SecretId: secretId }));
18139
18173
  return res.SecretString ? JSON.parse(res.SecretString) : null;
18140
18174
  } catch (err) {
18141
18175
  if (isResourceNotFound2(err)) return null;
@@ -18161,7 +18195,7 @@ async function writeSecret2(client, secretId, values, description) {
18161
18195
  }
18162
18196
  async function secretExists(client, secretId) {
18163
18197
  try {
18164
- await client.send(new GetSecretValueCommand7({ SecretId: secretId }));
18198
+ await client.send(new GetSecretValueCommand8({ SecretId: secretId }));
18165
18199
  return true;
18166
18200
  } catch (err) {
18167
18201
  if (isResourceNotFound2(err)) return false;
@@ -18241,7 +18275,7 @@ async function migrateConfigCommand(opts) {
18241
18275
  const context = detectContext(resolveStackOption(opts));
18242
18276
  const region = opts.region ?? process.env.AWS_REGION ?? "us-east-2";
18243
18277
  const ssmClient = new SSMClient4({ region });
18244
- const smClient = new SecretsManagerClient7({ region });
18278
+ const smClient = new SecretsManagerClient8({ region });
18245
18279
  logInfo(`Migrating .config mirrors for ${context.tenant}/${context.platformEnv}`);
18246
18280
  if (opts.dryRun) logInfo("DRY-RUN mode \u2014 no writes");
18247
18281
  if (opts.force) logInfo("FORCE mode \u2014 overwrite existing .config");
@@ -20174,8 +20208,8 @@ import * as fs35 from "fs";
20174
20208
  import * as os12 from "os";
20175
20209
  import * as path42 from "path";
20176
20210
  import {
20177
- SecretsManagerClient as SecretsManagerClient8,
20178
- GetSecretValueCommand as GetSecretValueCommand8
20211
+ SecretsManagerClient as SecretsManagerClient9,
20212
+ GetSecretValueCommand as GetSecretValueCommand9
20179
20213
  } from "@aws-sdk/client-secrets-manager";
20180
20214
  function resolveTenantEnv(options) {
20181
20215
  if (options.tenant && options.env) {
@@ -20438,10 +20472,10 @@ async function tunnelExternal(name, options) {
20438
20472
  const { appTenant, appStage } = resolveCredentialAxis(options);
20439
20473
  const secretId = externalSecretId(appTenant, appStage, name, key);
20440
20474
  const setHint = `mesh secrets set external/${name}${key ? ` --key=${key}` : ""}`;
20441
- const sm = new SecretsManagerClient8({});
20475
+ const sm = new SecretsManagerClient9({});
20442
20476
  let creds;
20443
20477
  try {
20444
- const out = await sm.send(new GetSecretValueCommand8({ SecretId: secretId }));
20478
+ const out = await sm.send(new GetSecretValueCommand9({ SecretId: secretId }));
20445
20479
  creds = JSON.parse(out.SecretString ?? "{}");
20446
20480
  } catch (err) {
20447
20481
  logError(
@@ -21134,7 +21168,9 @@ function computeHappyPath(nodes, edges) {
21134
21168
  );
21135
21169
  const allEndIds = new Set(endNodes.map((n) => n.id));
21136
21170
  function bfs(targetIds, allowExceptional) {
21137
- const queue = [{ id: startNode.id, path: [startNode.id] }];
21171
+ const queue = [
21172
+ { id: startNode.id, path: [startNode.id] }
21173
+ ];
21138
21174
  const visited = /* @__PURE__ */ new Set([startNode.id]);
21139
21175
  while (queue.length > 0) {
21140
21176
  const { id, path: path44 } = queue.shift();
@@ -21155,7 +21191,8 @@ function computeHappyPath(nodes, edges) {
21155
21191
  });
21156
21192
  for (const neighbor of sorted) {
21157
21193
  if (visited.has(neighbor.to)) continue;
21158
- if (!allowExceptional && (neighbor.isExceptional || neighbor.isTimeout || failureEndIds.has(neighbor.to))) continue;
21194
+ if (!allowExceptional && (neighbor.isExceptional || neighbor.isTimeout || failureEndIds.has(neighbor.to)))
21195
+ continue;
21159
21196
  visited.add(neighbor.to);
21160
21197
  queue.push({ id: neighbor.to, path: [...path44, neighbor.to] });
21161
21198
  }
@@ -21164,6 +21201,37 @@ function computeHappyPath(nodes, edges) {
21164
21201
  }
21165
21202
  return bfs(successEndIds, false) ?? bfs(allEndIds, false) ?? bfs(allEndIds, true) ?? [];
21166
21203
  }
21204
+ function isProcessArtifactProvenance(value) {
21205
+ return typeof value === "object" && value !== null && value.source === "process-artifact";
21206
+ }
21207
+ function childGraphsOf(node) {
21208
+ const graphs = [];
21209
+ const withChild = node;
21210
+ if (withChild.childGraph) graphs.push(withChild.childGraph);
21211
+ const withBacking = node;
21212
+ if (withBacking.backing?.backingGraph) graphs.push(withBacking.backing.backingGraph);
21213
+ return graphs;
21214
+ }
21215
+ function walkProcessNodes(ir, scope = []) {
21216
+ const out = [];
21217
+ for (const node of ir.nodes) {
21218
+ if (isProcessArtifactProvenance(node.provenance)) {
21219
+ out.push({ nodeId: node.id, scope, node, provenance: node.provenance });
21220
+ }
21221
+ for (const child of childGraphsOf(node)) {
21222
+ out.push(...walkProcessNodes(child, [...scope, node.id]));
21223
+ }
21224
+ }
21225
+ return out;
21226
+ }
21227
+ function findProcessNodesByCommand(ir, command) {
21228
+ return walkProcessNodes(ir).filter((m) => m.provenance.bind?.commands?.includes(command));
21229
+ }
21230
+ function findProcessNodesByElementId(ir, elementId, element) {
21231
+ return walkProcessNodes(ir).filter(
21232
+ (m) => m.provenance.elementId === elementId && (element === void 0 || m.provenance.element === element)
21233
+ );
21234
+ }
21167
21235
  function cloneWorkflow(workflow) {
21168
21236
  return JSON.parse(JSON.stringify(workflow));
21169
21237
  }
@@ -21215,13 +21283,17 @@ function sanitizeMainPath(workflow, mainPath) {
21215
21283
  }
21216
21284
  function getChildGraph(node) {
21217
21285
  if ("childGraph" in node && node.childGraph) return node.childGraph;
21218
- if ("backing" in node && node.backing?.backingGraph) return node.backing.backingGraph;
21286
+ if ("backing" in node && node.backing?.backingGraph)
21287
+ return node.backing.backingGraph;
21219
21288
  return null;
21220
21289
  }
21221
21290
  function setChildGraph(node, childGraph) {
21222
21291
  if ("childGraph" in node) return { ...node, childGraph };
21223
21292
  if ("backing" in node && node.backing?.backingGraph) {
21224
- return { ...node, backing: { ...node.backing, backingGraph: childGraph } };
21293
+ return {
21294
+ ...node,
21295
+ backing: { ...node.backing, backingGraph: childGraph }
21296
+ };
21225
21297
  }
21226
21298
  return { ...node, childGraph };
21227
21299
  }
@@ -21378,7 +21450,9 @@ function wrapIntoGroup(workflow, nodeIds, groupId, label, groupType) {
21378
21450
  let mainBody = inParentMain.length ? inParentMain : nodeIds;
21379
21451
  let mainTail = [];
21380
21452
  if (successExit) {
21381
- const exitSources = new Set(exitChildEdges.filter((e) => e.to === successExit.id).map((e) => e.from));
21453
+ const exitSources = new Set(
21454
+ exitChildEdges.filter((e) => e.to === successExit.id).map((e) => e.from)
21455
+ );
21382
21456
  const adj = /* @__PURE__ */ new Map();
21383
21457
  for (const e of internal) {
21384
21458
  if (e.isExceptional || e.isTimeout) continue;
@@ -21417,7 +21491,11 @@ function wrapIntoGroup(workflow, nodeIds, groupId, label, groupType) {
21417
21491
  childGraph = {
21418
21492
  name: label,
21419
21493
  description: "",
21420
- nodes: [{ id: startId, type: "start", label: "Start" }, ...groupedNodes, ...extraChildNodes],
21494
+ nodes: [
21495
+ { id: startId, type: "start", label: "Start" },
21496
+ ...groupedNodes,
21497
+ ...extraChildNodes
21498
+ ],
21421
21499
  edges: [
21422
21500
  { from: startId, to: entry, isMainPath: true },
21423
21501
  ...internal.map(markMain),
@@ -21426,7 +21504,13 @@ function wrapIntoGroup(workflow, nodeIds, groupId, label, groupType) {
21426
21504
  mainPath: [startId, ...mainBody, ...mainTail]
21427
21505
  };
21428
21506
  }
21429
- const groupNode = { id: groupId, type: "group", groupType, label, childGraph };
21507
+ const groupNode = {
21508
+ id: groupId,
21509
+ type: "group",
21510
+ groupType,
21511
+ label,
21512
+ childGraph
21513
+ };
21430
21514
  const newNodes = [];
21431
21515
  let placed = false;
21432
21516
  for (const n of workflow.nodes) {
@@ -21472,7 +21556,10 @@ function applyWorkflowPatch(workflow, patch) {
21472
21556
  return applyScopedPatch(workflow, patch.scope, patch);
21473
21557
  }
21474
21558
  if (patch.op === "batch") {
21475
- return patch.patches.reduce((current, operation) => applyWorkflowPatch(current, operation), workflow);
21559
+ return patch.patches.reduce(
21560
+ (current, operation) => applyWorkflowPatch(current, operation),
21561
+ workflow
21562
+ );
21476
21563
  }
21477
21564
  if (patch.op === "replace") {
21478
21565
  return cloneWorkflow(patch.workflow);
@@ -21506,7 +21593,9 @@ function applyWorkflowPatch(workflow, patch) {
21506
21593
  next.mainPath = next.mainPath.filter((nodeId) => nodeId !== patch.nodeId);
21507
21594
  const shouldRemoveEdges = patch.removeAttachedEdges ?? true;
21508
21595
  if (shouldRemoveEdges) {
21509
- next.edges = next.edges.filter((edge) => edge.from !== patch.nodeId && edge.to !== patch.nodeId);
21596
+ next.edges = next.edges.filter(
21597
+ (edge) => edge.from !== patch.nodeId && edge.to !== patch.nodeId
21598
+ );
21510
21599
  }
21511
21600
  return next;
21512
21601
  }
@@ -21554,7 +21643,13 @@ function applyWorkflowPatch(workflow, patch) {
21554
21643
  return next;
21555
21644
  }
21556
21645
  case "group": {
21557
- return wrapIntoGroup(next, patch.nodeIds, patch.groupId, patch.label, patch.groupType ?? "function");
21646
+ return wrapIntoGroup(
21647
+ next,
21648
+ patch.nodeIds,
21649
+ patch.groupId,
21650
+ patch.label,
21651
+ patch.groupType ?? "function"
21652
+ );
21558
21653
  }
21559
21654
  case "setMainPath": {
21560
21655
  next.mainPath = sanitizeMainPath(next, patch.mainPath);
@@ -21852,6 +21947,78 @@ var init_change_counts = __esm({
21852
21947
 
21853
21948
  // libs/workflow-model/src/process-artifact.ts
21854
21949
  import { z as z4 } from "zod";
21950
+ function outcomeTriggers(outcome) {
21951
+ const raw = Array.isArray(outcome.when) ? outcome.when : [outcome.when];
21952
+ return raw.map((trigger) => {
21953
+ if ("predicate" in trigger) return { kind: "predicate", name: trigger.predicate };
21954
+ if ("timeoutOf" in trigger) return { kind: "timeout", name: trigger.timeoutOf };
21955
+ return { kind: "command", name: trigger.command };
21956
+ });
21957
+ }
21958
+ function resolveSubstepActor(artifact, stage, substep) {
21959
+ const actorId = substep.actor ?? stage.actor;
21960
+ if (!actorId) return void 0;
21961
+ const actor = artifact.process.actors[actorId];
21962
+ if (!actor) return void 0;
21963
+ return {
21964
+ id: actorId,
21965
+ label: actor.label,
21966
+ ...actor.category ? { category: actor.category } : {},
21967
+ ...substep.selector ?? actor.selector ? { selector: substep.selector ?? actor.selector } : {},
21968
+ ...actor.cardinality ? { cardinality: actor.cardinality } : {}
21969
+ };
21970
+ }
21971
+ function resolveActorRef(artifact, actorId) {
21972
+ const actor = artifact.process.actors[actorId];
21973
+ if (!actor) return void 0;
21974
+ return {
21975
+ id: actorId,
21976
+ label: actor.label,
21977
+ ...actor.category ? { category: actor.category } : {},
21978
+ ...actor.selector ? { selector: actor.selector } : {},
21979
+ ...actor.cardinality ? { cardinality: actor.cardinality } : {}
21980
+ };
21981
+ }
21982
+ function buildProcessManifest(artifact, outcomeNodeIds) {
21983
+ const { process: process2 } = artifact;
21984
+ const actors = {};
21985
+ for (const actorId of Object.keys(process2.actors)) {
21986
+ actors[actorId] = resolveActorRef(artifact, actorId);
21987
+ }
21988
+ let selectors;
21989
+ if (process2.selectors) {
21990
+ selectors = {};
21991
+ for (const [name, selector] of Object.entries(process2.selectors)) {
21992
+ selectors[name] = {
21993
+ name,
21994
+ label: selector.label,
21995
+ ...selector.description ? { description: selector.description } : {}
21996
+ };
21997
+ }
21998
+ }
21999
+ const outcomes = process2.outcomes.map((outcome) => {
22000
+ const nodeId = outcomeNodeIds?.get(outcome.id);
22001
+ return {
22002
+ id: outcome.id,
22003
+ label: outcome.label,
22004
+ kind: outcome.kind,
22005
+ triggers: outcomeTriggers(outcome),
22006
+ ...outcome.description ? { description: outcome.description } : {},
22007
+ ...nodeId ? { nodeId } : {}
22008
+ };
22009
+ });
22010
+ const pointOfNoReturnStage = process2.stages.find((stage) => stage.pointOfNoReturn);
22011
+ return {
22012
+ source: "process-artifact",
22013
+ artifactVersion: process2.version,
22014
+ revision: process2.revision,
22015
+ workflowType: process2.workflowType,
22016
+ actors,
22017
+ ...selectors ? { selectors } : {},
22018
+ outcomes,
22019
+ ...pointOfNoReturnStage ? { pointOfNoReturnStageId: pointOfNoReturnStage.id } : {}
22020
+ };
22021
+ }
21855
22022
  function formatPath(path44) {
21856
22023
  return path44.length > 0 ? z4.core.toDotPath(path44) : "(root)";
21857
22024
  }
@@ -21871,19 +22038,74 @@ function issueToDiagnostic(issue) {
21871
22038
  path: path44
21872
22039
  };
21873
22040
  }
22041
+ function upgradeV1(artifact) {
22042
+ const diagnostics = [
22043
+ {
22044
+ severity: "warning",
22045
+ code: "DEPRECATED_ARTIFACT_VERSION",
22046
+ message: `process.version: artifact is version 1; upgraded in memory to version ${CURRENT_PROCESS_VERSION}. Migrate the file (add process.revision, declare actor categories/selectors) \u2014 version 1 is a deprecated input, not a second authoring format.`,
22047
+ path: "process.version"
22048
+ },
22049
+ {
22050
+ severity: "warning",
22051
+ code: "MISSING_PROCESS_REVISION",
22052
+ message: `process.revision: version 1 has no revision; recorded as "${UNVERSIONED_REVISION}". Anything pinned to this artifact cannot name which version of the process it ran.`,
22053
+ path: "process.revision"
22054
+ }
22055
+ ];
22056
+ const process2 = artifact.process;
22057
+ const actors = {};
22058
+ for (const [actorId, actor] of Object.entries(process2.actors)) {
22059
+ const legacyCategory = LEGACY_V1_ACTOR_CATEGORY[actorId];
22060
+ actors[actorId] = {
22061
+ ...actor,
22062
+ ...actor.category === void 0 && legacyCategory ? { category: legacyCategory } : {}
22063
+ };
22064
+ }
22065
+ return {
22066
+ artifact: {
22067
+ ...artifact,
22068
+ process: {
22069
+ ...process2,
22070
+ version: CURRENT_PROCESS_VERSION,
22071
+ revision: UNVERSIONED_REVISION,
22072
+ actors
22073
+ }
22074
+ },
22075
+ diagnostics
22076
+ };
22077
+ }
21874
22078
  function parseProcessArtifact(json) {
21875
22079
  const result = processArtifactSchema.safeParse(json);
21876
- if (result.success) {
21877
- return { artifact: result.data, diagnostics: [] };
22080
+ if (!result.success) {
22081
+ return { diagnostics: result.error.issues.map(issueToDiagnostic) };
22082
+ }
22083
+ if (result.data.process.version < CURRENT_PROCESS_VERSION) {
22084
+ return upgradeV1(result.data);
21878
22085
  }
21879
- return { diagnostics: result.error.issues.map(issueToDiagnostic) };
22086
+ return { artifact: result.data, diagnostics: [] };
21880
22087
  }
21881
- var actorSchema, substepBindSchema, substepSchema, outcomeWhenSchema, outcomeSchema, stageCompleteSchema, stageSchema, processBlockSchema, processArtifactSchema;
22088
+ var CURRENT_PROCESS_VERSION, SUPPORTED_PROCESS_VERSIONS, UNVERSIONED_REVISION, LEGACY_V1_ACTOR_CATEGORY, actorSchema, selectorSchema, substepBindSchema, substepSchema, outcomeTriggerSchema, outcomeWhenSchema, outcomeSchema, stageCompleteSchema, stageSchema, processBlockSchema, processArtifactSchema;
21882
22089
  var init_process_artifact = __esm({
21883
22090
  "libs/workflow-model/src/process-artifact.ts"() {
21884
22091
  "use strict";
22092
+ CURRENT_PROCESS_VERSION = 2;
22093
+ SUPPORTED_PROCESS_VERSIONS = [1, 2];
22094
+ UNVERSIONED_REVISION = "unversioned";
22095
+ LEGACY_V1_ACTOR_CATEGORY = {
22096
+ customer: "human",
22097
+ banker: "approval",
22098
+ system: "data"
22099
+ };
21885
22100
  actorSchema = z4.object({
21886
- label: z4.string()
22101
+ label: z4.string(),
22102
+ category: z4.string().min(1).optional(),
22103
+ selector: z4.string().min(1).optional(),
22104
+ cardinality: z4.enum(["one", "many"]).optional()
22105
+ }).strict();
22106
+ selectorSchema = z4.object({
22107
+ label: z4.string(),
22108
+ description: z4.string().optional()
21887
22109
  }).strict();
21888
22110
  substepBindSchema = z4.object({
21889
22111
  commands: z4.array(z4.string()).optional(),
@@ -21901,11 +22123,30 @@ var init_process_artifact = __esm({
21901
22123
  id: z4.string(),
21902
22124
  label: z4.string(),
21903
22125
  actor: z4.string().optional(),
22126
+ /**
22127
+ * Overrides the actor's own `selector` for this substep only — the case
22128
+ * where one cast entry is resolved differently at one step (a handoff
22129
+ * target, an escalation pool). Must be declared in `process.selectors`.
22130
+ */
22131
+ selector: z4.string().min(1).optional(),
21904
22132
  bind: substepBindSchema
21905
22133
  }).strict();
22134
+ outcomeTriggerSchema = z4.union([
22135
+ z4.object({ predicate: z4.string().min(1) }).strict(),
22136
+ z4.object({ timeoutOf: z4.string().min(1) }).strict(),
22137
+ z4.object({ command: z4.string().min(1) }).strict()
22138
+ ]);
21906
22139
  outcomeWhenSchema = z4.union([
21907
- z4.object({ predicate: z4.string() }).strict(),
21908
- z4.object({ timeoutOf: z4.string() }).strict()
22140
+ outcomeTriggerSchema,
22141
+ z4.array(outcomeTriggerSchema).superRefine((triggers, ctx) => {
22142
+ if (triggers.length === 0) {
22143
+ ctx.addIssue({
22144
+ code: "custom",
22145
+ message: "when must declare at least one trigger",
22146
+ params: { code: "EMPTY_OUTCOME_TRIGGER" }
22147
+ });
22148
+ }
22149
+ })
21909
22150
  ]);
21910
22151
  outcomeSchema = z4.object({
21911
22152
  id: z4.string(),
@@ -21928,20 +22169,64 @@ var init_process_artifact = __esm({
21928
22169
  }).strict();
21929
22170
  processBlockSchema = z4.object({
21930
22171
  version: z4.number(),
22172
+ /**
22173
+ * Pinned identity of this authored content. Opaque to the platform —
22174
+ * a date, a semver, a content hash, a monotonic counter all work. What
22175
+ * matters is that changing the process changes it, so an execution can
22176
+ * be pinned to the revision it started under. Required from V2; a V1
22177
+ * artifact is upgraded with {@link UNVERSIONED_REVISION} and a warning.
22178
+ */
22179
+ revision: z4.string().min(1).optional(),
21931
22180
  workflowType: z4.string(),
21932
22181
  actors: z4.record(z4.string(), actorSchema),
22182
+ /** Named selector registry — every `selector` reference resolves here. */
22183
+ selectors: z4.record(z4.string(), selectorSchema).optional(),
21933
22184
  stages: z4.array(stageSchema),
21934
22185
  outcomes: z4.array(outcomeSchema),
21935
22186
  internalCommands: z4.array(z4.string()).optional()
21936
22187
  }).strict().superRefine((process2, ctx) => {
21937
- if (process2.version !== 1) {
22188
+ if (!SUPPORTED_PROCESS_VERSIONS.includes(process2.version)) {
21938
22189
  ctx.addIssue({
21939
22190
  code: "custom",
21940
22191
  path: ["version"],
21941
- message: `Unsupported process.version ${JSON.stringify(process2.version)}; this parser only understands version 1. Upgrade the parser or downgrade the artifact.`,
22192
+ message: `Unsupported process.version ${JSON.stringify(process2.version)}; this parser understands ${SUPPORTED_PROCESS_VERSIONS.join(" and ")}. Upgrade the parser or downgrade the artifact.`,
21942
22193
  params: { code: "UNSUPPORTED_VERSION" }
21943
22194
  });
21944
22195
  }
22196
+ if (process2.version >= CURRENT_PROCESS_VERSION && process2.revision === void 0) {
22197
+ ctx.addIssue({
22198
+ code: "custom",
22199
+ path: ["revision"],
22200
+ message: `process.revision is required from version ${CURRENT_PROCESS_VERSION}. Give this authored content a pinned identity (a date, a semver, or a content hash) so an execution can name the revision it is running.`,
22201
+ params: { code: "MISSING_PROCESS_REVISION" }
22202
+ });
22203
+ }
22204
+ const declaredSelectors = new Set(Object.keys(process2.selectors ?? {}));
22205
+ const checkActor = (actorId, path44) => {
22206
+ if (actorId === void 0) return;
22207
+ if (!Object.prototype.hasOwnProperty.call(process2.actors, actorId)) {
22208
+ ctx.addIssue({
22209
+ code: "custom",
22210
+ path: path44,
22211
+ message: `Actor "${actorId}" is referenced at ${z4.core.toDotPath(path44)} but not declared in process.actors. Declare the actor (label, and optionally category/selector) or fix the reference.`,
22212
+ params: { code: "UNDECLARED_ACTOR" }
22213
+ });
22214
+ }
22215
+ };
22216
+ const checkSelector = (selector, path44) => {
22217
+ if (selector === void 0) return;
22218
+ if (!declaredSelectors.has(selector)) {
22219
+ ctx.addIssue({
22220
+ code: "custom",
22221
+ path: path44,
22222
+ message: `Selector "${selector}" is referenced at ${z4.core.toDotPath(path44)} but not declared in process.selectors. Declare the selector or fix the reference.`,
22223
+ params: { code: "UNDECLARED_SELECTOR" }
22224
+ });
22225
+ }
22226
+ };
22227
+ for (const [actorId, actor] of Object.entries(process2.actors)) {
22228
+ checkSelector(actor.selector, ["actors", actorId, "selector"]);
22229
+ }
21945
22230
  const SAME_KIND_CODE = {
21946
22231
  stage: "DUPLICATE_STAGE_ID",
21947
22232
  substep: "DUPLICATE_SUBSTEP_ID",
@@ -21966,8 +22251,17 @@ var init_process_artifact = __esm({
21966
22251
  };
21967
22252
  process2.stages.forEach((stage, stageIndex) => {
21968
22253
  checkId(stage.id, "stage", ["stages", stageIndex, "id"]);
22254
+ checkActor(stage.actor, ["stages", stageIndex, "actor"]);
21969
22255
  stage.substeps?.forEach((substep, substepIndex) => {
21970
22256
  checkId(substep.id, "substep", ["stages", stageIndex, "substeps", substepIndex, "id"]);
22257
+ checkActor(substep.actor, ["stages", stageIndex, "substeps", substepIndex, "actor"]);
22258
+ checkSelector(substep.selector, [
22259
+ "stages",
22260
+ stageIndex,
22261
+ "substeps",
22262
+ substepIndex,
22263
+ "selector"
22264
+ ]);
21971
22265
  });
21972
22266
  });
21973
22267
  process2.outcomes.forEach((outcome, outcomeIndex) => {
@@ -21990,7 +22284,7 @@ function stagePath(stageIndex) {
21990
22284
  function substepPath(stageIndex, substepIndex) {
21991
22285
  return `${stagePath(stageIndex)}.substeps[${substepIndex}]`;
21992
22286
  }
21993
- function lintProcess(artifact, inventory, predicateNames) {
22287
+ function lintProcess(artifact, inventory, predicateNames, options = {}) {
21994
22288
  const diagnostics = [];
21995
22289
  const process2 = artifact.process;
21996
22290
  const commandNames = new Set(inventory.commands.map((c) => c.name));
@@ -22009,6 +22303,19 @@ function lintProcess(artifact, inventory, predicateNames) {
22009
22303
  });
22010
22304
  }
22011
22305
  };
22306
+ if (options.selectorNames) {
22307
+ const selectorSet = new Set(options.selectorNames);
22308
+ for (const [name] of Object.entries(process2.selectors ?? {})) {
22309
+ if (!selectorSet.has(name)) {
22310
+ diagnostics.push({
22311
+ severity: "error",
22312
+ code: "UNKNOWN_SELECTOR",
22313
+ message: `Selector "${name}" declared at process.selectors.${name} is not in the selector registry; the application has no rule resolving it to subjects.`,
22314
+ path: `process.selectors.${name}`
22315
+ });
22316
+ }
22317
+ }
22318
+ }
22012
22319
  process2.stages.forEach((stage, stageIndex) => {
22013
22320
  const predicatePath = `${stagePath(stageIndex)}.complete.predicate`;
22014
22321
  const predicate = stage.complete?.predicate;
@@ -22050,9 +22357,25 @@ function lintProcess(artifact, inventory, predicateNames) {
22050
22357
  });
22051
22358
  });
22052
22359
  process2.outcomes.forEach((outcome, outcomeIndex) => {
22053
- if ("predicate" in outcome.when) {
22054
- checkPredicate(outcome.when.predicate, `process.outcomes[${outcomeIndex}].when.predicate`);
22055
- }
22360
+ const whenPath = `process.outcomes[${outcomeIndex}].when`;
22361
+ outcomeTriggers(outcome).forEach((trigger, triggerIndex) => {
22362
+ const path44 = Array.isArray(outcome.when) ? `${whenPath}[${triggerIndex}]` : whenPath;
22363
+ if (trigger.kind === "predicate") {
22364
+ checkPredicate(trigger.name, `${path44}.predicate`);
22365
+ } else if (trigger.kind === "command") {
22366
+ if (!boundCommandPaths.has(trigger.name)) {
22367
+ boundCommandPaths.set(trigger.name, `${path44}.command`);
22368
+ }
22369
+ if (!commandNames.has(trigger.name)) {
22370
+ diagnostics.push({
22371
+ severity: "error",
22372
+ code: "BOUND_COMMAND_NOT_IN_INVENTORY",
22373
+ message: `Command "${trigger.name}" triggering outcome "${outcome.id}" (${path44}.command) does not exist in the inventory.`,
22374
+ path: `${path44}.command`
22375
+ });
22376
+ }
22377
+ }
22378
+ });
22056
22379
  });
22057
22380
  const hasSuccessOutcome = process2.outcomes.some((o) => o.kind === "success");
22058
22381
  const hasFailureOutcome = process2.outcomes.some((o) => o.kind === "failure");
@@ -22097,7 +22420,7 @@ function lintProcess(artifact, inventory, predicateNames) {
22097
22420
  diagnostics.push({
22098
22421
  severity: "error",
22099
22422
  code: "UNBOUND_INVENTORY_COMMAND",
22100
- message: `Inventory command "${command.name}" is neither bound by any substep nor listed in process.internalCommands.`,
22423
+ message: `Inventory command "${command.name}" is neither bound by any substep, nor named as an outcome trigger, nor listed in process.internalCommands.`,
22101
22424
  path: "process.internalCommands"
22102
22425
  });
22103
22426
  }
@@ -22115,28 +22438,39 @@ function lintProcess(artifact, inventory, predicateNames) {
22115
22438
  var init_process_lint = __esm({
22116
22439
  "libs/workflow-model/src/process-lint.ts"() {
22117
22440
  "use strict";
22441
+ init_process_artifact();
22118
22442
  }
22119
22443
  });
22120
22444
 
22121
22445
  // libs/workflow-model/src/index.ts
22122
22446
  var src_exports2 = {};
22123
22447
  __export(src_exports2, {
22448
+ CURRENT_PROCESS_VERSION: () => CURRENT_PROCESS_VERSION,
22449
+ SUPPORTED_PROCESS_VERSIONS: () => SUPPORTED_PROCESS_VERSIONS,
22450
+ UNVERSIONED_REVISION: () => UNVERSIONED_REVISION,
22124
22451
  WorkflowPatchError: () => WorkflowPatchError,
22125
22452
  WorkflowPatchStream: () => WorkflowPatchStream,
22126
22453
  applyPreviewPatch: () => applyPreviewPatch,
22127
22454
  applyWorkflowPatch: () => applyWorkflowPatch,
22128
22455
  applyWorkflowPatches: () => applyWorkflowPatches,
22456
+ buildProcessManifest: () => buildProcessManifest,
22129
22457
  computeHappyPath: () => computeHappyPath,
22130
22458
  countChanges: () => countChanges,
22131
22459
  describeChanges: () => describeChanges,
22132
22460
  emptyWorkflowIR: () => emptyWorkflowIR,
22461
+ findProcessNodesByCommand: () => findProcessNodesByCommand,
22462
+ findProcessNodesByElementId: () => findProcessNodesByElementId,
22133
22463
  lintProcess: () => lintProcess,
22464
+ outcomeTriggers: () => outcomeTriggers,
22134
22465
  parseProcessArtifact: () => parseProcessArtifact,
22135
22466
  processArtifactSchema: () => processArtifactSchema,
22467
+ resolveActorRef: () => resolveActorRef,
22468
+ resolveSubstepActor: () => resolveSubstepActor,
22136
22469
  validateWorkflowIR: () => validateWorkflowIR,
22137
- validateWorkflowPatches: () => validateWorkflowPatches
22470
+ validateWorkflowPatches: () => validateWorkflowPatches,
22471
+ walkProcessNodes: () => walkProcessNodes
22138
22472
  });
22139
- var init_src3 = __esm({
22473
+ var init_src4 = __esm({
22140
22474
  "libs/workflow-model/src/index.ts"() {
22141
22475
  "use strict";
22142
22476
  init_workflow_ir();
@@ -22358,6 +22692,9 @@ async function uploadToS3(bucket, appName, workflows) {
22358
22692
  })
22359
22693
  );
22360
22694
  }
22695
+ function parseNameList(value) {
22696
+ return value ? value.split(",").map((name) => name.trim()).filter((name) => name.length > 0) : void 0;
22697
+ }
22361
22698
  function registerWorkflowCommands(program2) {
22362
22699
  const workflow = program2.command("workflow").description("Workflow tooling \u2014 IR extraction, visualization");
22363
22700
  workflow.command("extract-ir <path>").description(
@@ -22464,13 +22801,16 @@ function registerWorkflowCommands(program2) {
22464
22801
  }
22465
22802
  });
22466
22803
  workflow.command("lint-process <path>").description(
22467
- "Lint a process artifact's bindings against the as-built code inventory\n\nReuses the inventory extraction's aux/activity discovery, resolves the process\nartifact (--process, or the same sibling process/*.process.json discovery\nextract-ir uses), then runs parseProcessArtifact + lintProcess against it.\n\nWithout --predicates, the CLI cannot verify predicate names against the worker's\nregistry (it has no way to execute it) \u2014 predicate-existence findings are skipped\nand a note is printed; every other rule (bindings, coverage, ids, outcomes,\nworkflowType) still runs. Pass --predicates for full conformance, or rely on the\nworker's own process-conformance test which has the registry in-process."
22804
+ "Lint a process artifact's bindings against the as-built code inventory\n\nReuses the inventory extraction's aux/activity discovery, resolves the process\nartifact (--process, or the same sibling process/*.process.json discovery\nextract-ir uses), then runs parseProcessArtifact + lintProcess against it.\n\nWithout --predicates / --selectors, the CLI cannot verify those names against the\nworker's registries (it has no way to execute them) \u2014 the corresponding findings\nare skipped and a note is printed; every other rule (bindings, coverage, ids,\nactors, outcomes, workflowType) still runs. Pass both for full conformance, or rely\non the worker's own process-conformance test which has the registries in-process."
22468
22805
  ).option(
22469
22806
  "--process <path>",
22470
22807
  "Path to a process artifact JSON file. Absent this flag, a sibling process/*.process.json is auto-discovered per workflow file."
22471
22808
  ).option(
22472
22809
  "--predicates <names>",
22473
22810
  "Comma-separated named-predicate registry (enables UNKNOWN_PREDICATE checks)"
22811
+ ).option(
22812
+ "--selectors <names>",
22813
+ "Comma-separated named-selector registry (enables UNKNOWN_SELECTOR checks)"
22474
22814
  ).action(async (targetPath, opts) => {
22475
22815
  try {
22476
22816
  const resolvedPath = path43.resolve(targetPath);
@@ -22499,8 +22839,8 @@ function registerWorkflowCommands(program2) {
22499
22839
  let parseProcessArtifact2;
22500
22840
  let lintProcess2;
22501
22841
  try {
22502
- ({ parseProcessArtifact: parseProcessArtifact2 } = await Promise.resolve().then(() => (init_src3(), src_exports2)));
22503
- ({ lintProcess: lintProcess2 } = await Promise.resolve().then(() => (init_src3(), src_exports2)));
22842
+ ({ parseProcessArtifact: parseProcessArtifact2 } = await Promise.resolve().then(() => (init_src4(), src_exports2)));
22843
+ ({ lintProcess: lintProcess2 } = await Promise.resolve().then(() => (init_src4(), src_exports2)));
22504
22844
  } catch {
22505
22845
  logError(
22506
22846
  "Cannot resolve @mesh-tech/workflow-model (parseProcessArtifact / lintProcess).\nIt is bundled into the published CLI \u2014 on a registry install this means a broken install; reinstall the CLI. In the monorepo, run the scoped install (pnpm bootstrap:worktree @mesh-tech/mesh-cli)."
@@ -22508,12 +22848,16 @@ function registerWorkflowCommands(program2) {
22508
22848
  process.exitCode = 1;
22509
22849
  return;
22510
22850
  }
22511
- const predicateNames = opts.predicates ? opts.predicates.split(",").map((s) => s.trim()).filter((s) => s.length > 0) : void 0;
22851
+ const predicateNames = parseNameList(opts.predicates);
22852
+ const selectorNames = parseNameList(opts.selectors);
22512
22853
  if (!predicateNames) {
22513
22854
  logWarn(
22514
22855
  "predicate checks skipped \u2014 pass --predicates or run the worker conformance test"
22515
22856
  );
22516
22857
  }
22858
+ if (!selectorNames) {
22859
+ logWarn("selector checks skipped \u2014 pass --selectors or run the worker conformance test");
22860
+ }
22517
22861
  logInfo(`Linting process artifact(s) against ${resolvedPath}`);
22518
22862
  const results = runLintExtraction(resolvedPath, extractorPath, resolvedProcessPath);
22519
22863
  if (!results || results.length === 0) {
@@ -22535,7 +22879,12 @@ ${inventory.workflowType} (${sourceFile})`);
22535
22879
  const { artifact, diagnostics: parseDiagnostics } = parseProcessArtifact2(processArtifact);
22536
22880
  let diagnostics = parseDiagnostics;
22537
22881
  if (artifact) {
22538
- const lintDiagnostics = lintProcess2(artifact, inventory, predicateNames ?? []);
22882
+ const lintDiagnostics = lintProcess2(
22883
+ artifact,
22884
+ inventory,
22885
+ predicateNames ?? [],
22886
+ selectorNames ? { selectorNames } : {}
22887
+ );
22539
22888
  const reportable = predicateNames ? lintDiagnostics : lintDiagnostics.filter((d) => d.code !== "UNKNOWN_PREDICATE");
22540
22889
  diagnostics = diagnostics.concat(reportable);
22541
22890
  }