@sema-agent/sdk 0.0.126 → 0.0.127

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/sdk",
3
- "version": "0.0.126",
3
+ "version": "0.0.127",
4
4
  "description": "Typed, zero-runtime-dependency SDK for the Sema agent fleet usage plane. The shared substrate for all doors (CC/Codex MCP façade + web). Server-side only — tokens never enter the browser.",
5
5
  "type": "module",
6
6
  "license": "BUSL-1.1",
@@ -27,21 +27,23 @@
27
27
  "./registry": {
28
28
  "types": "./dist/registry/index.d.ts",
29
29
  "import": "./dist/registry/index.js"
30
- }
30
+ },
31
+ "./registry-openapi.yaml": "./registry-openapi.yaml"
31
32
  },
32
33
  "files": [
33
34
  "dist",
34
35
  "openapi.yaml",
35
36
  "README.md",
36
- "LICENSE"
37
+ "LICENSE",
38
+ "registry-openapi.yaml"
37
39
  ],
38
40
  "engines": {
39
41
  "node": ">=20"
40
42
  },
41
43
  "scripts": {
42
44
  "build": "tsc -b",
43
- "clean": "rm -rf dist openapi.yaml LICENSE tsconfig.tsbuildinfo",
44
- "prepack": "node -e \"require('fs').copyFileSync('../../spec/openapi.yaml','openapi.yaml');require('fs').copyFileSync('../../LICENSE','LICENSE')\"",
45
+ "clean": "rm -rf dist openapi.yaml registry-openapi.yaml LICENSE tsconfig.tsbuildinfo",
46
+ "prepack": "node -e \"require('fs').copyFileSync('../../spec/openapi.yaml','openapi.yaml');require('fs').copyFileSync('../../spec/registry-openapi.yaml','registry-openapi.yaml');require('fs').copyFileSync('../../LICENSE','LICENSE')\"",
45
47
  "publish:npm": "npm publish --access public --@sema-agent:registry=https://registry.npmjs.org/ --\"//registry.npmjs.org/:_authToken=${NPM_TOKEN}\"",
46
48
  "prepublishOnly": "rm -f tsconfig.tsbuildinfo && npm run build"
47
49
  },
@@ -0,0 +1,250 @@
1
+ openapi: 3.0.3
2
+ info:
3
+ title: Sema Registry — the wire the SDK's `@sema-agent/sdk/registry` subpath consumes
4
+ version: "0.1.0"
5
+ description: >
6
+ ⚠️ AUTHORITY POSTURE (read first): the registry SERVER lives in the sema-registry repo (web AI);
7
+ the machine contract single-source is `@sema-agent/registry-core` (zod schemas + path constants —
8
+ AUTH_V1_PATHS/SCOPES_V1_PATHS etc.; the SDK keeps value-copies with same-source anchor tests).
9
+ THIS file documents the surface the SDK's registry subpath actually calls, so a consumer generating
10
+ a client from spec sees the full face — it mirrors, it does not rule. Backfilled 2026-07-28 from
11
+ the [1895] retirement map (cli's hand-rolled HTTP → SDK verbs, G17 R1-R4 + G22 era); guarded by
12
+ `registry-spec-path-gate.test.ts` (reflective: every /api path the SDK hits must be here, zero baseline).
13
+ USER-PLANE paths carry real request/response shapes; the ADMIN family (RegistryAdmin, user-JWT
14
+ role-gated — "admin" is a registry-side ROLE, not a separate credential) is documented loose
15
+ (operator tooling; shapes live in registry-core/zod and the registry server).
16
+ x-sdk-subpath: "@sema-agent/sdk/registry"
17
+ paths:
18
+ /api/healthz:
19
+ get:
20
+ operationId: registryHealthz
21
+ summary: Registry liveness probe (probeRegistryHealth).
22
+ responses:
23
+ '200': { description: 'Alive. Open shape.' }
24
+
25
+ # ── auth (device flow RFC 8628 + refresh rotation + scope switch) ─────────────────────────────
26
+ /api/v1/auth/device/code:
27
+ post:
28
+ operationId: registryDeviceCode
29
+ summary: Start the device-authorization flow (requestDeviceCode).
30
+ responses:
31
+ '200':
32
+ description: '{device_code, user_code, verification_uri(_complete)?, interval, expires_in}.'
33
+ content: { application/json: { schema: { type: object, additionalProperties: true } } }
34
+ /api/v1/auth/device/token:
35
+ post:
36
+ operationId: registryDeviceToken
37
+ summary: Poll the device flow (pollUntilApproved's leg).
38
+ description: >
39
+ OAuth device-token semantics: `authorization_pending` keeps the interval, `slow_down` adds +5s,
40
+ `access_denied`/`expired_token` are terminal (typed errors in the SDK). Success = the token pair.
41
+ responses:
42
+ '200': { description: '{access_token, refresh_token, …} on approval.', content: { application/json: { schema: { type: object, additionalProperties: true } } } }
43
+ '400': { description: 'OAuth error envelope {error: authorization_pending|slow_down|access_denied|expired_token|…}.' }
44
+ /api/v1/auth/device/approve:
45
+ post:
46
+ operationId: registryDeviceApprove
47
+ summary: Approve a pending device code (the signed-in side of the flow).
48
+ responses:
49
+ '200': { description: 'Approved.' }
50
+ /api/v1/auth/token/refresh:
51
+ post:
52
+ operationId: registryTokenRefresh
53
+ summary: Refresh-token rotation (STRICT single-use).
54
+ description: >
55
+ Rotation is single-use: the presented refresh_token is consumed; `invalid_grant` ⇒ the SDK
56
+ clears the token pair and surfaces a typed re-login error. The RegistryClient wraps this in a
57
+ single-flight 401-refresh-retry-once.
58
+ responses:
59
+ '200': { description: 'The NEW token pair (both tokens rotate).', content: { application/json: { schema: { type: object, additionalProperties: true } } } }
60
+ '400': { description: '`invalid_grant` = consumed/expired refresh token → re-login.' }
61
+ /api/v1/auth/logout:
62
+ post:
63
+ operationId: registryLogout
64
+ summary: Revoke the session (logoutRegistry).
65
+ responses:
66
+ '200': { description: 'Revoked (SDK clears the TokenProvider either way).' }
67
+ /api/v1/auth/scope:
68
+ post:
69
+ operationId: registryScopeSwitch
70
+ summary: Switch the active scope (switchScope).
71
+ description: >
72
+ 🔴 The response carries a NEW access_token but NO refresh_token (the refresh grant is untouched)
73
+ — the SDK PRESERVES the old refreshToken when persisting (copying the grant-shaped write here
74
+ would wipe it ⇒ next refresh = surprise logout; pinned by test).
75
+ responses:
76
+ '200': { description: '{access_token, scope, …} — NO refresh_token by design.', content: { application/json: { schema: { type: object, additionalProperties: true } } } }
77
+
78
+ # ── scopes / effective config (user plane) ────────────────────────────────────────────────────
79
+ /api/v1/scopes:
80
+ get:
81
+ operationId: registryScopesList
82
+ summary: List the caller's scopes (listScopes; `?counts=` optional).
83
+ responses:
84
+ '200': { description: 'ScopesListResponse (registry-core schema).', content: { application/json: { schema: { type: object, additionalProperties: true } } } }
85
+ post:
86
+ operationId: registryScopesCreate
87
+ summary: 'ADMIN: create a scope/space.'
88
+ responses: { '200': { description: Created. } }
89
+ /api/v1/effective:
90
+ get:
91
+ operationId: registryEffective
92
+ summary: The effective (merged) config for the caller (getEffective; ETag/304 engine).
93
+ description: >
94
+ Conditional-GET face: the SDK sends `If-None-Match` from the last ETag; 304 = unchanged (no
95
+ body). `?principal=` lets a service-credential caller read another principal's effective view.
96
+ Body is open-domain (domains are registry-defined; the consumer treats it as data).
97
+ parameters:
98
+ - { in: query, name: principal, required: false, schema: { type: string } }
99
+ - { in: header, name: If-None-Match, required: false, schema: { type: string } }
100
+ responses:
101
+ '200': { description: 'The effective view (ETag header rides).', content: { application/json: { schema: { type: object, additionalProperties: true } } } }
102
+ '304': { description: 'Unchanged since the presented ETag.' }
103
+
104
+ # ── me / per-user config (CAS on the updatedAt STRING axis) ───────────────────────────────────
105
+ /api/v1/me:
106
+ get:
107
+ operationId: registryMe
108
+ summary: Who am I (claims + role).
109
+ responses:
110
+ '200': { description: 'The caller profile.', content: { application/json: { schema: { type: object, additionalProperties: true } } } }
111
+ /api/v1/me/config/{domain}:
112
+ parameters:
113
+ - { in: path, name: domain, required: true, schema: { type: string } }
114
+ get:
115
+ operationId: registryMeConfigGet
116
+ summary: Read one config domain of the caller (getMeConfig).
117
+ responses:
118
+ '200': { description: '{value, updatedAt, …}.', content: { application/json: { schema: { type: object, additionalProperties: true } } } }
119
+ put:
120
+ operationId: registryMeConfigPut
121
+ summary: CAS-write one config domain (putMeConfig; baseUpdatedAt = the compare axis).
122
+ description: >
123
+ 🔴 CAS axis = the `updatedAt` STRING (verbatim echo), NOT a version number — the scopes/config
124
+ face below uses a NUMBER `version` axis; the two axes must never be mixed (pinned). 409 carries
125
+ `currentUpdatedAt` (typed ConfigCasConflictError; retryOn409 merge-callback retries ONCE).
126
+ responses:
127
+ '200': { description: Written. }
128
+ '409': { description: '{currentUpdatedAt} — concurrent write; refetch+merge+retry once.' }
129
+ /api/v1/users/{u}/config/{domain}:
130
+ parameters:
131
+ - { in: path, name: u, required: true, schema: { type: string } }
132
+ - { in: path, name: domain, required: true, schema: { type: string } }
133
+ get:
134
+ operationId: registryUserConfigGet
135
+ summary: 'Managed variant of me/config (--user; operator writes another user''s domain).'
136
+ responses: { '200': { description: 'Same shape as me/config.' } }
137
+ put:
138
+ operationId: registryUserConfigPut
139
+ summary: Managed CAS-write (same updatedAt axis + 409 semantics as me/config).
140
+ responses:
141
+ '200': { description: Written. }
142
+ '409': { description: '{currentUpdatedAt}.' }
143
+
144
+ # ── scope config draft / publish (CAS on the version NUMBER axis) ─────────────────────────────
145
+ /api/v1/scopes/{s}/config/{domain}:
146
+ parameters:
147
+ - { in: path, name: s, required: true, schema: { type: string } }
148
+ - { in: path, name: domain, required: true, schema: { type: string } }
149
+ get:
150
+ operationId: registryScopeConfigDraftGet
151
+ summary: Read a scope's config DRAFT (getScopeConfigDraft).
152
+ responses: { '200': { description: '{value, version, …}.' } }
153
+ put:
154
+ operationId: registryScopeConfigDraftPut
155
+ summary: CAS-write the draft (putScopeConfigDraft; version NUMBER axis — not updatedAt).
156
+ responses:
157
+ '200': { description: Written. }
158
+ '409': { description: 'Version conflict.' }
159
+ /api/v1/scopes/{s}/publish:
160
+ parameters:
161
+ - { in: path, name: s, required: true, schema: { type: string } }
162
+ post:
163
+ operationId: registryScopePublishRequest
164
+ summary: Request publish of the draft (requestPublish; goes through the approval gate).
165
+ responses: { '200': { description: 'Publish requested/pending.' } }
166
+
167
+ # ── feedback ──────────────────────────────────────────────────────────────────────────────────
168
+ /api/v1/feedback:
169
+ post:
170
+ operationId: registryFeedback
171
+ summary: Post product feedback (postFeedback; the privacy gate stays client-side).
172
+ responses: { '200': { description: Accepted. } }
173
+
174
+ # ── ADMIN family (RegistryAdmin; user JWT, registry-side role gate; loose shapes by design) ───
175
+ /api/v1/instance/features:
176
+ get: { operationId: adminInstanceFeatures, summary: 'ADMIN: instance feature flags.', responses: { '200': { description: OK } } }
177
+ /api/v1/users:
178
+ get: { operationId: adminUsersList, summary: 'ADMIN: list users.', responses: { '200': { description: OK } } }
179
+ post: { operationId: adminUsersCreate, summary: 'ADMIN: create user.', responses: { '200': { description: OK } } }
180
+ /api/v1/users/{u}:
181
+ parameters: [{ in: path, name: u, required: true, schema: { type: string } }]
182
+ get: { operationId: adminUsersShow, summary: 'ADMIN: show user.', responses: { '200': { description: OK } } }
183
+ delete: { operationId: adminUsersDelete, summary: 'ADMIN: delete user.', responses: { '200': { description: OK } } }
184
+ /api/v1/access-requests:
185
+ get: { operationId: adminAccessRequestsList, summary: 'ADMIN: list access requests (filters via query).', responses: { '200': { description: OK } } }
186
+ /api/v1/access-requests/{id}/decision:
187
+ parameters: [{ in: path, name: id, required: true, schema: { type: string } }]
188
+ post: { operationId: adminAccessRequestDecide, summary: 'ADMIN: decide an access request.', responses: { '200': { description: OK } } }
189
+ /api/v1/scopes/{id}:
190
+ parameters: [{ in: path, name: id, required: true, schema: { type: string } }]
191
+ patch: { operationId: adminSpacesPatch, summary: 'ADMIN: patch scope/space.', responses: { '200': { description: OK } } }
192
+ delete: { operationId: adminSpacesDelete, summary: 'ADMIN: delete scope/space.', responses: { '200': { description: OK } } }
193
+ /api/v1/scopes/{id}/members:
194
+ parameters: [{ in: path, name: id, required: true, schema: { type: string } }]
195
+ get: { operationId: adminSpaceMembersList, summary: 'ADMIN: list scope members.', responses: { '200': { description: OK } } }
196
+ put: { operationId: adminSpaceMembersPut, summary: 'ADMIN: upsert scope member.', responses: { '200': { description: OK } } }
197
+ delete: { operationId: adminSpaceMembersDelete, summary: 'ADMIN: remove scope member.', responses: { '200': { description: OK } } }
198
+ /api/v1/groups:
199
+ get: { operationId: adminGroupsList, summary: 'ADMIN: list groups.', responses: { '200': { description: OK } } }
200
+ post: { operationId: adminGroupsCreate, summary: 'ADMIN: create group.', responses: { '200': { description: OK } } }
201
+ /api/v1/groups/{id}:
202
+ parameters: [{ in: path, name: id, required: true, schema: { type: string } }]
203
+ get: { operationId: adminGroupsShow, summary: 'ADMIN: show group.', responses: { '200': { description: OK } } }
204
+ put: { operationId: adminGroupsRename, summary: 'ADMIN: rename group.', responses: { '200': { description: OK } } }
205
+ delete: { operationId: adminGroupsDelete, summary: 'ADMIN: delete group.', responses: { '200': { description: OK } } }
206
+ /api/v1/groups/{g}/members:
207
+ parameters: [{ in: path, name: g, required: true, schema: { type: string } }]
208
+ get: { operationId: adminGroupMembersList, summary: 'ADMIN: list group members.', responses: { '200': { description: OK } } }
209
+ put: { operationId: adminGroupMembersAdd, summary: 'ADMIN: add group member.', responses: { '200': { description: OK } } }
210
+ delete: { operationId: adminGroupMembersRemove, summary: 'ADMIN: remove group member.', responses: { '200': { description: OK } } }
211
+ /api/v1/audit:
212
+ get: { operationId: adminAuditList, summary: 'ADMIN: global audit log.', responses: { '200': { description: OK } } }
213
+ /api/v1/audit/{id}/diff:
214
+ parameters: [{ in: path, name: id, required: true, schema: { type: string } }]
215
+ get: { operationId: adminAuditDiff, summary: 'ADMIN: one audit entry''s diff.', responses: { '200': { description: OK } } }
216
+ /api/v1/scopes/{s}/audit:
217
+ parameters: [{ in: path, name: s, required: true, schema: { type: string } }]
218
+ get: { operationId: adminAuditListScoped, summary: 'ADMIN: scope-scoped audit log.', responses: { '200': { description: OK } } }
219
+ /api/v1/scopes/{s}/audit/{id}/diff:
220
+ parameters:
221
+ - { in: path, name: s, required: true, schema: { type: string } }
222
+ - { in: path, name: id, required: true, schema: { type: string } }
223
+ get: { operationId: adminAuditDiffScoped, summary: 'ADMIN: scope-scoped audit diff.', responses: { '200': { description: OK } } }
224
+ /api/v1/images:
225
+ get: { operationId: adminImagesList, summary: 'ADMIN: sandbox image profiles.', responses: { '200': { description: OK } } }
226
+ /api/v1/images/{profile}:
227
+ parameters: [{ in: path, name: profile, required: true, schema: { type: string } }]
228
+ get: { operationId: adminImagesShow, summary: 'ADMIN: one image profile.', responses: { '200': { description: OK } } }
229
+ /api/v1/secrets:
230
+ get: { operationId: adminSecretsList, summary: 'ADMIN: secrets metadata (values never returned).', responses: { '200': { description: OK } } }
231
+ /api/v1/secrets/refs:
232
+ get: { operationId: adminSecretsRefs, summary: 'ADMIN: secret reference names.', responses: { '200': { description: OK } } }
233
+ /api/v1/auth-providers:
234
+ get: { operationId: adminAuthProvidersList, summary: 'ADMIN: auth providers.', responses: { '200': { description: OK } } }
235
+ /api/v1/auth-providers/{id}:
236
+ parameters: [{ in: path, name: id, required: true, schema: { type: string } }]
237
+ get: { operationId: adminAuthProvidersShow, summary: 'ADMIN: one auth provider.', responses: { '200': { description: OK } } }
238
+ /api/v1/scopes/{s}/rollback:
239
+ parameters: [{ in: path, name: s, required: true, schema: { type: string } }]
240
+ post: { operationId: adminScopeRollback, summary: 'ADMIN: rollback a scope''s published config.', responses: { '200': { description: OK } } }
241
+ /api/v1/usage/me:
242
+ get: { operationId: adminUsageMe, summary: 'Usage of the caller.', responses: { '200': { description: OK } } }
243
+ /api/v1/usage/summary:
244
+ get: { operationId: adminUsageSummary, summary: 'ADMIN: usage summary.', responses: { '200': { description: OK } } }
245
+ /api/v1/scopes/{s}/publish/approve:
246
+ parameters: [{ in: path, name: s, required: true, schema: { type: string } }]
247
+ post: { operationId: adminPublishApprove, summary: 'ADMIN: approve a pending publish.', responses: { '200': { description: OK } } }
248
+ /api/v1/scopes/{s}/publish/pending:
249
+ parameters: [{ in: path, name: s, required: true, schema: { type: string } }]
250
+ delete: { operationId: adminPublishWithdraw, summary: 'ADMIN: withdraw a pending publish.', responses: { '200': { description: OK } } }