diff --git a/deploy/artifact-catalog.dev.json b/deploy/artifact-catalog.dev.json index c17551b5..8acdfb25 100644 --- a/deploy/artifact-catalog.dev.json +++ b/deploy/artifact-catalog.dev.json @@ -5,13 +5,13 @@ "profile": "dev", "namespace": "hwlab-dev", "endpoint": "http://74.48.78.17:16667", - "commitId": "c7de474", - "artifactState": "contract-skeleton", + "commitId": "7de6edd", + "artifactState": "published", "publish": { - "ciPublished": false, - "registryVerified": false, - "provenance": "not_available_until_publish", - "note": "Artifact identity was refreshed to this source commit, but no publish report proved registry digests." + "ciPublished": true, + "registryVerified": true, + "provenance": "reports/dev-gate/dev-artifacts.json", + "note": "Digest fields were copied from a successful DEV artifact publish report for this source commit." }, "healthContract": { "method": "GET", @@ -66,11 +66,11 @@ "services": [ { "serviceId": "hwlab-cloud-api", - "commitId": "c7de474", - "image": "127.0.0.1:5000/hwlab/hwlab-cloud-api:c7de474", - "imageTag": "c7de474", - "digest": "not_published", - "publishState": "skeleton-only", + "commitId": "7de6edd", + "image": "127.0.0.1:5000/hwlab/hwlab-cloud-api:7de6edd", + "imageTag": "7de6edd", + "digest": "sha256:8a0120e976f8369c19887d33d004b763c7dbff3f43fd023fb186abffe199b3ea", + "publishState": "published", "profile": "dev", "namespace": "hwlab-dev", "healthPath": "/health/live", @@ -78,15 +78,15 @@ "publishEnabled": true, "artifactRequired": true, "artifactScope": "required", - "notPublishedReason": "publish_not_run" + "notPublishedReason": null }, { "serviceId": "hwlab-cloud-web", - "commitId": "c7de474", - "image": "127.0.0.1:5000/hwlab/hwlab-cloud-web:c7de474", - "imageTag": "c7de474", - "digest": "not_published", - "publishState": "skeleton-only", + "commitId": "7de6edd", + "image": "127.0.0.1:5000/hwlab/hwlab-cloud-web:7de6edd", + "imageTag": "7de6edd", + "digest": "sha256:a302a8b4f3c6e7a91e5046398b803e9915dba873cefac97bb7069886b5a48d61", + "publishState": "published", "profile": "dev", "namespace": "hwlab-dev", "healthPath": "/health/live", @@ -94,15 +94,15 @@ "publishEnabled": true, "artifactRequired": true, "artifactScope": "required", - "notPublishedReason": "publish_not_run" + "notPublishedReason": null }, { "serviceId": "hwlab-agent-mgr", - "commitId": "c7de474", - "image": "127.0.0.1:5000/hwlab/hwlab-agent-mgr:c7de474", - "imageTag": "c7de474", - "digest": "not_published", - "publishState": "skeleton-only", + "commitId": "7de6edd", + "image": "127.0.0.1:5000/hwlab/hwlab-agent-mgr:7de6edd", + "imageTag": "7de6edd", + "digest": "sha256:a16bba1762f58e4daed7ef94243e3f4f0522332704c9f40bcadab77f11acc7a2", + "publishState": "published", "profile": "dev", "namespace": "hwlab-dev", "healthPath": "/health/live", @@ -110,15 +110,15 @@ "publishEnabled": true, "artifactRequired": true, "artifactScope": "required", - "notPublishedReason": "publish_not_run" + "notPublishedReason": null }, { "serviceId": "hwlab-agent-worker", - "commitId": "c7de474", - "image": "127.0.0.1:5000/hwlab/hwlab-agent-worker:c7de474", - "imageTag": "c7de474", - "digest": "not_published", - "publishState": "skeleton-only", + "commitId": "7de6edd", + "image": "127.0.0.1:5000/hwlab/hwlab-agent-worker:7de6edd", + "imageTag": "7de6edd", + "digest": "sha256:3c75cb9d6c2b7392749d972c78f9b7a89e4f099a64f902d2eb9810f16d551ceb", + "publishState": "published", "profile": "dev", "namespace": "hwlab-dev", "healthPath": "/health/live", @@ -126,15 +126,15 @@ "publishEnabled": true, "artifactRequired": true, "artifactScope": "required", - "notPublishedReason": "publish_not_run" + "notPublishedReason": null }, { "serviceId": "hwlab-gateway", - "commitId": "c7de474", - "image": "127.0.0.1:5000/hwlab/hwlab-gateway:c7de474", - "imageTag": "c7de474", - "digest": "not_published", - "publishState": "skeleton-only", + "commitId": "7de6edd", + "image": "127.0.0.1:5000/hwlab/hwlab-gateway:7de6edd", + "imageTag": "7de6edd", + "digest": "sha256:b5374c763d54628c2e81b86c93c826a27629a8546ae789223e0553ca7c8ab80a", + "publishState": "published", "profile": "dev", "namespace": "hwlab-dev", "healthPath": "/health/live", @@ -142,15 +142,15 @@ "publishEnabled": true, "artifactRequired": true, "artifactScope": "required", - "notPublishedReason": "publish_not_run" + "notPublishedReason": null }, { "serviceId": "hwlab-gateway-simu", - "commitId": "c7de474", - "image": "127.0.0.1:5000/hwlab/hwlab-gateway-simu:c7de474", - "imageTag": "c7de474", - "digest": "not_published", - "publishState": "skeleton-only", + "commitId": "7de6edd", + "image": "127.0.0.1:5000/hwlab/hwlab-gateway-simu:7de6edd", + "imageTag": "7de6edd", + "digest": "sha256:be6aff770319c6c2b45f7b5974d4fb7aae4aab79a6670b1d8b87e194387e30dd", + "publishState": "published", "profile": "dev", "namespace": "hwlab-dev", "healthPath": "/health/live", @@ -158,15 +158,15 @@ "publishEnabled": true, "artifactRequired": true, "artifactScope": "required", - "notPublishedReason": "publish_not_run" + "notPublishedReason": null }, { "serviceId": "hwlab-box-simu", - "commitId": "c7de474", - "image": "127.0.0.1:5000/hwlab/hwlab-box-simu:c7de474", - "imageTag": "c7de474", - "digest": "not_published", - "publishState": "skeleton-only", + "commitId": "7de6edd", + "image": "127.0.0.1:5000/hwlab/hwlab-box-simu:7de6edd", + "imageTag": "7de6edd", + "digest": "sha256:54e77f4c2ea9e87fd16becde9a5fe05b935f2472dba443458257dfdda1e87c27", + "publishState": "published", "profile": "dev", "namespace": "hwlab-dev", "healthPath": "/health/live", @@ -174,15 +174,15 @@ "publishEnabled": true, "artifactRequired": true, "artifactScope": "required", - "notPublishedReason": "publish_not_run" + "notPublishedReason": null }, { "serviceId": "hwlab-patch-panel", - "commitId": "c7de474", - "image": "127.0.0.1:5000/hwlab/hwlab-patch-panel:c7de474", - "imageTag": "c7de474", - "digest": "not_published", - "publishState": "skeleton-only", + "commitId": "7de6edd", + "image": "127.0.0.1:5000/hwlab/hwlab-patch-panel:7de6edd", + "imageTag": "7de6edd", + "digest": "sha256:02bec561d5f08f850e2884a2098da72978ad319d5bd799704ad9650778f11ae2", + "publishState": "published", "profile": "dev", "namespace": "hwlab-dev", "healthPath": "/health/live", @@ -190,15 +190,15 @@ "publishEnabled": true, "artifactRequired": true, "artifactScope": "required", - "notPublishedReason": "publish_not_run" + "notPublishedReason": null }, { "serviceId": "hwlab-router", - "commitId": "c7de474", - "image": "127.0.0.1:5000/hwlab/hwlab-router:c7de474", - "imageTag": "c7de474", - "digest": "not_published", - "publishState": "skeleton-only", + "commitId": "7de6edd", + "image": "127.0.0.1:5000/hwlab/hwlab-router:7de6edd", + "imageTag": "7de6edd", + "digest": "sha256:bb3b10c486bb1305f4cae7ec2751947eeb0cc60c9993d54e93802b5df3b13fb6", + "publishState": "published", "profile": "dev", "namespace": "hwlab-dev", "healthPath": "/health/live", @@ -206,15 +206,15 @@ "publishEnabled": true, "artifactRequired": true, "artifactScope": "required", - "notPublishedReason": "publish_not_run" + "notPublishedReason": null }, { "serviceId": "hwlab-tunnel-client", - "commitId": "c7de474", - "image": "127.0.0.1:5000/hwlab/hwlab-tunnel-client:c7de474", - "imageTag": "c7de474", - "digest": "not_published", - "publishState": "skeleton-only", + "commitId": "7de6edd", + "image": "127.0.0.1:5000/hwlab/hwlab-tunnel-client:7de6edd", + "imageTag": "7de6edd", + "digest": "sha256:ce9fa92362dd10911dcd66c59412690ccde401ce4ef82b548cfcea37499a725f", + "publishState": "published", "profile": "dev", "namespace": "hwlab-dev", "healthPath": "/health/live", @@ -222,15 +222,15 @@ "publishEnabled": true, "artifactRequired": true, "artifactScope": "required", - "notPublishedReason": "publish_not_run" + "notPublishedReason": null }, { "serviceId": "hwlab-edge-proxy", - "commitId": "c7de474", - "image": "127.0.0.1:5000/hwlab/hwlab-edge-proxy:c7de474", - "imageTag": "c7de474", - "digest": "not_published", - "publishState": "skeleton-only", + "commitId": "7de6edd", + "image": "127.0.0.1:5000/hwlab/hwlab-edge-proxy:7de6edd", + "imageTag": "7de6edd", + "digest": "sha256:bf19f6812cbac3c54c938ad12a2b1ef9626a746017f85658b9960a1761afa32c", + "publishState": "published", "profile": "dev", "namespace": "hwlab-dev", "healthPath": "/health/live", @@ -238,15 +238,15 @@ "publishEnabled": true, "artifactRequired": true, "artifactScope": "required", - "notPublishedReason": "publish_not_run" + "notPublishedReason": null }, { "serviceId": "hwlab-cli", - "commitId": "c7de474", - "image": "127.0.0.1:5000/hwlab/hwlab-cli:c7de474", - "imageTag": "c7de474", - "digest": "not_published", - "publishState": "skeleton-only", + "commitId": "7de6edd", + "image": "127.0.0.1:5000/hwlab/hwlab-cli:7de6edd", + "imageTag": "7de6edd", + "digest": "sha256:b169c015c4c06dcdf6d43131d69dd72be1996cb5970e25014c4cec0271b459c0", + "publishState": "published", "profile": "dev", "namespace": "hwlab-dev", "healthPath": "/health/live", @@ -254,15 +254,15 @@ "publishEnabled": true, "artifactRequired": true, "artifactScope": "required", - "notPublishedReason": "publish_not_run" + "notPublishedReason": null }, { "serviceId": "hwlab-agent-skills", - "commitId": "c7de474", - "image": "127.0.0.1:5000/hwlab/hwlab-agent-skills:c7de474", - "imageTag": "c7de474", - "digest": "not_published", - "publishState": "skeleton-only", + "commitId": "7de6edd", + "image": "127.0.0.1:5000/hwlab/hwlab-agent-skills:7de6edd", + "imageTag": "7de6edd", + "digest": "sha256:7d1b6871763613bf7c6e37f5b05b1b49e2c367b4b0b94abfbb9bceb7ab5de4ed", + "publishState": "published", "profile": "dev", "namespace": "hwlab-dev", "healthPath": "/health/live", @@ -270,7 +270,7 @@ "publishEnabled": true, "artifactRequired": true, "artifactScope": "required", - "notPublishedReason": "publish_not_run" + "notPublishedReason": null } ], "serviceInventory": { diff --git a/deploy/deploy.json b/deploy/deploy.json index c8ea8b94..016aebcb 100644 --- a/deploy/deploy.json +++ b/deploy/deploy.json @@ -1,7 +1,7 @@ { "manifestVersion": "v1", "environment": "dev", - "commitId": "c7de474", + "commitId": "7de6edd", "namespace": "hwlab-dev", "endpoint": "http://74.48.78.17:16667", "health": { @@ -181,7 +181,7 @@ "services": [ { "serviceId": "hwlab-cloud-api", - "image": "127.0.0.1:5000/hwlab/hwlab-cloud-api:c7de474", + "image": "127.0.0.1:5000/hwlab/hwlab-cloud-api:7de6edd", "namespace": "hwlab-dev", "healthPath": "/health/live", "profile": "dev", @@ -189,9 +189,9 @@ "env": { "HWLAB_ENVIRONMENT": "dev", "HWLAB_PUBLIC_ENDPOINT": "http://74.48.78.17:16667", - "HWLAB_COMMIT_ID": "c7de474", - "HWLAB_IMAGE": "127.0.0.1:5000/hwlab/hwlab-cloud-api:c7de474", - "HWLAB_IMAGE_TAG": "c7de474", + "HWLAB_COMMIT_ID": "7de6edd", + "HWLAB_IMAGE": "127.0.0.1:5000/hwlab/hwlab-cloud-api:7de6edd", + "HWLAB_IMAGE_TAG": "7de6edd", "HWLAB_RUNTIME_SUBSTITUTE_FORBIDDEN": "unidesk-backend,provider-gateway,microservice-proxy", "HWLAB_CLOUD_DB_URL": "secretRef:hwlab-cloud-api-dev-db/database-url", "HWLAB_CLOUD_DB_SSL_MODE": "require", @@ -206,7 +206,7 @@ }, { "serviceId": "hwlab-cloud-web", - "image": "127.0.0.1:5000/hwlab/hwlab-cloud-web:c7de474", + "image": "127.0.0.1:5000/hwlab/hwlab-cloud-web:7de6edd", "namespace": "hwlab-dev", "healthPath": "/health/live", "profile": "dev", @@ -218,7 +218,7 @@ }, { "serviceId": "hwlab-agent-mgr", - "image": "127.0.0.1:5000/hwlab/hwlab-agent-mgr:c7de474", + "image": "127.0.0.1:5000/hwlab/hwlab-agent-mgr:7de6edd", "namespace": "hwlab-dev", "healthPath": "/health/live", "profile": "dev", @@ -230,7 +230,7 @@ }, { "serviceId": "hwlab-agent-worker", - "image": "127.0.0.1:5000/hwlab/hwlab-agent-worker:c7de474", + "image": "127.0.0.1:5000/hwlab/hwlab-agent-worker:7de6edd", "namespace": "hwlab-dev", "healthPath": "/health/live", "profile": "dev", @@ -242,7 +242,7 @@ }, { "serviceId": "hwlab-gateway", - "image": "127.0.0.1:5000/hwlab/hwlab-gateway:c7de474", + "image": "127.0.0.1:5000/hwlab/hwlab-gateway:7de6edd", "namespace": "hwlab-dev", "healthPath": "/health/live", "profile": "dev", @@ -254,12 +254,15 @@ }, { "serviceId": "hwlab-gateway-simu", - "image": "127.0.0.1:5000/hwlab/hwlab-gateway-simu:c7de474", + "image": "127.0.0.1:5000/hwlab/hwlab-gateway-simu:7de6edd", "namespace": "hwlab-dev", "healthPath": "/health/live", "profile": "dev", "replicas": 2, - "instanceNames": ["hwlab-gateway-simu-1", "hwlab-gateway-simu-2"], + "instanceNames": [ + "hwlab-gateway-simu-1", + "hwlab-gateway-simu-2" + ], "identityStrategy": "indexed-workloads", "env": { "HWLAB_GATEWAY_MODE": "simulator", @@ -271,12 +274,15 @@ }, { "serviceId": "hwlab-box-simu", - "image": "127.0.0.1:5000/hwlab/hwlab-box-simu:c7de474", + "image": "127.0.0.1:5000/hwlab/hwlab-box-simu:7de6edd", "namespace": "hwlab-dev", "healthPath": "/health/live", "profile": "dev", "replicas": 2, - "instanceNames": ["hwlab-box-simu-1", "hwlab-box-simu-2"], + "instanceNames": [ + "hwlab-box-simu-1", + "hwlab-box-simu-2" + ], "identityStrategy": "indexed-workloads", "env": { "HWLAB_BOX_ID": "boxsimu_1,boxsimu_2", @@ -287,7 +293,7 @@ }, { "serviceId": "hwlab-patch-panel", - "image": "127.0.0.1:5000/hwlab/hwlab-patch-panel:c7de474", + "image": "127.0.0.1:5000/hwlab/hwlab-patch-panel:7de6edd", "namespace": "hwlab-dev", "healthPath": "/health/live", "profile": "dev", @@ -300,7 +306,7 @@ }, { "serviceId": "hwlab-router", - "image": "127.0.0.1:5000/hwlab/hwlab-router:c7de474", + "image": "127.0.0.1:5000/hwlab/hwlab-router:7de6edd", "namespace": "hwlab-dev", "healthPath": "/health/live", "profile": "dev", @@ -312,7 +318,7 @@ }, { "serviceId": "hwlab-tunnel-client", - "image": "127.0.0.1:5000/hwlab/hwlab-tunnel-client:c7de474", + "image": "127.0.0.1:5000/hwlab/hwlab-tunnel-client:7de6edd", "namespace": "hwlab-dev", "healthPath": "/health/live", "profile": "dev", @@ -326,7 +332,7 @@ }, { "serviceId": "hwlab-edge-proxy", - "image": "127.0.0.1:5000/hwlab/hwlab-edge-proxy:c7de474", + "image": "127.0.0.1:5000/hwlab/hwlab-edge-proxy:7de6edd", "namespace": "hwlab-dev", "healthPath": "/health/live", "profile": "dev", @@ -338,7 +344,7 @@ }, { "serviceId": "hwlab-cli", - "image": "127.0.0.1:5000/hwlab/hwlab-cli:c7de474", + "image": "127.0.0.1:5000/hwlab/hwlab-cli:7de6edd", "namespace": "hwlab-dev", "healthPath": "/health/live", "profile": "dev", @@ -349,13 +355,13 @@ }, { "serviceId": "hwlab-agent-skills", - "image": "127.0.0.1:5000/hwlab/hwlab-agent-skills:c7de474", + "image": "127.0.0.1:5000/hwlab/hwlab-agent-skills:7de6edd", "namespace": "hwlab-dev", "healthPath": "/health/live", "profile": "dev", "replicas": 1, "env": { - "HWLAB_SKILLS_COMMIT_ID": "c7de474" + "HWLAB_SKILLS_COMMIT_ID": "7de6edd" } } ], diff --git a/deploy/k8s/base/workloads.yaml b/deploy/k8s/base/workloads.yaml index 85e7a41a..94aa6561 100644 --- a/deploy/k8s/base/workloads.yaml +++ b/deploy/k8s/base/workloads.yaml @@ -31,7 +31,7 @@ "containers": [ { "name": "hwlab-cloud-api", - "image": "127.0.0.1:5000/hwlab/hwlab-cloud-api:c7de474", + "image": "127.0.0.1:5000/hwlab/hwlab-cloud-api:7de6edd", "ports": [ { "name": "http", @@ -49,15 +49,15 @@ }, { "name": "HWLAB_COMMIT_ID", - "value": "c7de474" + "value": "7de6edd" }, { "name": "HWLAB_IMAGE", - "value": "127.0.0.1:5000/hwlab/hwlab-cloud-api:c7de474" + "value": "127.0.0.1:5000/hwlab/hwlab-cloud-api:7de6edd" }, { "name": "HWLAB_IMAGE_TAG", - "value": "c7de474" + "value": "7de6edd" }, { "name": "HWLAB_RUNTIME_SUBSTITUTE_FORBIDDEN", @@ -159,7 +159,7 @@ "containers": [ { "name": "hwlab-cloud-web", - "image": "127.0.0.1:5000/hwlab/hwlab-cloud-web:c7de474", + "image": "127.0.0.1:5000/hwlab/hwlab-cloud-web:7de6edd", "ports": [ { "name": "http", @@ -219,7 +219,7 @@ "containers": [ { "name": "hwlab-agent-mgr", - "image": "127.0.0.1:5000/hwlab/hwlab-agent-mgr:c7de474", + "image": "127.0.0.1:5000/hwlab/hwlab-agent-mgr:7de6edd", "ports": [ { "name": "http", @@ -275,7 +275,7 @@ "containers": [ { "name": "hwlab-agent-worker", - "image": "127.0.0.1:5000/hwlab/hwlab-agent-worker:c7de474", + "image": "127.0.0.1:5000/hwlab/hwlab-agent-worker:7de6edd", "env": [ { "name": "HWLAB_AGENT_SESSION_MODE", @@ -318,7 +318,7 @@ "containers": [ { "name": "hwlab-gateway", - "image": "127.0.0.1:5000/hwlab/hwlab-gateway:c7de474", + "image": "127.0.0.1:5000/hwlab/hwlab-gateway:7de6edd", "ports": [ { "name": "http", @@ -379,7 +379,7 @@ "containers": [ { "name": "hwlab-gateway-simu", - "image": "127.0.0.1:5000/hwlab/hwlab-gateway-simu:c7de474", + "image": "127.0.0.1:5000/hwlab/hwlab-gateway-simu:7de6edd", "ports": [ { "name": "http", @@ -452,7 +452,7 @@ "containers": [ { "name": "hwlab-box-simu", - "image": "127.0.0.1:5000/hwlab/hwlab-box-simu:c7de474", + "image": "127.0.0.1:5000/hwlab/hwlab-box-simu:7de6edd", "ports": [ { "name": "http", @@ -520,7 +520,7 @@ "containers": [ { "name": "hwlab-patch-panel", - "image": "127.0.0.1:5000/hwlab/hwlab-patch-panel:c7de474", + "image": "127.0.0.1:5000/hwlab/hwlab-patch-panel:7de6edd", "ports": [ { "name": "http", @@ -580,7 +580,7 @@ "containers": [ { "name": "hwlab-router", - "image": "127.0.0.1:5000/hwlab/hwlab-router:c7de474", + "image": "127.0.0.1:5000/hwlab/hwlab-router:7de6edd", "ports": [ { "name": "http", @@ -640,7 +640,7 @@ "containers": [ { "name": "hwlab-tunnel-client", - "image": "127.0.0.1:5000/hwlab/hwlab-tunnel-client:c7de474", + "image": "127.0.0.1:5000/hwlab/hwlab-tunnel-client:7de6edd", "ports": [ { "name": "http", @@ -712,7 +712,7 @@ "containers": [ { "name": "hwlab-edge-proxy", - "image": "127.0.0.1:5000/hwlab/hwlab-edge-proxy:c7de474", + "image": "127.0.0.1:5000/hwlab/hwlab-edge-proxy:7de6edd", "ports": [ { "name": "http", @@ -773,7 +773,7 @@ "containers": [ { "name": "hwlab-cli", - "image": "127.0.0.1:5000/hwlab/hwlab-cli:c7de474", + "image": "127.0.0.1:5000/hwlab/hwlab-cli:7de6edd", "env": [ { "name": "HWLAB_CLI_ENDPOINT", @@ -815,7 +815,7 @@ "containers": [ { "name": "hwlab-agent-skills", - "image": "127.0.0.1:5000/hwlab/hwlab-agent-skills:c7de474", + "image": "127.0.0.1:5000/hwlab/hwlab-agent-skills:7de6edd", "ports": [ { "name": "http", @@ -825,7 +825,7 @@ "env": [ { "name": "HWLAB_SKILLS_COMMIT_ID", - "value": "c7de474" + "value": "7de6edd" } ], "readinessProbe": { diff --git a/package.json b/package.json index c91d9387..6842d168 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "type": "module", "scripts": { "validate": "node scripts/validate-contract.mjs && node scripts/deploy-contract-plan.mjs --check && node scripts/deploy-desired-state-plan.mjs --check && node scripts/dev-runtime-migration.mjs --check", - "check": "node --check internal/protocol/index.mjs && node --check internal/agent/index.mjs && node --check internal/agent/runtime.mjs && node --check internal/audit/index.mjs && node --check internal/db/runtime-store.mjs && node --check internal/db/runtime-store.test.mjs && node --check internal/mvp-gate/summary.mjs && node --check internal/cloud/db-contract.mjs && node --check internal/cloud/code-agent-contract.mjs && node --check internal/cloud/json-rpc.mjs && node --check internal/cloud/code-agent-chat.mjs && node --check internal/cloud/server.mjs && node --check internal/sim/model.mjs && node --check internal/sim/http.mjs && node --check internal/sim/l2-runtime.mjs && node --check internal/patchpanel/model.mjs && node --check internal/patchpanel/runtime.mjs && node --check cmd/hwlab-cloud-api/main.mjs && node --check cmd/hwlab-agent-mgr/main.mjs && node --check cmd/hwlab-agent-worker/main.mjs && node --check cmd/hwlab-box-simu/main.mjs && node --check cmd/hwlab-gateway-simu/main.mjs && node --check scripts/cloud-api-runtime-smoke.mjs && node --check scripts/code-agent-chat-smoke.mjs && node --check scripts/dev-edge-health-smoke.mjs && node --check scripts/src/dev-edge-health-smoke-lib.mjs && node --check scripts/validate-contract.mjs && node --check scripts/deploy-desired-state-plan.mjs && node --check scripts/src/deploy-desired-state-plan.mjs && node --check scripts/report-lifecycle.mjs && node --check scripts/report-lifecycle.test.mjs && node --check scripts/validate-dev-gate-report.mjs && node --check scripts/dev-m3-hardware-loop-smoke.test.mjs && node --check scripts/validate-dev-m3-cardinality.mjs && node --check scripts/validate-artifact-catalog.mjs && node --check scripts/refresh-artifact-catalog.mjs && node --check scripts/dev-artifact-publish.mjs && node --check scripts/src/dev-artifact-services.mjs && node --check scripts/src/registry-capabilities.mjs && node --check scripts/preflight-dev-base-image.mjs && node --check scripts/src/dev-base-image-preflight.mjs && node --check scripts/dev-evidence-blocker-aggregator.mjs && node --check scripts/src/dev-evidence-blocker-aggregator.mjs && node --check scripts/d601-k3s-readonly-observability.mjs && node --check scripts/src/d601-k3s-readonly-observability.mjs && node --check scripts/dev-runtime-migration.mjs && node --check scripts/src/dev-runtime-migration.mjs && node --check scripts/src/dev-runtime-migration.test.mjs && node --check scripts/l2-runtime-contract-smoke.mjs && node --check scripts/patch-panel-runtime-smoke.mjs && node --check scripts/export-web-gate-summary.mjs && node --check scripts/l6-cli-web-smoke.mjs && node --check tools/hwlab-cli/bin/hwlab-cli.mjs && node --check tools/hwlab-cli/lib/cli.mjs && node --check web/hwlab-cloud-web/app.mjs && node --check web/hwlab-cloud-web/gate-summary.mjs && node --check web/hwlab-cloud-web/scripts/check.mjs && node --check web/hwlab-cloud-web/scripts/build.mjs && node --check web/hwlab-cloud-web/scripts/dist-contract.mjs && node scripts/validate-contract.mjs && node scripts/validate-dev-gate-report.mjs && node scripts/report-lifecycle.test.mjs && node scripts/validate-dev-m3-cardinality.mjs && node scripts/validate-artifact-catalog.mjs && node scripts/deploy-desired-state-plan.mjs --check && node scripts/dev-runtime-migration.mjs --check && node scripts/dev-evidence-blocker-aggregator.mjs --check && node scripts/l2-runtime-contract-smoke.mjs && node scripts/l6-cli-web-smoke.mjs && node web/hwlab-cloud-web/scripts/check.mjs && node scripts/code-agent-chat-smoke.mjs && node --test scripts/dev-m3-hardware-loop-smoke.test.mjs scripts/deploy-desired-state-plan.test.mjs scripts/src/dev-deploy-apply.test.mjs scripts/src/dev-runtime-migration.test.mjs internal/agent/index.test.mjs internal/audit/index.test.mjs internal/db/schema.test.mjs internal/db/runtime-store.test.mjs internal/cloud/json-rpc.test.mjs internal/cloud/server.test.mjs internal/patchpanel/model.test.mjs internal/patchpanel/runtime.test.mjs && node scripts/cloud-api-runtime-smoke.mjs && sh -n scripts/bootstrap-skills.sh scripts/worker-entrypoint.sh", + "check": "node --check internal/protocol/index.mjs && node --check internal/agent/index.mjs && node --check internal/agent/runtime.mjs && node --check internal/audit/index.mjs && node --check internal/db/runtime-store.mjs && node --check internal/db/runtime-store.test.mjs && node --check internal/mvp-gate/summary.mjs && node --check internal/cloud/db-contract.mjs && node --check internal/cloud/code-agent-contract.mjs && node --check internal/cloud/json-rpc.mjs && node --check internal/cloud/code-agent-chat.mjs && node --check internal/cloud/server.mjs && node --check internal/sim/model.mjs && node --check internal/sim/http.mjs && node --check internal/sim/l2-runtime.mjs && node --check internal/patchpanel/model.mjs && node --check internal/patchpanel/runtime.mjs && node --check cmd/hwlab-cloud-api/main.mjs && node --check cmd/hwlab-agent-mgr/main.mjs && node --check cmd/hwlab-agent-worker/main.mjs && node --check cmd/hwlab-box-simu/main.mjs && node --check cmd/hwlab-gateway-simu/main.mjs && node --check scripts/cloud-api-runtime-smoke.mjs && node --check scripts/code-agent-chat-smoke.mjs && node --check scripts/dev-edge-health-smoke.mjs && node --check scripts/src/dev-edge-health-smoke-lib.mjs && node --check scripts/validate-contract.mjs && node --check scripts/deploy-desired-state-plan.mjs && node --check scripts/src/deploy-desired-state-plan.mjs && node --check scripts/report-lifecycle.mjs && node --check scripts/report-lifecycle.test.mjs && node --check scripts/validate-dev-gate-report.mjs && node --check scripts/dev-m3-hardware-loop-smoke.test.mjs && node --check scripts/dev-cloud-workbench-smoke.test.mjs && node --check scripts/validate-dev-m3-cardinality.mjs && node --check scripts/validate-artifact-catalog.mjs && node --check scripts/refresh-artifact-catalog.mjs && node --check scripts/dev-artifact-publish.mjs && node --check scripts/src/dev-artifact-services.mjs && node --check scripts/src/registry-capabilities.mjs && node --check scripts/preflight-dev-base-image.mjs && node --check scripts/src/dev-base-image-preflight.mjs && node --check scripts/dev-evidence-blocker-aggregator.mjs && node --check scripts/src/dev-evidence-blocker-aggregator.mjs && node --check scripts/d601-k3s-readonly-observability.mjs && node --check scripts/src/d601-k3s-readonly-observability.mjs && node --check scripts/dev-runtime-migration.mjs && node --check scripts/src/dev-runtime-migration.mjs && node --check scripts/src/dev-runtime-migration.test.mjs && node --check scripts/l2-runtime-contract-smoke.mjs && node --check scripts/patch-panel-runtime-smoke.mjs && node --check scripts/export-web-gate-summary.mjs && node --check scripts/l6-cli-web-smoke.mjs && node --check tools/hwlab-cli/bin/hwlab-cli.mjs && node --check tools/hwlab-cli/lib/cli.mjs && node --check web/hwlab-cloud-web/app.mjs && node --check web/hwlab-cloud-web/gate-summary.mjs && node --check web/hwlab-cloud-web/scripts/check.mjs && node --check web/hwlab-cloud-web/scripts/build.mjs && node --check web/hwlab-cloud-web/scripts/dist-contract.mjs && node scripts/validate-contract.mjs && node scripts/validate-dev-gate-report.mjs && node scripts/report-lifecycle.test.mjs && node scripts/validate-dev-m3-cardinality.mjs && node scripts/validate-artifact-catalog.mjs && node scripts/deploy-desired-state-plan.mjs --check && node scripts/dev-runtime-migration.mjs --check && node scripts/dev-evidence-blocker-aggregator.mjs --check && node scripts/l2-runtime-contract-smoke.mjs && node scripts/l6-cli-web-smoke.mjs && node web/hwlab-cloud-web/scripts/check.mjs && node scripts/code-agent-chat-smoke.mjs && node --test scripts/dev-m3-hardware-loop-smoke.test.mjs scripts/dev-cloud-workbench-smoke.test.mjs scripts/deploy-desired-state-plan.test.mjs scripts/src/dev-deploy-apply.test.mjs scripts/src/dev-runtime-migration.test.mjs internal/agent/index.test.mjs internal/audit/index.test.mjs internal/db/schema.test.mjs internal/db/runtime-store.test.mjs internal/cloud/json-rpc.test.mjs internal/cloud/server.test.mjs internal/patchpanel/model.test.mjs internal/patchpanel/runtime.test.mjs && node scripts/cloud-api-runtime-smoke.mjs && sh -n scripts/bootstrap-skills.sh scripts/worker-entrypoint.sh", "dev-base-image:preflight": "node scripts/preflight-dev-base-image.mjs", "cloud-api:smoke": "node scripts/cloud-api-runtime-smoke.mjs", "m1:smoke": "node scripts/m1-contract-smoke.mjs", @@ -29,11 +29,11 @@ "artifact-catalog:refresh-blocked": "node scripts/refresh-artifact-catalog.mjs --target-ref origin/main --blocked", "dev-artifact:preflight": "node scripts/dev-artifact-publish.mjs --preflight", "dev-artifact:publish": "node scripts/dev-artifact-publish.mjs --publish", - "dev-runtime:migration": "node scripts/dev-runtime-migration.mjs", "docs:validate:m3-rollout": "node scripts/validate-m3-rollout-runbook.mjs", "d601:k3s:readonly": "node scripts/d601-k3s-readonly-observability.mjs", "runner:issue-visibility:preflight": "node scripts/runner-issue-visibility-preflight.mjs", - "report:lifecycle:test": "node scripts/report-lifecycle.test.mjs" + "report:lifecycle:test": "node scripts/report-lifecycle.test.mjs", + "dev-runtime:migration": "node scripts/dev-runtime-migration.mjs" }, "dependencies": { "pg": "^8.21.0" diff --git a/reports/dev-gate/dev-artifacts.json b/reports/dev-gate/dev-artifacts.json index 9f8d81c2..f31a302b 100644 --- a/reports/dev-gate/dev-artifacts.json +++ b/reports/dev-gate/dev-artifacts.json @@ -4,10 +4,18 @@ "reportVersion": "v1", "issue": "pikasTech/HWLAB#35", "taskId": "dev-artifact-publish", - "commitId": "7e29522", + "commitId": "7de6edd", "acceptanceLevel": "dev_artifact_publish", "devOnly": true, "prodDisabled": true, + "reportLifecycle": { + "version": "v1", + "state": "active", + "activeEndpoint": "http://74.48.78.17:16667", + "activeBrowserEndpoint": "http://74.48.78.17:16666", + "deprecatedEndpoint": null, + "summary": "Current DEV artifact publish report; it is not a DEV-LIVE runtime substitute." + }, "sourceContract": { "status": "pass", "documents": [ @@ -67,12 +75,12 @@ "status": "published", "mode": "publish", "repo": "pikasTech/HWLAB", - "sourceCommitId": "7e29522b65c8a316b7c3fde12bebcb5f6baf8c33", + "sourceCommitId": "7de6edd2c41fb50dcb007353780338be8832b27e", "registryPrefix": "127.0.0.1:5000/hwlab", "registryCapabilities": { "version": "v1", "registryPrefix": "127.0.0.1:5000/hwlab", - "generatedAt": "2026-05-22T05:06:03.750Z", + "generatedAt": "2026-05-22T23:50:08.588Z", "classification": "degraded", "dimensions": { "processHttpAccess": { @@ -120,7 +128,7 @@ "names": "unidesk-artifact-registry", "ports": "127.0.0.1:5000->5000/tcp", "state": "running", - "status": "Up 28 hours" + "status": "Up 46 hours" } ], "pushAttempted": false, @@ -180,16 +188,16 @@ "imageID": "127.0.0.1:5000/hwlab/hwlab-box-simu@sha256:ae5fcc06a276918561c2d752f2221156bd2bd4db7fb93967ae76af97bfdc51bf" }, { - "pod": "hwlab-cloud-api-f48dcdfb-9n6zh", + "pod": "hwlab-cloud-api-6f456ff979-z4mnw", "container": "hwlab-cloud-api", - "image": "127.0.0.1:5000/hwlab/hwlab-cloud-api:cb35ada", - "imageID": "127.0.0.1:5000/hwlab/hwlab-cloud-api@sha256:3946efdd7360409cea6a897cb03a6f3172f14819d3132df3aab821621909513f" + "image": "127.0.0.1:5000/hwlab/hwlab-cloud-api:c7de474", + "imageID": "127.0.0.1:5000/hwlab/hwlab-cloud-api@sha256:86fe92c0dd5d5883851b38805e6f5acc49d2876552599c18f85480cf0d3bf981" }, { - "pod": "hwlab-cloud-web-cd9655b98-gxtmc", + "pod": "hwlab-cloud-web-7d48b84f4b-n6hw6", "container": "hwlab-cloud-web", - "image": "127.0.0.1:5000/hwlab/hwlab-cloud-web:cb35ada", - "imageID": "127.0.0.1:5000/hwlab/hwlab-cloud-web@sha256:868e2a11c56cc1a27c77de3fa71a7b092f21ae98a4052104e9071bd93960abfa" + "image": "127.0.0.1:5000/hwlab/hwlab-cloud-web:c7de474", + "imageID": "127.0.0.1:5000/hwlab/hwlab-cloud-web@sha256:91cfacc1fc011a96bb47ff00d2ab09ee3c93003b7ec482308ec5ac35004e5627" }, { "pod": "hwlab-edge-proxy-567f978cdd-8fwc8", @@ -504,13 +512,13 @@ "publishPlan": { "version": "v2", "mode": "publish", - "sourceCommitId": "7e29522b65c8a316b7c3fde12bebcb5f6baf8c33", - "imageTag": "7e29522", + "sourceCommitId": "7de6edd2c41fb50dcb007353780338be8832b27e", + "imageTag": "7de6edd", "registryTarget": "127.0.0.1:5000/hwlab", "registryCapabilities": { "version": "v1", "registryPrefix": "127.0.0.1:5000/hwlab", - "generatedAt": "2026-05-22T05:06:03.750Z", + "generatedAt": "2026-05-22T23:50:08.588Z", "classification": "degraded", "dimensions": { "processHttpAccess": { @@ -558,7 +566,7 @@ "names": "unidesk-artifact-registry", "ports": "127.0.0.1:5000->5000/tcp", "state": "running", - "status": "Up 28 hours" + "status": "Up 46 hours" } ], "pushAttempted": false, @@ -618,16 +626,16 @@ "imageID": "127.0.0.1:5000/hwlab/hwlab-box-simu@sha256:ae5fcc06a276918561c2d752f2221156bd2bd4db7fb93967ae76af97bfdc51bf" }, { - "pod": "hwlab-cloud-api-f48dcdfb-9n6zh", + "pod": "hwlab-cloud-api-6f456ff979-z4mnw", "container": "hwlab-cloud-api", - "image": "127.0.0.1:5000/hwlab/hwlab-cloud-api:cb35ada", - "imageID": "127.0.0.1:5000/hwlab/hwlab-cloud-api@sha256:3946efdd7360409cea6a897cb03a6f3172f14819d3132df3aab821621909513f" + "image": "127.0.0.1:5000/hwlab/hwlab-cloud-api:c7de474", + "imageID": "127.0.0.1:5000/hwlab/hwlab-cloud-api@sha256:86fe92c0dd5d5883851b38805e6f5acc49d2876552599c18f85480cf0d3bf981" }, { - "pod": "hwlab-cloud-web-cd9655b98-gxtmc", + "pod": "hwlab-cloud-web-7d48b84f4b-n6hw6", "container": "hwlab-cloud-web", - "image": "127.0.0.1:5000/hwlab/hwlab-cloud-web:cb35ada", - "imageID": "127.0.0.1:5000/hwlab/hwlab-cloud-web@sha256:868e2a11c56cc1a27c77de3fa71a7b092f21ae98a4052104e9071bd93960abfa" + "image": "127.0.0.1:5000/hwlab/hwlab-cloud-web:c7de474", + "imageID": "127.0.0.1:5000/hwlab/hwlab-cloud-web@sha256:91cfacc1fc011a96bb47ff00d2ab09ee3c93003b7ec482308ec5ac35004e5627" }, { "pod": "hwlab-edge-proxy-567f978cdd-8fwc8", @@ -691,12 +699,12 @@ "enabled": true, "required": true, "artifactScope": "required", - "sourceCommitId": "7e29522b65c8a316b7c3fde12bebcb5f6baf8c33", + "sourceCommitId": "7de6edd2c41fb50dcb007353780338be8832b27e", "registryTarget": "127.0.0.1:5000/hwlab/hwlab-cloud-api", - "image": "127.0.0.1:5000/hwlab/hwlab-cloud-api:7e29522", - "imageTag": "7e29522", - "digest": "sha256:090b6f919be3b3c7594ca4073b193351689fc525f478b64d54d393725cb01ecf", - "repositoryDigest": "127.0.0.1:5000/hwlab/hwlab-cloud-api@sha256:090b6f919be3b3c7594ca4073b193351689fc525f478b64d54d393725cb01ecf", + "image": "127.0.0.1:5000/hwlab/hwlab-cloud-api:7de6edd", + "imageTag": "7de6edd", + "digest": "sha256:8a0120e976f8369c19887d33d004b763c7dbff3f43fd023fb186abffe199b3ea", + "repositoryDigest": "127.0.0.1:5000/hwlab/hwlab-cloud-api@sha256:8a0120e976f8369c19887d33d004b763c7dbff3f43fd023fb186abffe199b3ea", "digestStatus": "real", "status": "published", "notPublishedReason": null @@ -706,12 +714,12 @@ "enabled": true, "required": true, "artifactScope": "required", - "sourceCommitId": "7e29522b65c8a316b7c3fde12bebcb5f6baf8c33", + "sourceCommitId": "7de6edd2c41fb50dcb007353780338be8832b27e", "registryTarget": "127.0.0.1:5000/hwlab/hwlab-cloud-web", - "image": "127.0.0.1:5000/hwlab/hwlab-cloud-web:7e29522", - "imageTag": "7e29522", - "digest": "sha256:bc6e836b2bb796b30562ed3ae93d8534146a3b74a310906481c8641646d1b8f0", - "repositoryDigest": "127.0.0.1:5000/hwlab/hwlab-cloud-web@sha256:bc6e836b2bb796b30562ed3ae93d8534146a3b74a310906481c8641646d1b8f0", + "image": "127.0.0.1:5000/hwlab/hwlab-cloud-web:7de6edd", + "imageTag": "7de6edd", + "digest": "sha256:a302a8b4f3c6e7a91e5046398b803e9915dba873cefac97bb7069886b5a48d61", + "repositoryDigest": "127.0.0.1:5000/hwlab/hwlab-cloud-web@sha256:a302a8b4f3c6e7a91e5046398b803e9915dba873cefac97bb7069886b5a48d61", "digestStatus": "real", "status": "published", "notPublishedReason": null @@ -721,12 +729,12 @@ "enabled": true, "required": true, "artifactScope": "required", - "sourceCommitId": "7e29522b65c8a316b7c3fde12bebcb5f6baf8c33", + "sourceCommitId": "7de6edd2c41fb50dcb007353780338be8832b27e", "registryTarget": "127.0.0.1:5000/hwlab/hwlab-agent-mgr", - "image": "127.0.0.1:5000/hwlab/hwlab-agent-mgr:7e29522", - "imageTag": "7e29522", - "digest": "sha256:4d9b8944bc52012bb0a4a3a345f8939381d2804032e544d0c98f49fac0e46bfd", - "repositoryDigest": "127.0.0.1:5000/hwlab/hwlab-agent-mgr@sha256:4d9b8944bc52012bb0a4a3a345f8939381d2804032e544d0c98f49fac0e46bfd", + "image": "127.0.0.1:5000/hwlab/hwlab-agent-mgr:7de6edd", + "imageTag": "7de6edd", + "digest": "sha256:a16bba1762f58e4daed7ef94243e3f4f0522332704c9f40bcadab77f11acc7a2", + "repositoryDigest": "127.0.0.1:5000/hwlab/hwlab-agent-mgr@sha256:a16bba1762f58e4daed7ef94243e3f4f0522332704c9f40bcadab77f11acc7a2", "digestStatus": "real", "status": "published", "notPublishedReason": null @@ -736,12 +744,12 @@ "enabled": true, "required": true, "artifactScope": "required", - "sourceCommitId": "7e29522b65c8a316b7c3fde12bebcb5f6baf8c33", + "sourceCommitId": "7de6edd2c41fb50dcb007353780338be8832b27e", "registryTarget": "127.0.0.1:5000/hwlab/hwlab-agent-worker", - "image": "127.0.0.1:5000/hwlab/hwlab-agent-worker:7e29522", - "imageTag": "7e29522", - "digest": "sha256:2582176e2c35a2bfbce4edfd922749397d45f6f1f8c04d0394c501f7d87323ee", - "repositoryDigest": "127.0.0.1:5000/hwlab/hwlab-agent-worker@sha256:2582176e2c35a2bfbce4edfd922749397d45f6f1f8c04d0394c501f7d87323ee", + "image": "127.0.0.1:5000/hwlab/hwlab-agent-worker:7de6edd", + "imageTag": "7de6edd", + "digest": "sha256:3c75cb9d6c2b7392749d972c78f9b7a89e4f099a64f902d2eb9810f16d551ceb", + "repositoryDigest": "127.0.0.1:5000/hwlab/hwlab-agent-worker@sha256:3c75cb9d6c2b7392749d972c78f9b7a89e4f099a64f902d2eb9810f16d551ceb", "digestStatus": "real", "status": "published", "notPublishedReason": null @@ -751,12 +759,12 @@ "enabled": true, "required": true, "artifactScope": "required", - "sourceCommitId": "7e29522b65c8a316b7c3fde12bebcb5f6baf8c33", + "sourceCommitId": "7de6edd2c41fb50dcb007353780338be8832b27e", "registryTarget": "127.0.0.1:5000/hwlab/hwlab-gateway", - "image": "127.0.0.1:5000/hwlab/hwlab-gateway:7e29522", - "imageTag": "7e29522", - "digest": "sha256:290341fd6e6f036aa953943072ad58e0d0cfcc0e13950e6090f941b3310755a6", - "repositoryDigest": "127.0.0.1:5000/hwlab/hwlab-gateway@sha256:290341fd6e6f036aa953943072ad58e0d0cfcc0e13950e6090f941b3310755a6", + "image": "127.0.0.1:5000/hwlab/hwlab-gateway:7de6edd", + "imageTag": "7de6edd", + "digest": "sha256:b5374c763d54628c2e81b86c93c826a27629a8546ae789223e0553ca7c8ab80a", + "repositoryDigest": "127.0.0.1:5000/hwlab/hwlab-gateway@sha256:b5374c763d54628c2e81b86c93c826a27629a8546ae789223e0553ca7c8ab80a", "digestStatus": "real", "status": "published", "notPublishedReason": null @@ -766,12 +774,12 @@ "enabled": true, "required": true, "artifactScope": "required", - "sourceCommitId": "7e29522b65c8a316b7c3fde12bebcb5f6baf8c33", + "sourceCommitId": "7de6edd2c41fb50dcb007353780338be8832b27e", "registryTarget": "127.0.0.1:5000/hwlab/hwlab-gateway-simu", - "image": "127.0.0.1:5000/hwlab/hwlab-gateway-simu:7e29522", - "imageTag": "7e29522", - "digest": "sha256:02627f880c7ddf2102e2255a43097d1f604cfb7a22215736e328287e27fc5f03", - "repositoryDigest": "127.0.0.1:5000/hwlab/hwlab-gateway-simu@sha256:02627f880c7ddf2102e2255a43097d1f604cfb7a22215736e328287e27fc5f03", + "image": "127.0.0.1:5000/hwlab/hwlab-gateway-simu:7de6edd", + "imageTag": "7de6edd", + "digest": "sha256:be6aff770319c6c2b45f7b5974d4fb7aae4aab79a6670b1d8b87e194387e30dd", + "repositoryDigest": "127.0.0.1:5000/hwlab/hwlab-gateway-simu@sha256:be6aff770319c6c2b45f7b5974d4fb7aae4aab79a6670b1d8b87e194387e30dd", "digestStatus": "real", "status": "published", "notPublishedReason": null @@ -781,12 +789,12 @@ "enabled": true, "required": true, "artifactScope": "required", - "sourceCommitId": "7e29522b65c8a316b7c3fde12bebcb5f6baf8c33", + "sourceCommitId": "7de6edd2c41fb50dcb007353780338be8832b27e", "registryTarget": "127.0.0.1:5000/hwlab/hwlab-box-simu", - "image": "127.0.0.1:5000/hwlab/hwlab-box-simu:7e29522", - "imageTag": "7e29522", - "digest": "sha256:e02c270c71f83a2f4624722bb84616bea8374ef43439f27bdce310e4baa8dcca", - "repositoryDigest": "127.0.0.1:5000/hwlab/hwlab-box-simu@sha256:e02c270c71f83a2f4624722bb84616bea8374ef43439f27bdce310e4baa8dcca", + "image": "127.0.0.1:5000/hwlab/hwlab-box-simu:7de6edd", + "imageTag": "7de6edd", + "digest": "sha256:54e77f4c2ea9e87fd16becde9a5fe05b935f2472dba443458257dfdda1e87c27", + "repositoryDigest": "127.0.0.1:5000/hwlab/hwlab-box-simu@sha256:54e77f4c2ea9e87fd16becde9a5fe05b935f2472dba443458257dfdda1e87c27", "digestStatus": "real", "status": "published", "notPublishedReason": null @@ -796,12 +804,12 @@ "enabled": true, "required": true, "artifactScope": "required", - "sourceCommitId": "7e29522b65c8a316b7c3fde12bebcb5f6baf8c33", + "sourceCommitId": "7de6edd2c41fb50dcb007353780338be8832b27e", "registryTarget": "127.0.0.1:5000/hwlab/hwlab-patch-panel", - "image": "127.0.0.1:5000/hwlab/hwlab-patch-panel:7e29522", - "imageTag": "7e29522", - "digest": "sha256:f730323f83c62797f382932582a05df921045c86383dad7951f3d639b32456a9", - "repositoryDigest": "127.0.0.1:5000/hwlab/hwlab-patch-panel@sha256:f730323f83c62797f382932582a05df921045c86383dad7951f3d639b32456a9", + "image": "127.0.0.1:5000/hwlab/hwlab-patch-panel:7de6edd", + "imageTag": "7de6edd", + "digest": "sha256:02bec561d5f08f850e2884a2098da72978ad319d5bd799704ad9650778f11ae2", + "repositoryDigest": "127.0.0.1:5000/hwlab/hwlab-patch-panel@sha256:02bec561d5f08f850e2884a2098da72978ad319d5bd799704ad9650778f11ae2", "digestStatus": "real", "status": "published", "notPublishedReason": null @@ -811,12 +819,12 @@ "enabled": true, "required": true, "artifactScope": "required", - "sourceCommitId": "7e29522b65c8a316b7c3fde12bebcb5f6baf8c33", + "sourceCommitId": "7de6edd2c41fb50dcb007353780338be8832b27e", "registryTarget": "127.0.0.1:5000/hwlab/hwlab-router", - "image": "127.0.0.1:5000/hwlab/hwlab-router:7e29522", - "imageTag": "7e29522", - "digest": "sha256:7c40c573078a9c325402bd47b2eb78a46c0c80564a228cf7c9fcf0cfba5bc6d2", - "repositoryDigest": "127.0.0.1:5000/hwlab/hwlab-router@sha256:7c40c573078a9c325402bd47b2eb78a46c0c80564a228cf7c9fcf0cfba5bc6d2", + "image": "127.0.0.1:5000/hwlab/hwlab-router:7de6edd", + "imageTag": "7de6edd", + "digest": "sha256:bb3b10c486bb1305f4cae7ec2751947eeb0cc60c9993d54e93802b5df3b13fb6", + "repositoryDigest": "127.0.0.1:5000/hwlab/hwlab-router@sha256:bb3b10c486bb1305f4cae7ec2751947eeb0cc60c9993d54e93802b5df3b13fb6", "digestStatus": "real", "status": "published", "notPublishedReason": null @@ -826,12 +834,12 @@ "enabled": true, "required": true, "artifactScope": "required", - "sourceCommitId": "7e29522b65c8a316b7c3fde12bebcb5f6baf8c33", + "sourceCommitId": "7de6edd2c41fb50dcb007353780338be8832b27e", "registryTarget": "127.0.0.1:5000/hwlab/hwlab-tunnel-client", - "image": "127.0.0.1:5000/hwlab/hwlab-tunnel-client:7e29522", - "imageTag": "7e29522", - "digest": "sha256:b141dc88557b8f6391aa082fdb37c6f0db5bc3a76c97bb39ba00354a9208abd8", - "repositoryDigest": "127.0.0.1:5000/hwlab/hwlab-tunnel-client@sha256:b141dc88557b8f6391aa082fdb37c6f0db5bc3a76c97bb39ba00354a9208abd8", + "image": "127.0.0.1:5000/hwlab/hwlab-tunnel-client:7de6edd", + "imageTag": "7de6edd", + "digest": "sha256:ce9fa92362dd10911dcd66c59412690ccde401ce4ef82b548cfcea37499a725f", + "repositoryDigest": "127.0.0.1:5000/hwlab/hwlab-tunnel-client@sha256:ce9fa92362dd10911dcd66c59412690ccde401ce4ef82b548cfcea37499a725f", "digestStatus": "real", "status": "published", "notPublishedReason": null @@ -841,12 +849,12 @@ "enabled": true, "required": true, "artifactScope": "required", - "sourceCommitId": "7e29522b65c8a316b7c3fde12bebcb5f6baf8c33", + "sourceCommitId": "7de6edd2c41fb50dcb007353780338be8832b27e", "registryTarget": "127.0.0.1:5000/hwlab/hwlab-edge-proxy", - "image": "127.0.0.1:5000/hwlab/hwlab-edge-proxy:7e29522", - "imageTag": "7e29522", - "digest": "sha256:792505139793c3f3da7769f9dc9d1cb3203ff92797305b62e8f12207c69a9831", - "repositoryDigest": "127.0.0.1:5000/hwlab/hwlab-edge-proxy@sha256:792505139793c3f3da7769f9dc9d1cb3203ff92797305b62e8f12207c69a9831", + "image": "127.0.0.1:5000/hwlab/hwlab-edge-proxy:7de6edd", + "imageTag": "7de6edd", + "digest": "sha256:bf19f6812cbac3c54c938ad12a2b1ef9626a746017f85658b9960a1761afa32c", + "repositoryDigest": "127.0.0.1:5000/hwlab/hwlab-edge-proxy@sha256:bf19f6812cbac3c54c938ad12a2b1ef9626a746017f85658b9960a1761afa32c", "digestStatus": "real", "status": "published", "notPublishedReason": null @@ -856,12 +864,12 @@ "enabled": true, "required": true, "artifactScope": "required", - "sourceCommitId": "7e29522b65c8a316b7c3fde12bebcb5f6baf8c33", + "sourceCommitId": "7de6edd2c41fb50dcb007353780338be8832b27e", "registryTarget": "127.0.0.1:5000/hwlab/hwlab-cli", - "image": "127.0.0.1:5000/hwlab/hwlab-cli:7e29522", - "imageTag": "7e29522", - "digest": "sha256:43d1c175091bfb462eddd7dd22a780d6485d86bdb052c36b78201606f48fadc1", - "repositoryDigest": "127.0.0.1:5000/hwlab/hwlab-cli@sha256:43d1c175091bfb462eddd7dd22a780d6485d86bdb052c36b78201606f48fadc1", + "image": "127.0.0.1:5000/hwlab/hwlab-cli:7de6edd", + "imageTag": "7de6edd", + "digest": "sha256:b169c015c4c06dcdf6d43131d69dd72be1996cb5970e25014c4cec0271b459c0", + "repositoryDigest": "127.0.0.1:5000/hwlab/hwlab-cli@sha256:b169c015c4c06dcdf6d43131d69dd72be1996cb5970e25014c4cec0271b459c0", "digestStatus": "real", "status": "published", "notPublishedReason": null @@ -871,12 +879,12 @@ "enabled": true, "required": true, "artifactScope": "required", - "sourceCommitId": "7e29522b65c8a316b7c3fde12bebcb5f6baf8c33", + "sourceCommitId": "7de6edd2c41fb50dcb007353780338be8832b27e", "registryTarget": "127.0.0.1:5000/hwlab/hwlab-agent-skills", - "image": "127.0.0.1:5000/hwlab/hwlab-agent-skills:7e29522", - "imageTag": "7e29522", - "digest": "sha256:c75510038dc33196dc254472e78101d545c0d777a7b33b531f02b39faf2588b5", - "repositoryDigest": "127.0.0.1:5000/hwlab/hwlab-agent-skills@sha256:c75510038dc33196dc254472e78101d545c0d777a7b33b531f02b39faf2588b5", + "image": "127.0.0.1:5000/hwlab/hwlab-agent-skills:7de6edd", + "imageTag": "7de6edd", + "digest": "sha256:7d1b6871763613bf7c6e37f5b05b1b49e2c367b4b0b94abfbb9bceb7ab5de4ed", + "repositoryDigest": "127.0.0.1:5000/hwlab/hwlab-agent-skills@sha256:7d1b6871763613bf7c6e37f5b05b1b49e2c367b4b0b94abfbb9bceb7ab5de4ed", "digestStatus": "real", "status": "published", "notPublishedReason": null @@ -888,15 +896,15 @@ "disabledServiceCount": 0, "builtCount": 13, "publishedCount": 13, - "generatedAt": "2026-05-22T05:06:57.139Z", + "generatedAt": "2026-05-22T23:51:56.882Z", "services": [ { "serviceId": "hwlab-cloud-api", "status": "published", - "image": "127.0.0.1:5000/hwlab/hwlab-cloud-api:7e29522", - "imageTag": "7e29522", - "digest": "sha256:090b6f919be3b3c7594ca4073b193351689fc525f478b64d54d393725cb01ecf", - "repositoryDigest": "127.0.0.1:5000/hwlab/hwlab-cloud-api@sha256:090b6f919be3b3c7594ca4073b193351689fc525f478b64d54d393725cb01ecf", + "image": "127.0.0.1:5000/hwlab/hwlab-cloud-api:7de6edd", + "imageTag": "7de6edd", + "digest": "sha256:8a0120e976f8369c19887d33d004b763c7dbff3f43fd023fb186abffe199b3ea", + "repositoryDigest": "127.0.0.1:5000/hwlab/hwlab-cloud-api@sha256:8a0120e976f8369c19887d33d004b763c7dbff3f43fd023fb186abffe199b3ea", "runtimeKind": "node-command", "implementationState": "repo-entrypoint", "sourceState": "source-present", @@ -905,16 +913,102 @@ "artifactRequired": true, "artifactScope": "required", "notPublishedReason": null, - "localImageId": "sha256:517ecf1866b4876c0f99e9c938c4954662c72e65f7885b027f8248979c8c29b3" + "localImageId": "sha256:ae5cc610383beaba95f35c1a2d49bb8c008c427a7acb337d369d98ce3ba4494f" }, { "serviceId": "hwlab-cloud-web", "status": "published", - "image": "127.0.0.1:5000/hwlab/hwlab-cloud-web:7e29522", - "imageTag": "7e29522", - "digest": "sha256:bc6e836b2bb796b30562ed3ae93d8534146a3b74a310906481c8641646d1b8f0", - "repositoryDigest": "127.0.0.1:5000/hwlab/hwlab-cloud-web@sha256:bc6e836b2bb796b30562ed3ae93d8534146a3b74a310906481c8641646d1b8f0", + "image": "127.0.0.1:5000/hwlab/hwlab-cloud-web:7de6edd", + "imageTag": "7de6edd", + "digest": "sha256:a302a8b4f3c6e7a91e5046398b803e9915dba873cefac97bb7069886b5a48d61", + "repositoryDigest": "127.0.0.1:5000/hwlab/hwlab-cloud-web@sha256:a302a8b4f3c6e7a91e5046398b803e9915dba873cefac97bb7069886b5a48d61", "runtimeKind": "cloud-web", + "distFreshness": { + "status": "pass", + "distPath": "web/hwlab-cloud-web/dist", + "buildCommand": "node web/hwlab-cloud-web/scripts/build.mjs", + "freshnessCommand": "node web/hwlab-cloud-web/scripts/build.mjs", + "files": [ + { + "kind": "runtime", + "path": "index.html", + "sourcePath": "/workspace/hwlab/.worktrees/dc-dcsn-p0-2026-003-publish-7de6edd/web/hwlab-cloud-web/index.html", + "distPath": "/workspace/hwlab/.worktrees/dc-dcsn-p0-2026-003-publish-7de6edd/web/hwlab-cloud-web/dist/index.html", + "status": "match" + }, + { + "kind": "runtime", + "path": "styles.css", + "sourcePath": "/workspace/hwlab/.worktrees/dc-dcsn-p0-2026-003-publish-7de6edd/web/hwlab-cloud-web/styles.css", + "distPath": "/workspace/hwlab/.worktrees/dc-dcsn-p0-2026-003-publish-7de6edd/web/hwlab-cloud-web/dist/styles.css", + "status": "match" + }, + { + "kind": "runtime", + "path": "app.mjs", + "sourcePath": "/workspace/hwlab/.worktrees/dc-dcsn-p0-2026-003-publish-7de6edd/web/hwlab-cloud-web/app.mjs", + "distPath": "/workspace/hwlab/.worktrees/dc-dcsn-p0-2026-003-publish-7de6edd/web/hwlab-cloud-web/dist/app.mjs", + "status": "match" + }, + { + "kind": "runtime", + "path": "gate-summary.mjs", + "sourcePath": "/workspace/hwlab/.worktrees/dc-dcsn-p0-2026-003-publish-7de6edd/web/hwlab-cloud-web/gate-summary.mjs", + "distPath": "/workspace/hwlab/.worktrees/dc-dcsn-p0-2026-003-publish-7de6edd/web/hwlab-cloud-web/dist/gate-summary.mjs", + "status": "match" + }, + { + "kind": "runtime", + "path": "runtime.mjs", + "sourcePath": "/workspace/hwlab/.worktrees/dc-dcsn-p0-2026-003-publish-7de6edd/web/hwlab-cloud-web/runtime.mjs", + "distPath": "/workspace/hwlab/.worktrees/dc-dcsn-p0-2026-003-publish-7de6edd/web/hwlab-cloud-web/dist/runtime.mjs", + "status": "match" + }, + { + "kind": "runtime", + "path": "workbench-hardware-panel.mjs", + "sourcePath": "/workspace/hwlab/.worktrees/dc-dcsn-p0-2026-003-publish-7de6edd/web/hwlab-cloud-web/workbench-hardware-panel.mjs", + "distPath": "/workspace/hwlab/.worktrees/dc-dcsn-p0-2026-003-publish-7de6edd/web/hwlab-cloud-web/dist/workbench-hardware-panel.mjs", + "status": "match" + }, + { + "kind": "runtime", + "path": "help.md", + "sourcePath": "/workspace/hwlab/.worktrees/dc-dcsn-p0-2026-003-publish-7de6edd/web/hwlab-cloud-web/help.md", + "distPath": "/workspace/hwlab/.worktrees/dc-dcsn-p0-2026-003-publish-7de6edd/web/hwlab-cloud-web/dist/help.md", + "status": "match" + }, + { + "kind": "runtime", + "path": "third_party/marked/marked.esm.js", + "sourcePath": "/workspace/hwlab/.worktrees/dc-dcsn-p0-2026-003-publish-7de6edd/web/hwlab-cloud-web/third_party/marked/marked.esm.js", + "distPath": "/workspace/hwlab/.worktrees/dc-dcsn-p0-2026-003-publish-7de6edd/web/hwlab-cloud-web/dist/third_party/marked/marked.esm.js", + "status": "match" + }, + { + "kind": "runtime", + "path": "third_party/marked/LICENSE", + "sourcePath": "/workspace/hwlab/.worktrees/dc-dcsn-p0-2026-003-publish-7de6edd/web/hwlab-cloud-web/third_party/marked/LICENSE", + "distPath": "/workspace/hwlab/.worktrees/dc-dcsn-p0-2026-003-publish-7de6edd/web/hwlab-cloud-web/dist/third_party/marked/LICENSE", + "status": "match" + }, + { + "kind": "alias", + "path": "gate/index.html", + "sourcePath": "/workspace/hwlab/.worktrees/dc-dcsn-p0-2026-003-publish-7de6edd/web/hwlab-cloud-web/index.html", + "distPath": "/workspace/hwlab/.worktrees/dc-dcsn-p0-2026-003-publish-7de6edd/web/hwlab-cloud-web/dist/gate/index.html", + "status": "match" + }, + { + "kind": "alias", + "path": "diagnostics/gate/index.html", + "sourcePath": "/workspace/hwlab/.worktrees/dc-dcsn-p0-2026-003-publish-7de6edd/web/hwlab-cloud-web/index.html", + "distPath": "/workspace/hwlab/.worktrees/dc-dcsn-p0-2026-003-publish-7de6edd/web/hwlab-cloud-web/dist/diagnostics/gate/index.html", + "status": "match" + } + ], + "mismatches": [] + }, "implementationState": "static-web-wrapper", "sourceState": "source-present", "entrypoint": "web/hwlab-cloud-web/index.html", @@ -922,15 +1016,15 @@ "artifactRequired": true, "artifactScope": "required", "notPublishedReason": null, - "localImageId": "sha256:6cd8af9f8012f5814ad9e985a43d16865750beb36e4c7a2fc14f92895b4f545c" + "localImageId": "sha256:72114b552125d915ac6959a4a0cd85c3821a32a351cce55289d2fdecc39cffec" }, { "serviceId": "hwlab-agent-mgr", "status": "published", - "image": "127.0.0.1:5000/hwlab/hwlab-agent-mgr:7e29522", - "imageTag": "7e29522", - "digest": "sha256:4d9b8944bc52012bb0a4a3a345f8939381d2804032e544d0c98f49fac0e46bfd", - "repositoryDigest": "127.0.0.1:5000/hwlab/hwlab-agent-mgr@sha256:4d9b8944bc52012bb0a4a3a345f8939381d2804032e544d0c98f49fac0e46bfd", + "image": "127.0.0.1:5000/hwlab/hwlab-agent-mgr:7de6edd", + "imageTag": "7de6edd", + "digest": "sha256:a16bba1762f58e4daed7ef94243e3f4f0522332704c9f40bcadab77f11acc7a2", + "repositoryDigest": "127.0.0.1:5000/hwlab/hwlab-agent-mgr@sha256:a16bba1762f58e4daed7ef94243e3f4f0522332704c9f40bcadab77f11acc7a2", "runtimeKind": "node-command", "implementationState": "repo-entrypoint", "sourceState": "source-present", @@ -939,15 +1033,15 @@ "artifactRequired": true, "artifactScope": "required", "notPublishedReason": null, - "localImageId": "sha256:23020dc829869b4f69c3b87760ea18b10c63145e86db15e532396d7c85b6ceaa" + "localImageId": "sha256:0f9c17d6ee0da642f665b0526ae7fda0f9c882908f3f9d07eb48d836d6cd9016" }, { "serviceId": "hwlab-agent-worker", "status": "published", - "image": "127.0.0.1:5000/hwlab/hwlab-agent-worker:7e29522", - "imageTag": "7e29522", - "digest": "sha256:2582176e2c35a2bfbce4edfd922749397d45f6f1f8c04d0394c501f7d87323ee", - "repositoryDigest": "127.0.0.1:5000/hwlab/hwlab-agent-worker@sha256:2582176e2c35a2bfbce4edfd922749397d45f6f1f8c04d0394c501f7d87323ee", + "image": "127.0.0.1:5000/hwlab/hwlab-agent-worker:7de6edd", + "imageTag": "7de6edd", + "digest": "sha256:3c75cb9d6c2b7392749d972c78f9b7a89e4f099a64f902d2eb9810f16d551ceb", + "repositoryDigest": "127.0.0.1:5000/hwlab/hwlab-agent-worker@sha256:3c75cb9d6c2b7392749d972c78f9b7a89e4f099a64f902d2eb9810f16d551ceb", "runtimeKind": "node-command", "implementationState": "repo-entrypoint", "sourceState": "source-present", @@ -956,15 +1050,15 @@ "artifactRequired": true, "artifactScope": "required", "notPublishedReason": null, - "localImageId": "sha256:15eaf89bd880d5396efffbbee112e9392c6ad1ef15ccedd52ef9b49811020cd5" + "localImageId": "sha256:373bbbe01b5ab6dae41280b23841472c3ad7c34f31d080e930c6c78b2d1d7931" }, { "serviceId": "hwlab-gateway", "status": "published", - "image": "127.0.0.1:5000/hwlab/hwlab-gateway:7e29522", - "imageTag": "7e29522", - "digest": "sha256:290341fd6e6f036aa953943072ad58e0d0cfcc0e13950e6090f941b3310755a6", - "repositoryDigest": "127.0.0.1:5000/hwlab/hwlab-gateway@sha256:290341fd6e6f036aa953943072ad58e0d0cfcc0e13950e6090f941b3310755a6", + "image": "127.0.0.1:5000/hwlab/hwlab-gateway:7de6edd", + "imageTag": "7de6edd", + "digest": "sha256:b5374c763d54628c2e81b86c93c826a27629a8546ae789223e0553ca7c8ab80a", + "repositoryDigest": "127.0.0.1:5000/hwlab/hwlab-gateway@sha256:b5374c763d54628c2e81b86c93c826a27629a8546ae789223e0553ca7c8ab80a", "runtimeKind": "node-command", "implementationState": "repo-entrypoint", "sourceState": "source-present", @@ -973,15 +1067,15 @@ "artifactRequired": true, "artifactScope": "required", "notPublishedReason": null, - "localImageId": "sha256:3438aa4c6ad9a0d9ca91bf8112e0fd6889d2259550c7feaa0b0c6c68455ef3ed" + "localImageId": "sha256:f3715273de17f900530de81b7230d7e6f29edd152aee28ad2d7439e01c3c21af" }, { "serviceId": "hwlab-gateway-simu", "status": "published", - "image": "127.0.0.1:5000/hwlab/hwlab-gateway-simu:7e29522", - "imageTag": "7e29522", - "digest": "sha256:02627f880c7ddf2102e2255a43097d1f604cfb7a22215736e328287e27fc5f03", - "repositoryDigest": "127.0.0.1:5000/hwlab/hwlab-gateway-simu@sha256:02627f880c7ddf2102e2255a43097d1f604cfb7a22215736e328287e27fc5f03", + "image": "127.0.0.1:5000/hwlab/hwlab-gateway-simu:7de6edd", + "imageTag": "7de6edd", + "digest": "sha256:be6aff770319c6c2b45f7b5974d4fb7aae4aab79a6670b1d8b87e194387e30dd", + "repositoryDigest": "127.0.0.1:5000/hwlab/hwlab-gateway-simu@sha256:be6aff770319c6c2b45f7b5974d4fb7aae4aab79a6670b1d8b87e194387e30dd", "runtimeKind": "node-command", "implementationState": "repo-entrypoint", "sourceState": "source-present", @@ -990,15 +1084,15 @@ "artifactRequired": true, "artifactScope": "required", "notPublishedReason": null, - "localImageId": "sha256:7747b26033273379d2f1fc639e9f76db6cb805c9c11760b0ebbc1971c9886459" + "localImageId": "sha256:846265260eee93bfb167b686dd0ba26a9618bff215e88ba12e816668c62b3fd6" }, { "serviceId": "hwlab-box-simu", "status": "published", - "image": "127.0.0.1:5000/hwlab/hwlab-box-simu:7e29522", - "imageTag": "7e29522", - "digest": "sha256:e02c270c71f83a2f4624722bb84616bea8374ef43439f27bdce310e4baa8dcca", - "repositoryDigest": "127.0.0.1:5000/hwlab/hwlab-box-simu@sha256:e02c270c71f83a2f4624722bb84616bea8374ef43439f27bdce310e4baa8dcca", + "image": "127.0.0.1:5000/hwlab/hwlab-box-simu:7de6edd", + "imageTag": "7de6edd", + "digest": "sha256:54e77f4c2ea9e87fd16becde9a5fe05b935f2472dba443458257dfdda1e87c27", + "repositoryDigest": "127.0.0.1:5000/hwlab/hwlab-box-simu@sha256:54e77f4c2ea9e87fd16becde9a5fe05b935f2472dba443458257dfdda1e87c27", "runtimeKind": "node-command", "implementationState": "repo-entrypoint", "sourceState": "source-present", @@ -1007,15 +1101,15 @@ "artifactRequired": true, "artifactScope": "required", "notPublishedReason": null, - "localImageId": "sha256:daba9e3fb75f3e53e8824793cc2c1b7df0747b74c64e93ad92a748e8c9cba079" + "localImageId": "sha256:4ead52d214856f351aada9e97a17c8c1cebd273af367202dc458fe5b3eada134" }, { "serviceId": "hwlab-patch-panel", "status": "published", - "image": "127.0.0.1:5000/hwlab/hwlab-patch-panel:7e29522", - "imageTag": "7e29522", - "digest": "sha256:f730323f83c62797f382932582a05df921045c86383dad7951f3d639b32456a9", - "repositoryDigest": "127.0.0.1:5000/hwlab/hwlab-patch-panel@sha256:f730323f83c62797f382932582a05df921045c86383dad7951f3d639b32456a9", + "image": "127.0.0.1:5000/hwlab/hwlab-patch-panel:7de6edd", + "imageTag": "7de6edd", + "digest": "sha256:02bec561d5f08f850e2884a2098da72978ad319d5bd799704ad9650778f11ae2", + "repositoryDigest": "127.0.0.1:5000/hwlab/hwlab-patch-panel@sha256:02bec561d5f08f850e2884a2098da72978ad319d5bd799704ad9650778f11ae2", "runtimeKind": "node-command", "implementationState": "repo-entrypoint", "sourceState": "source-present", @@ -1024,15 +1118,15 @@ "artifactRequired": true, "artifactScope": "required", "notPublishedReason": null, - "localImageId": "sha256:4c51f39bbd4ef656d3a43ce1b6fa98e3d0b48b88108bbeb4cc66c9aaee2e6c10" + "localImageId": "sha256:c71176c15d1a9d40e482eb4bb986b355ed32ca85f011092e751f33187ad47925" }, { "serviceId": "hwlab-router", "status": "published", - "image": "127.0.0.1:5000/hwlab/hwlab-router:7e29522", - "imageTag": "7e29522", - "digest": "sha256:7c40c573078a9c325402bd47b2eb78a46c0c80564a228cf7c9fcf0cfba5bc6d2", - "repositoryDigest": "127.0.0.1:5000/hwlab/hwlab-router@sha256:7c40c573078a9c325402bd47b2eb78a46c0c80564a228cf7c9fcf0cfba5bc6d2", + "image": "127.0.0.1:5000/hwlab/hwlab-router:7de6edd", + "imageTag": "7de6edd", + "digest": "sha256:bb3b10c486bb1305f4cae7ec2751947eeb0cc60c9993d54e93802b5df3b13fb6", + "repositoryDigest": "127.0.0.1:5000/hwlab/hwlab-router@sha256:bb3b10c486bb1305f4cae7ec2751947eeb0cc60c9993d54e93802b5df3b13fb6", "runtimeKind": "node-command", "implementationState": "repo-entrypoint", "sourceState": "source-present", @@ -1041,15 +1135,15 @@ "artifactRequired": true, "artifactScope": "required", "notPublishedReason": null, - "localImageId": "sha256:67975dbccd542a357ee507efbff15afb16dec0d741aee7fb55b7bf425e1f0693" + "localImageId": "sha256:c0228ad3da36fe4ca1147d9485656ce4f4ab8fc0480debb6e538e02d846a544c" }, { "serviceId": "hwlab-tunnel-client", "status": "published", - "image": "127.0.0.1:5000/hwlab/hwlab-tunnel-client:7e29522", - "imageTag": "7e29522", - "digest": "sha256:b141dc88557b8f6391aa082fdb37c6f0db5bc3a76c97bb39ba00354a9208abd8", - "repositoryDigest": "127.0.0.1:5000/hwlab/hwlab-tunnel-client@sha256:b141dc88557b8f6391aa082fdb37c6f0db5bc3a76c97bb39ba00354a9208abd8", + "image": "127.0.0.1:5000/hwlab/hwlab-tunnel-client:7de6edd", + "imageTag": "7de6edd", + "digest": "sha256:ce9fa92362dd10911dcd66c59412690ccde401ce4ef82b548cfcea37499a725f", + "repositoryDigest": "127.0.0.1:5000/hwlab/hwlab-tunnel-client@sha256:ce9fa92362dd10911dcd66c59412690ccde401ce4ef82b548cfcea37499a725f", "runtimeKind": "node-command", "implementationState": "repo-entrypoint", "sourceState": "source-present", @@ -1058,15 +1152,15 @@ "artifactRequired": true, "artifactScope": "required", "notPublishedReason": null, - "localImageId": "sha256:92735bc2840a70c7d9149e08db4a23290a7a7a7e3591fb9588b8dcaafa8392b9" + "localImageId": "sha256:7a5c84a01525a1a99ed29527d6675cfea9ded353f077263c53b35d6c1eb6986b" }, { "serviceId": "hwlab-edge-proxy", "status": "published", - "image": "127.0.0.1:5000/hwlab/hwlab-edge-proxy:7e29522", - "imageTag": "7e29522", - "digest": "sha256:792505139793c3f3da7769f9dc9d1cb3203ff92797305b62e8f12207c69a9831", - "repositoryDigest": "127.0.0.1:5000/hwlab/hwlab-edge-proxy@sha256:792505139793c3f3da7769f9dc9d1cb3203ff92797305b62e8f12207c69a9831", + "image": "127.0.0.1:5000/hwlab/hwlab-edge-proxy:7de6edd", + "imageTag": "7de6edd", + "digest": "sha256:bf19f6812cbac3c54c938ad12a2b1ef9626a746017f85658b9960a1761afa32c", + "repositoryDigest": "127.0.0.1:5000/hwlab/hwlab-edge-proxy@sha256:bf19f6812cbac3c54c938ad12a2b1ef9626a746017f85658b9960a1761afa32c", "runtimeKind": "node-command", "implementationState": "repo-entrypoint", "sourceState": "source-present", @@ -1075,15 +1169,15 @@ "artifactRequired": true, "artifactScope": "required", "notPublishedReason": null, - "localImageId": "sha256:39fb8d2967998f80000695a7b15189b8eb0945acb63097e909f69476273248a6" + "localImageId": "sha256:1e76c2f2e81d5b48445ebe9ce8e23424462d5316a27a79e3226eefb5894c31ee" }, { "serviceId": "hwlab-cli", "status": "published", - "image": "127.0.0.1:5000/hwlab/hwlab-cli:7e29522", - "imageTag": "7e29522", - "digest": "sha256:43d1c175091bfb462eddd7dd22a780d6485d86bdb052c36b78201606f48fadc1", - "repositoryDigest": "127.0.0.1:5000/hwlab/hwlab-cli@sha256:43d1c175091bfb462eddd7dd22a780d6485d86bdb052c36b78201606f48fadc1", + "image": "127.0.0.1:5000/hwlab/hwlab-cli:7de6edd", + "imageTag": "7de6edd", + "digest": "sha256:b169c015c4c06dcdf6d43131d69dd72be1996cb5970e25014c4cec0271b459c0", + "repositoryDigest": "127.0.0.1:5000/hwlab/hwlab-cli@sha256:b169c015c4c06dcdf6d43131d69dd72be1996cb5970e25014c4cec0271b459c0", "runtimeKind": "cli", "implementationState": "repo-entrypoint", "sourceState": "source-present", @@ -1092,15 +1186,15 @@ "artifactRequired": true, "artifactScope": "required", "notPublishedReason": null, - "localImageId": "sha256:078ecef0bb5dccc008f54d9a3c2006d4fbe3873ec7a154cd36d23f26e457aea7" + "localImageId": "sha256:0964d211ac6a5d522f3547f12c814a45cd0dbf124e6945a3b64424275eaf3ea8" }, { "serviceId": "hwlab-agent-skills", "status": "published", - "image": "127.0.0.1:5000/hwlab/hwlab-agent-skills:7e29522", - "imageTag": "7e29522", - "digest": "sha256:c75510038dc33196dc254472e78101d545c0d777a7b33b531f02b39faf2588b5", - "repositoryDigest": "127.0.0.1:5000/hwlab/hwlab-agent-skills@sha256:c75510038dc33196dc254472e78101d545c0d777a7b33b531f02b39faf2588b5", + "image": "127.0.0.1:5000/hwlab/hwlab-agent-skills:7de6edd", + "imageTag": "7de6edd", + "digest": "sha256:7d1b6871763613bf7c6e37f5b05b1b49e2c367b4b0b94abfbb9bceb7ab5de4ed", + "repositoryDigest": "127.0.0.1:5000/hwlab/hwlab-agent-skills@sha256:7d1b6871763613bf7c6e37f5b05b1b49e2c367b4b0b94abfbb9bceb7ab5de4ed", "runtimeKind": "skills-bundle", "implementationState": "repo-bundle", "sourceState": "source-present", @@ -1109,18 +1203,10 @@ "artifactRequired": true, "artifactScope": "required", "notPublishedReason": null, - "localImageId": "sha256:9a9d0b0c2c06f41d6a4f43a9f9572797f1508fdc4c3a30cc508b6b82379cbdce" + "localImageId": "sha256:758db8bb259d106f11d23167a396fa8e340450d410c1e2b97ff2c7f0f605d212" } ], "blockers": [] }, - "notes": "DEV-only artifact report. Do not treat runtime placeholder images as real service implementations.", - "reportLifecycle": { - "version": "v1", - "state": "active", - "activeEndpoint": "http://74.48.78.17:16667", - "activeBrowserEndpoint": "http://74.48.78.17:16666", - "deprecatedEndpoint": null, - "summary": "Current DEV artifact publish report; it is not a DEV-LIVE runtime substitute." - } + "notes": "DEV-only artifact report. Do not treat runtime placeholder images as real service implementations." } diff --git a/scripts/dev-cloud-workbench-smoke.test.mjs b/scripts/dev-cloud-workbench-smoke.test.mjs new file mode 100644 index 00000000..4e6fac52 --- /dev/null +++ b/scripts/dev-cloud-workbench-smoke.test.mjs @@ -0,0 +1,102 @@ +import assert from "node:assert/strict"; +import test from "node:test"; + +import { + classifyLiveDeploymentIdentity, + classifyLiveWebAssetIdentity +} from "./src/dev-cloud-workbench-smoke-lib.mjs"; + +const sourceIdentity = Object.freeze({ + status: "observed", + commitId: "ff4b3f928f76a0b46cd7998a2a865f3e1caf02c8", + shortCommitId: "ff4b3f928f76", + reportCommitId: "ff4b3f928f76", + worktreeState: "clean", + dirty: false +}); + +const expectedRuntimeIdentity = Object.freeze({ + status: "observed", + serviceId: "hwlab-cloud-api", + source: "deploy/artifact-catalog.dev.json+deploy/deploy.json", + commitId: "7de6edd", + imageTag: "7de6edd", + image: "127.0.0.1:5000/hwlab/hwlab-cloud-api:7de6edd" +}); + +test("live workbench identity passes only when runtime commit or image tag matches current source", () => { + assert.equal( + classifyLiveDeploymentIdentity(sourceIdentity, { + status: "observed", + commitId: "7de6edd2c41f", + imageTag: "c7de474" + }, expectedRuntimeIdentity).status, + "pass" + ); + + assert.equal( + classifyLiveDeploymentIdentity(sourceIdentity, { + status: "observed", + commitId: "unknown", + imageTag: "7de6edd" + }, expectedRuntimeIdentity).status, + "pass" + ); + + const stale = classifyLiveDeploymentIdentity(sourceIdentity, { + status: "observed", + commitId: "c7de474", + imageTag: "c7de474" + }, expectedRuntimeIdentity); + assert.equal(stale.status, "blocked"); + assert.match(stale.summary, /Deployment drift/u); + assert.match(stale.reason, /does not match current source desired runtime identity/u); +}); + +test("live workbench identity blocks when source identity is dirty or runtime identity is absent", () => { + const dirty = classifyLiveDeploymentIdentity( + { ...sourceIdentity, worktreeState: "dirty", dirty: true, reportCommitId: "unknown" }, + { status: "observed", commitId: "7de6edd", imageTag: "7de6edd" }, + expectedRuntimeIdentity + ); + assert.equal(dirty.status, "blocked"); + assert.match(dirty.reason, /not cleanly attributable/u); + + const missingRuntime = classifyLiveDeploymentIdentity(sourceIdentity, { + status: "not_observed", + commitId: "not_observed", + imageTag: "not_observed" + }, expectedRuntimeIdentity); + assert.equal(missingRuntime.status, "blocked"); + assert.match(missingRuntime.reason, /not observed/u); +}); + +test("live workbench identity uses deploy desired runtime identity instead of git HEAD", () => { + const deployed = classifyLiveDeploymentIdentity(sourceIdentity, { + status: "observed", + commitId: "7de6edd", + imageTag: "7de6edd" + }, expectedRuntimeIdentity); + assert.equal(deployed.status, "pass"); + assert.equal(deployed.expectedCommit, "7de6edd"); + assert.equal(deployed.expectedReportCommitId, "ff4b3f928f76"); + assert.equal(deployed.expectedSource, "deploy/artifact-catalog.dev.json+deploy/deploy.json"); +}); + +test("live web asset identity blocks stale deployed primary assets", () => { + const pass = classifyLiveWebAssetIdentity([ + { path: "index.html", status: "match" }, + { path: "styles.css", status: "match" }, + { path: "app.mjs", status: "match" } + ]); + assert.equal(pass.status, "pass"); + + const stale = classifyLiveWebAssetIdentity([ + { path: "index.html", status: "match" }, + { path: "styles.css", status: "mismatch" }, + { path: "app.mjs", status: "mismatch" } + ]); + assert.equal(stale.status, "blocked"); + assert.deepEqual(stale.mismatches, ["styles.css", "app.mjs"]); + assert.match(stale.summary, /Deployment drift/u); +}); diff --git a/scripts/src/dev-cloud-workbench-smoke-lib.mjs b/scripts/src/dev-cloud-workbench-smoke-lib.mjs index eeb2951d..4dba9618 100644 --- a/scripts/src/dev-cloud-workbench-smoke-lib.mjs +++ b/scripts/src/dev-cloud-workbench-smoke-lib.mjs @@ -1,5 +1,6 @@ import fs from "node:fs"; import { execFileSync } from "node:child_process"; +import { createHash } from "node:crypto"; import http from "node:http"; import path from "node:path"; import { fileURLToPath } from "node:url"; @@ -31,6 +32,8 @@ const requiredWebAssets = Object.freeze([ "third_party/marked/LICENSE" ]); +const liveIdentityWebAssets = Object.freeze(["index.html", "styles.css", "app.mjs"]); + const chineseWorkbenchLabels = Object.freeze([ "HWLAB 云工作台", "硬件资源", @@ -369,13 +372,64 @@ function runStaticSmoke() { async function runLiveSmoke(args) { const checks = []; const blockers = []; + const sourceIdentity = observeSourceIdentity(); const runtimeIdentity = await observeLiveRuntimeIdentity(runtime.endpoints.api); + const expectedRuntimeIdentity = observeExpectedRuntimeIdentity("hwlab-cloud-api"); + const deploymentIdentity = classifyLiveDeploymentIdentity(sourceIdentity, runtimeIdentity, expectedRuntimeIdentity); + addCheck(checks, blockers, "live-runtime-current-main", deploymentIdentity.status, deploymentIdentity.summary, { + blocker: "observability_blocker", + evidence: deploymentIdentity.evidence, + observations: deploymentIdentity + }); + const http = await fetchText(args.url); addCheck(checks, blockers, "live-http-html", http.ok && http.status === 200 && /text\/html/u.test(http.contentType) && liveHtmlLooksLikeWorkbench(http.body), "Live URL serves the Cloud Workbench HTML.", { blocker: "runtime_blocker", evidence: [args.url, `HTTP ${http.status ?? "none"}`, http.contentType ?? "no content-type"] }); + const webAssetIdentity = http.ok + ? await inspectLiveWebAssetIdentity(args.url, http.body) + : { + status: "blocked", + summary: "Live web asset identity could not be checked because the 16666 HTML fetch failed.", + assets: [] + }; + addCheck(checks, blockers, "live-web-assets-current-main", webAssetIdentity.status, webAssetIdentity.summary, { + blocker: "observability_blocker", + evidence: webAssetIdentity.assets.map((asset) => `${asset.path}: ${asset.status}`), + observations: webAssetIdentity + }); + + const identityBlocked = deploymentIdentity.status !== "pass" || webAssetIdentity.status !== "pass"; + if (identityBlocked) { + return baseReport({ + mode: "live", + url: args.url, + status: "blocked", + checks, + blockers, + evidenceLevel: "BLOCKED", + devLive: false, + runtimeIdentity, + sourceIdentity, + deploymentIdentity, + expectedRuntimeIdentity, + webAssetIdentity, + safety: { + prodTouched: false, + servicesRestarted: false, + heavyE2E: false, + hardwareWriteApis: false, + sourceIsDevLive: false, + liveMode: "deployment-identity-preflight", + codeAgentBrowserJourneySkipped: true, + retainedApiFields: ["runtime.commitId", "runtime.imageTag", "web asset hash/status"], + statement: "Live smoke blocked before the browser chat journey because deployed runtime/assets do not match the current source identity; it did not post Code Agent chat, call hardware write APIs, restart services, or read secret values." + } + }); + } + const dom = http.ok ? await inspectLiveDom(args.url) : { status: "skip", summary: "Browser DOM check skipped because HTTP fetch failed.", evidence: [] }; addCheck(checks, blockers, "live-browser-dom", dom.status, dom.summary, { blocker: dom.status === "pass" ? null : "observability_blocker", @@ -418,6 +472,10 @@ async function runLiveSmoke(args) { evidenceLevel: status === "pass" ? "DEV-LIVE-BROWSER" : "BLOCKED", devLive: status === "pass", runtimeIdentity, + sourceIdentity, + deploymentIdentity, + expectedRuntimeIdentity, + webAssetIdentity, safety: { prodTouched: false, servicesRestarted: false, @@ -431,8 +489,22 @@ async function runLiveSmoke(args) { }); } -function baseReport({ mode, status, checks, blockers, evidenceLevel, devLive, help = undefined, safety, url = null, runtimeIdentity = null }) { - const sourceIdentity = observeSourceIdentity(); +function baseReport({ + mode, + status, + checks, + blockers, + evidenceLevel, + devLive, + help = undefined, + safety, + url = null, + runtimeIdentity = null, + sourceIdentity = observeSourceIdentity(), + deploymentIdentity = null, + expectedRuntimeIdentity = null, + webAssetIdentity = null +}) { const liveJourneyPassed = mode === "live" && status === "pass"; return { $schema: "https://hwlab.pikastech.local/schemas/dev-gate-report.schema.json", @@ -473,6 +545,9 @@ function baseReport({ mode, status, checks, blockers, evidenceLevel, devLive, he devLive, sourceIdentity, runtimeIdentity: runtimeIdentity ?? notObservedRuntimeIdentity("live runtime identity was not observed in this smoke mode"), + ...(expectedRuntimeIdentity ? { expectedRuntimeIdentity } : {}), + ...(deploymentIdentity ? { deploymentIdentity } : {}), + ...(webAssetIdentity ? { webAssetIdentity } : {}), sourceContract: { status: "pass", documents: [ @@ -630,6 +705,172 @@ function runGit(args) { } } +export function classifyLiveDeploymentIdentity(sourceIdentity, runtimeIdentity, expectedRuntimeIdentity = null) { + const expectedCommit = sanitizeRevision(expectedRuntimeIdentity?.commitId ?? sourceIdentity?.commitId); + const expectedShort = expectedCommit === "unknown" ? "unknown" : expectedCommit.slice(0, 7); + const expectedReport = sourceIdentity?.reportCommitId ?? "unknown"; + const expectedImageTag = sanitizeRuntimeString(expectedRuntimeIdentity?.imageTag ?? expectedShort).toLowerCase(); + const observedRuntimeCommit = sanitizeRevision(runtimeIdentity?.commitId); + const observedImageTag = sanitizeRuntimeString(runtimeIdentity?.imageTag).toLowerCase(); + const runtimeObserved = runtimeIdentity?.status === "observed"; + const expectedObserved = expectedRuntimeIdentity === null || expectedRuntimeIdentity?.status === "observed"; + const sourceClean = + sourceIdentity?.status === "observed" && + sourceIdentity?.worktreeState === "clean" && + expectedReport !== "unknown"; + const accepted = new Set([...commitIdentityVariants(expectedCommit), ...commitIdentityVariants(expectedImageTag)]); + const runtimeMatches = + runtimeObserved && + sourceClean && + expectedObserved && + expectedCommit !== "unknown" && + (identityVariantMatches(accepted, observedRuntimeCommit) || identityVariantMatches(accepted, observedImageTag)); + + if (runtimeMatches) { + return { + status: "pass", + expectedCommit, + expectedReportCommitId: expectedReport, + expectedImageTag, + expectedSource: expectedRuntimeIdentity?.source ?? "source-git-head", + observedRuntimeCommit, + observedImageTag, + evidence: [ + `expected=${expectedShort}`, + `expectedImageTag=${expectedImageTag}`, + `runtimeCommit=${observedRuntimeCommit}`, + `imageTag=${observedImageTag}` + ], + summary: "Live API runtime identity matches the current source desired runtime identity." + }; + } + + const reason = !sourceClean + ? "current source identity is not cleanly attributable" + : !expectedObserved || expectedCommit === "unknown" + ? "source desired runtime identity was not observed" + : !runtimeObserved + ? "live API runtime identity was not observed" + : "live API runtime commit/image tag does not match current source desired runtime identity"; + return { + status: "blocked", + expectedCommit, + expectedReportCommitId: expectedReport, + expectedImageTag, + expectedSource: expectedRuntimeIdentity?.source ?? "source-git-head", + observedRuntimeCommit, + observedImageTag, + reason, + evidence: [ + `expected=${expectedShort}`, + `expectedImageTag=${expectedImageTag}`, + `runtimeCommit=${observedRuntimeCommit}`, + `imageTag=${observedImageTag}` + ], + summary: `Deployment drift: ${reason}.` + }; +} + +function observeExpectedRuntimeIdentity(serviceId) { + const deploy = readJsonOrNull("deploy/deploy.json"); + const catalog = readJsonOrNull("deploy/artifact-catalog.dev.json"); + const deployService = Array.isArray(deploy?.services) + ? deploy.services.find((service) => service?.serviceId === serviceId) + : null; + const catalogService = Array.isArray(catalog?.services) + ? catalog.services.find((service) => service?.serviceId === serviceId) + : null; + const image = sanitizeRuntimeString(catalogService?.image ?? deployService?.image); + const imageTag = sanitizeRuntimeString(catalogService?.imageTag ?? deployService?.env?.HWLAB_IMAGE_TAG ?? imageTagFromImage(image)); + const commitId = sanitizeRevision(catalogService?.commitId ?? deployService?.env?.HWLAB_COMMIT_ID ?? deploy?.commitId ?? catalog?.commitId ?? imageTag); + const sources = []; + if (catalogService) sources.push("deploy/artifact-catalog.dev.json"); + if (deployService || deploy?.commitId) sources.push("deploy/deploy.json"); + const observed = commitId !== "unknown" || imageTag !== "unknown"; + return { + status: observed ? "observed" : "not_observed", + serviceId, + source: sources.length > 0 ? sources.join("+") : "not_observed", + commitId, + imageTag, + image, + summary: observed + ? "Expected DEV runtime identity was derived from current source deploy desired state, not from live health." + : "Expected DEV runtime identity was not found in current source deploy desired state." + }; +} + +function readJsonOrNull(relativePath) { + try { + return JSON.parse(readText(relativePath)); + } catch { + return null; + } +} + +function imageTagFromImage(image) { + if (typeof image !== "string") return "unknown"; + const match = image.match(/:([^:/]+)$/u); + return match?.[1] ?? "unknown"; +} + +function commitIdentityVariants(commitId) { + if (typeof commitId !== "string" || !/^[a-f0-9]{7,40}$/u.test(commitId)) return new Set(); + return new Set([commitId, commitId.slice(0, 12), commitId.slice(0, 7)]); +} + +function identityVariantMatches(accepted, observed) { + if (typeof observed !== "string" || observed === "unknown" || observed === "not_observed") return false; + if (accepted.has(observed)) return true; + return [...accepted].some((expected) => expected.length >= 7 && observed.startsWith(expected)); +} + +async function inspectLiveWebAssetIdentity(baseUrl, fetchedIndexHtml) { + const assets = []; + for (const assetPath of liveIdentityWebAssets) { + const sourceText = readText(`web/hwlab-cloud-web/${assetPath}`); + const live = assetPath === "index.html" + ? { ok: true, status: 200, contentType: "text/html; charset=utf-8", body: fetchedIndexHtml } + : await fetchText(new URL(assetPath, baseUrl).toString()); + assets.push(compareLiveAsset(assetPath, live, sourceText)); + } + return classifyLiveWebAssetIdentity(assets); +} + +export function classifyLiveWebAssetIdentity(assets) { + const mismatches = assets.filter((asset) => asset.status !== "match"); + return { + status: mismatches.length === 0 ? "pass" : "blocked", + assets, + mismatches: mismatches.map((asset) => asset.path), + summary: mismatches.length === 0 + ? "Live 16666 primary web assets match the current clean source files." + : `Deployment drift: live 16666 primary web assets differ from current source (${mismatches.map((asset) => asset.path).join(", ")}).` + }; +} + +function compareLiveAsset(assetPath, live, sourceText) { + const liveText = typeof live?.body === "string" ? live.body : ""; + const sourceHash = hashText(sourceText); + const liveHash = hashText(liveText); + const fetched = live?.ok === true && live?.status === 200; + const matches = fetched && liveText === sourceText; + return { + path: assetPath, + status: matches ? "match" : fetched ? "mismatch" : "fetch_failed", + httpStatus: live?.status ?? null, + contentType: live?.contentType ?? "", + sourceHash, + liveHash, + sourceLength: sourceText.length, + liveLength: liveText.length + }; +} + +function hashText(value) { + return createHash("sha256").update(value).digest("hex").slice(0, 16); +} + function assetsExist() { return requiredWebAssets.every((file) => fs.existsSync(path.join(webRoot, file))); } diff --git a/scripts/validate-dev-gate-report.mjs b/scripts/validate-dev-gate-report.mjs index 94526acb..eb69734a 100644 --- a/scripts/validate-dev-gate-report.mjs +++ b/scripts/validate-dev-gate-report.mjs @@ -1381,7 +1381,7 @@ async function validateDevCloudWorkbenchLiveReport(report, label, raw) { }); assertArray(report.checks, `${label}.checks`); - assert.ok(report.checks.length >= 3, `${label}.checks must include HTTP, DOM, and Code Agent journey checks`); + assert.ok(report.checks.length >= 3, `${label}.checks must include HTTP plus either deployment identity or browser journey checks`); assertUnique(report.checks.map((check) => check.id), `${label}.checks ids`); const checks = new Map(report.checks.map((check, index) => { const checkLabel = `${label}.checks[${index}]`; @@ -1400,6 +1400,16 @@ async function validateDevCloudWorkbenchLiveReport(report, label, raw) { } return [check.id, check]; })); + const deploymentIdentityPreflight = + report.status === "blocked" && + report.safety?.liveMode === "deployment-identity-preflight" && + checks.has("live-runtime-current-main") && + checks.has("live-web-assets-current-main"); + if (deploymentIdentityPreflight) { + assertDevCloudWorkbenchDeploymentPreflight(report, label, checks); + return; + } + for (const requiredCheck of [ "live-http-html", "live-browser-dom", @@ -1431,6 +1441,10 @@ async function validateDevCloudWorkbenchLiveReport(report, label, raw) { assert.equal(journey.observations.request.method, "POST", `${label}.live-code-agent-browser-journey.request.method`); assert.equal(journey.observations.request.urlPath, "/v1/agent/chat", `${label}.live-code-agent-browser-journey.request.urlPath`); assertObject(journey.observations.response, `${label}.live-code-agent-browser-journey.response`); + if (report.status === "blocked" && journey.status === "blocked") { + assertDevCloudWorkbenchCodeAgentBlocked(report, label, journey); + return; + } assert.equal(journey.observations.response.status, "completed", `${label}.live-code-agent-browser-journey.response.status`); for (const field of ["provider", "model", "backend", "traceId"]) { assertString(journey.observations.response[field], `${label}.live-code-agent-browser-journey.response.${field}`); @@ -1465,6 +1479,141 @@ async function validateDevCloudWorkbenchLiveReport(report, label, raw) { assertFreshDevCloudWorkbenchLiveEvidence(report, label, raw, journey); } +function assertDevCloudWorkbenchCodeAgentBlocked(report, label, journey) { + const response = journey.observations.response; + assert.equal(response.status, "failed", `${label}.live-code-agent-browser-journey.response.status`); + assert.equal(response.hasReply, false, `${label}.live-code-agent-browser-journey.response.hasReply`); + assertObject(response.error, `${label}.live-code-agent-browser-journey.response.error`); + assert.ok( + ["provider_unavailable", "untrusted_completion"].includes(response.error.code), + `${label}.live-code-agent-browser-journey.response.error.code` + ); + for (const field of ["provider", "model", "backend", "traceId"]) { + assertString(response[field], `${label}.live-code-agent-browser-journey.response.${field}`); + } + for (const forbiddenField of ["conversationId", "sessionId", "messageId", "reply"]) { + assert.equal( + Object.hasOwn(response, forbiddenField), + false, + `${label}.live-code-agent-browser-journey.response must not retain ${forbiddenField}` + ); + } + + assertObject(journey.observations.ui, `${label}.live-code-agent-browser-journey.ui`); + assert.equal(journey.observations.ui.agentChatStatus, "发送失败", `${label}.live-code-agent-browser-journey.ui.agentChatStatus`); + assert.equal(journey.observations.ui.completedMessageVisible, false, `${label}.live-code-agent-browser-journey.ui.completedMessageVisible`); + assert.equal(journey.observations.ui.failedMessageVisible, true, `${label}.live-code-agent-browser-journey.ui.failedMessageVisible`); + assertBlockers(report.blockers, `${label}.blockers`); + assert.ok( + report.blockers.some((blocker) => blocker.scope === "live-code-agent-browser-journey"), + `${label}.blockers must include live-code-agent-browser-journey` + ); + + assertObject(report.safety, `${label}.safety`); + for (const field of ["prodTouched", "servicesRestarted", "heavyE2E", "hardwareWriteApis", "sourceIsDevLive"]) { + assert.equal(report.safety[field], false, `${label}.safety.${field}`); + } + assert.equal(report.safety.liveMode, "browser-user-journey-with-code-agent-post", `${label}.safety.liveMode`); + assert.deepEqual( + report.safety.retainedApiFields, + ["status", "provider", "model", "backend", "traceId", "hasReply", "error.code", "error.missingEnv"], + `${label}.safety.retainedApiFields` + ); + assertString(report.safety.statement, `${label}.safety.statement`); +} + +function assertDevCloudWorkbenchDeploymentPreflight(report, label, checks) { + for (const requiredCheck of [ + "live-runtime-current-main", + "live-http-html", + "live-web-assets-current-main" + ]) { + assert.ok(checks.has(requiredCheck), `${label}.checks missing ${requiredCheck}`); + } + + const runtimeCheck = checks.get("live-runtime-current-main"); + const webAssetCheck = checks.get("live-web-assets-current-main"); + assert.ok(["pass", "blocked"].includes(runtimeCheck.status), `${label}.live-runtime-current-main.status`); + assert.ok(["pass", "blocked"].includes(webAssetCheck.status), `${label}.live-web-assets-current-main.status`); + assert.ok( + runtimeCheck.status === "blocked" || webAssetCheck.status === "blocked", + `${label}.deployment preflight requires runtime or web asset drift` + ); + assertObject(runtimeCheck.observations, `${label}.live-runtime-current-main.observations`); + assertObject(webAssetCheck.observations, `${label}.live-web-assets-current-main.observations`); + assertObject(report.expectedRuntimeIdentity, `${label}.expectedRuntimeIdentity`); + for (const field of ["status", "serviceId", "source", "commitId", "imageTag", "image", "summary"]) { + assertString(report.expectedRuntimeIdentity[field], `${label}.expectedRuntimeIdentity.${field}`); + } + assert.ok(["observed", "not_observed"].includes(report.expectedRuntimeIdentity.status), `${label}.expectedRuntimeIdentity.status`); + assertObject(report.deploymentIdentity, `${label}.deploymentIdentity`); + assert.equal(report.deploymentIdentity.status, runtimeCheck.status, `${label}.deploymentIdentity.status`); + assertString(report.deploymentIdentity.expectedCommit, `${label}.deploymentIdentity.expectedCommit`); + assertString(report.deploymentIdentity.expectedImageTag, `${label}.deploymentIdentity.expectedImageTag`); + assertString(report.deploymentIdentity.expectedSource, `${label}.deploymentIdentity.expectedSource`); + assertString(report.deploymentIdentity.observedRuntimeCommit, `${label}.deploymentIdentity.observedRuntimeCommit`); + assertString(report.deploymentIdentity.observedImageTag, `${label}.deploymentIdentity.observedImageTag`); + if (report.deploymentIdentity.status === "blocked") { + assertString(report.deploymentIdentity.reason, `${label}.deploymentIdentity.reason`); + } + assertString(report.deploymentIdentity.summary, `${label}.deploymentIdentity.summary`); + assertStringArray(report.deploymentIdentity.evidence, `${label}.deploymentIdentity.evidence`, { minLength: 4 }); + assert.deepEqual( + runtimeCheck.observations, + report.deploymentIdentity, + `${label}.live-runtime-current-main.observations` + ); + + assertObject(report.webAssetIdentity, `${label}.webAssetIdentity`); + assert.equal(report.webAssetIdentity.status, webAssetCheck.status, `${label}.webAssetIdentity.status`); + assertArray(report.webAssetIdentity.assets, `${label}.webAssetIdentity.assets`); + assertStringArray(report.webAssetIdentity.mismatches, `${label}.webAssetIdentity.mismatches`, { + minLength: report.webAssetIdentity.status === "blocked" ? 1 : 0 + }); + assertString(report.webAssetIdentity.summary, `${label}.webAssetIdentity.summary`); + for (const [index, asset] of report.webAssetIdentity.assets.entries()) { + const assetLabel = `${label}.webAssetIdentity.assets[${index}]`; + assertObject(asset, assetLabel); + for (const field of ["path", "status", "contentType", "sourceHash", "liveHash"]) { + assertString(asset[field], `${assetLabel}.${field}`); + } + assert.ok(["match", "mismatch", "fetch_failed"].includes(asset.status), `${assetLabel}.status`); + assert.ok(Number.isInteger(asset.sourceLength), `${assetLabel}.sourceLength`); + assert.ok(Number.isInteger(asset.liveLength), `${assetLabel}.liveLength`); + } + assert.deepEqual( + webAssetCheck.observations, + report.webAssetIdentity, + `${label}.live-web-assets-current-main.observations` + ); + + assertBlockers(report.blockers, `${label}.blockers`); + if (runtimeCheck.status === "blocked") { + assert.ok( + report.blockers.some((blocker) => blocker.scope === "live-runtime-current-main"), + `${label}.blockers must include runtime identity drift` + ); + } + if (webAssetCheck.status === "blocked") { + assert.ok( + report.blockers.some((blocker) => blocker.scope === "live-web-assets-current-main"), + `${label}.blockers must include web asset drift` + ); + } + + assertObject(report.safety, `${label}.safety`); + for (const field of ["prodTouched", "servicesRestarted", "heavyE2E", "hardwareWriteApis", "sourceIsDevLive", "codeAgentBrowserJourneySkipped"]) { + assert.equal(report.safety[field], field === "codeAgentBrowserJourneySkipped", `${label}.safety.${field}`); + } + assert.equal(report.safety.liveMode, "deployment-identity-preflight", `${label}.safety.liveMode`); + assert.deepEqual( + report.safety.retainedApiFields, + ["runtime.commitId", "runtime.imageTag", "web asset hash/status"], + `${label}.safety.retainedApiFields` + ); + assertString(report.safety.statement, `${label}.safety.statement`); +} + function assertDevCloudWorkbenchIdentity(report, label) { assertObject(report.sourceIdentity, `${label}.sourceIdentity`); for (const field of ["status", "source", "commitId", "shortCommitId", "ref", "worktreeState", "reportCommitId", "summary"]) {