@habitat-ai/cli 0.2.4 → 0.3.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/nx-plugin.js CHANGED
@@ -1733,6 +1733,7 @@ var HABITAT_CATALOG_PATHS = [
1733
1733
  var HABITAT_AUTHORITY_GLOB = `{${HABITAT_CATALOG_PATHS.join(",")}}`;
1734
1734
  var DEFAULT_CHECK_TARGET = "check:policy";
1735
1735
  var HABITAT_EXECUTABLE = "habitat";
1736
+ var PORTABLE_OWNER_PROJECT = /^[A-Za-z0-9@][A-Za-z0-9@._/+:-]*$/u;
1736
1737
  function createHabitatNxPlugin(binding) {
1737
1738
  const createNodes = [
1738
1739
  HABITAT_AUTHORITY_GLOB,
@@ -1778,8 +1779,7 @@ function projectCatalogTargets(matchedFiles, catalog, runtimeInputs) {
1778
1779
  addLeafTarget(project, compatibilityTargetName(rule), compatibilityTarget(rule, runtimeInputs));
1779
1780
  }
1780
1781
  return [...projects.entries()].sort(([left], [right]) => compareText(left, right)).map(([root, project]) => {
1781
- const leafTargets = Object.keys(project.targets).sort();
1782
- project.targets[DEFAULT_CHECK_TARGET] = ownerTarget(project.ownerProject, leafTargets, project.compatibilityManifests.size > 0);
1782
+ project.targets[DEFAULT_CHECK_TARGET] = ownerTarget(project.ownerProject, ownerInputs(project.targets, runtimeInputs), project.compatibilityManifests.size > 0);
1783
1783
  return [
1784
1784
  projectionSource(project),
1785
1785
  {
@@ -1900,17 +1900,32 @@ function compatibilityTarget(rule, runtimeInputs) {
1900
1900
  metadata: { description: `Check Habitat compatibility rule ${rule.ruleId}` }
1901
1901
  };
1902
1902
  }
1903
- function ownerTarget(ownerProject, leafTargets, hasCompatibility) {
1903
+ function ownerTarget(ownerProject, inputs, hasCompatibility) {
1904
+ if (!PORTABLE_OWNER_PROJECT.test(ownerProject)) {
1905
+ throw new Error(`Habitat Nx projection rejected owner '${ownerProject}': identity is not portable as an Nx command argument.`);
1906
+ }
1904
1907
  return {
1905
- executor: "nx:noop",
1906
- cache: false,
1908
+ command: `${HABITAT_EXECUTABLE} check --owner ${ownerProject}`,
1909
+ cache: true,
1910
+ inputs,
1907
1911
  outputs: [],
1908
- dependsOn: leafTargets.map((target) => ({ target })),
1912
+ options: { cwd: "{workspaceRoot}" },
1909
1913
  metadata: {
1910
1914
  description: hasCompatibility ? `Check resolved Habitat policy owned by ${ownerProject}` : `Check resolved Habitat applications owned by ${ownerProject}`
1911
1915
  }
1912
1916
  };
1913
1917
  }
1918
+ function ownerInputs(targets, runtimeInputs) {
1919
+ const runtimeStrings = new Set(runtimeInputs.filter((input) => typeof input === "string"));
1920
+ const corpusInputs = new Set;
1921
+ for (const target of Object.values(targets)) {
1922
+ for (const input of target.inputs ?? []) {
1923
+ if (typeof input === "string" && !runtimeStrings.has(input))
1924
+ corpusInputs.add(input);
1925
+ }
1926
+ }
1927
+ return [...runtimeInputs, ...[...corpusInputs].sort(compareText)];
1928
+ }
1914
1929
  function applicationInputs(application, runtimeInputs) {
1915
1930
  const files = new Set(HABITAT_CATALOG_PATHS.map(workspaceInput));
1916
1931
  if (application.runner.name === "grit") {
@@ -1934,15 +1949,25 @@ function compatibilityInputs(rule, runtimeInputs) {
1934
1949
  files.add(workspaceInput(rule.baseline.relativePath));
1935
1950
  for (const pattern of rule.coveragePatterns)
1936
1951
  files.add(workspaceInput(pattern));
1937
- files.add(workspaceInput(rule.runner.name === "grit" ? rule.runner.pattern.relativePath : rule.runner.structure.relativePath));
1952
+ if (rule.runner.name === "grit") {
1953
+ files.add(workspaceInput(rule.runner.pattern.relativePath));
1954
+ for (const entry of rule.runner.acquisition.entries) {
1955
+ addSubjectInputs(files, entry.path, entry.kind);
1956
+ }
1957
+ } else {
1958
+ files.add(workspaceInput(rule.runner.structure.relativePath));
1959
+ }
1938
1960
  return [...runtimeInputs, ...[...files].sort(compareText)];
1939
1961
  }
1940
1962
  function addSubjectInputs(target, path, kind) {
1941
1963
  const exact = workspaceInput(path);
1942
- target.add(exact);
1943
- if (kind === "directory") {
1944
- target.add(path === "." ? "{workspaceRoot}/**/*" : `${exact}/**/*`);
1964
+ if (kind === "directory" && path === ".") {
1965
+ target.add("{workspaceRoot}/**/*");
1966
+ return;
1945
1967
  }
1968
+ target.add(exact);
1969
+ if (kind === "directory")
1970
+ target.add(`${exact}/**/*`);
1946
1971
  }
1947
1972
  function workspaceInput(path) {
1948
1973
  const normalized = normalizeWorkspacePath(path);
@@ -44644,6 +44669,17 @@ var MAX_RULE_EVALUATION_FAILURE_DETAIL = 4096;
44644
44669
  var NonEmptyStringSchema = exports_typebox.String({
44645
44670
  minLength: 1
44646
44671
  });
44672
+ var RuleEvaluationProgramSchema = ReadonlyObject(exports_typebox.Object({
44673
+ id: exports_typebox.String({
44674
+ minLength: 1,
44675
+ maxLength: 1024,
44676
+ description: "Invocation-local program identity used to attribute findings"
44677
+ }),
44678
+ program: exports_typebox.String({
44679
+ minLength: 1,
44680
+ description: "Already-resolved evaluator program"
44681
+ })
44682
+ }), { additionalProperties: false });
44647
44683
  var RuleEvaluationPositionSchema = ReadonlyObject(exports_typebox.Object({
44648
44684
  line: exports_typebox.Integer({
44649
44685
  minimum: 1,
@@ -44670,20 +44706,31 @@ var RuleEvaluationFindingSchema = ReadonlyObject(exports_typebox.Object({
44670
44706
  })
44671
44707
  }), { additionalProperties: false });
44672
44708
  var RuleEvaluationRequestSchema = ReadonlyObject(exports_typebox.Object({
44673
- program: exports_typebox.String({
44674
- minLength: 1,
44675
- description: "Already-resolved evaluator program"
44709
+ programs: ReadonlyObject(exports_typebox.Array(RuleEvaluationProgramSchema), {
44710
+ minItems: 1,
44711
+ description: "Non-empty ordered programs evaluated against the same subjects"
44676
44712
  }),
44677
44713
  subjectPaths: ReadonlyObject(exports_typebox.Array(NonEmptyStringSchema), {
44678
44714
  minItems: 1,
44679
44715
  description: "Non-empty caller-resolved absolute subject paths"
44680
44716
  })
44681
44717
  }), { additionalProperties: false });
44682
- var RuleEvaluationResultSchema = ReadonlyObject(exports_typebox.Object({
44718
+ var RuleEvaluationProgramResultSchema = ReadonlyObject(exports_typebox.Object({
44719
+ programId: exports_typebox.String({
44720
+ minLength: 1,
44721
+ maxLength: 1024,
44722
+ description: "Invocation-local identity of the evaluated program"
44723
+ }),
44683
44724
  findings: ReadonlyObject(exports_typebox.Array(RuleEvaluationFindingSchema), {
44684
44725
  description: "Findings reported by the evaluator in provider-defined stable order"
44685
44726
  })
44686
44727
  }), { additionalProperties: false });
44728
+ var RuleEvaluationResultSchema = ReadonlyObject(exports_typebox.Object({
44729
+ results: ReadonlyObject(exports_typebox.Array(RuleEvaluationProgramResultSchema), {
44730
+ minItems: 1,
44731
+ description: "Per-program results in request order"
44732
+ })
44733
+ }), { additionalProperties: false });
44687
44734
  var RuleEvaluationFailureReasonSchema = exports_typebox.Union([
44688
44735
  exports_typebox.Literal("InvalidInput"),
44689
44736
  exports_typebox.Literal("SetupFailed"),
@@ -46194,8 +46241,9 @@ __export(exports_value, {
46194
46241
  });
46195
46242
  // ../../resources/rule-evaluation/dist/providers/grit-effect-platform-node/index.js
46196
46243
  var TEMP_CATALOG_PREFIX = "habitat-rule-evaluation-";
46197
- var GRIT_PATTERN_NAME = "habitat_rule_evaluation";
46198
- var MAX_GRIT_OUTPUT_BYTES = 256 * 1024;
46244
+ var GRIT_PATTERN_PREFIX = "habitat_rule_evaluation_";
46245
+ var MAX_GRIT_OUTPUT_BYTES_PER_PROGRAM = 256 * 1024;
46246
+ var MAX_GRIT_BATCH_OUTPUT_BYTES = 16 * 1024 * 1024;
46199
46247
  var GritRuleEvaluationProviderConfigSchema = ReadonlyObject(exports_typebox.Object({
46200
46248
  executable: exports_typebox.String({
46201
46249
  minLength: 1,
@@ -46213,11 +46261,12 @@ var GritPositionSchema = exports_typebox.Object({
46213
46261
  }, { additionalProperties: false, description: "Grit-reported source position" });
46214
46262
  var GritResultSchema = exports_typebox.Object({
46215
46263
  check_id: exports_typebox.String({
46216
- pattern: `^#${GRIT_PATTERN_NAME}/.+$`,
46217
- description: "Provider-owned Grit check identity with evaluator suffix"
46264
+ minLength: 1,
46265
+ description: "Grit check identity with evaluator suffix"
46218
46266
  }),
46219
- local_name: exports_typebox.Literal(GRIT_PATTERN_NAME, {
46220
- description: "Provider-owned local Grit pattern name"
46267
+ local_name: exports_typebox.String({
46268
+ minLength: 1,
46269
+ description: "Local Grit catalog pattern name"
46221
46270
  }),
46222
46271
  path: exports_typebox.String({ minLength: 1, description: "Grit-reported subject path" }),
46223
46272
  start: GritPositionSchema,
@@ -46247,13 +46296,27 @@ function makeGritRuleEvaluationResource(config) {
46247
46296
  return yield* fail11("InvalidInput", "Grit provider configuration does not match its structural contract");
46248
46297
  }
46249
46298
  if (!requestValidator.Check(input)) {
46250
- return yield* fail11("InvalidInput", "Rule-evaluation request requires one resolved program and non-empty subject paths");
46299
+ return yield* fail11("InvalidInput", "Rule-evaluation request requires resolved programs and non-empty subject paths");
46251
46300
  }
46252
46301
  const path = yield* exports_Path.Path;
46253
46302
  if (input.subjectPaths.some((subjectPath) => !path.isAbsolute(subjectPath))) {
46254
46303
  return yield* fail11("InvalidInput", "Rule-evaluation subject paths must be caller-resolved absolute paths");
46255
46304
  }
46256
- return yield* exports_Effect.scoped(withScopedGritCatalog(input.program, (catalog) => runGritCheck(config, catalog, input.subjectPaths)));
46305
+ const [firstProgram, ...remainingPrograms] = input.programs;
46306
+ if (firstProgram === undefined) {
46307
+ return yield* fail11("InvalidInput", "Rule-evaluation request requires at least one program");
46308
+ }
46309
+ if (new Set(input.programs.map(({ id: id3 }) => id3)).size !== input.programs.length) {
46310
+ return yield* fail11("InvalidInput", "Rule-evaluation program identities must be unique");
46311
+ }
46312
+ const programs = [
46313
+ materializeProgram(firstProgram, 0),
46314
+ ...remainingPrograms.map((program) => materializeProgram(program, 0))
46315
+ ];
46316
+ const evaluations = yield* exports_Effect.forEach(programs, (program) => exports_Effect.scoped(withScopedGritCatalog([program], (catalog) => runGritCheck(config, catalog, [program], input.subjectPaths))), { concurrency: 1 });
46317
+ return Object.freeze({
46318
+ results: Object.freeze(evaluations.flatMap(({ results }) => results))
46319
+ });
46257
46320
  });
46258
46321
  return Object.freeze({ evaluate: evaluate6 });
46259
46322
  }
@@ -46263,7 +46326,7 @@ function makeNodeGritRuleEvaluationResource(config) {
46263
46326
  evaluate: (input) => resource.evaluate(input).pipe(exports_Effect.provide(exports_NodeServices.layer))
46264
46327
  });
46265
46328
  }
46266
- function withScopedGritCatalog(program, use) {
46329
+ function withScopedGritCatalog(programs, use) {
46267
46330
  return exports_Effect.gen(function* () {
46268
46331
  const fs = yield* exports_FileSystem.FileSystem;
46269
46332
  const path = yield* exports_Path.Path;
@@ -46277,11 +46340,12 @@ function withScopedGritCatalog(program, use) {
46277
46340
  yield* fs.makeDirectory(catalog.gritDirectory).pipe(mapPlatform("SetupFailed", "Failed to create Grit catalog directory"));
46278
46341
  yield* fs.makeDirectory(catalog.userConfigDirectory).pipe(mapPlatform("SetupFailed", "Failed to create Grit user-config directory"));
46279
46342
  yield* fs.makeDirectory(catalog.cacheDirectory).pipe(mapPlatform("SetupFailed", "Failed to create Grit cache directory"));
46280
- yield* fs.writeFileString(path.join(catalog.gritDirectory, "grit.yaml"), renderGritCatalog(program)).pipe(mapPlatform("SetupFailed", "Failed to write temporary Grit catalog"));
46343
+ yield* fs.writeFileString(path.join(catalog.gritDirectory, "grit.yaml"), renderGritCatalog(programs)).pipe(mapPlatform("SetupFailed", "Failed to write temporary Grit catalog"));
46281
46344
  return yield* use(catalog);
46282
46345
  });
46283
46346
  }
46284
- function runGritCheck(config, catalog, subjectPaths) {
46347
+ function runGritCheck(config, catalog, programs, subjectPaths) {
46348
+ const outputLimit = gritOutputLimit(programs.length);
46285
46349
  const observe = exports_Effect.gen(function* () {
46286
46350
  const command = exports_ChildProcess.make(config.executable, ["--json", "check", "--no-cache", "--grit-dir", catalog.gritDirectory, ...subjectPaths], {
46287
46351
  cwd: catalog.root,
@@ -46290,7 +46354,8 @@ function runGritCheck(config, catalog, subjectPaths) {
46290
46354
  GRIT_CACHE_DIR: catalog.cacheDirectory,
46291
46355
  GRIT_USER_CONFIG: catalog.userConfigDirectory,
46292
46356
  GRIT_TELEMETRY_DISABLED: "true",
46293
- NO_COLOR: undefined
46357
+ NO_COLOR: undefined,
46358
+ RAYON_NUM_THREADS: "2"
46294
46359
  },
46295
46360
  extendEnv: true,
46296
46361
  stdin: "ignore",
@@ -46299,8 +46364,8 @@ function runGritCheck(config, catalog, subjectPaths) {
46299
46364
  });
46300
46365
  const process2 = yield* command.pipe(mapPlatform("ExecutionFailed", "Failed to start Grit"));
46301
46366
  const output = yield* exports_Effect.all({
46302
- stdout: collectBoundedOutput(process2.stdout, "stdout"),
46303
- stderr: collectBoundedOutput(process2.stderr, "stderr"),
46367
+ stdout: collectBoundedOutput(process2.stdout, "stdout", outputLimit),
46368
+ stderr: collectBoundedOutput(process2.stderr, "stderr", outputLimit),
46304
46369
  exitCode: process2.exitCode.pipe(exports_Effect.map(Number), mapPlatform("ExecutionFailed", "Failed to observe Grit exit"))
46305
46370
  }, { concurrency: "unbounded" });
46306
46371
  return Object.freeze(output);
@@ -46316,7 +46381,7 @@ function runGritCheck(config, catalog, subjectPaths) {
46316
46381
  if (observation.stdout.trim().length > 0) {
46317
46382
  return fail11("InvalidOutput", "Grit check emitted unexpected stdout alongside its stderr JSON report");
46318
46383
  }
46319
- return decodeGritReport(observation.stderr).pipe(exports_Effect.map(resultFromGritReport));
46384
+ return decodeGritReport(observation.stderr).pipe(exports_Effect.flatMap((report) => resultFromGritReport(report, programs)));
46320
46385
  }));
46321
46386
  }
46322
46387
  function decodeGritReport(stderr) {
@@ -46328,45 +46393,61 @@ function decodeGritReport(stderr) {
46328
46393
  catch: (error2) => failure("InvalidOutput", `Grit check emitted an invalid report: ${errorMessage(error2)}`)
46329
46394
  })));
46330
46395
  }
46331
- function resultFromGritReport(report) {
46332
- const findings = report.results.map((result4) => Object.freeze({
46333
- path: result4.path,
46334
- start: Object.freeze({
46335
- line: result4.start.line,
46336
- column: result4.start.col,
46337
- offset: result4.start.offset
46338
- }),
46339
- end: Object.freeze({
46340
- line: result4.end.line,
46341
- column: result4.end.col,
46342
- offset: result4.end.offset
46343
- }),
46344
- message: result4.extra.message
46345
- })).sort(compareFindings);
46396
+ function resultFromGritReport(report, programs) {
46397
+ const programsByPattern = new Map(programs.map((program) => [program.patternName, program]));
46398
+ const findingsByProgram = new Map(programs.map(({ id: id3 }) => [id3, []]));
46399
+ for (const result4 of report.results) {
46400
+ const program = programsByPattern.get(result4.local_name);
46401
+ if (program === undefined || !result4.check_id.startsWith(`#${result4.local_name}/`)) {
46402
+ return fail11("InvalidOutput", `Grit check reported unexpected pattern identity '${result4.local_name}'`);
46403
+ }
46404
+ findingsByProgram.get(program.id)?.push(Object.freeze({
46405
+ path: result4.path,
46406
+ start: Object.freeze({
46407
+ line: result4.start.line,
46408
+ column: result4.start.col,
46409
+ offset: result4.start.offset
46410
+ }),
46411
+ end: Object.freeze({
46412
+ line: result4.end.line,
46413
+ column: result4.end.col,
46414
+ offset: result4.end.offset
46415
+ }),
46416
+ message: result4.extra.message
46417
+ }));
46418
+ }
46419
+ return exports_Effect.succeed(Object.freeze({
46420
+ results: Object.freeze(programs.map(({ id: id3 }) => Object.freeze({
46421
+ programId: id3,
46422
+ findings: Object.freeze((findingsByProgram.get(id3) ?? []).sort(compareFindings))
46423
+ })))
46424
+ }));
46425
+ }
46426
+ function materializeProgram(program, index4) {
46346
46427
  return Object.freeze({
46347
- findings: Object.freeze(findings)
46428
+ id: program.id,
46429
+ patternName: `${GRIT_PATTERN_PREFIX}${index4}`,
46430
+ program: program.program
46348
46431
  });
46349
46432
  }
46350
- function renderGritCatalog(program) {
46433
+ function renderGritCatalog(programs) {
46351
46434
  return `${JSON.stringify({
46352
46435
  version: "0.0.2",
46353
- patterns: [
46354
- {
46355
- name: GRIT_PATTERN_NAME,
46356
- title: GRIT_PATTERN_NAME,
46357
- level: "error",
46358
- body: program
46359
- }
46360
- ]
46436
+ patterns: programs.map(({ id: id3, patternName, program }) => Object.freeze({
46437
+ name: patternName,
46438
+ title: id3,
46439
+ level: "error",
46440
+ body: program
46441
+ }))
46361
46442
  }, undefined, 2)}
46362
46443
  `;
46363
46444
  }
46364
- function collectBoundedOutput(stream2, channel) {
46365
- return exports_Stream.runFoldEffect(stream2.pipe(exports_Stream.mapError((error2) => failure("ExecutionFailed", `Failed to drain Grit ${channel}: ${error2.message}`))), () => Object.freeze({ buffer: new Uint8Array(MAX_GRIT_OUTPUT_BYTES), bytes: 0 }), (state2, chunk) => {
46445
+ function collectBoundedOutput(stream2, channel, outputLimit) {
46446
+ return exports_Stream.runFoldEffect(stream2.pipe(exports_Stream.mapError((error2) => failure("ExecutionFailed", `Failed to drain Grit ${channel}: ${error2.message}`))), () => Object.freeze({ buffer: new Uint8Array(outputLimit), bytes: 0 }), (state2, chunk) => {
46366
46447
  if (chunk.byteLength === 0)
46367
46448
  return exports_Effect.succeed(state2);
46368
46449
  const bytes = state2.bytes + chunk.byteLength;
46369
- return bytes > MAX_GRIT_OUTPUT_BYTES ? fail11("InvalidOutput", `Grit ${channel} exceeded its ${MAX_GRIT_OUTPUT_BYTES}-byte output limit`) : exports_Effect.sync(() => {
46450
+ return bytes > outputLimit ? fail11("InvalidOutput", `Grit ${channel} exceeded its ${outputLimit}-byte output limit`) : exports_Effect.sync(() => {
46370
46451
  state2.buffer.set(chunk, state2.bytes);
46371
46452
  return Object.freeze({ buffer: state2.buffer, bytes });
46372
46453
  });
@@ -46375,6 +46456,9 @@ function collectBoundedOutput(stream2, channel) {
46375
46456
  catch: (error2) => failure("InvalidOutput", `Grit ${channel} was not valid UTF-8: ${errorMessage(error2)}`)
46376
46457
  })));
46377
46458
  }
46459
+ function gritOutputLimit(programCount) {
46460
+ return Math.min(MAX_GRIT_BATCH_OUTPUT_BYTES, MAX_GRIT_OUTPUT_BYTES_PER_PROGRAM * programCount);
46461
+ }
46378
46462
  function mapPlatform(reason, context4) {
46379
46463
  return (effect2) => effect2.pipe(exports_Effect.mapError((error2) => failure(reason, `${context4}: ${error2.message}`)));
46380
46464
  }
@@ -51941,21 +52025,22 @@ var check7 = module3.check.effect(function* ({ context: context4, input }) {
51941
52025
  };
51942
52026
  }
51943
52027
  }
51944
- const reports = [];
51945
- for (const prepared of preparations) {
52028
+ const reports = new Array(preparations.length);
52029
+ const readyGritEvaluations = [];
52030
+ for (const [applicationIndex, prepared] of preparations.entries()) {
51946
52031
  if (prepared.kind === "structure") {
51947
52032
  if (prepared.preparation.kind === "failed") {
51948
- reports.push(prepared.preparation.report);
52033
+ reports[applicationIndex] = prepared.preparation.report;
51949
52034
  continue;
51950
52035
  }
51951
52036
  const admitted = prepared.preparation.value;
51952
52037
  const application2 = admitted.application;
51953
52038
  if (admitted.scopes.length === 0) {
51954
- reports.push(evaluatedStructureApplication(application2, []));
52039
+ reports[applicationIndex] = evaluatedStructureApplication(application2, []);
51955
52040
  continue;
51956
52041
  }
51957
52042
  if (inventoryPreparation.kind === "failed") {
51958
- reports.push(failedStructureApplication(application2, "InventoryFailed", inventoryPreparation.detail));
52043
+ reports[applicationIndex] = failedStructureApplication(application2, "InventoryFailed", inventoryPreparation.detail);
51959
52044
  continue;
51960
52045
  }
51961
52046
  if (inventoryPreparation.kind === "not-required") {
@@ -51969,48 +52054,85 @@ var check7 = module3.check.effect(function* ({ context: context4, input }) {
51969
52054
  return yield* prepareStructureObservations(structureChildObservationPaths(plan, roots.kinds), roots.kinds);
51970
52055
  });
51971
52056
  if (observations.kind === "failed") {
51972
- reports.push(failedStructureApplication(application2, "StructureObservationFailed", observations.detail));
52057
+ reports[applicationIndex] = failedStructureApplication(application2, "StructureObservationFailed", observations.detail);
51973
52058
  continue;
51974
52059
  }
51975
- reports.push(evaluatedStructureApplication(application2, evaluateStructurePlan(plan, observations.kinds)));
52060
+ reports[applicationIndex] = evaluatedStructureApplication(application2, evaluateStructurePlan(plan, observations.kinds));
51976
52061
  continue;
51977
52062
  }
51978
52063
  const application = prepared.application;
51979
52064
  const patternAttempt = yield* exports_Effect.result(context4.fileSystem.readFileString(application.runner.pattern.absolutePath));
51980
52065
  if (patternAttempt._tag === "Failure") {
51981
- reports.push(failedApplication(application, "PatternReadFailed", `Unable to read pattern asset "${application.runner.pattern.relativePath}".`));
52066
+ reports[applicationIndex] = failedApplication(application, "PatternReadFailed", `Unable to read pattern asset "${application.runner.pattern.relativePath}".`);
51982
52067
  continue;
51983
52068
  }
51984
52069
  const program = extractGritProgram(patternAttempt.success);
51985
52070
  if (!program.ok) {
51986
- reports.push(failedApplication(application, "PatternInvalid", program.detail));
52071
+ reports[applicationIndex] = failedApplication(application, "PatternInvalid", program.detail);
51987
52072
  continue;
51988
52073
  }
51989
52074
  const subjectPreparation = yield* prepareGritSubjects(application, context4.workspaceRoot, context4.fileSystem, context4.path);
51990
52075
  if (subjectPreparation.kind === "failed") {
51991
- reports.push(failedApplication(application, "SetupFailed", subjectPreparation.detail));
52076
+ reports[applicationIndex] = failedApplication(application, "SetupFailed", subjectPreparation.detail);
51992
52077
  continue;
51993
52078
  }
51994
52079
  if (subjectPreparation.kind === "not-applicable") {
51995
52080
  if (!isCompatibilityRule(application)) {
51996
52081
  return yield* exports_Effect.die(new Error("Version-three Grit applications cannot be not applicable."));
51997
52082
  }
51998
- reports.push(notApplicableApplication(application));
52083
+ reports[applicationIndex] = notApplicableApplication(application);
51999
52084
  continue;
52000
52085
  }
52001
52086
  const subjects = subjectPreparation.subjects;
52002
- const evaluation = yield* exports_Effect.result(context4.ruleEvaluation.evaluate({
52087
+ readyGritEvaluations.push({
52088
+ applicationIndex,
52089
+ application,
52090
+ programId: `application-${applicationIndex}`,
52003
52091
  program: program.program,
52092
+ subjects,
52004
52093
  subjectPaths: subjects.map((subject) => subject.absolutePath)
52094
+ });
52095
+ }
52096
+ for (const evaluations of groupGritEvaluations(readyGritEvaluations)) {
52097
+ const first = evaluations[0];
52098
+ if (first === undefined) {
52099
+ return yield* exports_Effect.die(new Error("A Grit evaluation group must not be empty."));
52100
+ }
52101
+ const evaluation = yield* exports_Effect.result(context4.ruleEvaluation.evaluate({
52102
+ programs: evaluations.map(({ programId, program }) => ({ id: programId, program })),
52103
+ subjectPaths: first.subjectPaths
52005
52104
  }));
52006
52105
  if (evaluation._tag === "Failure") {
52007
- reports.push(failedApplication(application, evaluation.failure.reason, evaluation.failure.detail));
52106
+ for (const prepared of evaluations) {
52107
+ reports[prepared.applicationIndex] = failedApplication(prepared.application, evaluation.failure.reason, evaluation.failure.detail);
52108
+ }
52008
52109
  continue;
52009
52110
  }
52010
- const normalized = normalizeFindings(evaluation.success.findings, subjects, context4.workspaceRoot, context4.path);
52011
- reports.push(normalized.ok ? evaluatedApplication(application, normalized.findings) : failedApplication(application, "FindingPathInvalid", normalized.detail));
52111
+ const resultMismatch = evaluationResultMismatch(evaluations, evaluation.success.results);
52112
+ if (resultMismatch !== undefined) {
52113
+ for (const prepared of evaluations) {
52114
+ reports[prepared.applicationIndex] = failedApplication(prepared.application, "InvalidOutput", resultMismatch);
52115
+ }
52116
+ continue;
52117
+ }
52118
+ const results = indexEvaluationResults(evaluation.success.results);
52119
+ for (const prepared of evaluations) {
52120
+ const result4 = results.get(prepared.programId);
52121
+ if (result4 === undefined) {
52122
+ return yield* exports_Effect.die(new Error(`Validated evaluation result '${prepared.programId}' is absent.`));
52123
+ }
52124
+ const normalized = normalizeFindings(result4.findings, prepared.subjects, context4.workspaceRoot, context4.path);
52125
+ reports[prepared.applicationIndex] = normalized.ok ? evaluatedApplication(prepared.application, normalized.findings) : failedApplication(prepared.application, "FindingPathInvalid", normalized.detail);
52126
+ }
52012
52127
  }
52013
- return completedCheck(reports);
52128
+ const completedReports = [];
52129
+ for (const [applicationIndex, report] of reports.entries()) {
52130
+ if (report === undefined) {
52131
+ return yield* exports_Effect.die(new Error(`Habitat check produced no report for application ${applicationIndex}.`));
52132
+ }
52133
+ completedReports.push(report);
52134
+ }
52135
+ return completedCheck(completedReports);
52014
52136
  });
52015
52137
  var catalog2 = { resolve: resolve5, check: check7 };
52016
52138
  function catalogRejected(issues) {
@@ -52043,6 +52165,32 @@ function hasExactCauseCode(error2, code) {
52043
52165
  const cause = "cause" in error2.reason ? error2.reason.cause : undefined;
52044
52166
  return typeof cause === "object" && cause !== null && "code" in cause && cause.code === code;
52045
52167
  }
52168
+ function groupGritEvaluations(evaluations) {
52169
+ const groups = new Map;
52170
+ for (const evaluation of evaluations) {
52171
+ const key = JSON.stringify(evaluation.subjectPaths);
52172
+ const group = groups.get(key);
52173
+ if (group === undefined)
52174
+ groups.set(key, [evaluation]);
52175
+ else
52176
+ group.push(evaluation);
52177
+ }
52178
+ return [...groups.values()];
52179
+ }
52180
+ function evaluationResultMismatch(evaluations, results) {
52181
+ if (results.length !== evaluations.length) {
52182
+ return `Rule evaluator returned ${results.length} program results for ${evaluations.length} requested programs.`;
52183
+ }
52184
+ const uniqueIds = new Set(results.map(({ programId }) => programId));
52185
+ if (uniqueIds.size !== results.length) {
52186
+ return "Rule evaluator returned duplicate program result identities.";
52187
+ }
52188
+ const unexpected = results.find((result4, index4) => result4.programId !== evaluations[index4]?.programId);
52189
+ return unexpected === undefined ? undefined : "Rule evaluator did not preserve requested program result order and identity.";
52190
+ }
52191
+ function indexEvaluationResults(results) {
52192
+ return new Map(results.map((result4) => [result4.programId, result4]));
52193
+ }
52046
52194
  function prepareGritSubjects(application, workspaceRoot, fileSystem, path) {
52047
52195
  if (!isCompatibilityRule(application)) {
52048
52196
  return exports_Effect.succeed({
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "commands": {},
3
- "version": "0.2.4"
3
+ "version": "0.3.1"
4
4
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@habitat-ai/cli",
3
- "version": "0.2.4",
3
+ "version": "0.3.1",
4
4
  "private": false,
5
5
  "repository": {
6
6
  "type": "git",
@@ -48,10 +48,10 @@
48
48
  "dependencies": {
49
49
  "@effect/platform-node": "4.0.0-beta.101",
50
50
  "@getgrit/cli": "0.1.0-alpha.1743007075",
51
- "@habitat-ai/plugin-cli": "0.2.4",
52
- "@habitat-ai/resource-rule-evaluation": "0.2.4",
53
- "@habitat-ai/resource-source-inventory": "0.2.4",
54
- "@habitat-ai/service": "0.2.4",
51
+ "@habitat-ai/plugin-cli": "0.3.1",
52
+ "@habitat-ai/resource-rule-evaluation": "0.3.1",
53
+ "@habitat-ai/resource-source-inventory": "0.3.1",
54
+ "@habitat-ai/service": "0.3.1",
55
55
  "@nx/devkit": "23.1.0",
56
56
  "@oclif/core": "^4.11.4",
57
57
  "@oclif/plugin-help": "^6.2.27",