@opensoha/contracts 0.1.17 → 0.1.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/COMPATIBILITY.md +45 -0
- package/README.md +37 -0
- package/auth/catalog_generated.go +2 -2
- package/auth/outpost_headers.go +17 -0
- package/auth/outpost_headers_test.go +16 -0
- package/auth/permission-catalog.json +383 -1
- package/compat/consumer-revisions.json +3 -0
- package/compat/json-schema-ecosystem-0.1.0.json +1 -1
- package/compat/openapi-loose-boundaries-0.1.0.json +78 -46
- package/delivery/canonical.go +136 -0
- package/delivery/document.go +203 -0
- package/delivery/document.schema.json +1527 -0
- package/delivery/document_test.go +191 -0
- package/delivery/parser.go +155 -0
- package/evaluation/evaluation-run.schema.json +64 -10
- package/fixtures/json-schema/gateway-manifest/invalid/bad-execution-mode.json +73 -0
- package/fixtures/json-schema/gateway-manifest/invalid/unversioned-check.json +85 -0
- package/fixtures/json-schema/gateway-manifest/valid/checks.json +86 -0
- package/fixtures/json-schema/gateway-manifest/valid/versioned-task.json +73 -0
- package/fixtures/json-schema/network-ingest-event/invalid/vpn-probe-negative-rtt.json +34 -0
- package/fixtures/json-schema/network-ingest-event/invalid/vpn-stats-secret.json +27 -0
- package/fixtures/json-schema/network-ingest-event/valid/vpn-probe-batch.json +45 -0
- package/fixtures/json-schema/network-ingest-event/valid/vpn-tunnel-stats.json +26 -0
- package/fixtures/json-schema/network-runtime-protocol/invalid/vpn-managed-bad-token.json +15 -0
- package/fixtures/json-schema/network-runtime-protocol/invalid/vpn-managed-mode-override.json +16 -0
- package/fixtures/json-schema/network-runtime-protocol/valid/managed-vpn-macos-enrollment.json +27 -0
- package/fixtures/json-schema/network-runtime-protocol/valid/vpn-managed-connect.json +15 -0
- package/fixtures/openapi/AgentChatInput/invalid/empty-question.json +4 -0
- package/fixtures/openapi/AgentChatInput/invalid/system-role.json +9 -0
- package/fixtures/openapi/AgentChatInput/valid/authorized-background.json +5 -0
- package/fixtures/openapi/AgentChatInput/valid/conversation.json +15 -0
- package/fixtures/openapi/AgentRunnerToolCallRequest/invalid/model-identity.json +1 -0
- package/fixtures/openapi/AgentRunnerToolCallRequest/valid/search.json +1 -0
- package/fixtures/openapi/ApplicationInput/invalid/negative-version.json +6 -0
- package/fixtures/openapi/ApplicationInput/invalid/zero-version.json +6 -0
- package/fixtures/openapi/ApplicationInput/valid/versioned.json +6 -0
- package/fixtures/openapi/ApprovalRequest/invalid/resolved-scopes.json +20 -0
- package/fixtures/openapi/ApprovalRequest/valid/resolved-scopes.json +21 -0
- package/fixtures/openapi/BuildTemplateInput/invalid/negative-revision.json +6 -0
- package/fixtures/openapi/BuildTemplateInput/valid/draft.json +11 -0
- package/fixtures/openapi/BuildTriggerRequest/invalid/no-ref.json +1 -0
- package/fixtures/openapi/BuildTriggerRequest/valid/no-environment.json +1 -0
- package/fixtures/openapi/BuildpacksConfiguration/invalid/mutable-builder.json +5 -0
- package/fixtures/openapi/BuildpacksConfiguration/invalid/privilege-override.json +6 -0
- package/fixtures/openapi/BuildpacksConfiguration/invalid/unsupported-platform.json +5 -0
- package/fixtures/openapi/BuildpacksConfiguration/valid/pinned.json +5 -0
- package/fixtures/openapi/CapabilityAssessment/invalid/contract.json +16 -0
- package/fixtures/openapi/CapabilityAssessment/valid/contract.json +16 -0
- package/fixtures/openapi/CapabilityPlan/invalid/contract.json +16 -0
- package/fixtures/openapi/CapabilityPlan/valid/contract.json +19 -0
- package/fixtures/openapi/CapabilityPlanValidation/invalid/contract.json +5 -0
- package/fixtures/openapi/CapabilityPlanValidation/valid/contract.json +11 -0
- package/fixtures/openapi/CapabilityTask/invalid/contract.json +33 -0
- package/fixtures/openapi/CapabilityTask/valid/contract.json +34 -0
- package/fixtures/openapi/CapabilityTaskInput/invalid/contract.json +22 -0
- package/fixtures/openapi/CapabilityTaskInput/valid/contract.json +22 -0
- package/fixtures/openapi/CapabilityValueSemantic/invalid/contract.json +4 -0
- package/fixtures/openapi/CapabilityValueSemantic/valid/contract.json +7 -0
- package/fixtures/openapi/DeliveryAccessResult/invalid/release-verification.json +8 -0
- package/fixtures/openapi/DeliveryAccessResult/valid/release-verification.json +8 -0
- package/fixtures/openapi/DeliveryBatchAssessment/invalid/release-verification.json +11 -0
- package/fixtures/openapi/DeliveryBatchAssessment/valid/release-verification.json +11 -0
- package/fixtures/openapi/DeliveryBatchAssessmentInput/invalid/release-verification.json +9 -0
- package/fixtures/openapi/DeliveryBatchAssessmentInput/valid/release-verification.json +8 -0
- package/fixtures/openapi/DeliveryBatchInput/invalid/ambiguous-source.json +19 -0
- package/fixtures/openapi/DeliveryBatchInput/invalid/missing-idempotency.json +16 -0
- package/fixtures/openapi/DeliveryBatchInput/valid/quick-deploy.json +17 -0
- package/fixtures/openapi/DeliveryBatchInput/valid/saved-workflow.json +5 -0
- package/fixtures/openapi/DeliveryBlueprintInput/invalid/unversioned-preset.json +31 -0
- package/fixtures/openapi/DeliveryBlueprintInput/valid/service-preset.json +32 -0
- package/fixtures/openapi/DeliveryDocument/invalid/unknown-field.json +24 -0
- package/fixtures/openapi/DeliveryDocument/invalid/unknown-kind.json +23 -0
- package/fixtures/openapi/DeliveryDocument/invalid/unknown-version.json +23 -0
- package/fixtures/openapi/DeliveryDocument/valid/build-template.json +26 -0
- package/fixtures/openapi/DeliveryDocument/valid/deployment-template.json +26 -0
- package/fixtures/openapi/DeliveryDocument/valid/legacy-dag.json +39 -0
- package/fixtures/openapi/DeliveryDocument/valid/workflow-template.json +23 -0
- package/fixtures/openapi/DeliveryDocument/valid/workflow.json +20 -0
- package/fixtures/openapi/DeliveryDraftInput/invalid/short-idempotency-key.json +30 -0
- package/fixtures/openapi/DeliveryDraftInput/valid/idempotent-create.json +30 -0
- package/fixtures/openapi/DeliveryDraftService/invalid/zero-version.json +7 -0
- package/fixtures/openapi/DeliveryDraftService/valid/versioned-template.json +9 -0
- package/fixtures/openapi/DeliveryExecutionHistoryPage/invalid-kind.json +9 -0
- package/fixtures/openapi/DeliveryExecutionHistoryPage/valid-build.json +16 -0
- package/fixtures/openapi/DeliveryExecutionHistoryPage/valid-empty.json +3 -0
- package/fixtures/openapi/DeliveryPlanApprovalInput/invalid/confirm.json +1 -0
- package/fixtures/openapi/DeliveryPlanApprovalInput/valid/approve.json +1 -0
- package/fixtures/openapi/DeliveryPlanInput/invalid/draft-manifest-revision.json +1 -0
- package/fixtures/openapi/DeliveryPlanInput/valid/manifest-revision.json +1 -0
- package/fixtures/openapi/DeliveryTargetInput/invalid/deploy-without-environment.json +7 -0
- package/fixtures/openapi/DeliveryTargetInput/valid/build-without-environment.json +6 -0
- package/fixtures/openapi/DeliveryTemplateSourceInput/invalid/duplicate-kind.json +13 -0
- package/fixtures/openapi/DeliveryTemplateSourceInput/invalid/embedded-credentials.json +16 -0
- package/fixtures/openapi/DeliveryTemplateSourceInput/invalid/empty-kinds.json +10 -0
- package/fixtures/openapi/DeliveryTemplateSourceInput/invalid/negative-generation.json +15 -0
- package/fixtures/openapi/DeliveryTemplateSourceInput/invalid/unknown-kind.json +12 -0
- package/fixtures/openapi/DeliveryTemplateSourceInput/valid/manual-source.json +15 -0
- package/fixtures/openapi/DeliveryWorkflowDefinition/invalid/unversioned-template.json +15 -0
- package/fixtures/openapi/DeliveryWorkflowDefinition/valid/cross-app.json +24 -0
- package/fixtures/openapi/DeliveryWorkflowInput/invalid/short-key.json +1 -0
- package/fixtures/openapi/DeliveryWorkflowInput/valid/idempotent-create.json +1 -0
- package/fixtures/openapi/DeploymentTemplatePreviewInput/invalid/invalid-target-override.json +7 -0
- package/fixtures/openapi/DeploymentTemplatePreviewInput/invalid/invalid-zero-version.json +6 -0
- package/fixtures/openapi/DeploymentTemplatePreviewInput/valid/valid.json +6 -0
- package/fixtures/openapi/DockerDeliveryConfiguration/invalid/empty-mappings.json +5 -0
- package/fixtures/openapi/DockerDeliveryConfiguration/valid/compose.json +7 -0
- package/fixtures/openapi/DockerDeliverySnapshot/invalid/missing-required-field.json +19 -0
- package/fixtures/openapi/DockerDeliverySnapshot/valid/frozen-project.json +20 -0
- package/fixtures/openapi/DockerOperationCallbackRequest/invalid/cancellation-acknowledged-not-boolean.json +13 -0
- package/fixtures/openapi/DockerOperationCallbackRequest/valid/cancellation-acknowledged.json +13 -0
- package/fixtures/openapi/DockerProjectInput/invalid/empty-host.json +4 -0
- package/fixtures/openapi/DockerProjectInput/valid/inline-idempotent.json +7 -0
- package/fixtures/openapi/DockerQuickCreateHostInput/invalid/capacity-type.json +1 -0
- package/fixtures/openapi/DockerQuickCreateHostInput/valid/capacity.json +1 -0
- package/fixtures/openapi/HelmDeliveryConfiguration/invalid/plain-credential.json +21 -0
- package/fixtures/openapi/HelmDeliveryConfiguration/invalid/release-name.json +20 -0
- package/fixtures/openapi/HelmDeliveryConfiguration/valid/private-oci.json +20 -0
- package/fixtures/openapi/HelmDeliverySnapshot/invalid/secret-plan.json +31 -0
- package/fixtures/openapi/HelmDeliverySnapshot/invalid/unknown-operation.json +28 -0
- package/fixtures/openapi/HelmDeliverySnapshot/valid/first-install.json +28 -0
- package/fixtures/openapi/IdentityApplication/invalid/outpost-status.json +1 -0
- package/fixtures/openapi/IdentityApplicationOnboardingInput/invalid/missing-access.json +12 -0
- package/fixtures/openapi/IdentityApplicationOnboardingInput/valid/link.json +13 -0
- package/fixtures/openapi/IdentityOutpost/invalid/missing-runtime-status.json +9 -0
- package/fixtures/openapi/IdentityOutpost/valid/healthy.json +17 -0
- package/fixtures/openapi/IdentityOutpost/valid/unregistered.json +12 -0
- package/fixtures/openapi/IdentityOutpostDeployment/invalid/missing-protocol.json +3 -0
- package/fixtures/openapi/IdentityOutpostDeployment/valid/remote.json +6 -0
- package/fixtures/openapi/IdentityOutpostInput/invalid/forged-observation.json +7 -0
- package/fixtures/openapi/IdentityOutpostInput/valid/forward-auth.json +6 -0
- package/fixtures/openapi/IdentityOutpostInput/valid/legacy.json +8 -0
- package/fixtures/openapi/IdentityProviderSetup/invalid/fake-verification.json +1 -0
- package/fixtures/openapi/IdentityProviderSetup/valid/incomplete.json +1 -0
- package/fixtures/openapi/IdentityProviderSetup/valid/remote.json +1 -0
- package/fixtures/openapi/IdentityProviderUserMetadata/invalid/attributes.json +8 -0
- package/fixtures/openapi/IdentityProviderUserMetadata/valid/attributes.json +13 -0
- package/fixtures/openapi/KubernetesCustomResource/invalid/negative-generation.json +1 -0
- package/fixtures/openapi/KubernetesCustomResource/valid/deleting-operator.json +1 -0
- package/fixtures/openapi/KubernetesResourceCreationReceipt/invalid/manifest-content.json +20 -0
- package/fixtures/openapi/KubernetesResourceCreationReceipt/valid/confirmed.json +20 -0
- package/fixtures/openapi/KubernetesResourceCreationReceipt/valid/unknown.json +19 -0
- package/fixtures/openapi/ManifestDeploymentEnvelope/invalid/negative-resource-observation.json +57 -0
- package/fixtures/openapi/ManifestDeploymentEnvelope/valid/operator-deleting.json +57 -0
- package/fixtures/openapi/ManifestExecutionTaskPayload/invalid/gitops-empty.json +7 -0
- package/fixtures/openapi/ManifestExecutionTaskPayload/valid/gitops-frozen-children.json +18 -0
- package/fixtures/openapi/ManifestKustomizeOptions/invalid/escaping-entry.json +1 -0
- package/fixtures/openapi/ManifestKustomizeOptions/invalid/mutable-image.json +1 -0
- package/fixtures/openapi/ManifestKustomizeOptions/valid/overlay-images.json +7 -0
- package/fixtures/openapi/ManifestRevisionInput/invalid/missing-version.json +1 -0
- package/fixtures/openapi/ManifestRevisionInput/valid/current-draft.json +1 -0
- package/fixtures/openapi/ManifestServiceTemplateInputs/invalid/mutable-image.json +8 -0
- package/fixtures/openapi/ManifestServiceTemplateInputs/valid/frozen-service-image.json +9 -0
- package/fixtures/openapi/NetworkVPNConnectionIntentInput/invalid/auto-with-gateway.json +6 -0
- package/fixtures/openapi/NetworkVPNConnectionIntentInput/invalid/missing-manual-gateway.json +5 -0
- package/fixtures/openapi/NetworkVPNConnectionIntentInput/invalid/mode-override.json +7 -0
- package/fixtures/openapi/NetworkVPNConnectionIntentInput/valid/auto-intent.json +5 -0
- package/fixtures/openapi/NetworkVPNConnectionIntentInput/valid/manual-intent.json +6 -0
- package/fixtures/openapi/NetworkVPNProfileInput/invalid/unknown-private-key.json +25 -0
- package/fixtures/openapi/NetworkVPNProfileInput/valid/assigned-profile.json +24 -0
- package/fixtures/openapi/NetworkVPNSelectionPolicyInput/invalid/unbounded-attempts.json +21 -0
- package/fixtures/openapi/NetworkVPNSelectionPolicyInput/valid/provider-policy.json +21 -0
- package/fixtures/openapi/ObservabilityMetricAssessmentInput/invalid/arbitrary-expression.json +10 -0
- package/fixtures/openapi/ObservabilityMetricAssessmentInput/valid/metric-window.json +10 -0
- package/fixtures/openapi/PluginManifest/valid/hermes-api.json +53 -0
- package/fixtures/openapi/ProgressiveRolloutControlInput/invalid/missing-identity.json +5 -0
- package/fixtures/openapi/ProgressiveRolloutControlInput/invalid/skip-analysis.json +5 -0
- package/fixtures/openapi/ProgressiveRolloutControlInput/valid/pause.json +5 -0
- package/fixtures/openapi/ReleaseTargetInput/invalid/missing-required-field.json +15 -0
- package/fixtures/openapi/ReleaseTargetInput/valid/docker-compose.json +16 -0
- package/fixtures/openapi/RepositoryAnalysis/invalid/moving-ref.json +11 -0
- package/fixtures/openapi/RepositoryAnalysis/valid/go.json +29 -0
- package/fixtures/openapi/RepositoryAnalysisInput/invalid/arbitrary-source-url.json +6 -0
- package/fixtures/openapi/RepositoryAnalysisInput/invalid/missing-ref.json +4 -0
- package/fixtures/openapi/RepositoryAnalysisInput/valid/nested-project.json +6 -0
- package/fixtures/openapi/ServiceDeploymentTemplateBinding/invalid/invalid-unpinned.json +4 -0
- package/fixtures/openapi/ServiceDeploymentTemplateBinding/valid/valid.json +7 -0
- package/fixtures/openapi/ServiceDeploymentTemplateInput/invalid/invalid-executable-schema.json +25 -0
- package/fixtures/openapi/ServiceDeploymentTemplateInput/invalid/invalid-missing-schema.json +21 -0
- package/fixtures/openapi/ServiceDeploymentTemplateInput/invalid/invalid-mixed-renderers.json +40 -0
- package/fixtures/openapi/ServiceDeploymentTemplateInput/valid/valid-nested-parameters.json +41 -0
- package/fixtures/openapi/ServiceDeploymentTemplateInput/valid/valid-yaml.json +34 -0
- package/fixtures/openapi/TemplatePublishInput/invalid/missing-revision.json +1 -0
- package/fixtures/openapi/TemplatePublishInput/invalid/unknown-field.json +4 -0
- package/fixtures/openapi/TemplatePublishInput/invalid/zero-revision.json +3 -0
- package/fixtures/openapi/TemplatePublishInput/valid/revision.json +3 -0
- package/fixtures/openapi/ToolCapability/invalid/bad-idempotency.json +35 -0
- package/fixtures/openapi/ToolCapability/invalid/unversioned-check.json +47 -0
- package/fixtures/openapi/ToolCapability/valid/checks.json +48 -0
- package/fixtures/openapi/ToolCapability/valid/versioned-task.json +35 -0
- package/fixtures/openapi/ToolInvocationRequest/invalid/empty-version.json +8 -0
- package/fixtures/openapi/ToolInvocationRequest/valid/versioned.json +8 -0
- package/fixtures/openapi/ToolInvocationResult/invalid/missing-status-call.json +13 -0
- package/fixtures/openapi/ToolInvocationResult/valid/task.json +20 -0
- package/fixtures/openapi/VirtualMachineCreateInput/invalid/capacity-type.json +1 -0
- package/fixtures/openapi/VirtualMachineCreateInput/valid/capacity.json +1 -0
- package/fixtures/openapi/VirtualizationCapacityInput/invalid/missing-disk.json +1 -0
- package/fixtures/openapi/VirtualizationCapacityInput/valid/running-vm.json +1 -0
- package/fixtures/openapi/VirtualizationCapacityResult/invalid/invented-status.json +1 -0
- package/fixtures/openapi/VirtualizationCapacityResult/valid/available.json +1 -0
- package/fixtures/openapi/VirtualizationWorkerCreateInput/invalid/secret-input.json +5 -0
- package/fixtures/openapi/VirtualizationWorkerCreateInput/valid/one-worker.json +4 -0
- package/fixtures/openapi/VirtualizationWorkerPoolInput/invalid/external-owner.json +27 -0
- package/fixtures/openapi/VirtualizationWorkerPoolInput/invalid/zero-budget.json +27 -0
- package/fixtures/openapi/VirtualizationWorkerPoolInput/valid/registered.json +27 -0
- package/fixtures/openapi/VirtualizationWorkerPoolListEnvelope/invalid/nested-data.json +5 -0
- package/fixtures/openapi/VirtualizationWorkerPoolListEnvelope/valid/empty.json +3 -0
- package/fixtures/openapi/WorkbenchContextSelection/invalid/missing-file-content.json +8 -0
- package/fixtures/openapi/WorkbenchContextSelection/invalid/unknown-reference-kind.json +8 -0
- package/fixtures/openapi/WorkbenchContextSelection/valid/text-and-resource.json +17 -0
- package/fixtures/openapi/WorkbenchInspectionTaskInput/invalid/trigger-without-plan.json +10 -0
- package/fixtures/openapi/WorkbenchInspectionTaskInput/invalid/unknown-trigger.json +28 -0
- package/fixtures/openapi/WorkbenchInspectionTaskInput/invalid/zero-interval.json +28 -0
- package/fixtures/openapi/WorkbenchInspectionTaskInput/valid/alert-capability.json +30 -0
- package/fixtures/openapi/WorkbenchInspectionTaskInput/valid/scheduled-capability.json +28 -0
- package/fixtures/openapi/WorkbenchInspectionTrigger/invalid/missing-rule.json +3 -0
- package/fixtures/openapi/WorkbenchInspectionTrigger/invalid/schedule-alert-selector.json +4 -0
- package/fixtures/openapi/WorkbenchInspectionTrigger/valid/schedule.json +3 -0
- package/fixtures/openapi/WorkbenchModelPreferences/invalid/unknown-effort.json +1 -0
- package/fixtures/openapi/WorkbenchModelPreferences/valid/selected.json +1 -0
- package/fixtures/openapi/WorkflowCatalogPage/invalid/unsupported-kind.json +6 -0
- package/fixtures/openapi/WorkflowCatalogPage/valid/build-without-environment.json +6 -0
- package/fixtures/openapi/WorkflowTemplateInput/invalid/zero-revision.json +6 -0
- package/fixtures/openapi/WorkflowTemplateInput/valid/draft.json +10 -0
- package/gen/go/sohaapi/client.go +1 -1
- package/gen/go/sohaapi/types.go +8070 -859
- package/gen/go/sohaapi/types_test.go +100 -1
- package/gen/go/sohaapi/vpn_models_test.go +21 -0
- package/gen/ts/sohaapi/index.ts +20343 -13162
- package/go.mod +107 -5
- package/go.sum +379 -16
- package/helmrelease/delivery.go +283 -0
- package/helmrelease/delivery_test.go +102 -0
- package/helmrelease/frozen_chart.go +91 -0
- package/helmrelease/result.go +27 -0
- package/helmrelease/runtime/delivery.go +508 -0
- package/helmrelease/runtime/delivery_test.go +236 -0
- package/helmrelease/runtime/resources_test.go +60 -0
- package/helmrelease/values.go +99 -0
- package/knowledge/context-envelope.schema.json +1 -0
- package/mcp/gateway-manifest.schema.json +387 -72
- package/network/README.md +40 -0
- package/network/network-ingest-event.schema.json +228 -0
- package/network/network-runtime-protocol.schema.json +457 -1
- package/openapi/soha-api.json +54958 -38899
- package/openapi/soha-api.yaml +12143 -4984
- package/openapi/spec_generated.go +3 -3
- package/package.json +3 -1
- package/resource/manifest_health.go +155 -0
- package/resource/manifest_health_test.go +37 -0
- package/resource/models.go +14 -0
- package/resource/runtime/argocd.go +166 -0
- package/resource/runtime/argocd_integration_test.go +142 -0
- package/resource/runtime/argocd_observation.go +143 -0
- package/resource/runtime/argocd_operation.go +253 -0
- package/resource/runtime/argocd_resources.go +115 -0
- package/resource/runtime/argocd_resources_test.go +107 -0
- package/resource/runtime/argocd_test.go +386 -0
- package/resource/runtime/health.go +115 -0
- package/resource/runtime/health_test.go +78 -0
- package/resource/runtime/operator_integration_test.go +174 -0
- package/resource/runtime/ownership.go +178 -0
- package/resource/runtime/ownership_test.go +178 -0
- package/resource/runtime/rollout_integration_test.go +287 -0
- package/resource/runtime/rollout_observation.go +252 -0
- package/resource/runtime/rollout_operation.go +398 -0
- package/resource/runtime/rollout_resources.go +385 -0
- package/resource/runtime/rollout_task.go +165 -0
- package/resource/runtime/rollout_test.go +450 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
package delivery
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"context"
|
|
5
|
+
"encoding/json"
|
|
6
|
+
"io/fs"
|
|
7
|
+
"math"
|
|
8
|
+
"os"
|
|
9
|
+
"os/exec"
|
|
10
|
+
"reflect"
|
|
11
|
+
"strings"
|
|
12
|
+
"testing"
|
|
13
|
+
"time"
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
const recipeYAML = `apiVersion: delivery.soha.io/v1alpha1
|
|
17
|
+
kind: WorkflowTemplate
|
|
18
|
+
metadata:
|
|
19
|
+
name: build-release
|
|
20
|
+
displayName: 联合构建发布
|
|
21
|
+
spec:
|
|
22
|
+
definition:
|
|
23
|
+
mode: delivery_batch
|
|
24
|
+
schemaVersion: 1
|
|
25
|
+
stages: [build, plan, deploy, health]
|
|
26
|
+
executionMode: build_all_then_deploy
|
|
27
|
+
stopOnFailure: true
|
|
28
|
+
maxConcurrency: 4
|
|
29
|
+
`
|
|
30
|
+
|
|
31
|
+
func TestDocumentRoundTrip(t *testing.T) {
|
|
32
|
+
fixtures := os.DirFS("../fixtures/openapi/DeliveryDocument/valid")
|
|
33
|
+
files, err := fs.Glob(fixtures, "*.json")
|
|
34
|
+
if err != nil || len(files) < 4 {
|
|
35
|
+
t.Fatalf("expected fixtures for all four kinds: %v", err)
|
|
36
|
+
}
|
|
37
|
+
for _, file := range files {
|
|
38
|
+
t.Run(file, func(t *testing.T) {
|
|
39
|
+
data, err := fs.ReadFile(fixtures, file)
|
|
40
|
+
if err != nil {
|
|
41
|
+
t.Fatal(err)
|
|
42
|
+
}
|
|
43
|
+
first, invalid := Parse(file, data)
|
|
44
|
+
if len(invalid) != 0 {
|
|
45
|
+
t.Fatalf("parse: %+v", invalid)
|
|
46
|
+
}
|
|
47
|
+
for _, format := range []string{"yaml", "json"} {
|
|
48
|
+
encoded, err := first.Encode(format)
|
|
49
|
+
if err != nil {
|
|
50
|
+
t.Fatal(err)
|
|
51
|
+
}
|
|
52
|
+
second, invalid := Parse("roundtrip."+format, encoded)
|
|
53
|
+
if len(invalid) != 0 || !reflect.DeepEqual(first, second) {
|
|
54
|
+
t.Fatalf("lossy %s roundtrip: %+v", format, invalid)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
func TestEquivalentDocumentDigest(t *testing.T) {
|
|
62
|
+
first, invalid := Parse("recipe.yaml", []byte(recipeYAML))
|
|
63
|
+
if len(invalid) != 0 {
|
|
64
|
+
t.Fatalf("parse: %+v", invalid)
|
|
65
|
+
}
|
|
66
|
+
data, _ := first.Encode("json")
|
|
67
|
+
second, invalid := Parse("recipe.json", data)
|
|
68
|
+
if len(invalid) != 0 {
|
|
69
|
+
t.Fatal(invalid)
|
|
70
|
+
}
|
|
71
|
+
a, err := first.NormalizedSpecDigest()
|
|
72
|
+
if err != nil {
|
|
73
|
+
t.Fatal(err)
|
|
74
|
+
}
|
|
75
|
+
b, _ := second.NormalizedSpecDigest()
|
|
76
|
+
if a != b || Digest([]byte(recipeYAML)) == Digest(data) {
|
|
77
|
+
t.Fatal("semantic and source digest scopes are incorrect")
|
|
78
|
+
}
|
|
79
|
+
second.Metadata.Description = "metadata is outside the spec digest"
|
|
80
|
+
b, _ = second.NormalizedSpecDigest()
|
|
81
|
+
if a != b {
|
|
82
|
+
t.Fatal("metadata changed the spec digest")
|
|
83
|
+
}
|
|
84
|
+
second.Spec["enabled"] = false
|
|
85
|
+
b, _ = second.NormalizedSpecDigest()
|
|
86
|
+
if a == b {
|
|
87
|
+
t.Fatal("definition change did not change the spec digest")
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
func TestDocumentRejectsAmbiguousAndUnsafeInput(t *testing.T) {
|
|
92
|
+
for _, test := range []struct{ name, content, code string }{
|
|
93
|
+
{"duplicate", strings.Replace(recipeYAML, " name: build-release", " name: first\n name: second", 1), "duplicate_key"},
|
|
94
|
+
{"alias", strings.Replace(recipeYAML, " name: build-release", " name: &name build-release\n description: *name", 1), "yaml_feature"},
|
|
95
|
+
{"merge", strings.Replace(recipeYAML, " name: build-release", " <<: {name: build-release}", 1), "non_string_key"},
|
|
96
|
+
{"tag", strings.Replace(recipeYAML, "name: build-release", "name: !!str build-release", 1), "yaml_feature"},
|
|
97
|
+
{"key", strings.Replace(recipeYAML, "name: build-release", "7: build-release", 1), "non_string_key"},
|
|
98
|
+
{"infinity", strings.Replace(recipeYAML, "maxConcurrency: 4", "maxConcurrency: .inf", 1), "invalid_number"},
|
|
99
|
+
{"unsafe integer", strings.Replace(recipeYAML, "maxConcurrency: 4", "maxConcurrency: 9007199254740993", 1), "number_precision"},
|
|
100
|
+
{"rounded unsafe boundary", strings.Replace(recipeYAML, "maxConcurrency: 4", "maxConcurrency: 9007199254740991.1", 1), "number_precision"},
|
|
101
|
+
{"negative rounded unsafe boundary", strings.Replace(recipeYAML, "maxConcurrency: 4", "maxConcurrency: -9007199254740991.1", 1), "number_precision"},
|
|
102
|
+
{"underflow", strings.Replace(recipeYAML, "maxConcurrency: 4", "maxConcurrency: 1e-9999", 1), "number_precision"},
|
|
103
|
+
{"hex", strings.Replace(recipeYAML, "maxConcurrency: 4", "maxConcurrency: 0x4", 1), "invalid_number"},
|
|
104
|
+
{"version", strings.Replace(recipeYAML, APIVersion, "delivery.soha.io/v9", 1), "unsupported_version"},
|
|
105
|
+
{"kind", strings.Replace(recipeYAML, "kind: WorkflowTemplate", "kind: Deployment", 1), "unsupported_kind"},
|
|
106
|
+
{"blueprint", strings.Replace(recipeYAML, "kind: WorkflowTemplate", "kind: Blueprint", 1), "unsupported_kind"},
|
|
107
|
+
{"two documents", recipeYAML + "---\n" + recipeYAML, "multiple_documents"},
|
|
108
|
+
{"server field", strings.Replace(recipeYAML, " name: build-release", " name: build-release\n publicationState: published", 1), "schema_"},
|
|
109
|
+
{"unknown field", strings.Replace(recipeYAML, "maxConcurrency: 4", "maxConcurrency: 4\n typo: true", 1), "schema_"},
|
|
110
|
+
{"invalid type", strings.Replace(recipeYAML, "maxConcurrency: 4", "maxConcurrency: private-value", 1), "schema_"},
|
|
111
|
+
{"surrogate", `{"apiVersion":"\ud800"}`, "syntax"},
|
|
112
|
+
} {
|
|
113
|
+
t.Run(test.name, func(t *testing.T) {
|
|
114
|
+
_, invalid := Parse("input.yaml", []byte(test.content))
|
|
115
|
+
if len(invalid) == 0 || !strings.HasPrefix(invalid[0].Code, test.code) {
|
|
116
|
+
t.Fatalf("expected %s, got %+v", test.code, invalid)
|
|
117
|
+
}
|
|
118
|
+
for _, item := range invalid {
|
|
119
|
+
if strings.Contains(item.Message, "private-value") {
|
|
120
|
+
t.Fatal("diagnostic disclosed input content")
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
})
|
|
124
|
+
}
|
|
125
|
+
_, invalid := Parse("input.yaml", []byte(strings.Replace(recipeYAML, " name: build-release", " name: first\n name: second", 1)))
|
|
126
|
+
if invalid[0].Pointer != "/metadata/name" || invalid[0].Line != 5 || invalid[0].Column != 3 {
|
|
127
|
+
t.Fatalf("missing duplicate key location: %+v", invalid)
|
|
128
|
+
}
|
|
129
|
+
for _, path := range []string{"../input.yaml", "/input.yaml", "a/../input.yaml", "a\\input.yaml", "a//input.yaml", "C:input.yaml"} {
|
|
130
|
+
if _, invalid := Parse(path, []byte(recipeYAML)); len(invalid) == 0 || invalid[0].Code != "invalid_path" {
|
|
131
|
+
t.Fatalf("accepted invalid path %q", path)
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
for _, input := range [][]byte{[]byte(strings.Repeat(" ", MaxFileBytes+1)), {0xff}, []byte("x: " + strings.Repeat("[", 66) + "0" + strings.Repeat("]", 66))} {
|
|
135
|
+
if _, invalid := Parse("input.yaml", input); len(invalid) == 0 {
|
|
136
|
+
t.Fatal("accepted oversized, invalid UTF-8 or deeply nested document")
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
func TestCanonicalJSONVectors(t *testing.T) {
|
|
142
|
+
for _, test := range []struct{ input, want string }{
|
|
143
|
+
{`{"z":-0.0,"a":1e-7,"b":0.000001,"c":333333333.33333329}`, `{"a":1e-7,"b":0.000001,"c":333333333.3333333,"z":0}`},
|
|
144
|
+
{`{"\ufb33":"letter","😀":"emoji","€":"euro","\r":"return","1":"one","ö":"o","\u0080":"control"}`, `{"\r":"return","1":"one","` + "\u0080" + `":"control","ö":"o","€":"euro","😀":"emoji","דּ":"letter"}`},
|
|
145
|
+
{`{"string":"<>&\u2028\u2029\u000f\b\t\n\f\r\\\"/"}`, "{\"string\":\"<>&\u2028\u2029\\u000f\\b\\t\\n\\f\\r\\\\\\\"/\"}"},
|
|
146
|
+
} {
|
|
147
|
+
var value any
|
|
148
|
+
if err := json.Unmarshal([]byte(test.input), &value); err != nil {
|
|
149
|
+
t.Fatal(err)
|
|
150
|
+
}
|
|
151
|
+
encoded, err := CanonicalJSON(value)
|
|
152
|
+
if err != nil || string(encoded) != test.want {
|
|
153
|
+
t.Fatalf("canonical JSON = %q, want %q; %v", encoded, test.want, err)
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
for _, value := range []any{math.NaN(), math.Inf(1), string([]byte{0xff})} {
|
|
157
|
+
if _, err := CanonicalJSON(value); err == nil {
|
|
158
|
+
t.Fatal("accepted non-I-JSON value")
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
func TestCanonicalJSONMatchesECMAScript(t *testing.T) {
|
|
164
|
+
if _, err := exec.LookPath("node"); err != nil {
|
|
165
|
+
t.Skip("Node.js is required for the contracts npm test cross-language gate")
|
|
166
|
+
}
|
|
167
|
+
input := `{"numbers":[-0.0,1e-6,1e-7,1e20,1e21,1e23,5e-324,1.7976931348623157e308,333333333.33333329],"keys":{"10":"ten","2":"two","\ufb33":"letter","😀":"emoji","€":"euro","\r":"return","ö":"o","\u0080":"control"},"string":"<>&\u2028\u2029\u000f\b\t\n\f\r\\\"/"}`
|
|
168
|
+
var value any
|
|
169
|
+
if err := json.Unmarshal([]byte(input), &value); err != nil {
|
|
170
|
+
t.Fatal(err)
|
|
171
|
+
}
|
|
172
|
+
got, err := CanonicalJSON(value)
|
|
173
|
+
if err != nil {
|
|
174
|
+
t.Fatal(err)
|
|
175
|
+
}
|
|
176
|
+
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
177
|
+
defer cancel()
|
|
178
|
+
command := exec.CommandContext(ctx, "node", "-e", `
|
|
179
|
+
const canonical = value => Array.isArray(value) ? '[' + value.map(canonical).join(',') + ']'
|
|
180
|
+
: value !== null && typeof value === 'object' ? '{' + Object.keys(value).sort().map(key => JSON.stringify(key) + ':' + canonical(value[key])).join(',') + '}'
|
|
181
|
+
: JSON.stringify(value);
|
|
182
|
+
process.stdout.write(canonical(JSON.parse(require('node:fs').readFileSync(0, 'utf8'))));`)
|
|
183
|
+
command.Stdin = strings.NewReader(input)
|
|
184
|
+
want, err := command.Output()
|
|
185
|
+
if err != nil {
|
|
186
|
+
t.Fatal(err)
|
|
187
|
+
}
|
|
188
|
+
if string(got) != string(want) {
|
|
189
|
+
t.Fatalf("Go canonical JSON differs from ECMAScript: got %q, want %q", got, want)
|
|
190
|
+
}
|
|
191
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
package delivery
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"bytes"
|
|
5
|
+
"io"
|
|
6
|
+
"math"
|
|
7
|
+
"math/big"
|
|
8
|
+
"path"
|
|
9
|
+
"regexp"
|
|
10
|
+
"strconv"
|
|
11
|
+
"strings"
|
|
12
|
+
"unicode/utf8"
|
|
13
|
+
|
|
14
|
+
"go.yaml.in/yaml/v3"
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
var jsonNumber = regexp.MustCompile(`^-?(0|[1-9][0-9]*)(\.[0-9]+)?([eE][+-]?[0-9]+)?$`)
|
|
18
|
+
|
|
19
|
+
func ValidPath(value string) bool {
|
|
20
|
+
return value != "" && len(value) <= 512 && utf8.ValidString(value) && value != "." &&
|
|
21
|
+
path.Clean(value) == value && !path.IsAbs(value) && value != ".." && !strings.HasPrefix(value, "../") &&
|
|
22
|
+
!strings.ContainsAny(value, "\\\x00\r\n:")
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Parse accepts one bounded JSON-subset YAML document, including JSON syntax.
|
|
26
|
+
// It retains source locations and rejects aliases before decoding any values.
|
|
27
|
+
func Parse(filePath string, content []byte) (Document, []Diagnostic) {
|
|
28
|
+
if !ValidPath(filePath) {
|
|
29
|
+
return Document{}, []Diagnostic{diagnostic(filePath, "", "invalid_path", "path must be a normalized relative file path", nil)}
|
|
30
|
+
}
|
|
31
|
+
if len(content) == 0 || len(content) > MaxFileBytes || !utf8.Valid(content) {
|
|
32
|
+
return Document{}, []Diagnostic{diagnostic(filePath, "", "file_limit", "file must be UTF-8 and contain 1 to 1048576 bytes", nil)}
|
|
33
|
+
}
|
|
34
|
+
decoder := yaml.NewDecoder(bytes.NewReader(content))
|
|
35
|
+
var root, trailing yaml.Node
|
|
36
|
+
if err := decoder.Decode(&root); err != nil || len(root.Content) != 1 {
|
|
37
|
+
return Document{}, []Diagnostic{diagnostic(filePath, "", "syntax", "invalid YAML or JSON syntax", &root)}
|
|
38
|
+
}
|
|
39
|
+
if err := decoder.Decode(&trailing); err != io.EOF {
|
|
40
|
+
return Document{}, []Diagnostic{diagnostic(filePath, "", "multiple_documents", "each file must contain exactly one document", &trailing)}
|
|
41
|
+
}
|
|
42
|
+
parser := documentParser{path: filePath, locations: map[string]*yaml.Node{}}
|
|
43
|
+
value, invalid := parser.value(root.Content[0], "", 0)
|
|
44
|
+
if invalid != nil {
|
|
45
|
+
return Document{}, []Diagnostic{*invalid}
|
|
46
|
+
}
|
|
47
|
+
return validateDocument(filePath, value, parser.locations)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
type documentParser struct {
|
|
51
|
+
path string
|
|
52
|
+
nodes int
|
|
53
|
+
locations map[string]*yaml.Node
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
func (p *documentParser) value(node *yaml.Node, pointer string, depth int) (any, *Diagnostic) {
|
|
57
|
+
p.nodes++
|
|
58
|
+
p.locations[pointer] = node
|
|
59
|
+
if depth > 64 || p.nodes > 100000 {
|
|
60
|
+
return nil, p.invalid(node, pointer, "structure_limit", "document exceeds its nesting or node limit")
|
|
61
|
+
}
|
|
62
|
+
if node.Kind == yaml.AliasNode || node.Anchor != "" || node.Style&yaml.TaggedStyle != 0 {
|
|
63
|
+
return nil, p.invalid(node, pointer, "yaml_feature", "aliases, anchors and explicit tags are unsupported")
|
|
64
|
+
}
|
|
65
|
+
switch node.Kind {
|
|
66
|
+
case yaml.MappingNode:
|
|
67
|
+
return p.mapping(node, pointer, depth)
|
|
68
|
+
case yaml.SequenceNode:
|
|
69
|
+
values := make([]any, 0, len(node.Content))
|
|
70
|
+
for index, child := range node.Content {
|
|
71
|
+
value, err := p.value(child, pointer+"/"+strconv.Itoa(index), depth+1)
|
|
72
|
+
if err != nil {
|
|
73
|
+
return nil, err
|
|
74
|
+
}
|
|
75
|
+
values = append(values, value)
|
|
76
|
+
}
|
|
77
|
+
return values, nil
|
|
78
|
+
case yaml.ScalarNode:
|
|
79
|
+
return p.scalar(node, pointer)
|
|
80
|
+
default:
|
|
81
|
+
return nil, p.invalid(node, pointer, "unsupported_value", "only JSON-compatible values are supported")
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
func (p *documentParser) mapping(node *yaml.Node, pointer string, depth int) (any, *Diagnostic) {
|
|
86
|
+
values := make(map[string]any, len(node.Content)/2)
|
|
87
|
+
for i := 0; i < len(node.Content); i += 2 {
|
|
88
|
+
key, child := node.Content[i], node.Content[i+1]
|
|
89
|
+
if key.Kind != yaml.ScalarNode || key.Tag != "!!str" || key.Style&yaml.TaggedStyle != 0 || key.Anchor != "" {
|
|
90
|
+
return nil, p.invalid(key, pointer, "non_string_key", "object keys must be strings; merge keys are unsupported")
|
|
91
|
+
}
|
|
92
|
+
childPointer := pointer + "/" + escapePointer(key.Value)
|
|
93
|
+
if _, exists := values[key.Value]; exists {
|
|
94
|
+
return nil, p.invalid(key, childPointer, "duplicate_key", "duplicate object key")
|
|
95
|
+
}
|
|
96
|
+
value, err := p.value(child, childPointer, depth+1)
|
|
97
|
+
if err != nil {
|
|
98
|
+
return nil, err
|
|
99
|
+
}
|
|
100
|
+
values[key.Value] = value
|
|
101
|
+
}
|
|
102
|
+
return values, nil
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
func (p *documentParser) scalar(node *yaml.Node, pointer string) (any, *Diagnostic) {
|
|
106
|
+
switch node.Tag {
|
|
107
|
+
case "!!str":
|
|
108
|
+
return node.Value, nil
|
|
109
|
+
case "!!null":
|
|
110
|
+
return nil, nil
|
|
111
|
+
case "!!bool":
|
|
112
|
+
return strings.EqualFold(node.Value, "true"), nil
|
|
113
|
+
case "!!int", "!!float":
|
|
114
|
+
if !jsonNumber.MatchString(node.Value) {
|
|
115
|
+
return nil, p.invalid(node, pointer, "invalid_number", "numbers must use finite JSON decimal notation")
|
|
116
|
+
}
|
|
117
|
+
value, err := strconv.ParseFloat(node.Value, 64)
|
|
118
|
+
if err != nil || math.IsInf(value, 0) || math.IsNaN(value) || math.Trunc(value) == value && math.Abs(value) > 9007199254740991 || documentNumberBoundaryLoss(node.Value, value) {
|
|
119
|
+
return nil, p.invalid(node, pointer, "number_precision", "number exceeds finite IEEE-754 or safe integer limits")
|
|
120
|
+
}
|
|
121
|
+
return value, nil
|
|
122
|
+
default:
|
|
123
|
+
return nil, p.invalid(node, pointer, "unsupported_scalar", "quote dates and other non-JSON scalar values as strings")
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
func documentNumberBoundaryLoss(raw string, value float64) bool {
|
|
128
|
+
if value != 0 && math.Abs(value) != 9007199254740991 {
|
|
129
|
+
return false
|
|
130
|
+
}
|
|
131
|
+
// Directed rounding detects values just outside the safe range even when
|
|
132
|
+
// ParseFloat rounds them onto its boundary. It also detects underflow to zero.
|
|
133
|
+
exact, _, err := big.ParseFloat(raw, 10, 64, big.AwayFromZero)
|
|
134
|
+
if err != nil {
|
|
135
|
+
return true
|
|
136
|
+
}
|
|
137
|
+
return value == 0 && exact.Sign() != 0 || exact.Abs(exact).Cmp(big.NewFloat(9007199254740991)) > 0
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
func (p *documentParser) invalid(node *yaml.Node, pointer, code, message string) *Diagnostic {
|
|
141
|
+
result := diagnostic(p.path, pointer, code, message, node)
|
|
142
|
+
return &result
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
func diagnostic(path, pointer, code, message string, node *yaml.Node) Diagnostic {
|
|
146
|
+
result := Diagnostic{Path: path, Document: 1, Pointer: pointer, Code: code, Message: message}
|
|
147
|
+
if node != nil {
|
|
148
|
+
result.Line, result.Column = node.Line, node.Column
|
|
149
|
+
}
|
|
150
|
+
return result
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
func escapePointer(value string) string {
|
|
154
|
+
return strings.ReplaceAll(strings.ReplaceAll(value, "~", "~0"), "/", "~1")
|
|
155
|
+
}
|
|
@@ -4,16 +4,70 @@
|
|
|
4
4
|
"title": "Evaluation Run",
|
|
5
5
|
"type": "object",
|
|
6
6
|
"additionalProperties": false,
|
|
7
|
-
"required": [
|
|
7
|
+
"required": [
|
|
8
|
+
"schemaVersion",
|
|
9
|
+
"id",
|
|
10
|
+
"datasetId",
|
|
11
|
+
"datasetVersion",
|
|
12
|
+
"candidateRefs",
|
|
13
|
+
"status",
|
|
14
|
+
"startedAt"
|
|
15
|
+
],
|
|
8
16
|
"properties": {
|
|
9
|
-
"schemaVersion": {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
"
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
"
|
|
17
|
+
"schemaVersion": {
|
|
18
|
+
"const": "opensoha.dev/evaluation-run/v1"
|
|
19
|
+
},
|
|
20
|
+
"id": {
|
|
21
|
+
"type": "string",
|
|
22
|
+
"minLength": 1,
|
|
23
|
+
"maxLength": 128
|
|
24
|
+
},
|
|
25
|
+
"datasetId": {
|
|
26
|
+
"type": "string",
|
|
27
|
+
"minLength": 1,
|
|
28
|
+
"maxLength": 128
|
|
29
|
+
},
|
|
30
|
+
"datasetVersion": {
|
|
31
|
+
"type": "string",
|
|
32
|
+
"minLength": 1,
|
|
33
|
+
"maxLength": 64
|
|
34
|
+
},
|
|
35
|
+
"candidateRefs": {
|
|
36
|
+
"type": "object",
|
|
37
|
+
"minProperties": 1,
|
|
38
|
+
"maxProperties": 32,
|
|
39
|
+
"additionalProperties": {
|
|
40
|
+
"type": "string",
|
|
41
|
+
"minLength": 1,
|
|
42
|
+
"maxLength": 512
|
|
43
|
+
},
|
|
44
|
+
"description": "Versioned candidate metadata. Gateway execution applies publicModel, routeId, endpoint, and optional systemPrompt and retrievalContext strings (at most 512 characters each); promptVersion and retrievalVersion identify the supplied variants."
|
|
45
|
+
},
|
|
46
|
+
"status": {
|
|
47
|
+
"enum": [
|
|
48
|
+
"queued",
|
|
49
|
+
"running",
|
|
50
|
+
"completed",
|
|
51
|
+
"failed",
|
|
52
|
+
"cancelled"
|
|
53
|
+
]
|
|
54
|
+
},
|
|
55
|
+
"startedAt": {
|
|
56
|
+
"type": "string",
|
|
57
|
+
"format": "date-time"
|
|
58
|
+
},
|
|
59
|
+
"completedAt": {
|
|
60
|
+
"type": "string",
|
|
61
|
+
"format": "date-time"
|
|
62
|
+
},
|
|
63
|
+
"aggregateScores": {
|
|
64
|
+
"type": "object",
|
|
65
|
+
"maxProperties": 128,
|
|
66
|
+
"additionalProperties": {
|
|
67
|
+
"type": "number",
|
|
68
|
+
"minimum": 0,
|
|
69
|
+
"maximum": 1
|
|
70
|
+
}
|
|
71
|
+
}
|
|
18
72
|
}
|
|
19
73
|
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "soha-ai-gateway",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"generatedAt": "2026-06-09T12:00:00Z",
|
|
5
|
+
"principal": {
|
|
6
|
+
"userId": "u-1",
|
|
7
|
+
"userName": "admin",
|
|
8
|
+
"email": "admin@example.com",
|
|
9
|
+
"roles": [
|
|
10
|
+
"platform-admin"
|
|
11
|
+
],
|
|
12
|
+
"teams": [],
|
|
13
|
+
"projects": [],
|
|
14
|
+
"tags": [],
|
|
15
|
+
"permissionKeys": [
|
|
16
|
+
"ai.gateway.view"
|
|
17
|
+
]
|
|
18
|
+
},
|
|
19
|
+
"caller": {
|
|
20
|
+
"identityMode": "user",
|
|
21
|
+
"source": "cli"
|
|
22
|
+
},
|
|
23
|
+
"permissionKeys": [
|
|
24
|
+
"ai.gateway.view"
|
|
25
|
+
],
|
|
26
|
+
"tools": [
|
|
27
|
+
{
|
|
28
|
+
"name": "docker.projects.deploy.trigger",
|
|
29
|
+
"title": "Deploy Docker project",
|
|
30
|
+
"description": "Start a durable Compose deployment.",
|
|
31
|
+
"domain": "docker",
|
|
32
|
+
"action": "execute",
|
|
33
|
+
"riskLevel": "execute",
|
|
34
|
+
"permissionKeys": [
|
|
35
|
+
"platform.pods.view"
|
|
36
|
+
],
|
|
37
|
+
"requiresApproval": true,
|
|
38
|
+
"inputSchema": {
|
|
39
|
+
"type": "object",
|
|
40
|
+
"required": [
|
|
41
|
+
"projectId",
|
|
42
|
+
"idempotencyKey"
|
|
43
|
+
],
|
|
44
|
+
"properties": {
|
|
45
|
+
"projectId": {
|
|
46
|
+
"type": "string"
|
|
47
|
+
},
|
|
48
|
+
"idempotencyKey": {
|
|
49
|
+
"type": "string"
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
"version": "1",
|
|
54
|
+
"execution": {
|
|
55
|
+
"mode": "magic",
|
|
56
|
+
"idempotent": true,
|
|
57
|
+
"idempotencyKeyField": "idempotencyKey",
|
|
58
|
+
"taskKind": "docker.operation",
|
|
59
|
+
"statusTool": "docker.operations.get"
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
],
|
|
63
|
+
"resources": [],
|
|
64
|
+
"prompts": [],
|
|
65
|
+
"skills": [],
|
|
66
|
+
"summary": {
|
|
67
|
+
"toolCount": 1,
|
|
68
|
+
"resourceCount": 0,
|
|
69
|
+
"promptCount": 0,
|
|
70
|
+
"skillCount": 0,
|
|
71
|
+
"deniedCount": 0
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "soha-ai-gateway",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"generatedAt": "2026-06-09T12:00:00Z",
|
|
5
|
+
"principal": {
|
|
6
|
+
"userId": "u-1",
|
|
7
|
+
"userName": "admin",
|
|
8
|
+
"email": "admin@example.com",
|
|
9
|
+
"roles": [
|
|
10
|
+
"platform-admin"
|
|
11
|
+
],
|
|
12
|
+
"teams": [],
|
|
13
|
+
"projects": [],
|
|
14
|
+
"tags": [],
|
|
15
|
+
"permissionKeys": [
|
|
16
|
+
"ai.gateway.view"
|
|
17
|
+
]
|
|
18
|
+
},
|
|
19
|
+
"caller": {
|
|
20
|
+
"identityMode": "user",
|
|
21
|
+
"source": "cli"
|
|
22
|
+
},
|
|
23
|
+
"permissionKeys": [
|
|
24
|
+
"ai.gateway.view"
|
|
25
|
+
],
|
|
26
|
+
"tools": [
|
|
27
|
+
{
|
|
28
|
+
"name": "docker.projects.deploy.trigger",
|
|
29
|
+
"title": "Deploy Docker project",
|
|
30
|
+
"description": "Start a durable Compose deployment.",
|
|
31
|
+
"domain": "docker",
|
|
32
|
+
"action": "execute",
|
|
33
|
+
"riskLevel": "execute",
|
|
34
|
+
"permissionKeys": [
|
|
35
|
+
"platform.pods.view"
|
|
36
|
+
],
|
|
37
|
+
"requiresApproval": true,
|
|
38
|
+
"inputSchema": {
|
|
39
|
+
"type": "object",
|
|
40
|
+
"required": [
|
|
41
|
+
"projectId",
|
|
42
|
+
"idempotencyKey"
|
|
43
|
+
],
|
|
44
|
+
"properties": {
|
|
45
|
+
"projectId": {
|
|
46
|
+
"type": "string"
|
|
47
|
+
},
|
|
48
|
+
"idempotencyKey": {
|
|
49
|
+
"type": "string"
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
"version": "1",
|
|
54
|
+
"execution": {
|
|
55
|
+
"mode": "async",
|
|
56
|
+
"idempotent": true,
|
|
57
|
+
"idempotencyKeyField": "idempotencyKey",
|
|
58
|
+
"taskKind": "docker.operation",
|
|
59
|
+
"statusTool": "docker.operations.get",
|
|
60
|
+
"checks": [
|
|
61
|
+
{
|
|
62
|
+
"purpose": "precondition",
|
|
63
|
+
"toolName": "docker.projects.deploy.plan"
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"purpose": "verification",
|
|
67
|
+
"toolName": "docker.projects.runtime.assess",
|
|
68
|
+
"capabilityVersion": "1"
|
|
69
|
+
}
|
|
70
|
+
],
|
|
71
|
+
"recoveryMode": "original_call"
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
],
|
|
75
|
+
"resources": [],
|
|
76
|
+
"prompts": [],
|
|
77
|
+
"skills": [],
|
|
78
|
+
"summary": {
|
|
79
|
+
"toolCount": 1,
|
|
80
|
+
"resourceCount": 0,
|
|
81
|
+
"promptCount": 0,
|
|
82
|
+
"skillCount": 0,
|
|
83
|
+
"deniedCount": 0
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "soha-ai-gateway",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"generatedAt": "2026-06-09T12:00:00Z",
|
|
5
|
+
"principal": {
|
|
6
|
+
"userId": "u-1",
|
|
7
|
+
"userName": "admin",
|
|
8
|
+
"email": "admin@example.com",
|
|
9
|
+
"roles": [
|
|
10
|
+
"platform-admin"
|
|
11
|
+
],
|
|
12
|
+
"teams": [],
|
|
13
|
+
"projects": [],
|
|
14
|
+
"tags": [],
|
|
15
|
+
"permissionKeys": [
|
|
16
|
+
"ai.gateway.view"
|
|
17
|
+
]
|
|
18
|
+
},
|
|
19
|
+
"caller": {
|
|
20
|
+
"identityMode": "user",
|
|
21
|
+
"source": "cli"
|
|
22
|
+
},
|
|
23
|
+
"permissionKeys": [
|
|
24
|
+
"ai.gateway.view"
|
|
25
|
+
],
|
|
26
|
+
"tools": [
|
|
27
|
+
{
|
|
28
|
+
"name": "docker.projects.deploy.trigger",
|
|
29
|
+
"title": "Deploy Docker project",
|
|
30
|
+
"description": "Start a durable Compose deployment.",
|
|
31
|
+
"domain": "docker",
|
|
32
|
+
"action": "execute",
|
|
33
|
+
"riskLevel": "execute",
|
|
34
|
+
"permissionKeys": [
|
|
35
|
+
"platform.pods.view"
|
|
36
|
+
],
|
|
37
|
+
"requiresApproval": true,
|
|
38
|
+
"inputSchema": {
|
|
39
|
+
"type": "object",
|
|
40
|
+
"required": [
|
|
41
|
+
"projectId",
|
|
42
|
+
"idempotencyKey"
|
|
43
|
+
],
|
|
44
|
+
"properties": {
|
|
45
|
+
"projectId": {
|
|
46
|
+
"type": "string"
|
|
47
|
+
},
|
|
48
|
+
"idempotencyKey": {
|
|
49
|
+
"type": "string"
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
"version": "1",
|
|
54
|
+
"execution": {
|
|
55
|
+
"mode": "async",
|
|
56
|
+
"idempotent": true,
|
|
57
|
+
"idempotencyKeyField": "idempotencyKey",
|
|
58
|
+
"taskKind": "docker.operation",
|
|
59
|
+
"statusTool": "docker.operations.get",
|
|
60
|
+
"checks": [
|
|
61
|
+
{
|
|
62
|
+
"purpose": "precondition",
|
|
63
|
+
"toolName": "docker.projects.deploy.plan",
|
|
64
|
+
"capabilityVersion": "1"
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"purpose": "verification",
|
|
68
|
+
"toolName": "docker.projects.runtime.assess",
|
|
69
|
+
"capabilityVersion": "1"
|
|
70
|
+
}
|
|
71
|
+
],
|
|
72
|
+
"recoveryMode": "original_call"
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
],
|
|
76
|
+
"resources": [],
|
|
77
|
+
"prompts": [],
|
|
78
|
+
"skills": [],
|
|
79
|
+
"summary": {
|
|
80
|
+
"toolCount": 1,
|
|
81
|
+
"resourceCount": 0,
|
|
82
|
+
"promptCount": 0,
|
|
83
|
+
"skillCount": 0,
|
|
84
|
+
"deniedCount": 0
|
|
85
|
+
}
|
|
86
|
+
}
|