@disclosureos/cli 1.0.0 → 1.1.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/CHANGELOG.md +38 -0
- package/dist/index.js +160 -11
- package/package.json +7 -6
package/CHANGELOG.md
CHANGED
|
@@ -4,11 +4,49 @@ All notable changes to `@disclosureos/cli` are documented here. The format is ba
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and this project adheres to
|
|
5
5
|
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## 1.1.0
|
|
8
|
+
|
|
9
|
+
### Minor Changes
|
|
10
|
+
|
|
11
|
+
- e6ff99c: Add the instruments layer: the UAP Sensor Manifest standard
|
|
12
|
+
(`@disclosureos/instruments@1.0.0`, first published alongside this release —
|
|
13
|
+
see its own changelog).
|
|
14
|
+
|
|
15
|
+
- **`@disclosureos/records`** — `SensorReading` gains optional `sensorRef`
|
|
16
|
+
(`"<org-slug>:<sensor-id>"`, with `sensorRef`/`isSensorRef`/`parseSensorRef`
|
|
17
|
+
helpers in `records/shared`); `SensorType` gains 18 instrument classes
|
|
18
|
+
(infrasonic/ultrasonic sensors, ADS-B receiver, RF spectrum analyzer, passive
|
|
19
|
+
radar, particle detector, atomic clock, GNSS receiver, fluxgate magnetometer,
|
|
20
|
+
air-quality sensor, barometer, lightning detector, IMU, EEG, GSR, pulse
|
|
21
|
+
oximeter, pupillometer, hardware RNG); `DetectionMethod` gains 7 methods
|
|
22
|
+
(particle, gnss, time_reference, chemical, inertial, physiological, pressure).
|
|
23
|
+
Records schema artifact bumped to `1.1.0`.
|
|
24
|
+
- **`@disclosureos/schema`** — composed enriched-observation artifact re-emitted
|
|
25
|
+
at `1.1.0` to carry the records changes.
|
|
26
|
+
- **`@disclosureos/cli`** — new `disclosureos manifest validate <path...>`
|
|
27
|
+
command (`--recursive`, `--json`) delegating to `validateSensorManifest`.
|
|
28
|
+
|
|
29
|
+
### Patch Changes
|
|
30
|
+
|
|
31
|
+
- c351793: `disclosureos validate` now warns when a `SensorReading.sensorRef` does not
|
|
32
|
+
match the `"<org-slug>:<sensor-id>"` manifest convention — mirroring the
|
|
33
|
+
existing malformed/dangling warnings for claim `evidenceRefs`.
|
|
34
|
+
- Updated dependencies [b373962]
|
|
35
|
+
- Updated dependencies [e6ff99c]
|
|
36
|
+
- Updated dependencies [19bad3d]
|
|
37
|
+
- @disclosureos/instruments@1.0.0
|
|
38
|
+
- @disclosureos/records@1.1.0
|
|
39
|
+
- @disclosureos/schema@1.1.0
|
|
40
|
+
- @disclosureos/scoring@1.1.0
|
|
41
|
+
- @disclosureos/observables@1.0.1
|
|
42
|
+
- @disclosureos/origins@1.0.1
|
|
43
|
+
|
|
7
44
|
## 1.0.0
|
|
8
45
|
|
|
9
46
|
Initial public release — developer tools for the DisclosureOS ecosystem.
|
|
10
47
|
|
|
11
48
|
### Added
|
|
49
|
+
|
|
12
50
|
- `scaffold` — generate typed observation/data-structure templates.
|
|
13
51
|
- `validate` — validate observation JSON files through `parseEnrichedObservation`;
|
|
14
52
|
warns on dangling `evidenceRefs`.
|
package/dist/index.js
CHANGED
|
@@ -395,7 +395,7 @@ Generate typed templates for DisclosureOS data structures.
|
|
|
395
395
|
}
|
|
396
396
|
|
|
397
397
|
// src/commands/validate.ts
|
|
398
|
-
import { parseEvidenceRef } from "@disclosureos/records/shared";
|
|
398
|
+
import { parseEvidenceRef, isSensorRef } from "@disclosureos/records/shared";
|
|
399
399
|
import { parseEnrichedObservation } from "@disclosureos/schema";
|
|
400
400
|
function validate(args2) {
|
|
401
401
|
if (args2.flags["help"]) {
|
|
@@ -503,7 +503,7 @@ function validateFileStructured(filePath, strict) {
|
|
|
503
503
|
const obs = data;
|
|
504
504
|
const structuralErrors = parseEnrichedObservation(obs).issues;
|
|
505
505
|
const semanticErrors = validateSemantics(obs);
|
|
506
|
-
const warnings = [...validateEvidenceRefs(obs), ...strict ? validateStrict(obs) : []];
|
|
506
|
+
const warnings = [...validateEvidenceRefs(obs), ...validateSensorRefs(obs), ...strict ? validateStrict(obs) : []];
|
|
507
507
|
const allErrors = [...structuralErrors, ...semanticErrors];
|
|
508
508
|
return {
|
|
509
509
|
file: filePath,
|
|
@@ -527,7 +527,7 @@ function validateFile(filePath, strict) {
|
|
|
527
527
|
const obs = data;
|
|
528
528
|
const structuralErrors = parseEnrichedObservation(obs).issues;
|
|
529
529
|
const semanticErrors = validateSemantics(obs);
|
|
530
|
-
const warnings = [...validateEvidenceRefs(obs), ...strict ? validateStrict(obs) : []];
|
|
530
|
+
const warnings = [...validateEvidenceRefs(obs), ...validateSensorRefs(obs), ...strict ? validateStrict(obs) : []];
|
|
531
531
|
const allErrors = [...structuralErrors, ...semanticErrors];
|
|
532
532
|
if (allErrors.length > 0 || warnings.length > 0) {
|
|
533
533
|
console.log(`
|
|
@@ -628,6 +628,25 @@ function validateEvidenceRefs(obs) {
|
|
|
628
628
|
}
|
|
629
629
|
return warnings;
|
|
630
630
|
}
|
|
631
|
+
function validateSensorRefs(obs) {
|
|
632
|
+
const warnings = [];
|
|
633
|
+
const sensorEvidence = obs["sensorEvidence"];
|
|
634
|
+
if (!sensorEvidence || typeof sensorEvidence !== "object") return warnings;
|
|
635
|
+
const sensors = sensorEvidence["sensors"];
|
|
636
|
+
if (!Array.isArray(sensors)) return warnings;
|
|
637
|
+
sensors.forEach((sensor, i) => {
|
|
638
|
+
if (!sensor || typeof sensor !== "object") return;
|
|
639
|
+
const ref = sensor["sensorRef"];
|
|
640
|
+
if (ref === void 0) return;
|
|
641
|
+
if (typeof ref !== "string" || !isSensorRef(ref)) {
|
|
642
|
+
warnings.push({
|
|
643
|
+
path: `sensorEvidence.sensors[${i}].sensorRef`,
|
|
644
|
+
message: `malformed sensor ref ${JSON.stringify(ref)} (expected "<org-slug>:<sensor-id>", e.g. "eldaeon:dionysus-passive-radar")`
|
|
645
|
+
});
|
|
646
|
+
}
|
|
647
|
+
});
|
|
648
|
+
return warnings;
|
|
649
|
+
}
|
|
631
650
|
function validateStrict(obs) {
|
|
632
651
|
const warnings = [];
|
|
633
652
|
const recommended = ["summary", "description", "objectCharacteristics", "sourceData", "witnesses"];
|
|
@@ -679,6 +698,7 @@ Validate observation JSON files against the full schema.
|
|
|
679
698
|
console.log(` \u2022 OCS node IDs in origin classifications (if present)`);
|
|
680
699
|
console.log(` \u2022 Unknown top-level keys rejected (third-party data belongs under "extensions")`);
|
|
681
700
|
console.log(` \u2022 Evidence refs on claims resolve to in-record evidence (warns on dangling)`);
|
|
701
|
+
console.log(` \u2022 Sensor refs follow the "<org-slug>:<sensor-id>" manifest convention (warns on malformed)`);
|
|
682
702
|
console.log(` \u2022 Semantic sanity (non-negative duration, speed, counts)
|
|
683
703
|
`);
|
|
684
704
|
console.log(`${dim("Examples:")}`);
|
|
@@ -690,17 +710,142 @@ Validate observation JSON files against the full schema.
|
|
|
690
710
|
`);
|
|
691
711
|
}
|
|
692
712
|
|
|
713
|
+
// src/commands/manifest.ts
|
|
714
|
+
import { validateSensorManifest } from "@disclosureos/instruments";
|
|
715
|
+
function manifest(args2) {
|
|
716
|
+
if (args2.flags["help"] || args2.subcommand !== "validate") {
|
|
717
|
+
printUsage3();
|
|
718
|
+
if (!args2.flags["help"] && args2.subcommand !== void 0 && args2.subcommand !== "validate") {
|
|
719
|
+
process.exit(1);
|
|
720
|
+
}
|
|
721
|
+
return;
|
|
722
|
+
}
|
|
723
|
+
const targets = args2.positional.filter((t) => !!t);
|
|
724
|
+
if (targets.length === 0) {
|
|
725
|
+
printUsage3();
|
|
726
|
+
return;
|
|
727
|
+
}
|
|
728
|
+
const recursive = !!args2.flags["recursive"];
|
|
729
|
+
const jsonMode = !!args2.flags["json"];
|
|
730
|
+
const files = [...new Set(targets.flatMap((target) => findJSONFiles(target, recursive)))];
|
|
731
|
+
if (files.length === 0) {
|
|
732
|
+
console.log(error(`No JSON files found at: ${targets.join(", ")}`));
|
|
733
|
+
process.exit(1);
|
|
734
|
+
}
|
|
735
|
+
const results = files.map(validateManifestFile);
|
|
736
|
+
if (jsonMode) {
|
|
737
|
+
const valid = results.every((result) => result.valid);
|
|
738
|
+
console.log(JSON.stringify({ files: results, valid }, null, 2));
|
|
739
|
+
if (!valid) process.exit(1);
|
|
740
|
+
return;
|
|
741
|
+
}
|
|
742
|
+
let failed = false;
|
|
743
|
+
for (const result of results) {
|
|
744
|
+
if (!result.valid) {
|
|
745
|
+
failed = true;
|
|
746
|
+
console.log(`
|
|
747
|
+
${error(`${result.file} INVALID`)}`);
|
|
748
|
+
for (const issue of result.errors) {
|
|
749
|
+
console.log(` ${error(`${issue.path}: ${issue.message}`)}`);
|
|
750
|
+
}
|
|
751
|
+
continue;
|
|
752
|
+
}
|
|
753
|
+
const summary = result.summary;
|
|
754
|
+
console.log(`
|
|
755
|
+
${success(`${result.file}`)}`);
|
|
756
|
+
console.log(` ${dim(formatManifestLine(summary))}`);
|
|
757
|
+
console.log(` ${dim("by modality:")} ${Object.entries(summary.byModality).map(([m, n]) => `${m} ${n}`).join(", ")}`);
|
|
758
|
+
if (summary.proposedSensorTypes.length > 0) {
|
|
759
|
+
console.log(` ${dim(`proposed sensor types (${summary.proposedSensorTypes.length}):`)} ${summary.proposedSensorTypes.join(", ")}`);
|
|
760
|
+
}
|
|
761
|
+
if (summary.proposedDetectionMethods.length > 0) {
|
|
762
|
+
console.log(` ${dim(`proposed detection methods (${summary.proposedDetectionMethods.length}):`)} ${summary.proposedDetectionMethods.join(", ")}`);
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
console.log("");
|
|
766
|
+
if (failed) {
|
|
767
|
+
console.log(error(`Validation failed`));
|
|
768
|
+
process.exit(1);
|
|
769
|
+
}
|
|
770
|
+
console.log(success(`All ${files.length} manifest(s) valid`));
|
|
771
|
+
}
|
|
772
|
+
function formatManifestLine(summary) {
|
|
773
|
+
return `${summary.org} (${summary.orgSlug}) \u2014 ${summary.sensors} sensors, ${summary.futureUpgrades} future upgrades`;
|
|
774
|
+
}
|
|
775
|
+
function validateManifestFile(file) {
|
|
776
|
+
let data;
|
|
777
|
+
try {
|
|
778
|
+
data = readJSON(file);
|
|
779
|
+
} catch {
|
|
780
|
+
return { file, valid: false, errors: [{ path: "", message: "Failed to parse JSON" }] };
|
|
781
|
+
}
|
|
782
|
+
const issues = validateSensorManifest(data);
|
|
783
|
+
if (issues.length > 0) {
|
|
784
|
+
return { file, valid: false, errors: issues };
|
|
785
|
+
}
|
|
786
|
+
const parsed = data;
|
|
787
|
+
const byModality = {};
|
|
788
|
+
const proposedSensorTypes = [];
|
|
789
|
+
const proposedDetectionMethods = [];
|
|
790
|
+
for (const sensor of parsed.sensors) {
|
|
791
|
+
byModality[sensor.modality] = (byModality[sensor.modality] ?? 0) + 1;
|
|
792
|
+
if (sensor.recordsMapping?.proposedSensorType && sensor.recordsMapping.sensorType) {
|
|
793
|
+
proposedSensorTypes.push(sensor.recordsMapping.sensorType);
|
|
794
|
+
}
|
|
795
|
+
if (sensor.recordsMapping?.proposedDetectionMethod && sensor.recordsMapping.detectionMethod) {
|
|
796
|
+
proposedDetectionMethods.push(sensor.recordsMapping.detectionMethod);
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
return {
|
|
800
|
+
file,
|
|
801
|
+
valid: true,
|
|
802
|
+
errors: [],
|
|
803
|
+
summary: {
|
|
804
|
+
org: parsed.org,
|
|
805
|
+
orgSlug: parsed.orgSlug,
|
|
806
|
+
sensors: parsed.sensors.length,
|
|
807
|
+
byModality,
|
|
808
|
+
proposedSensorTypes,
|
|
809
|
+
proposedDetectionMethods,
|
|
810
|
+
futureUpgrades: parsed.futureUpgrades?.length ?? 0
|
|
811
|
+
}
|
|
812
|
+
};
|
|
813
|
+
}
|
|
814
|
+
function printUsage3() {
|
|
815
|
+
console.log(heading(`${BRAND} manifest`));
|
|
816
|
+
console.log(`
|
|
817
|
+
Validate sensor-manifest JSON files (@disclosureos/instruments).
|
|
818
|
+
`);
|
|
819
|
+
console.log(`${dim("Usage:")}`);
|
|
820
|
+
console.log(` disclosureos manifest validate <path...> [options]
|
|
821
|
+
`);
|
|
822
|
+
console.log(`${dim("Options:")}`);
|
|
823
|
+
console.log(` --recursive, -r Validate all JSON files in directories recursively`);
|
|
824
|
+
console.log(` --json, -j Output results as structured JSON (for CI and scripts)`);
|
|
825
|
+
console.log(` --help, -h Show this help message
|
|
826
|
+
`);
|
|
827
|
+
console.log(`${dim("Validation checks:")}`);
|
|
828
|
+
console.log(` \u2022 Manifest structure (org, orgSlug, sensors, calibration, timing, measurements)`);
|
|
829
|
+
console.log(` \u2022 Sensor ids unique and kebab-case`);
|
|
830
|
+
console.log(` \u2022 recordsMapping values exist in the records enums or carry proposed* flags
|
|
831
|
+
`);
|
|
832
|
+
console.log(`${dim("Examples:")}`);
|
|
833
|
+
console.log(` disclosureos manifest validate ./manifests/eldaeon-sensors.json`);
|
|
834
|
+
console.log(` disclosureos manifest validate ./manifests/ --recursive --json
|
|
835
|
+
`);
|
|
836
|
+
}
|
|
837
|
+
|
|
693
838
|
// src/commands/completeness.ts
|
|
694
839
|
import { parseEnrichedObservation as parseEnrichedObservation2 } from "@disclosureos/schema";
|
|
695
840
|
import { getCompleteness } from "@disclosureos/scoring";
|
|
696
841
|
function completeness(args2) {
|
|
697
842
|
if (args2.flags["help"]) {
|
|
698
|
-
|
|
843
|
+
printUsage4();
|
|
699
844
|
return;
|
|
700
845
|
}
|
|
701
846
|
const targets = [args2.subcommand, ...args2.positional].filter((t) => !!t);
|
|
702
847
|
if (targets.length === 0) {
|
|
703
|
-
|
|
848
|
+
printUsage4();
|
|
704
849
|
return;
|
|
705
850
|
}
|
|
706
851
|
const recursive = !!args2.flags["recursive"];
|
|
@@ -866,7 +1011,7 @@ function scoreFileQuiet(filePath) {
|
|
|
866
1011
|
missing: comp.missing
|
|
867
1012
|
};
|
|
868
1013
|
}
|
|
869
|
-
function
|
|
1014
|
+
function printUsage4() {
|
|
870
1015
|
console.log(heading(`${BRAND} completeness`));
|
|
871
1016
|
console.log(`
|
|
872
1017
|
Measure how fully observation JSON files populate the records schema.
|
|
@@ -916,7 +1061,7 @@ import {
|
|
|
916
1061
|
} from "@disclosureos/origins";
|
|
917
1062
|
function registry(args2) {
|
|
918
1063
|
if (args2.flags["help"]) {
|
|
919
|
-
|
|
1064
|
+
printUsage5();
|
|
920
1065
|
return;
|
|
921
1066
|
}
|
|
922
1067
|
const sub = args2.subcommand;
|
|
@@ -931,7 +1076,7 @@ function registry(args2) {
|
|
|
931
1076
|
registryOrigins(args2);
|
|
932
1077
|
break;
|
|
933
1078
|
default:
|
|
934
|
-
|
|
1079
|
+
printUsage5();
|
|
935
1080
|
}
|
|
936
1081
|
}
|
|
937
1082
|
function registryFields(args2) {
|
|
@@ -1070,7 +1215,7 @@ ${label("ID", node.id)}`);
|
|
|
1070
1215
|
}
|
|
1071
1216
|
console.log("");
|
|
1072
1217
|
}
|
|
1073
|
-
function
|
|
1218
|
+
function printUsage5() {
|
|
1074
1219
|
console.log(heading(`${BRAND} registry`));
|
|
1075
1220
|
console.log(`
|
|
1076
1221
|
Introspect DisclosureOS field, observable, and origin registries.
|
|
@@ -1110,7 +1255,7 @@ import {
|
|
|
1110
1255
|
var FIELD_PATH_COUNT = deriveFieldPaths2().length;
|
|
1111
1256
|
function info(args2) {
|
|
1112
1257
|
if (args2.flags["help"]) {
|
|
1113
|
-
|
|
1258
|
+
printUsage6();
|
|
1114
1259
|
return;
|
|
1115
1260
|
}
|
|
1116
1261
|
const sub = args2.subcommand;
|
|
@@ -1257,7 +1402,7 @@ ${label("ID", node.id)}`);
|
|
|
1257
1402
|
}
|
|
1258
1403
|
console.log("");
|
|
1259
1404
|
}
|
|
1260
|
-
function
|
|
1405
|
+
function printUsage6() {
|
|
1261
1406
|
console.log(heading(`${BRAND} info`));
|
|
1262
1407
|
console.log(`
|
|
1263
1408
|
Quick reference for DisclosureOS types and definitions.
|
|
@@ -1291,6 +1436,9 @@ if (args.flags["version"]) {
|
|
|
1291
1436
|
case "validate":
|
|
1292
1437
|
validate(args);
|
|
1293
1438
|
break;
|
|
1439
|
+
case "manifest":
|
|
1440
|
+
manifest(args);
|
|
1441
|
+
break;
|
|
1294
1442
|
case "completeness":
|
|
1295
1443
|
completeness(args);
|
|
1296
1444
|
break;
|
|
@@ -1322,6 +1470,7 @@ function printHelp() {
|
|
|
1322
1470
|
console.log(`${dim("Commands:")}`);
|
|
1323
1471
|
console.log(` scaffold Generate typed data structure templates`);
|
|
1324
1472
|
console.log(` validate Validate observation JSON files`);
|
|
1473
|
+
console.log(` manifest Validate sensor-manifest JSON files`);
|
|
1325
1474
|
console.log(` completeness Measure how fully observations populate the schema`);
|
|
1326
1475
|
console.log(` registry Introspect field, observable, and origin registries`);
|
|
1327
1476
|
console.log(` info Quick reference for types and definitions`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@disclosureos/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Developer tools for the DisclosureOS ecosystem — scaffold, validate, and inspect observations",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -42,11 +42,12 @@
|
|
|
42
42
|
],
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"picocolors": "^1.1.1",
|
|
45
|
-
"@disclosureos/records": "1.
|
|
46
|
-
"@disclosureos/
|
|
47
|
-
"@disclosureos/
|
|
48
|
-
"@disclosureos/
|
|
49
|
-
"@disclosureos/
|
|
45
|
+
"@disclosureos/records": "1.1.0",
|
|
46
|
+
"@disclosureos/instruments": "1.0.0",
|
|
47
|
+
"@disclosureos/observables": "1.0.1",
|
|
48
|
+
"@disclosureos/scoring": "1.1.0",
|
|
49
|
+
"@disclosureos/origins": "1.0.1",
|
|
50
|
+
"@disclosureos/schema": "1.1.0"
|
|
50
51
|
},
|
|
51
52
|
"devDependencies": {
|
|
52
53
|
"@types/node": "^20",
|