@habitat-ai/cli 0.2.3 → 0.3.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/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,24 @@ 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, index4) => materializeProgram(program, index4 + 1))
46315
+ ];
46316
+ return yield* exports_Effect.scoped(withScopedGritCatalog(programs, (catalog) => runGritCheck(config, catalog, programs, input.subjectPaths)));
46257
46317
  });
46258
46318
  return Object.freeze({ evaluate: evaluate6 });
46259
46319
  }
@@ -46263,7 +46323,7 @@ function makeNodeGritRuleEvaluationResource(config) {
46263
46323
  evaluate: (input) => resource.evaluate(input).pipe(exports_Effect.provide(exports_NodeServices.layer))
46264
46324
  });
46265
46325
  }
46266
- function withScopedGritCatalog(program, use) {
46326
+ function withScopedGritCatalog(programs, use) {
46267
46327
  return exports_Effect.gen(function* () {
46268
46328
  const fs = yield* exports_FileSystem.FileSystem;
46269
46329
  const path = yield* exports_Path.Path;
@@ -46277,11 +46337,12 @@ function withScopedGritCatalog(program, use) {
46277
46337
  yield* fs.makeDirectory(catalog.gritDirectory).pipe(mapPlatform("SetupFailed", "Failed to create Grit catalog directory"));
46278
46338
  yield* fs.makeDirectory(catalog.userConfigDirectory).pipe(mapPlatform("SetupFailed", "Failed to create Grit user-config directory"));
46279
46339
  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"));
46340
+ yield* fs.writeFileString(path.join(catalog.gritDirectory, "grit.yaml"), renderGritCatalog(programs)).pipe(mapPlatform("SetupFailed", "Failed to write temporary Grit catalog"));
46281
46341
  return yield* use(catalog);
46282
46342
  });
46283
46343
  }
46284
- function runGritCheck(config, catalog, subjectPaths) {
46344
+ function runGritCheck(config, catalog, programs, subjectPaths) {
46345
+ const outputLimit = gritOutputLimit(programs.length);
46285
46346
  const observe = exports_Effect.gen(function* () {
46286
46347
  const command = exports_ChildProcess.make(config.executable, ["--json", "check", "--no-cache", "--grit-dir", catalog.gritDirectory, ...subjectPaths], {
46287
46348
  cwd: catalog.root,
@@ -46290,7 +46351,8 @@ function runGritCheck(config, catalog, subjectPaths) {
46290
46351
  GRIT_CACHE_DIR: catalog.cacheDirectory,
46291
46352
  GRIT_USER_CONFIG: catalog.userConfigDirectory,
46292
46353
  GRIT_TELEMETRY_DISABLED: "true",
46293
- NO_COLOR: undefined
46354
+ NO_COLOR: undefined,
46355
+ RAYON_NUM_THREADS: "2"
46294
46356
  },
46295
46357
  extendEnv: true,
46296
46358
  stdin: "ignore",
@@ -46299,8 +46361,8 @@ function runGritCheck(config, catalog, subjectPaths) {
46299
46361
  });
46300
46362
  const process2 = yield* command.pipe(mapPlatform("ExecutionFailed", "Failed to start Grit"));
46301
46363
  const output = yield* exports_Effect.all({
46302
- stdout: collectBoundedOutput(process2.stdout, "stdout"),
46303
- stderr: collectBoundedOutput(process2.stderr, "stderr"),
46364
+ stdout: collectBoundedOutput(process2.stdout, "stdout", outputLimit),
46365
+ stderr: collectBoundedOutput(process2.stderr, "stderr", outputLimit),
46304
46366
  exitCode: process2.exitCode.pipe(exports_Effect.map(Number), mapPlatform("ExecutionFailed", "Failed to observe Grit exit"))
46305
46367
  }, { concurrency: "unbounded" });
46306
46368
  return Object.freeze(output);
@@ -46316,7 +46378,7 @@ function runGritCheck(config, catalog, subjectPaths) {
46316
46378
  if (observation.stdout.trim().length > 0) {
46317
46379
  return fail11("InvalidOutput", "Grit check emitted unexpected stdout alongside its stderr JSON report");
46318
46380
  }
46319
- return decodeGritReport(observation.stderr).pipe(exports_Effect.map(resultFromGritReport));
46381
+ return decodeGritReport(observation.stderr).pipe(exports_Effect.flatMap((report) => resultFromGritReport(report, programs)));
46320
46382
  }));
46321
46383
  }
46322
46384
  function decodeGritReport(stderr) {
@@ -46328,45 +46390,61 @@ function decodeGritReport(stderr) {
46328
46390
  catch: (error2) => failure("InvalidOutput", `Grit check emitted an invalid report: ${errorMessage(error2)}`)
46329
46391
  })));
46330
46392
  }
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);
46393
+ function resultFromGritReport(report, programs) {
46394
+ const programsByPattern = new Map(programs.map((program) => [program.patternName, program]));
46395
+ const findingsByProgram = new Map(programs.map(({ id: id3 }) => [id3, []]));
46396
+ for (const result4 of report.results) {
46397
+ const program = programsByPattern.get(result4.local_name);
46398
+ if (program === undefined || !result4.check_id.startsWith(`#${result4.local_name}/`)) {
46399
+ return fail11("InvalidOutput", `Grit check reported unexpected pattern identity '${result4.local_name}'`);
46400
+ }
46401
+ findingsByProgram.get(program.id)?.push(Object.freeze({
46402
+ path: result4.path,
46403
+ start: Object.freeze({
46404
+ line: result4.start.line,
46405
+ column: result4.start.col,
46406
+ offset: result4.start.offset
46407
+ }),
46408
+ end: Object.freeze({
46409
+ line: result4.end.line,
46410
+ column: result4.end.col,
46411
+ offset: result4.end.offset
46412
+ }),
46413
+ message: result4.extra.message
46414
+ }));
46415
+ }
46416
+ return exports_Effect.succeed(Object.freeze({
46417
+ results: Object.freeze(programs.map(({ id: id3 }) => Object.freeze({
46418
+ programId: id3,
46419
+ findings: Object.freeze((findingsByProgram.get(id3) ?? []).sort(compareFindings))
46420
+ })))
46421
+ }));
46422
+ }
46423
+ function materializeProgram(program, index4) {
46346
46424
  return Object.freeze({
46347
- findings: Object.freeze(findings)
46425
+ id: program.id,
46426
+ patternName: `${GRIT_PATTERN_PREFIX}${index4}`,
46427
+ program: program.program
46348
46428
  });
46349
46429
  }
46350
- function renderGritCatalog(program) {
46430
+ function renderGritCatalog(programs) {
46351
46431
  return `${JSON.stringify({
46352
46432
  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
- ]
46433
+ patterns: programs.map(({ id: id3, patternName, program }) => Object.freeze({
46434
+ name: patternName,
46435
+ title: id3,
46436
+ level: "error",
46437
+ body: program
46438
+ }))
46361
46439
  }, undefined, 2)}
46362
46440
  `;
46363
46441
  }
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) => {
46442
+ function collectBoundedOutput(stream2, channel, outputLimit) {
46443
+ 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
46444
  if (chunk.byteLength === 0)
46367
46445
  return exports_Effect.succeed(state2);
46368
46446
  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(() => {
46447
+ return bytes > outputLimit ? fail11("InvalidOutput", `Grit ${channel} exceeded its ${outputLimit}-byte output limit`) : exports_Effect.sync(() => {
46370
46448
  state2.buffer.set(chunk, state2.bytes);
46371
46449
  return Object.freeze({ buffer: state2.buffer, bytes });
46372
46450
  });
@@ -46375,6 +46453,9 @@ function collectBoundedOutput(stream2, channel) {
46375
46453
  catch: (error2) => failure("InvalidOutput", `Grit ${channel} was not valid UTF-8: ${errorMessage(error2)}`)
46376
46454
  })));
46377
46455
  }
46456
+ function gritOutputLimit(programCount) {
46457
+ return Math.min(MAX_GRIT_BATCH_OUTPUT_BYTES, MAX_GRIT_OUTPUT_BYTES_PER_PROGRAM * programCount);
46458
+ }
46378
46459
  function mapPlatform(reason, context4) {
46379
46460
  return (effect2) => effect2.pipe(exports_Effect.mapError((error2) => failure(reason, `${context4}: ${error2.message}`)));
46380
46461
  }
@@ -48230,7 +48311,7 @@ function createRouterClient(router, ...rest3) {
48230
48311
  return recursive;
48231
48312
  }
48232
48313
 
48233
- // ../../packages/typebox-adapter/dist/index.js
48314
+ // ../../node_modules/.bun/@habitat-ai+typebox-adapter@0.1.1/node_modules/@habitat-ai/typebox-adapter/dist/index.js
48234
48315
  function standard(schema5) {
48235
48316
  const validator = new Validator({}, Clone2(schema5));
48236
48317
  const jsonSchema = ({ target: target2 }) => {
@@ -51941,21 +52022,22 @@ var check7 = module3.check.effect(function* ({ context: context4, input }) {
51941
52022
  };
51942
52023
  }
51943
52024
  }
51944
- const reports = [];
51945
- for (const prepared of preparations) {
52025
+ const reports = new Array(preparations.length);
52026
+ const readyGritEvaluations = [];
52027
+ for (const [applicationIndex, prepared] of preparations.entries()) {
51946
52028
  if (prepared.kind === "structure") {
51947
52029
  if (prepared.preparation.kind === "failed") {
51948
- reports.push(prepared.preparation.report);
52030
+ reports[applicationIndex] = prepared.preparation.report;
51949
52031
  continue;
51950
52032
  }
51951
52033
  const admitted = prepared.preparation.value;
51952
52034
  const application2 = admitted.application;
51953
52035
  if (admitted.scopes.length === 0) {
51954
- reports.push(evaluatedStructureApplication(application2, []));
52036
+ reports[applicationIndex] = evaluatedStructureApplication(application2, []);
51955
52037
  continue;
51956
52038
  }
51957
52039
  if (inventoryPreparation.kind === "failed") {
51958
- reports.push(failedStructureApplication(application2, "InventoryFailed", inventoryPreparation.detail));
52040
+ reports[applicationIndex] = failedStructureApplication(application2, "InventoryFailed", inventoryPreparation.detail);
51959
52041
  continue;
51960
52042
  }
51961
52043
  if (inventoryPreparation.kind === "not-required") {
@@ -51969,48 +52051,85 @@ var check7 = module3.check.effect(function* ({ context: context4, input }) {
51969
52051
  return yield* prepareStructureObservations(structureChildObservationPaths(plan, roots.kinds), roots.kinds);
51970
52052
  });
51971
52053
  if (observations.kind === "failed") {
51972
- reports.push(failedStructureApplication(application2, "StructureObservationFailed", observations.detail));
52054
+ reports[applicationIndex] = failedStructureApplication(application2, "StructureObservationFailed", observations.detail);
51973
52055
  continue;
51974
52056
  }
51975
- reports.push(evaluatedStructureApplication(application2, evaluateStructurePlan(plan, observations.kinds)));
52057
+ reports[applicationIndex] = evaluatedStructureApplication(application2, evaluateStructurePlan(plan, observations.kinds));
51976
52058
  continue;
51977
52059
  }
51978
52060
  const application = prepared.application;
51979
52061
  const patternAttempt = yield* exports_Effect.result(context4.fileSystem.readFileString(application.runner.pattern.absolutePath));
51980
52062
  if (patternAttempt._tag === "Failure") {
51981
- reports.push(failedApplication(application, "PatternReadFailed", `Unable to read pattern asset "${application.runner.pattern.relativePath}".`));
52063
+ reports[applicationIndex] = failedApplication(application, "PatternReadFailed", `Unable to read pattern asset "${application.runner.pattern.relativePath}".`);
51982
52064
  continue;
51983
52065
  }
51984
52066
  const program = extractGritProgram(patternAttempt.success);
51985
52067
  if (!program.ok) {
51986
- reports.push(failedApplication(application, "PatternInvalid", program.detail));
52068
+ reports[applicationIndex] = failedApplication(application, "PatternInvalid", program.detail);
51987
52069
  continue;
51988
52070
  }
51989
52071
  const subjectPreparation = yield* prepareGritSubjects(application, context4.workspaceRoot, context4.fileSystem, context4.path);
51990
52072
  if (subjectPreparation.kind === "failed") {
51991
- reports.push(failedApplication(application, "SetupFailed", subjectPreparation.detail));
52073
+ reports[applicationIndex] = failedApplication(application, "SetupFailed", subjectPreparation.detail);
51992
52074
  continue;
51993
52075
  }
51994
52076
  if (subjectPreparation.kind === "not-applicable") {
51995
52077
  if (!isCompatibilityRule(application)) {
51996
52078
  return yield* exports_Effect.die(new Error("Version-three Grit applications cannot be not applicable."));
51997
52079
  }
51998
- reports.push(notApplicableApplication(application));
52080
+ reports[applicationIndex] = notApplicableApplication(application);
51999
52081
  continue;
52000
52082
  }
52001
52083
  const subjects = subjectPreparation.subjects;
52002
- const evaluation = yield* exports_Effect.result(context4.ruleEvaluation.evaluate({
52084
+ readyGritEvaluations.push({
52085
+ applicationIndex,
52086
+ application,
52087
+ programId: `application-${applicationIndex}`,
52003
52088
  program: program.program,
52089
+ subjects,
52004
52090
  subjectPaths: subjects.map((subject) => subject.absolutePath)
52091
+ });
52092
+ }
52093
+ for (const evaluations of groupGritEvaluations(readyGritEvaluations)) {
52094
+ const first = evaluations[0];
52095
+ if (first === undefined) {
52096
+ return yield* exports_Effect.die(new Error("A Grit evaluation group must not be empty."));
52097
+ }
52098
+ const evaluation = yield* exports_Effect.result(context4.ruleEvaluation.evaluate({
52099
+ programs: evaluations.map(({ programId, program }) => ({ id: programId, program })),
52100
+ subjectPaths: first.subjectPaths
52005
52101
  }));
52006
52102
  if (evaluation._tag === "Failure") {
52007
- reports.push(failedApplication(application, evaluation.failure.reason, evaluation.failure.detail));
52103
+ for (const prepared of evaluations) {
52104
+ reports[prepared.applicationIndex] = failedApplication(prepared.application, evaluation.failure.reason, evaluation.failure.detail);
52105
+ }
52008
52106
  continue;
52009
52107
  }
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));
52108
+ const resultMismatch = evaluationResultMismatch(evaluations, evaluation.success.results);
52109
+ if (resultMismatch !== undefined) {
52110
+ for (const prepared of evaluations) {
52111
+ reports[prepared.applicationIndex] = failedApplication(prepared.application, "InvalidOutput", resultMismatch);
52112
+ }
52113
+ continue;
52114
+ }
52115
+ const results = indexEvaluationResults(evaluation.success.results);
52116
+ for (const prepared of evaluations) {
52117
+ const result4 = results.get(prepared.programId);
52118
+ if (result4 === undefined) {
52119
+ return yield* exports_Effect.die(new Error(`Validated evaluation result '${prepared.programId}' is absent.`));
52120
+ }
52121
+ const normalized = normalizeFindings(result4.findings, prepared.subjects, context4.workspaceRoot, context4.path);
52122
+ reports[prepared.applicationIndex] = normalized.ok ? evaluatedApplication(prepared.application, normalized.findings) : failedApplication(prepared.application, "FindingPathInvalid", normalized.detail);
52123
+ }
52012
52124
  }
52013
- return completedCheck(reports);
52125
+ const completedReports = [];
52126
+ for (const [applicationIndex, report] of reports.entries()) {
52127
+ if (report === undefined) {
52128
+ return yield* exports_Effect.die(new Error(`Habitat check produced no report for application ${applicationIndex}.`));
52129
+ }
52130
+ completedReports.push(report);
52131
+ }
52132
+ return completedCheck(completedReports);
52014
52133
  });
52015
52134
  var catalog2 = { resolve: resolve5, check: check7 };
52016
52135
  function catalogRejected(issues) {
@@ -52043,6 +52162,32 @@ function hasExactCauseCode(error2, code) {
52043
52162
  const cause = "cause" in error2.reason ? error2.reason.cause : undefined;
52044
52163
  return typeof cause === "object" && cause !== null && "code" in cause && cause.code === code;
52045
52164
  }
52165
+ function groupGritEvaluations(evaluations) {
52166
+ const groups = new Map;
52167
+ for (const evaluation of evaluations) {
52168
+ const key = JSON.stringify(evaluation.subjectPaths);
52169
+ const group = groups.get(key);
52170
+ if (group === undefined)
52171
+ groups.set(key, [evaluation]);
52172
+ else
52173
+ group.push(evaluation);
52174
+ }
52175
+ return [...groups.values()];
52176
+ }
52177
+ function evaluationResultMismatch(evaluations, results) {
52178
+ if (results.length !== evaluations.length) {
52179
+ return `Rule evaluator returned ${results.length} program results for ${evaluations.length} requested programs.`;
52180
+ }
52181
+ const uniqueIds = new Set(results.map(({ programId }) => programId));
52182
+ if (uniqueIds.size !== results.length) {
52183
+ return "Rule evaluator returned duplicate program result identities.";
52184
+ }
52185
+ const unexpected = results.find((result4, index4) => result4.programId !== evaluations[index4]?.programId);
52186
+ return unexpected === undefined ? undefined : "Rule evaluator did not preserve requested program result order and identity.";
52187
+ }
52188
+ function indexEvaluationResults(results) {
52189
+ return new Map(results.map((result4) => [result4.programId, result4]));
52190
+ }
52046
52191
  function prepareGritSubjects(application, workspaceRoot, fileSystem, path) {
52047
52192
  if (!isCompatibilityRule(application)) {
52048
52193
  return exports_Effect.succeed({
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "commands": {},
3
- "version": "0.2.3"
3
+ "version": "0.3.0"
4
4
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@habitat-ai/cli",
3
- "version": "0.2.3",
3
+ "version": "0.3.0",
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.3",
52
- "@habitat-ai/resource-rule-evaluation": "0.2.3",
53
- "@habitat-ai/resource-source-inventory": "0.2.3",
54
- "@habitat-ai/service": "0.2.3",
51
+ "@habitat-ai/plugin-cli": "0.3.0",
52
+ "@habitat-ai/resource-rule-evaluation": "0.3.0",
53
+ "@habitat-ai/resource-source-inventory": "0.3.0",
54
+ "@habitat-ai/service": "0.3.0",
55
55
  "@nx/devkit": "23.1.0",
56
56
  "@oclif/core": "^4.11.4",
57
57
  "@oclif/plugin-help": "^6.2.27",