@opensoha/contracts 0.1.14 → 0.1.16

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.
Files changed (111) hide show
  1. package/auth/catalog_generated.go +2 -2
  2. package/auth/permission-catalog.json +113 -8
  3. package/compat/json-schema-ecosystem-0.1.0.json +1 -1
  4. package/compat/openapi-loose-boundaries-0.1.0.json +58 -1
  5. package/fixtures/openapi/AISkillsSettingsEnvelope/invalid/missing-registry.json +3 -0
  6. package/fixtures/openapi/AISkillsSettingsEnvelope/valid/registry.json +13 -0
  7. package/fixtures/openapi/AlertEventStreamSignal/invalid/missing-observed-at.json +4 -0
  8. package/fixtures/openapi/AlertEventStreamSignal/valid/changed.json +9 -0
  9. package/fixtures/openapi/AlertRuleEnvelope/invalid/missing-data.json +1 -0
  10. package/fixtures/openapi/AlertRuleEnvelope/valid/metrics.json +11 -0
  11. package/fixtures/openapi/AlertRuleInput/invalid/missing-name.json +3 -0
  12. package/fixtures/openapi/AlertRuleInput/valid/metrics.json +25 -0
  13. package/fixtures/openapi/AlertRuleRunListEnvelope/invalid/missing-rule-id.json +12 -0
  14. package/fixtures/openapi/AlertRuleRunListEnvelope/valid/items.json +13 -0
  15. package/fixtures/openapi/AlertRuleTestResultEnvelope/invalid/missing-executed-at.json +5 -0
  16. package/fixtures/openapi/AlertRuleTestResultEnvelope/valid/clear.json +10 -0
  17. package/fixtures/openapi/ApplicationRuntimeEnvironment/invalid/unknown-status.json +7 -0
  18. package/fixtures/openapi/ApplicationRuntimeEnvironment/valid/unavailable.json +7 -0
  19. package/fixtures/openapi/ApplicationWorkflowInput/invalid/owner-fields.json +7 -0
  20. package/fixtures/openapi/ApplicationWorkflowInput/valid/minimal.json +9 -0
  21. package/fixtures/openapi/BrandingAssetUploadEnvelope/valid/svg-data-url.json +5 -0
  22. package/fixtures/openapi/BrowserHandoffCompletionEnvelope/invalid/token-leak.json +7 -0
  23. package/fixtures/openapi/BrowserHandoffCompletionEnvelope/valid/completed.json +6 -0
  24. package/fixtures/openapi/BrowserHandoffEnvelope/invalid/missing-principal.json +12 -0
  25. package/fixtures/openapi/BrowserHandoffEnvelope/valid/pending.json +11 -0
  26. package/fixtures/openapi/ChangePasswordRequest/invalid/missing-current-password.json +3 -0
  27. package/fixtures/openapi/ChangePasswordRequest/invalid/short-new-password.json +4 -0
  28. package/fixtures/openapi/ChangePasswordRequest/valid/password.json +4 -0
  29. package/fixtures/openapi/ComputeOverviewEnvelope/valid/partial.json +6 -0
  30. package/fixtures/openapi/ComputeTaskListEnvelope/invalid/progress-overflow.json +21 -0
  31. package/fixtures/openapi/ComputeTaskListEnvelope/valid/running.json +15 -0
  32. package/fixtures/openapi/ComputeTaskStreamEvent/invalid/missing-observed-at.json +3 -0
  33. package/fixtures/openapi/ComputeTaskStreamEvent/valid/snapshot.json +27 -0
  34. package/fixtures/openapi/DesktopAuthAttemptCreateRequest/invalid/https-redirect.json +6 -0
  35. package/fixtures/openapi/DesktopAuthAttemptCreateRequest/invalid/localhost-redirect.json +6 -0
  36. package/fixtures/openapi/DesktopAuthAttemptCreateRequest/invalid/plain-challenge.json +6 -0
  37. package/fixtures/openapi/DesktopAuthAttemptCreateRequest/invalid/privileged-port.json +6 -0
  38. package/fixtures/openapi/DesktopAuthAttemptCreateRequest/invalid/short-callback.json +6 -0
  39. package/fixtures/openapi/DesktopAuthAttemptCreateRequest/valid/loopback.json +6 -0
  40. package/fixtures/openapi/DesktopAuthAttemptEnvelope/invalid/missing-expiry.json +6 -0
  41. package/fixtures/openapi/DesktopAuthAttemptEnvelope/valid/attempt.json +7 -0
  42. package/fixtures/openapi/DesktopAuthAttemptExchangeRequest/invalid/short-verifier.json +4 -0
  43. package/fixtures/openapi/DesktopAuthAttemptExchangeRequest/valid/exchange.json +4 -0
  44. package/fixtures/openapi/IdentityApplication/invalid/missing-id.json +7 -0
  45. package/fixtures/openapi/IdentityApplication/valid/full.json +30 -0
  46. package/fixtures/openapi/IdentityApplicationEnvelope/invalid/missing-data.json +1 -0
  47. package/fixtures/openapi/IdentityApplicationEnvelope/valid/minimal.json +10 -0
  48. package/fixtures/openapi/IdentityApplicationLaunchListEnvelope/invalid/missing-user.json +11 -0
  49. package/fixtures/openapi/IdentityApplicationLaunchListEnvelope/valid/minimal.json +12 -0
  50. package/fixtures/openapi/KubernetesResourceGraphEnvelope/invalid/edge-without-target.json +16 -0
  51. package/fixtures/openapi/KubernetesResourceGraphEnvelope/valid/deployment.json +53 -0
  52. package/fixtures/openapi/KubernetesResourceSearchEnvelope/invalid/missing-resource.json +10 -0
  53. package/fixtures/openapi/KubernetesResourceSearchEnvelope/valid/mixed.json +29 -0
  54. package/fixtures/openapi/KubernetesResourceStreamEvent/invalid/missing-observed-at.json +5 -0
  55. package/fixtures/openapi/KubernetesResourceStreamEvent/valid/modified.json +18 -0
  56. package/fixtures/openapi/KubernetesSecurityPostureEnvelope/invalid/finding-without-severity.json +24 -0
  57. package/fixtures/openapi/KubernetesSecurityPostureEnvelope/valid/kubescape.json +34 -0
  58. package/fixtures/openapi/KubernetesService/invalid/node-port-out-of-range.json +14 -0
  59. package/fixtures/openapi/KubernetesService/valid/port-mappings.json +22 -0
  60. package/fixtures/openapi/KubernetesServiceDetail/invalid/empty-target-port.json +13 -0
  61. package/fixtures/openapi/KubernetesServiceDetail/valid/port-mappings.json +22 -0
  62. package/fixtures/openapi/LogQuery/invalid/empty-data-source-id.json +7 -0
  63. package/fixtures/openapi/LogQuery/valid/durable-data-source.json +14 -0
  64. package/fixtures/openapi/ManifestPackage/invalid/missing-application.json +12 -0
  65. package/fixtures/openapi/ManifestPackage/valid/service-scoped.json +29 -0
  66. package/fixtures/openapi/OIDCClientInput/valid/generated-client-id.json +8 -0
  67. package/fixtures/openapi/OIDCClientSecretRevealEnvelope/invalid/missing-secret.json +6 -0
  68. package/fixtures/openapi/OIDCClientSecretRevealEnvelope/valid/reveal.json +7 -0
  69. package/fixtures/openapi/OperationalPlanEnvelope/invalid/kubernetes-owner-without-manager.json +29 -0
  70. package/fixtures/openapi/OperationalPlanEnvelope/valid/kubernetes-resource-update.json +30 -0
  71. package/fixtures/openapi/PasswordChangeResult/invalid/unexpected-status.json +3 -0
  72. package/fixtures/openapi/PasswordChangeResult/valid/ok.json +3 -0
  73. package/fixtures/openapi/PortalApplicationListEnvelope/invalid/missing-items.json +1 -0
  74. package/fixtures/openapi/PortalApplicationListEnvelope/valid/minimal.json +12 -0
  75. package/fixtures/openapi/PortalBootstrapEnvelope/invalid/missing-security.json +17 -0
  76. package/fixtures/openapi/PortalBootstrapEnvelope/valid/minimal.json +31 -0
  77. package/fixtures/openapi/PortalLaunchDecisionEnvelope/invalid/missing-launch-url.json +14 -0
  78. package/fixtures/openapi/PortalLaunchDecisionEnvelope/valid/desktop-handoff.json +16 -0
  79. package/fixtures/openapi/PortalLaunchRequest/invalid/unknown-surface.json +3 -0
  80. package/fixtures/openapi/PortalLaunchRequest/valid/default-web.json +1 -0
  81. package/fixtures/openapi/PortalLaunchRequest/valid/desktop.json +3 -0
  82. package/fixtures/openapi/PortalSecuritySummaryEnvelope/invalid/missing-session-count.json +15 -0
  83. package/fixtures/openapi/PortalSecuritySummaryEnvelope/valid/minimal.json +16 -0
  84. package/fixtures/openapi/ScopeGrantInput/invalid/delivery-missing-business-line.json +7 -0
  85. package/fixtures/openapi/ScopeGrantInput/invalid/platform-missing-clusters.json +7 -0
  86. package/fixtures/openapi/ScopeGrantInput/valid/platform.json +8 -0
  87. package/fixtures/openapi/SoftwarePackage/valid/software-package.json +6 -0
  88. package/fixtures/openapi/SoftwarePackageDownloadRecord/invalid/negative-duration.json +6 -0
  89. package/fixtures/openapi/SoftwarePackageDownloadRecord/valid/software-package-download-record.json +8 -0
  90. package/fixtures/openapi/SoftwarePackageURLImportRequest/valid/software-package-url-import-request.json +4 -0
  91. package/fixtures/openapi/SoftwareStorage/invalid/software-storage.json +1 -1
  92. package/fixtures/openapi/SoftwareStorage/valid/software-storage.json +13 -1
  93. package/fixtures/openapi/SystemIntegrationCreateRequest/invalid/s3-storage-missing-name.json +5 -0
  94. package/fixtures/openapi/SystemIntegrationCreateRequest/valid/s3-software-storage.json +18 -0
  95. package/fixtures/openapi/UpdateProfileRequest/invalid/invalid-avatar-fit.json +5 -0
  96. package/fixtures/openapi/UpdateProfileRequest/invalid/missing-email.json +3 -0
  97. package/fixtures/openapi/UpdateProfileRequest/valid/profile.json +7 -0
  98. package/fixtures/openapi/WorkbenchAgentRunListEnvelope/invalid/callback-token.json +17 -0
  99. package/fixtures/openapi/WorkbenchAgentRunListEnvelope/valid/session-scoped.json +17 -0
  100. package/fixtures/openapi/WorkbenchSessionEnvelope/invalid/missing-owner.json +8 -0
  101. package/fixtures/openapi/WorkbenchSessionEnvelope/valid/current.json +17 -0
  102. package/gen/go/sohaapi/client.go +210 -5
  103. package/gen/go/sohaapi/client_test.go +128 -5
  104. package/gen/go/sohaapi/types.go +2820 -190
  105. package/gen/go/sohaapi/types_test.go +62 -0
  106. package/gen/ts/sohaapi/index.ts +9118 -4206
  107. package/openapi/soha-api.json +16369 -6935
  108. package/openapi/soha-api.yaml +12986 -8319
  109. package/openapi/spec_generated.go +3 -3
  110. package/package.json +1 -1
  111. package/resource/models.go +197 -0
@@ -0,0 +1,3 @@
1
+ {
2
+ "surface": "mobile"
3
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "surface": "desktop"
3
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "data": {
3
+ "principal": {
4
+ "userId": "user-1",
5
+ "userName": "OpenSoha",
6
+ "email": "opensoha@example.com",
7
+ "roles": [],
8
+ "teams": [],
9
+ "projects": [],
10
+ "tags": []
11
+ },
12
+ "mfaEnabled": false,
13
+ "linkedSources": []
14
+ }
15
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "data": {
3
+ "principal": {
4
+ "userId": "user-1",
5
+ "userName": "OpenSoha",
6
+ "email": "opensoha@example.com",
7
+ "roles": [],
8
+ "teams": [],
9
+ "projects": [],
10
+ "tags": []
11
+ },
12
+ "mfaEnabled": false,
13
+ "linkedSources": [],
14
+ "activeSession": 1
15
+ }
16
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "subjectType": "user",
3
+ "subjectId": "user-123",
4
+ "scopeType": "delivery",
5
+ "role": "developer",
6
+ "enabled": true
7
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "subjectType": "team",
3
+ "subjectId": "team-123",
4
+ "scopeType": "platform",
5
+ "role": "operator",
6
+ "enabled": true
7
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "subjectType": "team",
3
+ "subjectId": "team-123",
4
+ "scopeType": "platform",
5
+ "clusterIds": ["cluster-123"],
6
+ "role": "operator",
7
+ "enabled": true
8
+ }
@@ -5,6 +5,10 @@
5
5
  "description": "Command-line client for Soha.",
6
6
  "publisher": "OpenSoha",
7
7
  "category": "Developer Tools",
8
+ "tenantId": "default",
9
+ "workspaceId": "default",
10
+ "visibility": "workspace",
11
+ "status": "ready",
8
12
  "version": "0.1.0",
9
13
  "platform": "darwin",
10
14
  "arch": "arm64",
@@ -12,6 +16,8 @@
12
16
  "sizeBytes": 1048576,
13
17
  "sha256": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
14
18
  "downloadPath": "/api/v1/software/packages/pkg-01j9z3m4w8n2/download",
19
+ "downloadCount": 12,
20
+ "storageIntegrationId": "software-object-storage",
15
21
  "createdAt": "2026-08-08T00:00:00Z",
16
22
  "updatedAt": "2026-08-08T00:00:00Z"
17
23
  }
@@ -0,0 +1,6 @@
1
+ {
2
+ "id": "audit-01j9z3m4w8n2",
3
+ "actorId": "user-01j9z3m4w8n2",
4
+ "downloadedAt": "2026-08-27T08:00:00Z",
5
+ "durationMs": -1
6
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "id": "audit-01j9z3m4w8n2",
3
+ "actorId": "user-01j9z3m4w8n2",
4
+ "actorName": "OpenSoha",
5
+ "downloadedAt": "2026-08-27T08:00:00Z",
6
+ "durationMs": 245,
7
+ "sourceIp": "127.0.0.1"
8
+ }
@@ -4,9 +4,13 @@
4
4
  "description": "Desktop client for Soha.",
5
5
  "publisher": "OpenSoha",
6
6
  "category": "Developer Tools",
7
+ "tenantId": "default",
8
+ "workspaceId": "default",
9
+ "visibility": "workspace",
7
10
  "version": "0.2.0",
8
11
  "platform": "darwin",
9
12
  "arch": "arm64",
13
+ "storageIntegrationId": "software-object-storage",
10
14
  "url": "https://downloads.opensoha.dev/soha-desktop-0.2.0-arm64.dmg",
11
15
  "fileName": "soha-desktop-0.2.0-arm64.dmg"
12
16
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "backend": "filesystem",
2
+ "backend": "s3",
3
3
  "objectCount": -1,
4
4
  "totalBytes": -1,
5
5
  "items": []
@@ -1,5 +1,12 @@
1
1
  {
2
- "backend": "filesystem",
2
+ "backend": "s3",
3
+ "integrationId": "software-object-storage",
4
+ "providerType": "s3",
5
+ "endpoint": "https://minio.example.com",
6
+ "bucket": "soha-software",
7
+ "region": "us-east-1",
8
+ "healthStatus": "healthy",
9
+ "lastCheckedAt": "2026-08-25T00:00:00Z",
3
10
  "objectCount": 1,
4
11
  "totalBytes": 1048576,
5
12
  "items": [
@@ -8,6 +15,10 @@
8
15
  "softwareId": "soha-cli",
9
16
  "name": "Soha CLI",
10
17
  "publisher": "OpenSoha",
18
+ "tenantId": "default",
19
+ "workspaceId": "default",
20
+ "visibility": "workspace",
21
+ "status": "ready",
11
22
  "version": "0.1.0",
12
23
  "platform": "darwin",
13
24
  "arch": "arm64",
@@ -15,6 +26,7 @@
15
26
  "sizeBytes": 1048576,
16
27
  "sha256": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
17
28
  "downloadPath": "/api/v1/software/packages/pkg-01j9z3m4w8n2/download",
29
+ "storageIntegrationId": "software-object-storage",
18
30
  "createdAt": "2026-08-08T00:00:00Z",
19
31
  "updatedAt": "2026-08-08T00:00:00Z"
20
32
  }
@@ -0,0 +1,5 @@
1
+ {
2
+ "category": "storage",
3
+ "providerType": "s3",
4
+ "enabled": true
5
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "category": "storage",
3
+ "providerType": "s3",
4
+ "name": "Software object storage",
5
+ "description": "S3-compatible storage for managed software packages.",
6
+ "enabled": true,
7
+ "configuration": [
8
+ { "key": "endpoint", "value": "https://minio.example.com" },
9
+ { "key": "bucket", "value": "soha-software" },
10
+ { "key": "region", "value": "us-east-1" },
11
+ { "key": "path_style", "value": "true" },
12
+ { "key": "allow_private", "value": "false" }
13
+ ],
14
+ "credentials": [
15
+ { "key": "access_key_id", "value": "fixture-access-key" },
16
+ { "key": "secret_access_key", "value": "fixture-secret-key" }
17
+ ]
18
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "displayName": "Ada Lovelace",
3
+ "email": "ada@example.com",
4
+ "avatarFit": "stretch"
5
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "displayName": "Ada Lovelace"
3
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "displayName": "Ada Lovelace",
3
+ "email": "ada@example.com",
4
+ "phone": "+86 138 0013 8000",
5
+ "avatarUrl": "https://example.com/avatar.png",
6
+ "avatarFit": "contain"
7
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "items": [
3
+ {
4
+ "id": "run-1",
5
+ "providerId": "external-agent",
6
+ "providerKind": "general",
7
+ "capabilityId": "root-cause-analysis",
8
+ "createdBy": "user-1",
9
+ "status": "running",
10
+ "callbackToken": "must-not-cross-the-user-api-boundary",
11
+ "timeoutSeconds": 600,
12
+ "queuedAt": "2026-08-19T08:00:00Z",
13
+ "createdAt": "2026-08-19T08:00:00Z",
14
+ "updatedAt": "2026-08-19T08:01:00Z"
15
+ }
16
+ ]
17
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "items": [
3
+ {
4
+ "id": "run-1",
5
+ "providerId": "external-agent",
6
+ "providerKind": "general",
7
+ "capabilityId": "root-cause-analysis",
8
+ "sessionId": "session-1",
9
+ "createdBy": "user-1",
10
+ "status": "running",
11
+ "timeoutSeconds": 600,
12
+ "queuedAt": "2026-08-19T08:00:00Z",
13
+ "createdAt": "2026-08-19T08:00:00Z",
14
+ "updatedAt": "2026-08-19T08:01:00Z"
15
+ }
16
+ ]
17
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "data": {
3
+ "id": "session-1",
4
+ "title": "Investigate checkout latency",
5
+ "createdAt": "2026-08-19T08:00:00Z",
6
+ "updatedAt": "2026-08-19T08:01:00Z"
7
+ }
8
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "data": {
3
+ "id": "session-1",
4
+ "title": "Investigate checkout latency",
5
+ "createdBy": "user-1",
6
+ "metadata": {
7
+ "mode": "root_cause",
8
+ "status": "active",
9
+ "scope": {
10
+ "clusterId": "prod",
11
+ "namespace": "checkout"
12
+ }
13
+ },
14
+ "createdAt": "2026-08-19T08:00:00Z",
15
+ "updatedAt": "2026-08-19T08:01:00Z"
16
+ }
17
+ }
@@ -2,8 +2,10 @@
2
2
  package sohaapi
3
3
 
4
4
  import (
5
+ "bufio"
5
6
  "bytes"
6
7
  "context"
8
+ "crypto/sha256"
7
9
  "encoding/json"
8
10
  "fmt"
9
11
  "io"
@@ -14,7 +16,7 @@ import (
14
16
  "time"
15
17
  )
16
18
 
17
- const defaultUserAgent = "opensoha-contracts/0.1.14"
19
+ const defaultUserAgent = "opensoha-contracts/0.1.16"
18
20
 
19
21
  type Client struct {
20
22
  BaseURL string
@@ -544,6 +546,97 @@ func (c *Client) queryLogs(ctx context.Context, path string, query LogQuery) (Lo
544
546
  return out.Data, nil
545
547
  }
546
548
 
549
+ func (c *Client) GetComputeCapabilities(ctx context.Context) (ComputeCapabilityManifest, error) {
550
+ var out ComputeCapabilityManifestEnvelope
551
+ if err := c.doJSON(ctx, http.MethodGet, "/compute/capabilities", true, nil, &out); err != nil {
552
+ return ComputeCapabilityManifest{}, err
553
+ }
554
+ return out.Data, nil
555
+ }
556
+
557
+ func (c *Client) GetComputeOverview(ctx context.Context) (ComputeOverview, error) {
558
+ var out ComputeOverviewEnvelope
559
+ if err := c.doJSON(ctx, http.MethodGet, "/compute/overview", true, nil, &out); err != nil {
560
+ return ComputeOverview{}, err
561
+ }
562
+ return out.Data, nil
563
+ }
564
+
565
+ func (c *Client) ListComputeAccessSources(ctx context.Context, params ListComputeAccessSourcesParams) (ComputeAccessSourceListEnvelope, error) {
566
+ var out ComputeAccessSourceListEnvelope
567
+ if err := c.doJSON(ctx, http.MethodGet, "/compute/access-sources"+computeAccessSourceQuery(params), true, nil, &out); err != nil {
568
+ return ComputeAccessSourceListEnvelope{}, err
569
+ }
570
+ return out, nil
571
+ }
572
+
573
+ func (c *Client) ListComputeProviders(ctx context.Context, params ListComputeProvidersParams) (ComputeProviderListEnvelope, error) {
574
+ var out ComputeProviderListEnvelope
575
+ if err := c.doJSON(ctx, http.MethodGet, "/compute/providers"+computeProviderQuery(params), true, nil, &out); err != nil {
576
+ return ComputeProviderListEnvelope{}, err
577
+ }
578
+ return out, nil
579
+ }
580
+
581
+ func (c *Client) ListComputeProviderInstances(ctx context.Context, params ListComputeProviderInstancesParams) (ComputeProviderInstanceListEnvelope, error) {
582
+ var out ComputeProviderInstanceListEnvelope
583
+ if err := c.doJSON(ctx, http.MethodGet, "/compute/provider-instances"+computeProviderInstanceQuery(params), true, nil, &out); err != nil {
584
+ return ComputeProviderInstanceListEnvelope{}, err
585
+ }
586
+ return out, nil
587
+ }
588
+
589
+ func (c *Client) GetComputeProviderInstance(ctx context.Context, domain ComputeProviderDomain, providerKey, instanceRef string) (ComputeProviderInstance, error) {
590
+ var out ComputeProviderInstanceEnvelope
591
+ if err := c.doJSON(ctx, http.MethodGet, computeProviderInstancePath(domain, providerKey, instanceRef), true, nil, &out); err != nil {
592
+ return ComputeProviderInstance{}, err
593
+ }
594
+ return out.Data, nil
595
+ }
596
+
597
+ func (c *Client) CheckComputeProviderInstanceHealth(ctx context.Context, domain ComputeProviderDomain, providerKey, instanceRef, idempotencyKey string, req ComputeProviderReadRequest) (ComputeTaskView, error) {
598
+ return c.mutateComputeProviderInstance(ctx, domain, providerKey, instanceRef, "health-checks", idempotencyKey, req)
599
+ }
600
+
601
+ func (c *Client) DiscoverComputeProviderInstance(ctx context.Context, domain ComputeProviderDomain, providerKey, instanceRef, idempotencyKey string, req ComputeProviderDiscoverRequest) (ComputeTaskView, error) {
602
+ return c.mutateComputeProviderInstance(ctx, domain, providerKey, instanceRef, "discoveries", idempotencyKey, req)
603
+ }
604
+
605
+ func (c *Client) mutateComputeProviderInstance(ctx context.Context, domain ComputeProviderDomain, providerKey, instanceRef, action, idempotencyKey string, req any) (ComputeTaskView, error) {
606
+ var out ComputeTaskEnvelope
607
+ path := computeProviderInstancePath(domain, providerKey, instanceRef) + "/" + action
608
+ if err := c.doJSONWithHeaders(ctx, http.MethodPost, path, true, req, &out, idempotencyHeader(idempotencyKey)); err != nil {
609
+ return ComputeTaskView{}, err
610
+ }
611
+ return out.Data, nil
612
+ }
613
+
614
+ func (c *Client) GetComputeResource(ctx context.Context, domain ComputeDomain, kind ComputeResourceKind, resourceID string) (AnyValue, error) {
615
+ var out GenericDataEnvelope
616
+ if err := c.doJSON(ctx, http.MethodGet, computeResourcePath(domain, kind, resourceID), true, nil, &out); err != nil {
617
+ return nil, err
618
+ }
619
+ return out.Data, nil
620
+ }
621
+
622
+ func (c *Client) ListComputeResourceRelations(ctx context.Context, domain ComputeDomain, kind ComputeResourceKind, resourceID string, params ListComputeResourceRelationsParams) (ComputeResourceRelations, error) {
623
+ var out ComputeResourceRelationListEnvelope
624
+ path := computeResourcePath(domain, kind, resourceID) + "/relations" + computeRelationQuery(params)
625
+ if err := c.doJSON(ctx, http.MethodGet, path, true, nil, &out); err != nil {
626
+ return ComputeResourceRelations{}, err
627
+ }
628
+ return out.Data, nil
629
+ }
630
+
631
+ func (c *Client) ExecuteComputeResourceAction(ctx context.Context, domain ComputeDomain, kind ComputeResourceKind, resourceID, action, idempotencyKey string, req ComputeResourceActionRequest) (ComputeTaskView, error) {
632
+ var out ComputeTaskEnvelope
633
+ path := computeResourcePath(domain, kind, resourceID) + "/actions/" + url.PathEscape(strings.TrimSpace(action))
634
+ if err := c.doJSONWithHeaders(ctx, http.MethodPost, path, true, req, &out, idempotencyHeader(idempotencyKey)); err != nil {
635
+ return ComputeTaskView{}, err
636
+ }
637
+ return out.Data, nil
638
+ }
639
+
547
640
  func (c *Client) ListComputeTasks(ctx context.Context, params ListComputeTasksParams) (ComputeTaskListEnvelope, error) {
548
641
  var out ComputeTaskListEnvelope
549
642
  if err := c.doJSON(ctx, http.MethodGet, "/compute/tasks"+computeTaskQuery(params), true, nil, &out); err != nil {
@@ -569,22 +662,80 @@ func (c *Client) ListComputeTaskLogs(ctx context.Context, domain ComputeTaskDoma
569
662
  }
570
663
 
571
664
  func (c *Client) CancelComputeTask(ctx context.Context, domain ComputeTaskDomain, taskID string, req ComputeTaskMutationRequest) (ComputeTaskView, error) {
572
- return c.mutateComputeTask(ctx, domain, taskID, "cancel", req)
665
+ return c.CancelComputeTaskWithKey(ctx, domain, taskID, computeMutationKey(domain, taskID, "cancel", req), req)
573
666
  }
574
667
 
575
668
  func (c *Client) RetryComputeTask(ctx context.Context, domain ComputeTaskDomain, taskID string, req ComputeTaskMutationRequest) (ComputeTaskView, error) {
576
- return c.mutateComputeTask(ctx, domain, taskID, "retry", req)
669
+ return c.RetryComputeTaskWithKey(ctx, domain, taskID, computeMutationKey(domain, taskID, "retry", req), req)
670
+ }
671
+
672
+ func (c *Client) CancelComputeTaskWithKey(ctx context.Context, domain ComputeTaskDomain, taskID, idempotencyKey string, req ComputeTaskMutationRequest) (ComputeTaskView, error) {
673
+ return c.mutateComputeTask(ctx, domain, taskID, "cancel", idempotencyKey, req)
577
674
  }
578
675
 
579
- func (c *Client) mutateComputeTask(ctx context.Context, domain ComputeTaskDomain, taskID, action string, req ComputeTaskMutationRequest) (ComputeTaskView, error) {
676
+ func (c *Client) RetryComputeTaskWithKey(ctx context.Context, domain ComputeTaskDomain, taskID, idempotencyKey string, req ComputeTaskMutationRequest) (ComputeTaskView, error) {
677
+ return c.mutateComputeTask(ctx, domain, taskID, "retry", idempotencyKey, req)
678
+ }
679
+
680
+ func (c *Client) mutateComputeTask(ctx context.Context, domain ComputeTaskDomain, taskID, action, idempotencyKey string, req ComputeTaskMutationRequest) (ComputeTaskView, error) {
580
681
  var out ComputeTaskEnvelope
581
682
  path := computeTaskPath(domain, taskID) + "/" + action
582
- if err := c.doJSON(ctx, http.MethodPost, path, true, req, &out); err != nil {
683
+ if err := c.doJSONWithHeaders(ctx, http.MethodPost, path, true, req, &out, idempotencyHeader(idempotencyKey)); err != nil {
583
684
  return ComputeTaskView{}, err
584
685
  }
585
686
  return out.Data, nil
586
687
  }
587
688
 
689
+ func (c *Client) StreamComputeTask(ctx context.Context, domain ComputeTaskDomain, taskID string, handle func(ComputeTaskStreamEvent) error) error {
690
+ if handle == nil {
691
+ return fmt.Errorf("compute task stream handler is required")
692
+ }
693
+ endpoint, err := c.endpoint(computeTaskPath(domain, taskID) + "/stream")
694
+ if err != nil {
695
+ return err
696
+ }
697
+ req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
698
+ if err != nil {
699
+ return err
700
+ }
701
+ req.Header.Set("Accept", "text/event-stream")
702
+ if c.UserAgent != "" {
703
+ req.Header.Set("User-Agent", c.UserAgent)
704
+ }
705
+ if strings.TrimSpace(c.Token) != "" {
706
+ req.Header.Set("Authorization", "Bearer "+strings.TrimSpace(c.Token))
707
+ }
708
+ client := c.HTTPClient
709
+ if client == nil {
710
+ client = http.DefaultClient
711
+ }
712
+ resp, err := client.Do(req)
713
+ if err != nil {
714
+ return err
715
+ }
716
+ defer resp.Body.Close()
717
+ if resp.StatusCode >= http.StatusBadRequest {
718
+ raw, _ := io.ReadAll(io.LimitReader(resp.Body, 1<<20))
719
+ return fmt.Errorf("GET %s failed: %s: %s", computeTaskPath(domain, taskID)+"/stream", resp.Status, responseErrorMessage(raw))
720
+ }
721
+ scanner := bufio.NewScanner(resp.Body)
722
+ scanner.Buffer(make([]byte, 64<<10), 1<<20)
723
+ for scanner.Scan() {
724
+ line := strings.TrimSpace(scanner.Text())
725
+ if !strings.HasPrefix(line, "data:") {
726
+ continue
727
+ }
728
+ var event ComputeTaskStreamEvent
729
+ if err := json.Unmarshal([]byte(strings.TrimSpace(strings.TrimPrefix(line, "data:"))), &event); err != nil {
730
+ return fmt.Errorf("decode compute task stream event: %w", err)
731
+ }
732
+ if err := handle(event); err != nil {
733
+ return err
734
+ }
735
+ }
736
+ return scanner.Err()
737
+ }
738
+
588
739
  func (c *Client) ListAIGatewayAuditLogs(ctx context.Context, params ListAIGatewayAuditLogsParams) ([]AuditLog, error) {
589
740
  var out AuditLogListEnvelope
590
741
  if err := c.doJSON(ctx, http.MethodGet, "/ai-gateway/audit-logs"+auditLogQuery(params), true, nil, &out); err != nil {
@@ -959,6 +1110,48 @@ func computeTaskPath(domain ComputeTaskDomain, taskID string) string {
959
1110
  return "/compute/tasks/" + url.PathEscape(string(domain)) + "/" + url.PathEscape(strings.TrimSpace(taskID))
960
1111
  }
961
1112
 
1113
+ func computeProviderInstancePath(domain ComputeProviderDomain, providerKey, instanceRef string) string {
1114
+ return "/compute/provider-instances/" + url.PathEscape(string(domain)) + "/" + url.PathEscape(strings.TrimSpace(providerKey)) + "/" + url.PathEscape(strings.TrimSpace(instanceRef))
1115
+ }
1116
+
1117
+ func computeResourcePath(domain ComputeDomain, kind ComputeResourceKind, resourceID string) string {
1118
+ return "/compute/resources/" + url.PathEscape(string(domain)) + "/" + url.PathEscape(string(kind)) + "/" + url.PathEscape(strings.TrimSpace(resourceID))
1119
+ }
1120
+
1121
+ func computeAccessSourceQuery(params ListComputeAccessSourcesParams) string {
1122
+ values := url.Values{}
1123
+ addQueryString(values, "sourceType", string(params.SourceType))
1124
+ addQueryString(values, "providerKey", params.ProviderKey)
1125
+ addQueryString(values, "cursor", string(params.Cursor))
1126
+ addQueryInt(values, "limit", int(params.Limit))
1127
+ return encodeQuery(values)
1128
+ }
1129
+
1130
+ func computeProviderQuery(params ListComputeProvidersParams) string {
1131
+ values := url.Values{}
1132
+ addQueryString(values, "domain", string(params.Domain))
1133
+ addQueryString(values, "source", string(params.Source))
1134
+ addQueryString(values, "cursor", string(params.Cursor))
1135
+ addQueryInt(values, "limit", int(params.Limit))
1136
+ return encodeQuery(values)
1137
+ }
1138
+
1139
+ func computeProviderInstanceQuery(params ListComputeProviderInstancesParams) string {
1140
+ values := url.Values{}
1141
+ addQueryString(values, "domain", string(params.Domain))
1142
+ addQueryString(values, "providerKey", params.ProviderKey)
1143
+ addQueryString(values, "cursor", string(params.Cursor))
1144
+ addQueryInt(values, "limit", int(params.Limit))
1145
+ return encodeQuery(values)
1146
+ }
1147
+
1148
+ func computeRelationQuery(params ListComputeResourceRelationsParams) string {
1149
+ values := url.Values{}
1150
+ addQueryString(values, "cursor", string(params.Cursor))
1151
+ addQueryInt(values, "limit", int(params.Limit))
1152
+ return encodeQuery(values)
1153
+ }
1154
+
962
1155
  func computeTaskQuery(params ListComputeTasksParams) string {
963
1156
  values := url.Values{}
964
1157
  addQueryString(values, "domain", string(params.Domain))
@@ -967,11 +1160,23 @@ func computeTaskQuery(params ListComputeTasksParams) string {
967
1160
  addQueryString(values, "category", string(params.Category))
968
1161
  addQueryString(values, "resourceKind", params.ResourceKind)
969
1162
  addQueryString(values, "resourceId", params.ResourceID)
1163
+ addQueryString(values, "sortBy", params.SortBy)
1164
+ addQueryString(values, "sortOrder", string(params.SortOrder))
970
1165
  addQueryString(values, "cursor", string(params.Cursor))
971
1166
  addQueryInt(values, "limit", int(params.Limit))
972
1167
  return encodeQuery(values)
973
1168
  }
974
1169
 
1170
+ func idempotencyHeader(key string) http.Header {
1171
+ return http.Header{"Idempotency-Key": []string{strings.TrimSpace(key)}}
1172
+ }
1173
+
1174
+ func computeMutationKey(domain ComputeTaskDomain, taskID, action string, req ComputeTaskMutationRequest) string {
1175
+ payload, _ := json.Marshal(req)
1176
+ sum := sha256.Sum256([]byte(string(domain) + "\x00" + strings.TrimSpace(taskID) + "\x00" + action + "\x00" + string(payload)))
1177
+ return fmt.Sprintf("compute-%x", sum)
1178
+ }
1179
+
975
1180
  func addQueryString(values url.Values, key, value string) {
976
1181
  if value != "" {
977
1182
  values.Set(key, value)