@shipeasy/openapi 3.1.0 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-UV333BIK.js → chunk-DW33MG4B.js} +147 -2
- package/dist/chunk-DW33MG4B.js.map +1 -0
- package/dist/{chunk-V4ZESAIF.js → chunk-RTN572TI.js} +67 -1
- package/dist/chunk-RTN572TI.js.map +1 -0
- package/dist/{client-yqGYju-M.d.ts → client-BPVn4hMC.d.ts} +687 -3
- package/dist/client.d.ts +1 -1
- package/dist/client.js +15 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +16 -2
- package/dist/index.js.map +1 -1
- package/dist/schemas.d.ts +376 -1
- package/dist/schemas.js +67 -1
- package/openapi-sdk.json +1747 -0
- package/openapi.json +1485 -178
- package/openapi.yaml +922 -2
- package/package.json +6 -4
- package/spec/components/responses.yaml +39 -0
- package/spec/components/schemas/gates.yaml +87 -0
- package/spec/components/schemas/keys.yaml +2 -2
- package/spec/components/schemas/killswitches.yaml +64 -0
- package/spec/components/schemas/ops.yaml +185 -0
- package/spec/openapi-sdk.yaml +86 -0
- package/spec/openapi.yaml +18 -0
- package/spec/paths/gates.yaml +282 -0
- package/spec/paths/killswitches.yaml +96 -0
- package/spec/paths/ops.yaml +179 -0
- package/dist/chunk-UV333BIK.js.map +0 -1
- package/dist/chunk-V4ZESAIF.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shipeasy/openapi",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.0",
|
|
4
4
|
"description": "Shipeasy admin OpenAPI 3.2 spec (hand-authored, single source of truth) + the generated TypeScript client, Zod schemas, and types. Consumed by @shipeasy/cli and @shipeasy/mcp.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -40,17 +40,19 @@
|
|
|
40
40
|
"default": "./src/codegen.ts"
|
|
41
41
|
},
|
|
42
42
|
"./openapi.yaml": "./openapi.yaml",
|
|
43
|
-
"./openapi.json": "./openapi.json"
|
|
43
|
+
"./openapi.json": "./openapi.json",
|
|
44
|
+
"./openapi-sdk.json": "./openapi-sdk.json"
|
|
44
45
|
},
|
|
45
46
|
"files": [
|
|
46
47
|
"dist/",
|
|
47
48
|
"openapi.yaml",
|
|
48
49
|
"openapi.json",
|
|
50
|
+
"openapi-sdk.json",
|
|
49
51
|
"spec/"
|
|
50
52
|
],
|
|
51
53
|
"scripts": {
|
|
52
|
-
"lint:spec": "redocly lint spec/openapi.yaml",
|
|
53
|
-
"bundle": "redocly bundle spec/openapi.yaml -o openapi.yaml && redocly bundle spec/openapi.yaml -o openapi.json",
|
|
54
|
+
"lint:spec": "redocly lint spec/openapi.yaml && redocly lint spec/openapi-sdk.yaml",
|
|
55
|
+
"bundle": "redocly bundle spec/openapi.yaml -o openapi.yaml && redocly bundle spec/openapi.yaml -o openapi.json && redocly bundle spec/openapi-sdk.yaml -o openapi-sdk.json",
|
|
54
56
|
"gen:sdk": "openapi-ts",
|
|
55
57
|
"gen": "pnpm bundle && pnpm gen:sdk",
|
|
56
58
|
"prebuild": "pnpm gen",
|
|
@@ -120,3 +120,42 @@ BadGateway:
|
|
|
120
120
|
summary: Downstream provider dispatch failed
|
|
121
121
|
value:
|
|
122
122
|
error: "GitHub API responded 403: resource not accessible by integration."
|
|
123
|
+
PayloadTooLarge:
|
|
124
|
+
description: The request body exceeded the intake's fixed size cap (16 KB).
|
|
125
|
+
content:
|
|
126
|
+
application/json:
|
|
127
|
+
schema:
|
|
128
|
+
$ref: ./errors.yaml#/Error
|
|
129
|
+
examples:
|
|
130
|
+
BAD_REQUEST:
|
|
131
|
+
summary: Body too large
|
|
132
|
+
value:
|
|
133
|
+
error: Body too large
|
|
134
|
+
code: BAD_REQUEST
|
|
135
|
+
TooManyRequests:
|
|
136
|
+
description: |-
|
|
137
|
+
The public intake is rate-limited per (project, client IP). Back off and retry;
|
|
138
|
+
the limit is a burst guard, not a quota, so a modest delay clears it.
|
|
139
|
+
content:
|
|
140
|
+
application/json:
|
|
141
|
+
schema:
|
|
142
|
+
$ref: ./errors.yaml#/Error
|
|
143
|
+
examples:
|
|
144
|
+
BAD_REQUEST:
|
|
145
|
+
summary: Rate limited
|
|
146
|
+
value:
|
|
147
|
+
error: Too many requests
|
|
148
|
+
code: BAD_REQUEST
|
|
149
|
+
ServiceUnavailable:
|
|
150
|
+
description: |-
|
|
151
|
+
A backing store the endpoint needs is not bound in this environment. Transient
|
|
152
|
+
and infrastructural — no stable `ErrorCode` is assigned, so `code` is omitted.
|
|
153
|
+
content:
|
|
154
|
+
application/json:
|
|
155
|
+
schema:
|
|
156
|
+
$ref: ./errors.yaml#/Error
|
|
157
|
+
examples:
|
|
158
|
+
UNAVAILABLE:
|
|
159
|
+
summary: Storage unbound
|
|
160
|
+
value:
|
|
161
|
+
error: Unavailable
|
|
@@ -980,3 +980,90 @@ ListGateActivityResponse:
|
|
|
980
980
|
- createdAt
|
|
981
981
|
additionalProperties: false
|
|
982
982
|
description: Recent audit rows for one gate, newest first.
|
|
983
|
+
GateWhitelistAttr:
|
|
984
|
+
type: string
|
|
985
|
+
enum:
|
|
986
|
+
- email
|
|
987
|
+
- user_id
|
|
988
|
+
description: Which identity attribute the whitelist matches on. `email` compares the caller's `email`; `user_id` compares the caller's `userID`. A whitelist matches on exactly one of the two at a time.
|
|
989
|
+
GateWhitelist:
|
|
990
|
+
type: object
|
|
991
|
+
properties:
|
|
992
|
+
id:
|
|
993
|
+
type: string
|
|
994
|
+
description: Resolved gate id.
|
|
995
|
+
name:
|
|
996
|
+
type: string
|
|
997
|
+
description: The gate's stable `name`.
|
|
998
|
+
attr:
|
|
999
|
+
$ref: "#/GateWhitelistAttr"
|
|
1000
|
+
entries:
|
|
1001
|
+
type: array
|
|
1002
|
+
items:
|
|
1003
|
+
type: string
|
|
1004
|
+
minLength: 1
|
|
1005
|
+
description: The whitelisted identities, in the order they are stored. Deduplicated case-sensitively; an empty array means the gate has no whitelist.
|
|
1006
|
+
required:
|
|
1007
|
+
- id
|
|
1008
|
+
- name
|
|
1009
|
+
- attr
|
|
1010
|
+
- entries
|
|
1011
|
+
additionalProperties: false
|
|
1012
|
+
description: A gate's whitelist — the always-first allowlist that admits the listed identities before any targeting rule or rollout runs. Backed by the pinned `whitelist` entry at the head of the gate's `stack`, so it is the same list the dashboard's Whitelist block edits.
|
|
1013
|
+
SetGateWhitelistRequest:
|
|
1014
|
+
type: object
|
|
1015
|
+
properties:
|
|
1016
|
+
attr:
|
|
1017
|
+
anyOf:
|
|
1018
|
+
- $ref: "#/GateWhitelistAttr"
|
|
1019
|
+
description: Identity attribute to match on. Defaults to the whitelist's current attribute, or `email` when the gate has no whitelist yet.
|
|
1020
|
+
entries:
|
|
1021
|
+
type: array
|
|
1022
|
+
items:
|
|
1023
|
+
type: string
|
|
1024
|
+
minLength: 1
|
|
1025
|
+
description: The complete whitelist after the call. Pass `[]` to remove the whitelist from the gate entirely.
|
|
1026
|
+
required:
|
|
1027
|
+
- entries
|
|
1028
|
+
additionalProperties: false
|
|
1029
|
+
description: Body for `PUT /api/admin/gates/{id}/whitelist`. Replaces the whole list — idempotent, and the only call that can switch `attr` or clear the whitelist.
|
|
1030
|
+
AddToGateWhitelistRequest:
|
|
1031
|
+
type: object
|
|
1032
|
+
properties:
|
|
1033
|
+
attr:
|
|
1034
|
+
anyOf:
|
|
1035
|
+
- $ref: "#/GateWhitelistAttr"
|
|
1036
|
+
description: Identity attribute to match on. Only honoured when the gate has no whitelist yet (this call creates it); passing an attribute that disagrees with an existing whitelist is a 409 rather than a silent re-key of the entries already there.
|
|
1037
|
+
entries:
|
|
1038
|
+
type: array
|
|
1039
|
+
minItems: 1
|
|
1040
|
+
items:
|
|
1041
|
+
type: string
|
|
1042
|
+
minLength: 1
|
|
1043
|
+
description: Identities to admit. Already-listed entries are skipped, so the call is idempotent.
|
|
1044
|
+
required:
|
|
1045
|
+
- entries
|
|
1046
|
+
additionalProperties: false
|
|
1047
|
+
description: Body for `POST /api/admin/gates/{id}/whitelist`. Adds entries to the whitelist, creating it if the gate doesn't have one.
|
|
1048
|
+
RemoveFromGateWhitelistRequest:
|
|
1049
|
+
type: object
|
|
1050
|
+
properties:
|
|
1051
|
+
entries:
|
|
1052
|
+
type: array
|
|
1053
|
+
minItems: 1
|
|
1054
|
+
items:
|
|
1055
|
+
type: string
|
|
1056
|
+
minLength: 1
|
|
1057
|
+
description: Identities to stop admitting. Entries that aren't listed are skipped, so the call is idempotent.
|
|
1058
|
+
required:
|
|
1059
|
+
- entries
|
|
1060
|
+
additionalProperties: false
|
|
1061
|
+
description: "Body for `DELETE /api/admin/gates/{id}/whitelist`. Removes entries from the whitelist. Removing the last entry leaves an empty whitelist in place — use `PUT` with `entries: []` to drop the block itself."
|
|
1062
|
+
GetGateWhitelistResponse:
|
|
1063
|
+
$ref: "#/GateWhitelist"
|
|
1064
|
+
SetGateWhitelistResponse:
|
|
1065
|
+
$ref: "#/GateWhitelist"
|
|
1066
|
+
AddToGateWhitelistResponse:
|
|
1067
|
+
$ref: "#/GateWhitelist"
|
|
1068
|
+
RemoveFromGateWhitelistResponse:
|
|
1069
|
+
$ref: "#/GateWhitelist"
|
|
@@ -102,8 +102,8 @@ CreateKeyRequest:
|
|
|
102
102
|
name:
|
|
103
103
|
type: string
|
|
104
104
|
minLength: 1
|
|
105
|
-
maxLength:
|
|
106
|
-
description: Optional human label. Programmatic (API) mints that omit it get an auto-generated descriptive name; dashboard mints may leave it blank.
|
|
105
|
+
maxLength: 160
|
|
106
|
+
description: "Optional human label. Programmatic (API) mints that omit it get an auto-generated descriptive name; dashboard mints may leave it blank. The cap is generous because provenance labels are composed, not typed — `shipeasy setup` mints keys named with the stack, package, date, and operator that produced them, which does not fit in a one-line-input budget."
|
|
107
107
|
scopes:
|
|
108
108
|
type: array
|
|
109
109
|
items:
|
|
@@ -343,3 +343,67 @@ SetKillswitchValueResponse:
|
|
|
343
343
|
- version
|
|
344
344
|
- published
|
|
345
345
|
additionalProperties: false
|
|
346
|
+
ToggleKillswitchRequest:
|
|
347
|
+
type: object
|
|
348
|
+
properties:
|
|
349
|
+
switchKey:
|
|
350
|
+
anyOf:
|
|
351
|
+
- type: string
|
|
352
|
+
maxLength: 64
|
|
353
|
+
pattern: ^[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?$
|
|
354
|
+
- type: "null"
|
|
355
|
+
description: Which target to flip. Omit (or `null`) to flip the killswitch's own flat `value`; name a switch key to flip that nested sub-switch instead, creating the entry if it doesn't exist yet.
|
|
356
|
+
value:
|
|
357
|
+
anyOf:
|
|
358
|
+
- type: boolean
|
|
359
|
+
- type: "null"
|
|
360
|
+
description: The value to publish. Omit (or `null`) to flip whatever is stored now — read-modify-write in one call. Pass an explicit `true`/`false` to make the call idempotent, so a retry can't undo the first attempt.
|
|
361
|
+
env:
|
|
362
|
+
anyOf:
|
|
363
|
+
- $ref: ./common.yaml#/Env
|
|
364
|
+
description: Environment to publish on. Defaults to `prod` — the environment an incident response means when it says "kill it".
|
|
365
|
+
additionalProperties: false
|
|
366
|
+
description: |-
|
|
367
|
+
Body for `POST /api/admin/killswitches/{id}/toggle`. Every field is optional, so the four useful calls read as one method with a widening argument list:
|
|
368
|
+
|
|
369
|
+
- `{}` — flip the flat value on prod.
|
|
370
|
+
- `{ "switchKey": "eu_region" }` — flip that sub-switch on prod.
|
|
371
|
+
- `{ "switchKey": "eu_region", "value": true }` — set that sub-switch on prod, idempotently.
|
|
372
|
+
- `{ "switchKey": "eu_region", "value": true, "env": "staging" }` — the same, on a chosen env.
|
|
373
|
+
ToggleKillswitchResponse:
|
|
374
|
+
type: object
|
|
375
|
+
properties:
|
|
376
|
+
id:
|
|
377
|
+
type: string
|
|
378
|
+
description: Resolved killswitch id.
|
|
379
|
+
env:
|
|
380
|
+
$ref: ./common.yaml#/Env
|
|
381
|
+
switchKey:
|
|
382
|
+
anyOf:
|
|
383
|
+
- type: string
|
|
384
|
+
maxLength: 64
|
|
385
|
+
pattern: ^[a-z0-9](?:[a-z0-9_-]*[a-z0-9])?$
|
|
386
|
+
- type: "null"
|
|
387
|
+
description: The sub-switch that was flipped, or `null` when the flat `value` was.
|
|
388
|
+
previous:
|
|
389
|
+
type: boolean
|
|
390
|
+
description: The value that was in effect before this call. Equal to `value` when an idempotent `value` matched what was already stored.
|
|
391
|
+
value:
|
|
392
|
+
type: boolean
|
|
393
|
+
description: The value now in effect on `env`.
|
|
394
|
+
version:
|
|
395
|
+
type: integer
|
|
396
|
+
minimum: -9007199254740991
|
|
397
|
+
maximum: 9007199254740991
|
|
398
|
+
description: Newly published version on `env`.
|
|
399
|
+
published:
|
|
400
|
+
$ref: ./common.yaml#/KillswitchValue
|
|
401
|
+
required:
|
|
402
|
+
- id
|
|
403
|
+
- env
|
|
404
|
+
- switchKey
|
|
405
|
+
- previous
|
|
406
|
+
- value
|
|
407
|
+
- version
|
|
408
|
+
- published
|
|
409
|
+
additionalProperties: false
|
|
@@ -1063,6 +1063,175 @@ CreateFeatureRequestRequest:
|
|
|
1063
1063
|
- title
|
|
1064
1064
|
additionalProperties: false
|
|
1065
1065
|
description: "Feature-request-kind fields for `POST /api/admin/ops` (sent with `type: feature_request`)."
|
|
1066
|
+
CreatePublicBugRequest:
|
|
1067
|
+
type: object
|
|
1068
|
+
properties:
|
|
1069
|
+
title:
|
|
1070
|
+
type: string
|
|
1071
|
+
minLength: 1
|
|
1072
|
+
maxLength: 200
|
|
1073
|
+
pattern: '^\S(.*\S)?$'
|
|
1074
|
+
description: One-line bug title (no leading/trailing whitespace).
|
|
1075
|
+
stepsToReproduce:
|
|
1076
|
+
type: string
|
|
1077
|
+
maxLength: 8000
|
|
1078
|
+
default: ""
|
|
1079
|
+
description: How to reproduce the bug.
|
|
1080
|
+
actualResult:
|
|
1081
|
+
type: string
|
|
1082
|
+
maxLength: 8000
|
|
1083
|
+
default: ""
|
|
1084
|
+
description: What actually happened.
|
|
1085
|
+
expectedResult:
|
|
1086
|
+
type: string
|
|
1087
|
+
maxLength: 8000
|
|
1088
|
+
default: ""
|
|
1089
|
+
description: What was expected instead.
|
|
1090
|
+
priority:
|
|
1091
|
+
anyOf:
|
|
1092
|
+
- $ref: "#/OpsItemPriority"
|
|
1093
|
+
- type: "null"
|
|
1094
|
+
description: Initial triage priority, or `null`.
|
|
1095
|
+
status:
|
|
1096
|
+
anyOf:
|
|
1097
|
+
- $ref: "#/OpsItemStatus"
|
|
1098
|
+
description: Initial lifecycle status; defaults to `open` when omitted.
|
|
1099
|
+
assigneeId:
|
|
1100
|
+
anyOf:
|
|
1101
|
+
- type: string
|
|
1102
|
+
- type: "null"
|
|
1103
|
+
description: The `users.id` of the person to assign as owner at creation, or `null`.
|
|
1104
|
+
subscribers:
|
|
1105
|
+
type: array
|
|
1106
|
+
items:
|
|
1107
|
+
type: string
|
|
1108
|
+
format: email
|
|
1109
|
+
default: []
|
|
1110
|
+
description: Emails of teammates to subscribe to this item's Slack pings at creation.
|
|
1111
|
+
tags:
|
|
1112
|
+
type: array
|
|
1113
|
+
items:
|
|
1114
|
+
type: string
|
|
1115
|
+
default: []
|
|
1116
|
+
description: Tag names to attach at creation (get-or-created by name, deduped case-insensitively).
|
|
1117
|
+
reporterEmail:
|
|
1118
|
+
anyOf:
|
|
1119
|
+
- type: string
|
|
1120
|
+
format: email
|
|
1121
|
+
- type: "null"
|
|
1122
|
+
description: Email of the reporter, or `null`.
|
|
1123
|
+
pageUrl:
|
|
1124
|
+
anyOf:
|
|
1125
|
+
- type: string
|
|
1126
|
+
format: uri
|
|
1127
|
+
- type: "null"
|
|
1128
|
+
description: URL of the page the bug relates to, or `null`.
|
|
1129
|
+
userAgent:
|
|
1130
|
+
anyOf:
|
|
1131
|
+
- type: string
|
|
1132
|
+
maxLength: 500
|
|
1133
|
+
- type: "null"
|
|
1134
|
+
description: Reporter's user-agent string, or `null`.
|
|
1135
|
+
viewport:
|
|
1136
|
+
anyOf:
|
|
1137
|
+
- type: string
|
|
1138
|
+
maxLength: 40
|
|
1139
|
+
- type: "null"
|
|
1140
|
+
description: Reporter's viewport (e.g. `1280x720`), or `null`.
|
|
1141
|
+
context:
|
|
1142
|
+
anyOf:
|
|
1143
|
+
- type: object
|
|
1144
|
+
additionalProperties: {}
|
|
1145
|
+
- type: "null"
|
|
1146
|
+
description: Arbitrary capture context, or `null`.
|
|
1147
|
+
notify:
|
|
1148
|
+
anyOf:
|
|
1149
|
+
- $ref: ./common.yaml#/NotificationTarget
|
|
1150
|
+
- type: "null"
|
|
1151
|
+
description: Where this bug's completion notification lands.
|
|
1152
|
+
required:
|
|
1153
|
+
- title
|
|
1154
|
+
additionalProperties: false
|
|
1155
|
+
description: "Body for `POST /ops/bug`. The same bug fields as `CreateBugRequest`, minus the `type` discriminator — the path already says what is being filed."
|
|
1156
|
+
CreatePublicFeatureRequestRequest:
|
|
1157
|
+
type: object
|
|
1158
|
+
properties:
|
|
1159
|
+
title:
|
|
1160
|
+
type: string
|
|
1161
|
+
minLength: 1
|
|
1162
|
+
maxLength: 200
|
|
1163
|
+
pattern: '^\S(.*\S)?$'
|
|
1164
|
+
description: One-line feature-request title (no leading/trailing whitespace).
|
|
1165
|
+
description:
|
|
1166
|
+
type: string
|
|
1167
|
+
maxLength: 8000
|
|
1168
|
+
default: ""
|
|
1169
|
+
description: What the feature is.
|
|
1170
|
+
useCase:
|
|
1171
|
+
type: string
|
|
1172
|
+
maxLength: 8000
|
|
1173
|
+
default: ""
|
|
1174
|
+
description: Why it's needed / the use case.
|
|
1175
|
+
priority:
|
|
1176
|
+
anyOf:
|
|
1177
|
+
- $ref: "#/OpsItemPriority"
|
|
1178
|
+
- type: "null"
|
|
1179
|
+
description: Initial triage priority, or `null`.
|
|
1180
|
+
status:
|
|
1181
|
+
anyOf:
|
|
1182
|
+
- $ref: "#/OpsItemStatus"
|
|
1183
|
+
description: Initial lifecycle status; defaults to `open` when omitted.
|
|
1184
|
+
assigneeId:
|
|
1185
|
+
anyOf:
|
|
1186
|
+
- type: string
|
|
1187
|
+
- type: "null"
|
|
1188
|
+
description: The `users.id` of the person to assign as owner at creation, or `null`.
|
|
1189
|
+
subscribers:
|
|
1190
|
+
type: array
|
|
1191
|
+
items:
|
|
1192
|
+
type: string
|
|
1193
|
+
format: email
|
|
1194
|
+
default: []
|
|
1195
|
+
description: Emails of teammates to subscribe to this item's Slack pings at creation.
|
|
1196
|
+
tags:
|
|
1197
|
+
type: array
|
|
1198
|
+
items:
|
|
1199
|
+
type: string
|
|
1200
|
+
default: []
|
|
1201
|
+
description: Tag names to attach at creation (get-or-created by name, deduped case-insensitively).
|
|
1202
|
+
reporterEmail:
|
|
1203
|
+
anyOf:
|
|
1204
|
+
- type: string
|
|
1205
|
+
format: email
|
|
1206
|
+
- type: "null"
|
|
1207
|
+
description: Email of the reporter, or `null`.
|
|
1208
|
+
pageUrl:
|
|
1209
|
+
anyOf:
|
|
1210
|
+
- type: string
|
|
1211
|
+
format: uri
|
|
1212
|
+
- type: "null"
|
|
1213
|
+
description: URL of the page the request relates to, or `null`.
|
|
1214
|
+
userAgent:
|
|
1215
|
+
anyOf:
|
|
1216
|
+
- type: string
|
|
1217
|
+
maxLength: 500
|
|
1218
|
+
- type: "null"
|
|
1219
|
+
description: Reporter's user-agent string, or `null`.
|
|
1220
|
+
context:
|
|
1221
|
+
anyOf:
|
|
1222
|
+
- type: object
|
|
1223
|
+
additionalProperties: {}
|
|
1224
|
+
- type: "null"
|
|
1225
|
+
description: Arbitrary capture context, or `null`.
|
|
1226
|
+
notify:
|
|
1227
|
+
anyOf:
|
|
1228
|
+
- $ref: ./common.yaml#/NotificationTarget
|
|
1229
|
+
- type: "null"
|
|
1230
|
+
description: Where this request's completion notification lands.
|
|
1231
|
+
required:
|
|
1232
|
+
- title
|
|
1233
|
+
additionalProperties: false
|
|
1234
|
+
description: "Body for `POST /ops/feature-request`. The same feature-request fields as `CreateFeatureRequestRequest`, minus the `type` discriminator — the path already says what is being filed."
|
|
1066
1235
|
CreateOpsItemRequest:
|
|
1067
1236
|
oneOf:
|
|
1068
1237
|
- $ref: "#/CreateBugRequest"
|
|
@@ -1073,6 +1242,22 @@ CreateOpsItemRequest:
|
|
|
1073
1242
|
bug: "#/CreateBugRequest"
|
|
1074
1243
|
feature_request: "#/CreateFeatureRequestRequest"
|
|
1075
1244
|
description: "Body for `POST /api/admin/ops`. A discriminated union on `type`: `bug` carries the bug fields, `feature_request` the feature fields. Only these two user-fileable types are accepted — `error`, `alert`, and `measure_plan` tickets are auto-filed by the platform and cannot be created over the API."
|
|
1245
|
+
CreatePublicTicketResponse:
|
|
1246
|
+
type: object
|
|
1247
|
+
properties:
|
|
1248
|
+
id:
|
|
1249
|
+
type: string
|
|
1250
|
+
description: The new item's id. Absent on a deduped response — nothing was created, so there is no new id to report.
|
|
1251
|
+
number:
|
|
1252
|
+
type: integer
|
|
1253
|
+
description: Per-project item number. On a deduped response this is the number of the OPEN ticket already tracking the report, which is what a caller should show the user either way.
|
|
1254
|
+
deduped:
|
|
1255
|
+
type: boolean
|
|
1256
|
+
description: "`true` when an open ticket already tracked this report and nothing was filed (HTTP 200). Absent on a fresh file (HTTP 201)."
|
|
1257
|
+
required:
|
|
1258
|
+
- number
|
|
1259
|
+
additionalProperties: false
|
|
1260
|
+
description: "Response for the public ticket intake. A fresh file returns `201` with `id` + `number`; a repeat of a report already tracked by an open ticket returns `200` with that ticket's `number` and `deduped: true`."
|
|
1076
1261
|
CreateOpsItemResponse:
|
|
1077
1262
|
type: object
|
|
1078
1263
|
properties:
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# The SERVER-SDK surface of the admin API — a second root over the same
|
|
2
|
+
# `paths/` and `components/schemas/` tree that `openapi.yaml` bundles.
|
|
3
|
+
#
|
|
4
|
+
# WHY A SECOND ROOT. The CLI and MCP consume the FULL spec (127 operations);
|
|
5
|
+
# the published server SDKs ship a deliberately tiny opt-in `AdminClient`, and
|
|
6
|
+
# vendoring the whole contract into eight repos bloated each generated client
|
|
7
|
+
# into the megabytes for operations nobody called. This file IS that smaller
|
|
8
|
+
# contract, declared rather than derived: `paths` below lists exactly the
|
|
9
|
+
# operations the SDKs expose, and `redocly bundle` pulls in only the schemas
|
|
10
|
+
# those operations transitively reference.
|
|
11
|
+
#
|
|
12
|
+
# WHY IT'S EXACT. Every path listed here is a dedicated, single-purpose route,
|
|
13
|
+
# so a path item never drags in a method the SDK shouldn't have. That is the
|
|
14
|
+
# reason the SDK-facing endpoints were built as their own paths instead of
|
|
15
|
+
# extra query/discriminator modes on the generic CRUD routes.
|
|
16
|
+
#
|
|
17
|
+
# ADDING AN OPERATION is one `$ref` here — then `pnpm gen` in this package and
|
|
18
|
+
# `pnpm sdk:spec:regen` in the monorepo, and commit the regenerated client
|
|
19
|
+
# inside each SDK submodule. A monorepo pre-commit hook blocks any commit that
|
|
20
|
+
# changes this contract while a vendored copy is stale.
|
|
21
|
+
#
|
|
22
|
+
# DELIBERATELY ABSENT, so nobody re-adds them by accident: everything else.
|
|
23
|
+
# Listing, generic CRUD, key minting, projects, connectors, i18n, errors,
|
|
24
|
+
# alerts, metrics, events, experiments, universes, templates, attributes, and
|
|
25
|
+
# the AI-agent working seam (ack / link-pr / investigations) all stay reachable
|
|
26
|
+
# through the CLI and MCP, which read `openapi.yaml`.
|
|
27
|
+
openapi: 3.2.0
|
|
28
|
+
info:
|
|
29
|
+
title: Shipeasy Admin API (server-SDK surface)
|
|
30
|
+
version: 2.0.0
|
|
31
|
+
description: |-
|
|
32
|
+
The slice of the Shipeasy admin API that the published server SDKs expose as their optional `AdminClient`. Authenticate with an admin SDK key (`Authorization: Bearer sdk_admin_…`) and scope every request to a project via the `X-Project-Id` header.
|
|
33
|
+
|
|
34
|
+
Three capabilities, nothing else:
|
|
35
|
+
|
|
36
|
+
- **File a public ticket** — a bug or a feature request onto the project's ops queue.
|
|
37
|
+
- **Toggle a kill switch** — flip the switch itself, or one of its named sub-switches, on one environment.
|
|
38
|
+
- **Manage a flag's whitelist** — read, replace, add to, or remove from the allowlist that admits specific identities ahead of every targeting rule.
|
|
39
|
+
|
|
40
|
+
Everything else in the admin API is reachable through the Shipeasy CLI and MCP server, which speak the complete contract (`openapi.yaml`).
|
|
41
|
+
contact:
|
|
42
|
+
name: Shipeasy
|
|
43
|
+
url: https://shipeasy.ai
|
|
44
|
+
license:
|
|
45
|
+
name: Proprietary
|
|
46
|
+
identifier: LicenseRef-Proprietary
|
|
47
|
+
servers:
|
|
48
|
+
- url: https://shipeasy.ai
|
|
49
|
+
description: Production
|
|
50
|
+
- url: http://localhost:3000
|
|
51
|
+
description: Local Next.js dev server
|
|
52
|
+
security:
|
|
53
|
+
- bearerSdkKey: []
|
|
54
|
+
tags:
|
|
55
|
+
- name: Flags
|
|
56
|
+
description: A feature gate's whitelist — the always-first allowlist that admits named identities before any targeting rule or percentage rollout runs.
|
|
57
|
+
- name: Killswitch
|
|
58
|
+
description: Per-env boolean overrides for incident response. `toggle` reads the current value and publishes its opposite in one call, on the flat value or on one named sub-switch.
|
|
59
|
+
- name: Ops
|
|
60
|
+
description: The public ticket queue — filing a bug or a feature request, which also fires the project's GitHub/Slack connectors.
|
|
61
|
+
paths:
|
|
62
|
+
/ops/bug:
|
|
63
|
+
$ref: ./paths/ops.yaml#/~1ops~1bug
|
|
64
|
+
/ops/feature-request:
|
|
65
|
+
$ref: ./paths/ops.yaml#/~1ops~1feature-request
|
|
66
|
+
/api/admin/killswitches/{id}/toggle:
|
|
67
|
+
$ref: ./paths/killswitches.yaml#/~1api~1admin~1killswitches~1{id}~1toggle
|
|
68
|
+
/api/admin/gates/{id}/whitelist:
|
|
69
|
+
$ref: ./paths/gates.yaml#/~1api~1admin~1gates~1{id}~1whitelist
|
|
70
|
+
components:
|
|
71
|
+
securitySchemes:
|
|
72
|
+
bearerSdkKey:
|
|
73
|
+
type: http
|
|
74
|
+
scheme: bearer
|
|
75
|
+
bearerFormat: sdk_admin_*
|
|
76
|
+
description: 'Pass an admin SDK key as `Authorization: Bearer sdk_admin_…`. Mint via `POST /api/admin/keys` with `type: "admin"`.'
|
|
77
|
+
clientSdkKey:
|
|
78
|
+
type: apiKey
|
|
79
|
+
in: header
|
|
80
|
+
name: X-SDK-Key
|
|
81
|
+
description: |-
|
|
82
|
+
Pass a **client** SDK key as `X-SDK-Key: sdk_client_…`. Used only by the public ticket intake (`POST /ops/bug`, `POST /ops/feature-request`), which is served by the edge worker rather than the admin API.
|
|
83
|
+
|
|
84
|
+
Client keys are designed to be embedded in shipped code — a CLI, an installer, a browser bundle — so presenting one here is safe by construction: it can do nothing but file a rate-limited ticket into its own project, in a state a human must approve. It carries no read access and cannot reach any other operation in this contract.
|
|
85
|
+
|
|
86
|
+
The key must additionally carry the `tickets:public_create` scope, and its project must have public ticket creation enabled.
|
package/spec/openapi.yaml
CHANGED
|
@@ -282,6 +282,8 @@ paths:
|
|
|
282
282
|
$ref: ./paths/gates.yaml#/~1api~1admin~1gates~1{id}~1disable
|
|
283
283
|
/api/admin/gates/{id}/activity:
|
|
284
284
|
$ref: ./paths/gates.yaml#/~1api~1admin~1gates~1{id}~1activity
|
|
285
|
+
/api/admin/gates/{id}/whitelist:
|
|
286
|
+
$ref: ./paths/gates.yaml#/~1api~1admin~1gates~1{id}~1whitelist
|
|
285
287
|
/api/admin/experiments:
|
|
286
288
|
$ref: ./paths/experiments.yaml#/~1api~1admin~1experiments
|
|
287
289
|
/api/admin/experiments/{id}:
|
|
@@ -322,6 +324,8 @@ paths:
|
|
|
322
324
|
$ref: ./paths/killswitches.yaml#/~1api~1admin~1killswitches~1{id}~1switch
|
|
323
325
|
/api/admin/killswitches/{id}/value:
|
|
324
326
|
$ref: ./paths/killswitches.yaml#/~1api~1admin~1killswitches~1{id}~1value
|
|
327
|
+
/api/admin/killswitches/{id}/toggle:
|
|
328
|
+
$ref: ./paths/killswitches.yaml#/~1api~1admin~1killswitches~1{id}~1toggle
|
|
325
329
|
/api/admin/universes:
|
|
326
330
|
$ref: ./paths/universes.yaml#/~1api~1admin~1universes
|
|
327
331
|
/api/admin/universes/{id}:
|
|
@@ -352,6 +356,10 @@ paths:
|
|
|
352
356
|
$ref: ./paths/events.yaml#/~1api~1admin~1events~1{id}~1approve
|
|
353
357
|
/api/admin/ops:
|
|
354
358
|
$ref: ./paths/ops.yaml#/~1api~1admin~1ops
|
|
359
|
+
/ops/bug:
|
|
360
|
+
$ref: ./paths/ops.yaml#/~1ops~1bug
|
|
361
|
+
/ops/feature-request:
|
|
362
|
+
$ref: ./paths/ops.yaml#/~1ops~1feature-request
|
|
355
363
|
/api/admin/ops/{handle}:
|
|
356
364
|
$ref: ./paths/ops.yaml#/~1api~1admin~1ops~1{handle}
|
|
357
365
|
/api/admin/ops/{handle}/link-pr:
|
|
@@ -437,3 +445,13 @@ components:
|
|
|
437
445
|
scheme: bearer
|
|
438
446
|
bearerFormat: sdk_admin_*
|
|
439
447
|
description: 'Pass an admin SDK key as `Authorization: Bearer sdk_admin_…`. Mint via `POST /api/admin/keys` with `type: "admin"`.'
|
|
448
|
+
clientSdkKey:
|
|
449
|
+
type: apiKey
|
|
450
|
+
in: header
|
|
451
|
+
name: X-SDK-Key
|
|
452
|
+
description: |-
|
|
453
|
+
Pass a **client** SDK key as `X-SDK-Key: sdk_client_…`. Used only by the public ticket intake (`POST /ops/bug`, `POST /ops/feature-request`), which is served by the edge worker rather than the admin API.
|
|
454
|
+
|
|
455
|
+
Client keys are designed to be embedded in shipped code — a CLI, an installer, a browser bundle — so presenting one here is safe by construction: it can do nothing but file a rate-limited ticket into its own project, in a state a human must approve. It carries no read access and cannot reach any other operation in this contract.
|
|
456
|
+
|
|
457
|
+
The key must additionally carry the `tickets:public_create` scope, and its project must have public ticket creation enabled.
|