@aigrc/cli 0.1.0 → 0.2.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/index.js CHANGED
@@ -700,59 +700,414 @@ function sanitizeFileName2(name) {
700
700
 
701
701
  // src/commands/validate.ts
702
702
  import { Command as Command4 } from "commander";
703
- import chalk5 from "chalk";
703
+ import chalk6 from "chalk";
704
704
  import ora3 from "ora";
705
- import path4 from "path";
705
+ import path5 from "path";
706
706
  import fs3 from "fs/promises";
707
+ import YAML from "yaml";
707
708
  import {
708
709
  loadAssetCard,
709
710
  classifyRisk,
710
711
  validateAssetCard
711
712
  } from "@aigrc/core";
713
+
714
+ // src/utils/exit-codes.ts
715
+ function exit(code) {
716
+ process.exit(code);
717
+ }
718
+
719
+ // src/formatters/sarif.ts
720
+ function formatSarif(results, version = "0.1.0") {
721
+ const sarifResults = [];
722
+ const artifacts = [];
723
+ const seenPaths = /* @__PURE__ */ new Set();
724
+ for (const result of results) {
725
+ if (!seenPaths.has(result.path)) {
726
+ seenPaths.add(result.path);
727
+ artifacts.push({
728
+ location: {
729
+ uri: result.path
730
+ },
731
+ mimeType: "application/x-yaml"
732
+ });
733
+ }
734
+ if (!result.validation.valid) {
735
+ for (const error of result.validation.errors) {
736
+ sarifResults.push({
737
+ ruleId: "AIGRC001",
738
+ level: "error",
739
+ message: {
740
+ text: error
741
+ },
742
+ locations: [
743
+ {
744
+ physicalLocation: {
745
+ artifactLocation: {
746
+ uri: result.path
747
+ }
748
+ }
749
+ }
750
+ ],
751
+ properties: {
752
+ cardId: result.card?.id,
753
+ cardName: result.card?.name
754
+ }
755
+ });
756
+ }
757
+ } else {
758
+ sarifResults.push({
759
+ ruleId: "AIGRC001",
760
+ level: "none",
761
+ message: {
762
+ text: "Asset card validation passed"
763
+ },
764
+ locations: [
765
+ {
766
+ physicalLocation: {
767
+ artifactLocation: {
768
+ uri: result.path
769
+ }
770
+ }
771
+ }
772
+ ],
773
+ properties: {
774
+ cardId: result.card?.id,
775
+ cardName: result.card?.name,
776
+ riskLevel: result.card?.classification?.riskLevel
777
+ }
778
+ });
779
+ }
780
+ }
781
+ const sarif = {
782
+ version: "2.1.0",
783
+ $schema: "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
784
+ runs: [
785
+ {
786
+ tool: {
787
+ driver: {
788
+ name: "AIGRC",
789
+ version,
790
+ informationUri: "https://github.com/aigrc/aigrc",
791
+ rules: [
792
+ {
793
+ id: "AIGRC001",
794
+ name: "AssetCardValidation",
795
+ shortDescription: {
796
+ text: "Asset card must be valid according to AIGRC schema"
797
+ },
798
+ fullDescription: {
799
+ text: "Validates asset cards against AIGRC compliance requirements including risk classification, ownership, and regulatory framework mappings."
800
+ },
801
+ help: {
802
+ text: "Ensure your asset card includes all required fields and follows the AIGRC schema. Run 'aigrc validate --help' for more information."
803
+ },
804
+ defaultConfiguration: {
805
+ level: "error"
806
+ }
807
+ }
808
+ ]
809
+ }
810
+ },
811
+ results: sarifResults,
812
+ artifacts
813
+ }
814
+ ]
815
+ };
816
+ return sarif;
817
+ }
818
+ function sarifToJson(sarif, pretty = true) {
819
+ return JSON.stringify(sarif, null, pretty ? 2 : 0);
820
+ }
821
+
822
+ // src/formatters/text.ts
823
+ import chalk5 from "chalk";
824
+ import path4 from "path";
825
+ function printValidationSummary(results) {
826
+ console.log(chalk5.bold("Validation Summary"));
827
+ console.log(chalk5.dim("\u2500".repeat(50)));
828
+ console.log();
829
+ for (const result of results) {
830
+ const fileName = path4.basename(result.path);
831
+ if (!result.validation.valid) {
832
+ console.log(chalk5.red(`\u2717 ${fileName}`));
833
+ for (const error of result.validation.errors) {
834
+ console.log(chalk5.red(` Error: ${error}`));
835
+ }
836
+ if (result.fixed && result.fixedFields && result.fixedFields.length > 0) {
837
+ console.log(chalk5.yellow(` Fixed: ${result.fixedFields.join(", ")}`));
838
+ }
839
+ } else {
840
+ console.log(chalk5.green(`\u2713 ${fileName}`));
841
+ if (result.fixed && result.fixedFields && result.fixedFields.length > 0) {
842
+ console.log(chalk5.yellow(` Fixed: ${result.fixedFields.join(", ")}`));
843
+ }
844
+ if (result.card && result.classification) {
845
+ const tierColor = getRiskLevelColor(result.classification.riskLevel);
846
+ console.log(
847
+ chalk5.dim(" Risk Level: ") + tierColor(result.classification.riskLevel)
848
+ );
849
+ if (result.classification.euAiActCategory) {
850
+ console.log(
851
+ chalk5.dim(" EU AI Act: ") + chalk5.yellow(result.classification.euAiActCategory)
852
+ );
853
+ }
854
+ }
855
+ }
856
+ console.log();
857
+ }
858
+ const valid = results.filter((r) => r.validation.valid).length;
859
+ const invalid = results.filter((r) => !r.validation.valid).length;
860
+ const fixed = results.filter((r) => r.fixed).length;
861
+ console.log(chalk5.dim("\u2500".repeat(50)));
862
+ console.log(
863
+ `Total: ${results.length} | ` + chalk5.green(`Valid: ${valid}`) + ` | ` + chalk5.red(`Invalid: ${invalid}`) + (fixed > 0 ? ` | ${chalk5.yellow(`Fixed: ${fixed}`)}` : "")
864
+ );
865
+ }
866
+ function getRiskLevelColor(level) {
867
+ switch (level) {
868
+ case "minimal":
869
+ return chalk5.green;
870
+ case "limited":
871
+ return chalk5.yellow;
872
+ case "high":
873
+ return chalk5.red;
874
+ case "unacceptable":
875
+ return chalk5.magenta;
876
+ default:
877
+ return chalk5.white;
878
+ }
879
+ }
880
+
881
+ // src/fixers/auto-fix.ts
882
+ function autoFixAssetCard(card) {
883
+ const fixedFields = [];
884
+ const fixedCard = JSON.parse(JSON.stringify(card));
885
+ if (!fixedCard.metadata?.createdAt) {
886
+ if (!fixedCard.metadata) {
887
+ fixedCard.metadata = {
888
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
889
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
890
+ version: "1.0.0"
891
+ };
892
+ } else {
893
+ fixedCard.metadata.createdAt = (/* @__PURE__ */ new Date()).toISOString();
894
+ }
895
+ fixedFields.push("metadata.createdAt");
896
+ } else if (!isValidISODate(fixedCard.metadata.createdAt)) {
897
+ fixedCard.metadata.createdAt = normalizeDate(fixedCard.metadata.createdAt);
898
+ fixedFields.push("metadata.createdAt (format)");
899
+ }
900
+ if (!fixedCard.metadata?.updatedAt) {
901
+ if (!fixedCard.metadata) {
902
+ fixedCard.metadata = {
903
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
904
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
905
+ version: "1.0.0"
906
+ };
907
+ } else {
908
+ fixedCard.metadata.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
909
+ }
910
+ fixedFields.push("metadata.updatedAt");
911
+ } else if (!isValidISODate(fixedCard.metadata.updatedAt)) {
912
+ fixedCard.metadata.updatedAt = normalizeDate(fixedCard.metadata.updatedAt);
913
+ fixedFields.push("metadata.updatedAt (format)");
914
+ }
915
+ if (!fixedCard.metadata?.version) {
916
+ if (!fixedCard.metadata) {
917
+ fixedCard.metadata = {
918
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
919
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
920
+ version: "1.0.0"
921
+ };
922
+ } else {
923
+ fixedCard.metadata.version = "1.0.0";
924
+ }
925
+ fixedFields.push("metadata.version");
926
+ }
927
+ if (fixedCard.classification?.riskLevel) {
928
+ const normalized = normalizeRiskLevel(fixedCard.classification.riskLevel);
929
+ if (normalized !== fixedCard.classification.riskLevel) {
930
+ fixedCard.classification.riskLevel = normalized;
931
+ fixedFields.push("classification.riskLevel");
932
+ }
933
+ }
934
+ if (fixedCard.classification?.riskFactors?.piiProcessing !== void 0) {
935
+ const pii = fixedCard.classification.riskFactors.piiProcessing;
936
+ if (typeof pii === "boolean") {
937
+ fixedCard.classification.riskFactors.piiProcessing = pii ? "yes" : "no";
938
+ fixedFields.push("classification.riskFactors.piiProcessing");
939
+ }
940
+ }
941
+ if (!fixedCard.id) {
942
+ fixedCard.id = generateId(fixedCard.name);
943
+ fixedFields.push("id");
944
+ }
945
+ if (!fixedCard.name) {
946
+ fixedCard.name = "Unnamed Asset";
947
+ fixedFields.push("name");
948
+ }
949
+ if (!fixedCard.description) {
950
+ fixedCard.description = "No description provided";
951
+ fixedFields.push("description");
952
+ }
953
+ if (!fixedCard.classification) {
954
+ fixedCard.classification = {
955
+ riskLevel: "minimal",
956
+ riskFactors: {
957
+ autonomousDecisions: false,
958
+ customerFacing: false,
959
+ toolExecution: false,
960
+ externalDataAccess: false,
961
+ piiProcessing: "unknown",
962
+ highStakesDecisions: false
963
+ }
964
+ };
965
+ fixedFields.push("classification");
966
+ }
967
+ if (!fixedCard.classification.riskFactors) {
968
+ fixedCard.classification.riskFactors = {
969
+ autonomousDecisions: false,
970
+ customerFacing: false,
971
+ toolExecution: false,
972
+ externalDataAccess: false,
973
+ piiProcessing: "unknown",
974
+ highStakesDecisions: false
975
+ };
976
+ fixedFields.push("classification.riskFactors");
977
+ }
978
+ return {
979
+ fixed: fixedFields.length > 0,
980
+ fixedFields,
981
+ card: fixedCard
982
+ };
983
+ }
984
+ function isValidISODate(dateStr) {
985
+ const iso8601Regex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{3})?Z?$/;
986
+ if (!iso8601Regex.test(dateStr)) {
987
+ return false;
988
+ }
989
+ const date = new Date(dateStr);
990
+ return !isNaN(date.getTime());
991
+ }
992
+ function normalizeDate(dateStr) {
993
+ const date = new Date(dateStr);
994
+ if (isNaN(date.getTime())) {
995
+ return (/* @__PURE__ */ new Date()).toISOString();
996
+ }
997
+ return date.toISOString();
998
+ }
999
+ function normalizeRiskLevel(level) {
1000
+ const normalized = level.toLowerCase().trim();
1001
+ const validLevels = ["minimal", "limited", "high", "unacceptable"];
1002
+ if (validLevels.includes(normalized)) {
1003
+ return normalized;
1004
+ }
1005
+ const mappings = {
1006
+ low: "minimal",
1007
+ medium: "limited",
1008
+ critical: "unacceptable",
1009
+ extreme: "unacceptable",
1010
+ none: "minimal"
1011
+ };
1012
+ return mappings[normalized] || "minimal";
1013
+ }
1014
+ function generateId(name) {
1015
+ return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").substring(0, 50);
1016
+ }
1017
+
1018
+ // src/commands/validate.ts
712
1019
  var DEFAULT_CARDS_DIR3 = ".aigrc/cards";
713
- var validateCommand = new Command4("validate").description("Validate asset cards against compliance requirements").argument("[path]", "Path to asset card or cards directory").option("-s, --strict", "Fail on warnings as well as errors").option("-o, --output <format>", "Output format (text, json)", "text").option("-a, --all", "Validate all cards in the cards directory").action(async (cardPath, options) => {
1020
+ var validateCommand = new Command4("validate").description("Validate asset cards against compliance requirements").argument("[path]", "Path to asset card or cards directory").option("-s, --strict", "Fail on warnings as well as errors").option("-o, --output <format>", "Output format (text, json, sarif)", "text").option("-a, --all", "Validate all cards in the cards directory").option("--fix", "Automatically fix common issues").option("--dry-run", "Preview fixes without saving (requires --fix)").option("--output-file <path>", "Write output to file instead of stdout").action(async (cardPath, options) => {
714
1021
  await runValidate(cardPath, options);
715
1022
  });
716
1023
  async function runValidate(cardPath, options) {
717
- if (options.output === "text") {
1024
+ if (options.dryRun && !options.fix) {
1025
+ if (options.output === "text") {
1026
+ console.error(chalk6.red("Error: --dry-run requires --fix"));
1027
+ } else {
1028
+ console.error(JSON.stringify({ error: "--dry-run requires --fix" }));
1029
+ }
1030
+ exit(2 /* INVALID_ARGUMENTS */);
1031
+ }
1032
+ if (options.output === "text" && !options.outputFile) {
718
1033
  printHeader();
719
1034
  }
720
1035
  const cardsToValidate = [];
721
1036
  if (options.all || !cardPath) {
722
- const cardsDir = path4.join(process.cwd(), DEFAULT_CARDS_DIR3);
1037
+ const cardsDir = path5.join(process.cwd(), DEFAULT_CARDS_DIR3);
723
1038
  try {
724
1039
  const files = await fs3.readdir(cardsDir);
725
1040
  const yamlFiles = files.filter((f) => f.endsWith(".yaml") || f.endsWith(".yml"));
726
1041
  for (const file of yamlFiles) {
727
- cardsToValidate.push(path4.join(cardsDir, file));
1042
+ cardsToValidate.push(path5.join(cardsDir, file));
728
1043
  }
729
- } catch {
730
- if (options.output === "text") {
731
- console.log(chalk5.yellow("No cards directory found."));
732
- console.log(chalk5.dim(`Expected: ${cardsDir}`));
733
- console.log(chalk5.dim("Run `aigrc init` to initialize AIGRC."));
1044
+ } catch (error) {
1045
+ if (options.output === "text" && !options.outputFile) {
1046
+ console.log(chalk6.yellow("No cards directory found."));
1047
+ console.log(chalk6.dim(`Expected: ${cardsDir}`));
1048
+ console.log(chalk6.dim("Run `aigrc init` to initialize AIGRC."));
734
1049
  } else {
735
- console.log(JSON.stringify({ error: "No cards directory found" }));
1050
+ const output = JSON.stringify({ error: "No cards directory found" });
1051
+ if (options.outputFile) {
1052
+ await fs3.writeFile(options.outputFile, output);
1053
+ } else {
1054
+ console.log(output);
1055
+ }
736
1056
  }
737
- process.exit(1);
1057
+ exit(4 /* FILE_NOT_FOUND */);
738
1058
  }
739
1059
  } else {
740
- cardsToValidate.push(path4.resolve(process.cwd(), cardPath));
1060
+ const resolvedPath = path5.resolve(process.cwd(), cardPath);
1061
+ try {
1062
+ await fs3.access(resolvedPath);
1063
+ cardsToValidate.push(resolvedPath);
1064
+ } catch {
1065
+ if (options.output === "text" && !options.outputFile) {
1066
+ console.log(chalk6.red(`Error: File not found: ${cardPath}`));
1067
+ } else {
1068
+ const output = JSON.stringify({ error: `File not found: ${cardPath}` });
1069
+ if (options.outputFile) {
1070
+ await fs3.writeFile(options.outputFile, output);
1071
+ } else {
1072
+ console.log(output);
1073
+ }
1074
+ }
1075
+ exit(4 /* FILE_NOT_FOUND */);
1076
+ }
741
1077
  }
742
1078
  if (cardsToValidate.length === 0) {
743
- if (options.output === "text") {
744
- console.log(chalk5.yellow("No asset cards found to validate."));
1079
+ if (options.output === "text" && !options.outputFile) {
1080
+ console.log(chalk6.yellow("No asset cards found to validate."));
745
1081
  } else {
746
- console.log(JSON.stringify({ error: "No asset cards found" }));
1082
+ const output = JSON.stringify({ error: "No asset cards found" });
1083
+ if (options.outputFile) {
1084
+ await fs3.writeFile(options.outputFile, output);
1085
+ } else {
1086
+ console.log(output);
1087
+ }
747
1088
  }
748
- process.exit(1);
1089
+ exit(4 /* FILE_NOT_FOUND */);
749
1090
  }
750
1091
  const results = [];
751
1092
  let hasErrors = false;
752
1093
  for (const cardFile of cardsToValidate) {
753
- const spinner = options.output === "text" ? ora3(`Validating ${path4.basename(cardFile)}...`).start() : void 0;
1094
+ const spinner = options.output === "text" && !options.outputFile ? ora3(`Validating ${path5.basename(cardFile)}...`).start() : void 0;
754
1095
  try {
755
- const card = loadAssetCard(cardFile);
1096
+ let card = loadAssetCard(cardFile);
1097
+ let fixed = false;
1098
+ let fixedFields = [];
1099
+ if (options.fix) {
1100
+ const fixResult = autoFixAssetCard(card);
1101
+ if (fixResult.fixed) {
1102
+ fixed = true;
1103
+ fixedFields = fixResult.fixedFields;
1104
+ card = fixResult.card;
1105
+ if (!options.dryRun) {
1106
+ const yamlContent = YAML.stringify(card);
1107
+ await fs3.writeFile(cardFile, yamlContent, "utf-8");
1108
+ }
1109
+ }
1110
+ }
756
1111
  const validation = validateAssetCard(card);
757
1112
  const classification = classifyRisk(card.classification.riskFactors);
758
1113
  results.push({
@@ -762,13 +1117,17 @@ async function runValidate(cardPath, options) {
762
1117
  valid: validation.valid,
763
1118
  errors: validation.errors ?? []
764
1119
  },
765
- classification
1120
+ classification,
1121
+ fixed,
1122
+ fixedFields
766
1123
  });
767
1124
  if (!validation.valid) {
768
1125
  hasErrors = true;
769
- spinner?.fail(`${path4.basename(cardFile)}: Invalid`);
1126
+ spinner?.fail(`${path5.basename(cardFile)}: Invalid`);
1127
+ } else if (fixed) {
1128
+ spinner?.succeed(`${path5.basename(cardFile)}: Valid (fixed ${fixedFields.length} issues)`);
770
1129
  } else {
771
- spinner?.succeed(`${path4.basename(cardFile)}: Valid`);
1130
+ spinner?.succeed(`${path5.basename(cardFile)}: Valid`);
772
1131
  }
773
1132
  } catch (error) {
774
1133
  hasErrors = true;
@@ -780,76 +1139,93 @@ async function runValidate(cardPath, options) {
780
1139
  errors: [errorMessage]
781
1140
  }
782
1141
  });
783
- spinner?.fail(`${path4.basename(cardFile)}: Parse error`);
1142
+ spinner?.fail(`${path5.basename(cardFile)}: Parse error`);
784
1143
  }
785
1144
  }
786
- if (options.output === "json") {
787
- console.log(JSON.stringify({ results }, null, 2));
788
- } else {
789
- console.log();
790
- printValidationSummary(results);
791
- }
1145
+ await outputResults(results, options);
792
1146
  if (hasErrors) {
793
- process.exit(1);
1147
+ exit(3 /* VALIDATION_ERRORS */);
794
1148
  }
1149
+ exit(0 /* SUCCESS */);
795
1150
  }
796
- function printValidationSummary(results) {
797
- console.log(chalk5.bold("Validation Summary"));
798
- console.log(chalk5.dim("\u2500".repeat(50)));
799
- console.log();
1151
+ async function outputResults(results, options) {
1152
+ let output;
1153
+ switch (options.output) {
1154
+ case "sarif": {
1155
+ const sarif = formatSarif(results);
1156
+ output = sarifToJson(sarif);
1157
+ break;
1158
+ }
1159
+ case "json": {
1160
+ output = JSON.stringify({ results }, null, 2);
1161
+ break;
1162
+ }
1163
+ case "text":
1164
+ default: {
1165
+ if (options.outputFile) {
1166
+ output = formatTextOutput(results);
1167
+ } else {
1168
+ console.log();
1169
+ printValidationSummary(results);
1170
+ return;
1171
+ }
1172
+ }
1173
+ }
1174
+ if (options.outputFile) {
1175
+ await fs3.writeFile(options.outputFile, output, "utf-8");
1176
+ if (options.output === "text") {
1177
+ console.log(chalk6.green(`\u2713 Output written to ${options.outputFile}`));
1178
+ }
1179
+ } else {
1180
+ console.log(output);
1181
+ }
1182
+ }
1183
+ function formatTextOutput(results) {
1184
+ const lines = [];
1185
+ lines.push("Validation Summary");
1186
+ lines.push("\u2500".repeat(50));
1187
+ lines.push("");
800
1188
  for (const result of results) {
801
- const fileName = path4.basename(result.path);
1189
+ const fileName = path5.basename(result.path);
802
1190
  if (!result.validation.valid) {
803
- console.log(chalk5.red(`\u2717 ${fileName}`));
1191
+ lines.push(`\u2717 ${fileName}`);
804
1192
  for (const error of result.validation.errors) {
805
- console.log(chalk5.red(` Error: ${error}`));
1193
+ lines.push(` Error: ${error}`);
806
1194
  }
807
1195
  } else {
808
- console.log(chalk5.green(`\u2713 ${fileName}`));
1196
+ lines.push(`\u2713 ${fileName}`);
1197
+ if (result.fixed && result.fixedFields && result.fixedFields.length > 0) {
1198
+ lines.push(` Fixed: ${result.fixedFields.join(", ")}`);
1199
+ }
809
1200
  if (result.card && result.classification) {
810
- const tierColor = getRiskLevelColor(result.classification.riskLevel);
811
- console.log(
812
- chalk5.dim(" Risk Level: ") + tierColor(result.classification.riskLevel)
813
- );
1201
+ lines.push(` Risk Level: ${result.classification.riskLevel}`);
814
1202
  if (result.classification.euAiActCategory) {
815
- console.log(
816
- chalk5.dim(" EU AI Act: ") + chalk5.yellow(result.classification.euAiActCategory)
817
- );
1203
+ lines.push(` EU AI Act: ${result.classification.euAiActCategory}`);
818
1204
  }
819
1205
  }
820
1206
  }
821
- console.log();
1207
+ lines.push("");
822
1208
  }
823
1209
  const valid = results.filter((r) => r.validation.valid).length;
824
1210
  const invalid = results.filter((r) => !r.validation.valid).length;
825
- console.log(chalk5.dim("\u2500".repeat(50)));
826
- console.log(
827
- `Total: ${results.length} | ` + chalk5.green(`Valid: ${valid}`) + ` | ` + chalk5.red(`Invalid: ${invalid}`)
1211
+ const fixed = results.filter((r) => r.fixed).length;
1212
+ lines.push("\u2500".repeat(50));
1213
+ lines.push(
1214
+ `Total: ${results.length} | Valid: ${valid} | Invalid: ${invalid}` + (fixed > 0 ? ` | Fixed: ${fixed}` : "")
828
1215
  );
829
- }
830
- function getRiskLevelColor(level) {
831
- switch (level) {
832
- case "minimal":
833
- return chalk5.green;
834
- case "limited":
835
- return chalk5.yellow;
836
- case "high":
837
- return chalk5.red;
838
- case "unacceptable":
839
- return chalk5.magenta;
840
- default:
841
- return chalk5.white;
842
- }
1216
+ return lines.join("\n");
843
1217
  }
844
1218
 
845
1219
  // src/commands/status.ts
846
1220
  import { Command as Command5 } from "commander";
847
- import chalk6 from "chalk";
848
- import path5 from "path";
1221
+ import chalk7 from "chalk";
1222
+ import path6 from "path";
849
1223
  import fs4 from "fs/promises";
850
1224
  import {
851
1225
  loadAssetCard as loadAssetCard2,
852
- classifyRisk as classifyRisk2
1226
+ classifyRisk as classifyRisk2,
1227
+ extractGoldenThreadComponents,
1228
+ verifyGoldenThreadHashSync
853
1229
  } from "@aigrc/core";
854
1230
  var DEFAULT_CARDS_DIR4 = ".aigrc/cards";
855
1231
  var DEFAULT_CONFIG_FILE2 = ".aigrc.yaml";
@@ -861,14 +1237,14 @@ async function runStatus(options) {
861
1237
  if (options.output === "text") {
862
1238
  printHeader();
863
1239
  }
864
- const configPath = path5.join(cwd, DEFAULT_CONFIG_FILE2);
865
- const cardsDir = path5.join(cwd, DEFAULT_CARDS_DIR4);
1240
+ const configPath = path6.join(cwd, DEFAULT_CONFIG_FILE2);
1241
+ const cardsDir = path6.join(cwd, DEFAULT_CARDS_DIR4);
866
1242
  const configExists = await fileExists2(configPath);
867
1243
  const cardsDirExists = await directoryExists(cardsDir);
868
1244
  if (!configExists && !cardsDirExists) {
869
1245
  if (options.output === "text") {
870
- console.log(chalk6.yellow("AIGRC is not initialized in this directory."));
871
- console.log(chalk6.dim("\nRun `aigrc init` to get started."));
1246
+ console.log(chalk7.yellow("AIGRC is not initialized in this directory."));
1247
+ console.log(chalk7.dim("\nRun `aigrc init` to get started."));
872
1248
  } else {
873
1249
  console.log(JSON.stringify({ initialized: false }));
874
1250
  }
@@ -881,7 +1257,7 @@ async function runStatus(options) {
881
1257
  const yamlFiles = files.filter((f) => f.endsWith(".yaml") || f.endsWith(".yml"));
882
1258
  for (const file of yamlFiles) {
883
1259
  try {
884
- const filePath = path5.join(cardsDir, file);
1260
+ const filePath = path6.join(cardsDir, file);
885
1261
  const card = loadAssetCard2(filePath);
886
1262
  const classification = classifyRisk2(card.classification.riskFactors);
887
1263
  cards.push({ path: filePath, card, classification });
@@ -912,19 +1288,21 @@ async function runStatus(options) {
912
1288
  );
913
1289
  return;
914
1290
  }
915
- console.log(chalk6.bold("AIGRC Status"));
916
- console.log(chalk6.dim("\u2500".repeat(50)));
1291
+ console.log(chalk7.bold("AIGRC Status"));
1292
+ console.log(chalk7.dim("\u2500".repeat(50)));
917
1293
  console.log();
918
- console.log(chalk6.dim("Config:"), configExists ? chalk6.green("\u2713") : chalk6.red("\u2717"), configPath);
919
- console.log(chalk6.dim("Cards:"), cardsDirExists ? chalk6.green("\u2713") : chalk6.red("\u2717"), cardsDir);
1294
+ console.log(chalk7.dim("Config:"), configExists ? chalk7.green("\u2713") : chalk7.red("\u2717"), configPath);
1295
+ console.log(chalk7.dim("Cards:"), cardsDirExists ? chalk7.green("\u2713") : chalk7.red("\u2717"), cardsDir);
1296
+ console.log();
1297
+ printGoldenThreadStatus(cards);
920
1298
  console.log();
921
1299
  if (cards.length === 0) {
922
- console.log(chalk6.yellow("No asset cards registered."));
923
- console.log(chalk6.dim("\nRun `aigrc init` or `aigrc register` to create an asset card."));
1300
+ console.log(chalk7.yellow("No asset cards registered."));
1301
+ console.log(chalk7.dim("\nRun `aigrc init` or `aigrc register` to create an asset card."));
924
1302
  return;
925
1303
  }
926
- console.log(chalk6.bold(`Registered Assets (${cards.length})`));
927
- console.log(chalk6.dim("\u2500".repeat(50)));
1304
+ console.log(chalk7.bold(`Registered Assets (${cards.length})`));
1305
+ console.log(chalk7.dim("\u2500".repeat(50)));
928
1306
  console.log();
929
1307
  const byLevel = groupByRiskLevel(cards);
930
1308
  for (const level of ["unacceptable", "high", "limited", "minimal"]) {
@@ -934,18 +1312,18 @@ async function runStatus(options) {
934
1312
  console.log(levelColor(`${level.toUpperCase()} (${levelCards.length})`));
935
1313
  console.log();
936
1314
  for (const { card, classification } of levelCards) {
937
- console.log(` ${chalk6.bold(card.name)}`);
938
- console.log(chalk6.dim(` ID: ${card.id}`));
939
- console.log(chalk6.dim(` Risk Level: ${classification.riskLevel}`));
1315
+ console.log(` ${chalk7.bold(card.name)}`);
1316
+ console.log(chalk7.dim(` ID: ${card.id}`));
1317
+ console.log(chalk7.dim(` Risk Level: ${classification.riskLevel}`));
940
1318
  if (classification.euAiActCategory) {
941
- console.log(chalk6.dim(` EU AI Act: `) + chalk6.yellow(classification.euAiActCategory));
1319
+ console.log(chalk7.dim(` EU AI Act: `) + chalk7.yellow(classification.euAiActCategory));
942
1320
  }
943
1321
  if (card.ownership?.owner) {
944
- console.log(chalk6.dim(` Owner: ${card.ownership.owner.name} <${card.ownership.owner.email}>`));
1322
+ console.log(chalk7.dim(` Owner: ${card.ownership.owner.name} <${card.ownership.owner.email}>`));
945
1323
  }
946
1324
  const activeRisks = getActiveRiskFactors(card);
947
1325
  if (activeRisks.length > 0) {
948
- console.log(chalk6.dim(` Risks: `) + activeRisks.join(", "));
1326
+ console.log(chalk7.dim(` Risks: `) + activeRisks.join(", "));
949
1327
  }
950
1328
  console.log();
951
1329
  }
@@ -964,17 +1342,17 @@ function groupByRiskLevel(cards) {
964
1342
  return byLevel;
965
1343
  }
966
1344
  function printStatusSummary(cards) {
967
- console.log(chalk6.dim("\u2500".repeat(50)));
1345
+ console.log(chalk7.dim("\u2500".repeat(50)));
968
1346
  const minimal = cards.filter((c) => c.classification.riskLevel === "minimal").length;
969
1347
  const limited = cards.filter((c) => c.classification.riskLevel === "limited").length;
970
1348
  const high = cards.filter((c) => c.classification.riskLevel === "high").length;
971
1349
  const unacceptable = cards.filter((c) => c.classification.riskLevel === "unacceptable").length;
972
1350
  console.log(
973
- `Total: ${cards.length} | ` + chalk6.green(`Minimal: ${minimal}`) + ` | ` + chalk6.yellow(`Limited: ${limited}`) + ` | ` + chalk6.red(`High: ${high}`) + ` | ` + chalk6.magenta(`Unacceptable: ${unacceptable}`)
1351
+ `Total: ${cards.length} | ` + chalk7.green(`Minimal: ${minimal}`) + ` | ` + chalk7.yellow(`Limited: ${limited}`) + ` | ` + chalk7.red(`High: ${high}`) + ` | ` + chalk7.magenta(`Unacceptable: ${unacceptable}`)
974
1352
  );
975
1353
  if (high > 0 || unacceptable > 0) {
976
1354
  console.log();
977
- console.log(chalk6.yellow("\u26A0 High-risk assets detected. Review compliance requirements."));
1355
+ console.log(chalk7.yellow("\u26A0 High-risk assets detected. Review compliance requirements."));
978
1356
  }
979
1357
  }
980
1358
  function getActiveRiskFactors(card) {
@@ -992,15 +1370,15 @@ function getActiveRiskFactors(card) {
992
1370
  function getRiskLevelColor2(level) {
993
1371
  switch (level) {
994
1372
  case "minimal":
995
- return chalk6.green;
1373
+ return chalk7.green;
996
1374
  case "limited":
997
- return chalk6.yellow;
1375
+ return chalk7.yellow;
998
1376
  case "high":
999
- return chalk6.red;
1377
+ return chalk7.red;
1000
1378
  case "unacceptable":
1001
- return chalk6.magenta;
1379
+ return chalk7.magenta;
1002
1380
  default:
1003
- return chalk6.white;
1381
+ return chalk7.white;
1004
1382
  }
1005
1383
  }
1006
1384
  async function fileExists2(filePath) {
@@ -1019,6 +1397,41 @@ async function directoryExists(dirPath) {
1019
1397
  return false;
1020
1398
  }
1021
1399
  }
1400
+ function printGoldenThreadStatus(cards) {
1401
+ const cardsWithGoldenThread = cards.filter((c) => {
1402
+ const components = extractGoldenThreadComponents(c.card);
1403
+ return components && c.card.golden_thread?.hash;
1404
+ });
1405
+ if (cardsWithGoldenThread.length === 0) {
1406
+ return;
1407
+ }
1408
+ console.log(chalk7.bold("Golden Thread"));
1409
+ console.log(chalk7.dim("\u2500".repeat(50)));
1410
+ console.log();
1411
+ for (const { card } of cardsWithGoldenThread) {
1412
+ const components = extractGoldenThreadComponents(card);
1413
+ if (!components || !card.golden_thread?.hash) continue;
1414
+ console.log(chalk7.bold(card.name));
1415
+ console.log(chalk7.dim(` Hash: ${card.golden_thread.hash}`));
1416
+ try {
1417
+ const verification = verifyGoldenThreadHashSync(components, card.golden_thread.hash);
1418
+ if (verification.verified) {
1419
+ console.log(chalk7.dim(" Status: ") + chalk7.green("\u2713 Verified"));
1420
+ } else {
1421
+ console.log(chalk7.dim(" Status: ") + chalk7.red("\u2717 Verification Failed"));
1422
+ if (verification.mismatch_reason) {
1423
+ console.log(chalk7.dim(" Reason: ") + chalk7.yellow(verification.mismatch_reason));
1424
+ }
1425
+ }
1426
+ } catch (error) {
1427
+ console.log(chalk7.dim(" Status: ") + chalk7.red("\u2717 Error"));
1428
+ }
1429
+ if (card.golden_thread?.signature) {
1430
+ console.log(chalk7.dim(" Signature: ") + chalk7.green("\u2713 RSA-SHA256"));
1431
+ }
1432
+ console.log();
1433
+ }
1434
+ }
1022
1435
  export {
1023
1436
  initCommand,
1024
1437
  registerCommand,