@habitat-ai/service 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.
|
@@ -599,21 +599,22 @@ const check = module.check.effect(function* ({ context, input }) {
|
|
|
599
599
|
};
|
|
600
600
|
}
|
|
601
601
|
}
|
|
602
|
-
const reports =
|
|
603
|
-
|
|
602
|
+
const reports = new Array(preparations.length);
|
|
603
|
+
const readyGritEvaluations = [];
|
|
604
|
+
for (const [applicationIndex, prepared] of preparations.entries()) {
|
|
604
605
|
if (prepared.kind === "structure") {
|
|
605
606
|
if (prepared.preparation.kind === "failed") {
|
|
606
|
-
reports
|
|
607
|
+
reports[applicationIndex] = prepared.preparation.report;
|
|
607
608
|
continue;
|
|
608
609
|
}
|
|
609
610
|
const admitted = prepared.preparation.value;
|
|
610
611
|
const application = admitted.application;
|
|
611
612
|
if (admitted.scopes.length === 0) {
|
|
612
|
-
reports
|
|
613
|
+
reports[applicationIndex] = evaluatedStructureApplication(application, []);
|
|
613
614
|
continue;
|
|
614
615
|
}
|
|
615
616
|
if (inventoryPreparation.kind === "failed") {
|
|
616
|
-
reports
|
|
617
|
+
reports[applicationIndex] = failedStructureApplication(application, "InventoryFailed", inventoryPreparation.detail);
|
|
617
618
|
continue;
|
|
618
619
|
}
|
|
619
620
|
if (inventoryPreparation.kind === "not-required") {
|
|
@@ -627,50 +628,87 @@ const check = module.check.effect(function* ({ context, input }) {
|
|
|
627
628
|
return yield* prepareStructureObservations(structureChildObservationPaths(plan, roots.kinds), roots.kinds);
|
|
628
629
|
});
|
|
629
630
|
if (observations.kind === "failed") {
|
|
630
|
-
reports
|
|
631
|
+
reports[applicationIndex] = failedStructureApplication(application, "StructureObservationFailed", observations.detail);
|
|
631
632
|
continue;
|
|
632
633
|
}
|
|
633
|
-
reports
|
|
634
|
+
reports[applicationIndex] = evaluatedStructureApplication(application, evaluateStructurePlan(plan, observations.kinds));
|
|
634
635
|
continue;
|
|
635
636
|
}
|
|
636
637
|
const application = prepared.application;
|
|
637
638
|
const patternAttempt = yield* Effect.result(context.fileSystem.readFileString(application.runner.pattern.absolutePath));
|
|
638
639
|
if (patternAttempt._tag === "Failure") {
|
|
639
|
-
reports
|
|
640
|
+
reports[applicationIndex] = failedApplication(application, "PatternReadFailed", `Unable to read pattern asset "${application.runner.pattern.relativePath}".`);
|
|
640
641
|
continue;
|
|
641
642
|
}
|
|
642
643
|
const program = extractGritProgram(patternAttempt.success);
|
|
643
644
|
if (!program.ok) {
|
|
644
|
-
reports
|
|
645
|
+
reports[applicationIndex] = failedApplication(application, "PatternInvalid", program.detail);
|
|
645
646
|
continue;
|
|
646
647
|
}
|
|
647
648
|
const subjectPreparation = yield* prepareGritSubjects(application, context.workspaceRoot, context.fileSystem, context.path);
|
|
648
649
|
if (subjectPreparation.kind === "failed") {
|
|
649
|
-
reports
|
|
650
|
+
reports[applicationIndex] = failedApplication(application, "SetupFailed", subjectPreparation.detail);
|
|
650
651
|
continue;
|
|
651
652
|
}
|
|
652
653
|
if (subjectPreparation.kind === "not-applicable") {
|
|
653
654
|
if (!isCompatibilityRule(application)) {
|
|
654
655
|
return yield* Effect.die(new Error("Version-three Grit applications cannot be not applicable."));
|
|
655
656
|
}
|
|
656
|
-
reports
|
|
657
|
+
reports[applicationIndex] = notApplicableApplication(application);
|
|
657
658
|
continue;
|
|
658
659
|
}
|
|
659
660
|
const subjects = subjectPreparation.subjects;
|
|
660
|
-
|
|
661
|
+
readyGritEvaluations.push({
|
|
662
|
+
applicationIndex,
|
|
663
|
+
application,
|
|
664
|
+
programId: `application-${applicationIndex}`,
|
|
661
665
|
program: program.program,
|
|
666
|
+
subjects,
|
|
662
667
|
subjectPaths: subjects.map((subject) => subject.absolutePath),
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
for (const evaluations of groupGritEvaluations(readyGritEvaluations)) {
|
|
671
|
+
const first = evaluations[0];
|
|
672
|
+
if (first === undefined) {
|
|
673
|
+
return yield* Effect.die(new Error("A Grit evaluation group must not be empty."));
|
|
674
|
+
}
|
|
675
|
+
const evaluation = yield* Effect.result(context.ruleEvaluation.evaluate({
|
|
676
|
+
programs: evaluations.map(({ programId, program }) => ({ id: programId, program })),
|
|
677
|
+
subjectPaths: first.subjectPaths,
|
|
663
678
|
}));
|
|
664
679
|
if (evaluation._tag === "Failure") {
|
|
665
|
-
|
|
680
|
+
for (const prepared of evaluations) {
|
|
681
|
+
reports[prepared.applicationIndex] = failedApplication(prepared.application, evaluation.failure.reason, evaluation.failure.detail);
|
|
682
|
+
}
|
|
666
683
|
continue;
|
|
667
684
|
}
|
|
668
|
-
const
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
685
|
+
const resultMismatch = evaluationResultMismatch(evaluations, evaluation.success.results);
|
|
686
|
+
if (resultMismatch !== undefined) {
|
|
687
|
+
for (const prepared of evaluations) {
|
|
688
|
+
reports[prepared.applicationIndex] = failedApplication(prepared.application, "InvalidOutput", resultMismatch);
|
|
689
|
+
}
|
|
690
|
+
continue;
|
|
691
|
+
}
|
|
692
|
+
const results = indexEvaluationResults(evaluation.success.results);
|
|
693
|
+
for (const prepared of evaluations) {
|
|
694
|
+
const result = results.get(prepared.programId);
|
|
695
|
+
if (result === undefined) {
|
|
696
|
+
return yield* Effect.die(new Error(`Validated evaluation result '${prepared.programId}' is absent.`));
|
|
697
|
+
}
|
|
698
|
+
const normalized = normalizeFindings(result.findings, prepared.subjects, context.workspaceRoot, context.path);
|
|
699
|
+
reports[prepared.applicationIndex] = normalized.ok
|
|
700
|
+
? evaluatedApplication(prepared.application, normalized.findings)
|
|
701
|
+
: failedApplication(prepared.application, "FindingPathInvalid", normalized.detail);
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
const completedReports = [];
|
|
705
|
+
for (const [applicationIndex, report] of reports.entries()) {
|
|
706
|
+
if (report === undefined) {
|
|
707
|
+
return yield* Effect.die(new Error(`Habitat check produced no report for application ${applicationIndex}.`));
|
|
708
|
+
}
|
|
709
|
+
completedReports.push(report);
|
|
672
710
|
}
|
|
673
|
-
return completedCheck(
|
|
711
|
+
return completedCheck(completedReports);
|
|
674
712
|
});
|
|
675
713
|
/** Grouped catalog operation tree consumed by the module composition face. */
|
|
676
714
|
export const catalog = { resolve, check };
|
|
@@ -707,6 +745,34 @@ function hasExactCauseCode(error, code) {
|
|
|
707
745
|
const cause = "cause" in error.reason ? error.reason.cause : undefined;
|
|
708
746
|
return typeof cause === "object" && cause !== null && "code" in cause && cause.code === code;
|
|
709
747
|
}
|
|
748
|
+
function groupGritEvaluations(evaluations) {
|
|
749
|
+
const groups = new Map();
|
|
750
|
+
for (const evaluation of evaluations) {
|
|
751
|
+
const key = JSON.stringify(evaluation.subjectPaths);
|
|
752
|
+
const group = groups.get(key);
|
|
753
|
+
if (group === undefined)
|
|
754
|
+
groups.set(key, [evaluation]);
|
|
755
|
+
else
|
|
756
|
+
group.push(evaluation);
|
|
757
|
+
}
|
|
758
|
+
return [...groups.values()];
|
|
759
|
+
}
|
|
760
|
+
function evaluationResultMismatch(evaluations, results) {
|
|
761
|
+
if (results.length !== evaluations.length) {
|
|
762
|
+
return `Rule evaluator returned ${results.length} program results for ${evaluations.length} requested programs.`;
|
|
763
|
+
}
|
|
764
|
+
const uniqueIds = new Set(results.map(({ programId }) => programId));
|
|
765
|
+
if (uniqueIds.size !== results.length) {
|
|
766
|
+
return "Rule evaluator returned duplicate program result identities.";
|
|
767
|
+
}
|
|
768
|
+
const unexpected = results.find((result, index) => result.programId !== evaluations[index]?.programId);
|
|
769
|
+
return unexpected === undefined
|
|
770
|
+
? undefined
|
|
771
|
+
: "Rule evaluator did not preserve requested program result order and identity.";
|
|
772
|
+
}
|
|
773
|
+
function indexEvaluationResults(results) {
|
|
774
|
+
return new Map(results.map((result) => [result.programId, result]));
|
|
775
|
+
}
|
|
710
776
|
function prepareGritSubjects(application, workspaceRoot, fileSystem, path) {
|
|
711
777
|
if (!isCompatibilityRule(application)) {
|
|
712
778
|
return Effect.succeed({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@habitat-ai/service",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -24,8 +24,8 @@
|
|
|
24
24
|
"test": "bun test test"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@habitat-ai/resource-rule-evaluation": "0.
|
|
28
|
-
"@habitat-ai/resource-source-inventory": "0.
|
|
27
|
+
"@habitat-ai/resource-rule-evaluation": "0.3.1",
|
|
28
|
+
"@habitat-ai/resource-source-inventory": "0.3.1",
|
|
29
29
|
"@opentelemetry/api": "^1.9.0",
|
|
30
30
|
"@orpc/contract": "2.0.0-beta.20",
|
|
31
31
|
"@orpc/experimental-effect": "2.0.0-beta.20",
|