feat(ontology): add device asset infrastructure domains
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import assert from 'node:assert/strict'
|
||||
import fs from 'node:fs/promises'
|
||||
import path from 'node:path'
|
||||
import { loadCatalog, serviceRoot } from '../catalog.mjs'
|
||||
|
||||
const catalog = await loadCatalog()
|
||||
const directFixture = JSON.parse(
|
||||
await fs.readFile(path.join(serviceRoot, 'examples/device-direct-b2-composition.fixture.json'), 'utf8'),
|
||||
)
|
||||
const infrastructureFixture = JSON.parse(
|
||||
await fs.readFile(path.join(serviceRoot, 'examples/infrastructure-edge-host-composition.fixture.json'), 'utf8'),
|
||||
)
|
||||
|
||||
const requiredEntities = [
|
||||
directFixture.project.entityId,
|
||||
directFixture.asset.entityId,
|
||||
directFixture.device.entityId,
|
||||
directFixture.device.restrictedIdentifiers[0].entityId,
|
||||
directFixture.device.modelProfile.entityId,
|
||||
directFixture.binding.entityId,
|
||||
directFixture.positionObservation.entityId,
|
||||
directFixture.positionObservation.projectionEntityId,
|
||||
infrastructureFixture.host.entityId,
|
||||
infrastructureFixture.endpoint.entityId,
|
||||
infrastructureFixture.deployment.entityId,
|
||||
infrastructureFixture.serviceInstance.entityId,
|
||||
infrastructureFixture.edgeRegistration.entityId,
|
||||
infrastructureFixture.healthObservation.entityId,
|
||||
infrastructureFixture.management.credentialEntityId,
|
||||
infrastructureFixture.management.consoleEntityId,
|
||||
]
|
||||
|
||||
for (const entityId of requiredEntities) {
|
||||
assert.equal(catalog.entityById.has(entityId), true, `fixture entity missing from catalog: ${entityId}`)
|
||||
}
|
||||
|
||||
for (const relationId of [
|
||||
'device.tracking_device.tracks_asset',
|
||||
'device.asset_binding.attaches_device',
|
||||
'device.asset_binding.attaches_asset',
|
||||
'asset.asset.is_map_moving_object',
|
||||
'observation.position_observation.positions_asset',
|
||||
'infrastructure.host.exposes_endpoint',
|
||||
'infrastructure.deployment.targets_host',
|
||||
'infrastructure.service_instance.runs_on_host',
|
||||
'infrastructure.service_instance.realizes_deployment',
|
||||
'device.edge_registration.runs_as_service_instance',
|
||||
'observation.health_observation.targets_infrastructure',
|
||||
'gelios.unit.maps_to_asset',
|
||||
'gelios.tracker_device.maps_to_tracking_device',
|
||||
'gelios.position_fix.maps_to_position_observation',
|
||||
]) {
|
||||
assert.equal(catalog.relationById.has(relationId), true, `required relation missing: ${relationId}`)
|
||||
}
|
||||
|
||||
assert.notEqual(directFixture.asset.sourceId, directFixture.device.sourceId)
|
||||
assert.equal(directFixture.binding.assetSourceId, directFixture.asset.sourceId)
|
||||
assert.equal(directFixture.binding.deviceSourceId, directFixture.device.sourceId)
|
||||
assert.equal(directFixture.binding.validTo, null)
|
||||
assert.equal(directFixture.device.restrictedIdentifiers[0].valueExposure, 'restricted')
|
||||
assert.equal(JSON.stringify(directFixture).includes('integration.connection'), false)
|
||||
|
||||
const infrastructureIds = [
|
||||
infrastructureFixture.host.sourceId,
|
||||
infrastructureFixture.endpoint.sourceId,
|
||||
infrastructureFixture.deployment.sourceId,
|
||||
infrastructureFixture.serviceInstance.sourceId,
|
||||
infrastructureFixture.edgeRegistration.sourceId,
|
||||
]
|
||||
assert.equal(new Set(infrastructureIds).size, infrastructureIds.length)
|
||||
assert.equal(infrastructureFixture.management.secretExposure, 'opaque-reference-only')
|
||||
assert.equal(infrastructureFixture.management.mode, 'privileged-break-glass')
|
||||
|
||||
for (const guardrailId of [
|
||||
'guardrail.asset.identity_survives_device_changes',
|
||||
'guardrail.device.identifiers_are_restricted_not_identity',
|
||||
'guardrail.device.asset_attachment_is_temporal',
|
||||
'guardrail.infrastructure.host_is_not_connection',
|
||||
'guardrail.infrastructure.management_is_break_glass',
|
||||
'guardrail.observation.missing_is_not_automatically_unhealthy',
|
||||
'guardrail.gelios.neutral_composition_requires_explicit_binding',
|
||||
]) {
|
||||
assert.equal(
|
||||
catalog.guardrails.rules.some((rule) => rule.id === guardrailId),
|
||||
true,
|
||||
`required guardrail missing: ${guardrailId}`,
|
||||
)
|
||||
}
|
||||
|
||||
console.log(JSON.stringify({
|
||||
ok: true,
|
||||
checks: [
|
||||
'direct_b2_and_trike_have_distinct_identities',
|
||||
'device_asset_binding_is_temporal',
|
||||
'imei_exposure_is_restricted',
|
||||
'direct_fixture_has_no_gelios_dependency',
|
||||
'host_runtime_layers_have_distinct_identities',
|
||||
'management_secret_is_opaque_reference_only',
|
||||
'neutral_gelios_bridge_relations_exist',
|
||||
'composition_guardrails_exist',
|
||||
],
|
||||
}, null, 2))
|
||||
@@ -66,6 +66,51 @@ try {
|
||||
})
|
||||
assert.equal(guardrails.result.structuredContent.rules.some((rule) => rule.id === 'guardrail.gelios.commands_are_red_domain'), true)
|
||||
|
||||
const directTracker = await rpc(baseUrl, TOKEN, 51, 'tools/call', {
|
||||
name: 'ontology_get_entity',
|
||||
arguments: { term: 'B2 tracker' },
|
||||
})
|
||||
assert.equal(directTracker.result.structuredContent.entity.id, 'device.tracking_device')
|
||||
|
||||
const infrastructureHost = await rpc(baseUrl, TOKEN, 52, 'tools/call', {
|
||||
name: 'ontology_get_entity',
|
||||
arguments: { term: 'внешний VPS' },
|
||||
})
|
||||
assert.equal(infrastructureHost.result.structuredContent.entity.id, 'infrastructure.host')
|
||||
|
||||
const restrictedIdentifierGuardrails = await rpc(baseUrl, TOKEN, 53, 'tools/call', {
|
||||
name: 'ontology_get_guardrails',
|
||||
arguments: { entityId: 'device.restricted_identifier' },
|
||||
})
|
||||
assert.equal(
|
||||
restrictedIdentifierGuardrails.result.structuredContent.rules.some(
|
||||
(rule) => rule.id === 'guardrail.device.identifiers_are_restricted_not_identity',
|
||||
),
|
||||
true,
|
||||
)
|
||||
|
||||
const hostGuardrails = await rpc(baseUrl, TOKEN, 54, 'tools/call', {
|
||||
name: 'ontology_get_guardrails',
|
||||
arguments: { entityId: 'infrastructure.host' },
|
||||
})
|
||||
assert.equal(
|
||||
hostGuardrails.result.structuredContent.rules.some(
|
||||
(rule) => rule.id === 'guardrail.infrastructure.host_is_not_connection',
|
||||
),
|
||||
true,
|
||||
)
|
||||
|
||||
const positionObservation = await rpc(baseUrl, TOKEN, 55, 'tools/call', {
|
||||
name: 'ontology_get_entity',
|
||||
arguments: { entityId: 'observation.position_observation' },
|
||||
})
|
||||
assert.equal(
|
||||
positionObservation.result.structuredContent.relations.some(
|
||||
(relation) => relation.id === 'gelios.position_fix.maps_to_position_observation',
|
||||
),
|
||||
true,
|
||||
)
|
||||
|
||||
const unauthorized = await fetch(`${baseUrl}/mcp`, {
|
||||
method: 'POST',
|
||||
headers: { 'content-type': 'application/json' },
|
||||
@@ -82,6 +127,11 @@ try {
|
||||
'gelios_alias_resolution',
|
||||
'gelios_value_contract_visible',
|
||||
'gelios_command_guardrail_visible',
|
||||
'direct_b2_tracker_alias_resolution',
|
||||
'infrastructure_host_alias_resolution',
|
||||
'restricted_identifier_guardrail_visible',
|
||||
'host_connection_conflation_blocked',
|
||||
'gelios_position_neutral_mapping_visible',
|
||||
'evidence_paths_not_exposed',
|
||||
'internal_bearer_required',
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user