hngr 0.3.0 → 0.4.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/README.md +6 -3
- package/dist/{auth-CgBUQUQn.mjs → auth-ByGgd_Yn.mjs} +49 -11
- package/dist/{catalog-DbGLqQ8a.mjs → catalog-Bv304SXJ.mjs} +412 -20
- package/dist/{commands-BHXSZ04F.mjs → commands-CLqLwQHY.mjs} +1 -1
- package/dist/{http-2HWxLIOq.mjs → http-DgHX7Fux.mjs} +13 -7
- package/dist/index.mjs +117 -18
- package/dist/{mcp-b6bE3ZRt.mjs → mcp-DWF_ELDU.mjs} +26 -47
- package/dist/{pagination-BbKLKSw3.mjs → pagination-BXyFxGYA.mjs} +1 -1
- package/dist/{publish-EUqgdp0G.mjs → scope-Ch6wZCgE.mjs} +24 -3
- package/dist/{skills-XDbBCC5f.mjs → skills-CxMeJ5PF.mjs} +18 -34
- package/package.json +1 -1
|
@@ -28,7 +28,11 @@ function catalogFlagType(flag) {
|
|
|
28
28
|
* cannot decide lives here, so the ambiguity is reviewable in one place
|
|
29
29
|
* instead of hidden in the derivation algorithm.
|
|
30
30
|
*/
|
|
31
|
-
/**
|
|
31
|
+
/**
|
|
32
|
+
* Endpoints that never appear in the command tree. An entry excludes that path
|
|
33
|
+
* and everything below it, so a subtree needs one line rather than one line
|
|
34
|
+
* per operation.
|
|
35
|
+
*/
|
|
32
36
|
const EXCLUDED_PATHS = [
|
|
33
37
|
"/api/v1/login",
|
|
34
38
|
"/api/v1/logout",
|
|
@@ -45,7 +49,8 @@ const EXCLUDED_PATHS = [
|
|
|
45
49
|
"/api/v1/auth/device/token",
|
|
46
50
|
"/api/v1/auth/device/lookup",
|
|
47
51
|
"/api/v1/auth/device/approve",
|
|
48
|
-
"/api/v1/auth/device/deny"
|
|
52
|
+
"/api/v1/auth/device/deny",
|
|
53
|
+
"/api/v1/oauth"
|
|
49
54
|
];
|
|
50
55
|
/**
|
|
51
56
|
* Single operations whose path still generates other commands. Category
|
|
@@ -150,11 +155,12 @@ const METHOD_KEY = {
|
|
|
150
155
|
*/
|
|
151
156
|
function deriveCommands(doc) {
|
|
152
157
|
const excluded = new Set(EXCLUDED_PATHS);
|
|
158
|
+
const excludedSubtrees = EXCLUDED_PATHS.map((path) => `${path}/`);
|
|
153
159
|
const excludedOperations = new Set(EXCLUDED_OPERATIONS.map((operation) => `${operation.method} ${operation.path}`));
|
|
154
160
|
const overrides = new Map(COMMAND_OVERRIDES.map((item) => [overrideKey(item), item]));
|
|
155
161
|
const commands = [];
|
|
156
162
|
for (const [path, pathItem] of Object.entries(doc.paths)) {
|
|
157
|
-
if (excluded.has(path)) continue;
|
|
163
|
+
if (excluded.has(path) || excludedSubtrees.some((prefix) => path.startsWith(prefix))) continue;
|
|
158
164
|
for (const method of METHODS) {
|
|
159
165
|
const operation = pathItem[METHOD_KEY[method]];
|
|
160
166
|
if (!operation || excludedOperations.has(`${method} ${path}`)) continue;
|
|
@@ -876,10 +882,11 @@ const COMMANDS = deriveCommands({
|
|
|
876
882
|
"/api/v1/me/tokens/{token_id}": { "delete": {
|
|
877
883
|
"operationId": "revokeMyAccessToken",
|
|
878
884
|
"summary": "Revoke one of the authenticated Principal's Access Tokens.",
|
|
885
|
+
"description": "When the token belongs to an OAuth grant, every access token and\nrefresh token sharing its `grant_id` is revoked in the same\ntransaction.\n",
|
|
879
886
|
"security": [{ "sessionCookie": [] }, { "bearerToken": [] }],
|
|
880
887
|
"parameters": [{ "$ref": "#/components/parameters/TokenID" }],
|
|
881
888
|
"responses": {
|
|
882
|
-
"204": { "description": "Revoked. The token
|
|
889
|
+
"204": { "description": "Revoked. The token and, when it belongs to a grant, its siblings stop authenticating immediately." },
|
|
883
890
|
"400": { "$ref": "#/components/responses/Problem" },
|
|
884
891
|
"401": { "$ref": "#/components/responses/Problem" },
|
|
885
892
|
"403": { "$ref": "#/components/responses/Problem" },
|
|
@@ -887,6 +894,106 @@ const COMMANDS = deriveCommands({
|
|
|
887
894
|
"default": { "$ref": "#/components/responses/Problem" }
|
|
888
895
|
}
|
|
889
896
|
} },
|
|
897
|
+
"/api/v1/oauth/register": { "post": {
|
|
898
|
+
"operationId": "registerOAuthClient",
|
|
899
|
+
"summary": "Register a public OAuth client through Dynamic Client Registration.",
|
|
900
|
+
"description": "Anonymous. Implements RFC 7591 registration for the public native\nclients that connect to Native MCP: no client secret is issued, and\npossession of the `client_id` plus an exactly matching redirect URI is\nthe whole identity. `client_name` is 1–100 characters and\n`redirect_uris` holds 1–10 absolute URIs without a fragment: `https`\nanywhere, `http` only on localhost, 127.0.0.1 or [::1]. Exact\nduplicates are removed. Registration failures are RFC 6749 error\nbodies, not problem details.\n",
|
|
901
|
+
"security": [],
|
|
902
|
+
"requestBody": {
|
|
903
|
+
"required": true,
|
|
904
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/OAuthClientRegistrationRequest" } } }
|
|
905
|
+
},
|
|
906
|
+
"responses": {
|
|
907
|
+
"201": {
|
|
908
|
+
"description": "The registered client. Hangar never issues a client secret.",
|
|
909
|
+
"headers": { "Cache-Control": {
|
|
910
|
+
"description": "Always `no-store`.",
|
|
911
|
+
"schema": { "type": "string" }
|
|
912
|
+
} },
|
|
913
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/OAuthClientRegistration" } } }
|
|
914
|
+
},
|
|
915
|
+
"400": { "$ref": "#/components/responses/OAuthError" },
|
|
916
|
+
"413": { "$ref": "#/components/responses/OAuthProblem" },
|
|
917
|
+
"default": { "$ref": "#/components/responses/OAuthProblem" }
|
|
918
|
+
}
|
|
919
|
+
} },
|
|
920
|
+
"/api/v1/oauth/token": { "post": {
|
|
921
|
+
"operationId": "exchangeOAuthToken",
|
|
922
|
+
"summary": "Exchange an authorization code or refresh token for an MCP token pair.",
|
|
923
|
+
"description": "Anonymous and form-encoded. `grant_type=authorization_code` requires\n`code`, `redirect_uri`, `client_id` and `code_verifier`; the code is\nsingle use and the PKCE challenge must be S256.\n`grant_type=refresh_token` requires `refresh_token` and `client_id` and\nrotates the refresh token. Presenting an already rotated refresh token\nrevokes the whole grant. Every failure is an RFC 6749 error body with\nstatus 400.\n",
|
|
924
|
+
"security": [],
|
|
925
|
+
"requestBody": {
|
|
926
|
+
"required": true,
|
|
927
|
+
"content": { "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/ExchangeOAuthTokenRequest" } } }
|
|
928
|
+
},
|
|
929
|
+
"responses": {
|
|
930
|
+
"200": {
|
|
931
|
+
"description": "A fresh MCP access token and a refresh token.",
|
|
932
|
+
"headers": { "Cache-Control": {
|
|
933
|
+
"description": "Always `no-store`.",
|
|
934
|
+
"schema": { "type": "string" }
|
|
935
|
+
} },
|
|
936
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/OAuthToken" } } }
|
|
937
|
+
},
|
|
938
|
+
"400": { "$ref": "#/components/responses/OAuthError" },
|
|
939
|
+
"413": { "$ref": "#/components/responses/OAuthProblem" },
|
|
940
|
+
"default": { "$ref": "#/components/responses/OAuthProblem" }
|
|
941
|
+
}
|
|
942
|
+
} },
|
|
943
|
+
"/api/v1/oauth/authorization-request": {
|
|
944
|
+
"get": {
|
|
945
|
+
"operationId": "lookupOAuthAuthorizationRequest",
|
|
946
|
+
"summary": "Read the consent page preview of an authorization request.",
|
|
947
|
+
"description": "Session-only. Validates `response_type=code`, the registered client,\nthe exact `redirect_uri` and `code_challenge_method=S256` before\nreturning anything displayable. An invalid request answers 400 and\nnever redirects, so an unknown client cannot be used for an open\nredirect. The preview never includes the redirect URI itself.\n",
|
|
948
|
+
"security": [{ "sessionCookie": [] }],
|
|
949
|
+
"parameters": [
|
|
950
|
+
{ "$ref": "#/components/parameters/OAuthResponseType" },
|
|
951
|
+
{ "$ref": "#/components/parameters/OAuthClientID" },
|
|
952
|
+
{ "$ref": "#/components/parameters/OAuthRedirectURI" },
|
|
953
|
+
{ "$ref": "#/components/parameters/OAuthState" },
|
|
954
|
+
{ "$ref": "#/components/parameters/OAuthCodeChallenge" },
|
|
955
|
+
{ "$ref": "#/components/parameters/OAuthCodeChallengeMethod" }
|
|
956
|
+
],
|
|
957
|
+
"responses": {
|
|
958
|
+
"200": {
|
|
959
|
+
"description": "The client name and callback host shown on the consent page.",
|
|
960
|
+
"headers": { "Cache-Control": {
|
|
961
|
+
"description": "Always `no-store`.",
|
|
962
|
+
"schema": { "type": "string" }
|
|
963
|
+
} },
|
|
964
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/OAuthAuthorizationPreview" } } }
|
|
965
|
+
},
|
|
966
|
+
"400": { "$ref": "#/components/responses/OAuthProblem" },
|
|
967
|
+
"401": { "$ref": "#/components/responses/OAuthProblem" },
|
|
968
|
+
"default": { "$ref": "#/components/responses/OAuthProblem" }
|
|
969
|
+
}
|
|
970
|
+
},
|
|
971
|
+
"post": {
|
|
972
|
+
"operationId": "decideOAuthAuthorizationRequest",
|
|
973
|
+
"summary": "Approve or deny an authorization request.",
|
|
974
|
+
"description": "Session-only and CSRF protected. Repeats the authorization request's\nparameters plus `decision`. Approval issues a single-use ten-minute\ncode and answers with the client redirect carrying `code` and `state`;\ndenial answers with `error=access_denied` and `state`. The redirect\npreserves any query already present on the registered redirect URI.\nAn invalid request answers 400 and never redirects.\n",
|
|
975
|
+
"security": [{ "sessionCookie": [] }],
|
|
976
|
+
"requestBody": {
|
|
977
|
+
"required": true,
|
|
978
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/DecideOAuthAuthorizationRequest" } } }
|
|
979
|
+
},
|
|
980
|
+
"responses": {
|
|
981
|
+
"200": {
|
|
982
|
+
"description": "The URL the consent page navigates to.",
|
|
983
|
+
"headers": { "Cache-Control": {
|
|
984
|
+
"description": "Always `no-store`.",
|
|
985
|
+
"schema": { "type": "string" }
|
|
986
|
+
} },
|
|
987
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/OAuthAuthorizationDecision" } } }
|
|
988
|
+
},
|
|
989
|
+
"400": { "$ref": "#/components/responses/OAuthProblem" },
|
|
990
|
+
"401": { "$ref": "#/components/responses/OAuthProblem" },
|
|
991
|
+
"403": { "$ref": "#/components/responses/OAuthProblem" },
|
|
992
|
+
"413": { "$ref": "#/components/responses/OAuthProblem" },
|
|
993
|
+
"default": { "$ref": "#/components/responses/OAuthProblem" }
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
},
|
|
890
997
|
"/api/v1/principals/{principal_id}/tokens": {
|
|
891
998
|
"parameters": [{ "$ref": "#/components/parameters/PrincipalID" }],
|
|
892
999
|
"get": {
|
|
@@ -928,10 +1035,10 @@ const COMMANDS = deriveCommands({
|
|
|
928
1035
|
"delete": {
|
|
929
1036
|
"operationId": "revokePrincipalAccessToken",
|
|
930
1037
|
"summary": "Revoke one of a Principal's Access Tokens.",
|
|
931
|
-
"description": "Allowed for the Principal itself, and for a Platform Admin when the
|
|
1038
|
+
"description": "Allowed for the Principal itself, and for a Platform Admin when the\nPrincipal is an Agent. When the token belongs to an OAuth grant, every\naccess token and refresh token sharing its `grant_id` is revoked in\nthe same transaction.\n",
|
|
932
1039
|
"security": [{ "sessionCookie": [] }, { "bearerToken": [] }],
|
|
933
1040
|
"responses": {
|
|
934
|
-
"204": { "description": "Revoked. The token
|
|
1041
|
+
"204": { "description": "Revoked. The token and, when it belongs to a grant, its siblings stop authenticating immediately." },
|
|
935
1042
|
"400": { "$ref": "#/components/responses/Problem" },
|
|
936
1043
|
"401": { "$ref": "#/components/responses/Problem" },
|
|
937
1044
|
"403": { "$ref": "#/components/responses/Problem" },
|
|
@@ -1192,7 +1299,7 @@ const COMMANDS = deriveCommands({
|
|
|
1192
1299
|
"operationId": "createCapability",
|
|
1193
1300
|
"summary": "Create a Capability together with its first Version.",
|
|
1194
1301
|
"description": "An `application/json` body creates an MCP Capability; a\n`multipart/form-data` body creates a Skill Capability from its\nCapability Package. The Capability, its first Version and the Audit\nEvent are committed in one transaction. A rejected package returns 400\nwith a `detail` that names what to fix. The caller must manage the\ntarget Namespace: a Platform Admin for system, a member User of the Team for a Team Namespace, and\nthe owner for a principal Namespace. A live Capability with the same\nkind and name in the Namespace returns 409.\n",
|
|
1195
|
-
"security": [{ "sessionCookie": [] }],
|
|
1302
|
+
"security": [{ "sessionCookie": [] }, { "bearerToken": [] }],
|
|
1196
1303
|
"parameters": [{ "$ref": "#/components/parameters/IdempotencyKey" }],
|
|
1197
1304
|
"requestBody": {
|
|
1198
1305
|
"required": true,
|
|
@@ -1312,7 +1419,7 @@ const COMMANDS = deriveCommands({
|
|
|
1312
1419
|
"operationId": "publishCapabilityVersion",
|
|
1313
1420
|
"summary": "Publish a new Version of a Capability and make it current.",
|
|
1314
1421
|
"description": "The media type must match the Capability's kind: `application/json`\nfor MCP and `multipart/form-data` for Skill; the other one returns\n400. The Version, the new current version and the Audit Event are\ncommitted in one transaction, and concurrent publishes each receive\ntheir own CalVer. The caller must manage the Capability's Namespace,\nunder the same rules as `createCapability`. A deleted or unavailable\nCapability, one in an unusable Namespace, and a private one the\ncaller does not manage return 404.\n",
|
|
1315
|
-
"security": [{ "sessionCookie": [] }],
|
|
1422
|
+
"security": [{ "sessionCookie": [] }, { "bearerToken": [] }],
|
|
1316
1423
|
"parameters": [{ "$ref": "#/components/parameters/IdempotencyKey" }],
|
|
1317
1424
|
"requestBody": {
|
|
1318
1425
|
"required": true,
|
|
@@ -2109,6 +2216,55 @@ const COMMANDS = deriveCommands({
|
|
|
2109
2216
|
"type": "string",
|
|
2110
2217
|
"format": "uuid"
|
|
2111
2218
|
}
|
|
2219
|
+
},
|
|
2220
|
+
"OAuthResponseType": {
|
|
2221
|
+
"name": "response_type",
|
|
2222
|
+
"in": "query",
|
|
2223
|
+
"required": true,
|
|
2224
|
+
"description": "Must be `code`.",
|
|
2225
|
+
"schema": {
|
|
2226
|
+
"type": "string",
|
|
2227
|
+
"enum": ["code"]
|
|
2228
|
+
}
|
|
2229
|
+
},
|
|
2230
|
+
"OAuthClientID": {
|
|
2231
|
+
"name": "client_id",
|
|
2232
|
+
"in": "query",
|
|
2233
|
+
"required": true,
|
|
2234
|
+
"schema": { "type": "string" }
|
|
2235
|
+
},
|
|
2236
|
+
"OAuthRedirectURI": {
|
|
2237
|
+
"name": "redirect_uri",
|
|
2238
|
+
"in": "query",
|
|
2239
|
+
"required": true,
|
|
2240
|
+
"description": "Must exactly match a URI registered for the client.",
|
|
2241
|
+
"schema": {
|
|
2242
|
+
"type": "string",
|
|
2243
|
+
"format": "uri"
|
|
2244
|
+
}
|
|
2245
|
+
},
|
|
2246
|
+
"OAuthState": {
|
|
2247
|
+
"name": "state",
|
|
2248
|
+
"in": "query",
|
|
2249
|
+
"required": false,
|
|
2250
|
+
"description": "Opaque value echoed on the client redirect.",
|
|
2251
|
+
"schema": { "type": "string" }
|
|
2252
|
+
},
|
|
2253
|
+
"OAuthCodeChallenge": {
|
|
2254
|
+
"name": "code_challenge",
|
|
2255
|
+
"in": "query",
|
|
2256
|
+
"required": true,
|
|
2257
|
+
"schema": { "type": "string" }
|
|
2258
|
+
},
|
|
2259
|
+
"OAuthCodeChallengeMethod": {
|
|
2260
|
+
"name": "code_challenge_method",
|
|
2261
|
+
"in": "query",
|
|
2262
|
+
"required": true,
|
|
2263
|
+
"description": "Must be `S256`.",
|
|
2264
|
+
"schema": {
|
|
2265
|
+
"type": "string",
|
|
2266
|
+
"enum": ["S256"]
|
|
2267
|
+
}
|
|
2112
2268
|
}
|
|
2113
2269
|
},
|
|
2114
2270
|
"requestBodies": { "CreateAccessToken": {
|
|
@@ -2153,27 +2309,59 @@ const COMMANDS = deriveCommands({
|
|
|
2153
2309
|
"ReplayedAccessToken": {
|
|
2154
2310
|
"description": "A replayed creation under `Idempotency-Key`: the first created Access Token's metadata, never its value.",
|
|
2155
2311
|
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ReplayedAccessToken" } } }
|
|
2312
|
+
},
|
|
2313
|
+
"OAuthError": {
|
|
2314
|
+
"description": "An RFC 6749 error. `Cache-Control` is always `no-store`.",
|
|
2315
|
+
"headers": { "Cache-Control": {
|
|
2316
|
+
"description": "Always `no-store`.",
|
|
2317
|
+
"schema": { "type": "string" }
|
|
2318
|
+
} },
|
|
2319
|
+
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/OAuthError" } } }
|
|
2320
|
+
},
|
|
2321
|
+
"OAuthProblem": {
|
|
2322
|
+
"description": "An RFC 9457 problem detail. `Cache-Control` is always `no-store`.",
|
|
2323
|
+
"headers": { "Cache-Control": {
|
|
2324
|
+
"description": "Always `no-store`.",
|
|
2325
|
+
"schema": { "type": "string" }
|
|
2326
|
+
} },
|
|
2327
|
+
"content": { "application/problem+json": { "schema": { "$ref": "#/components/schemas/ProblemDetails" } } }
|
|
2156
2328
|
}
|
|
2157
2329
|
},
|
|
2158
2330
|
"schemas": {
|
|
2159
2331
|
"CreateAccessTokenRequest": {
|
|
2160
2332
|
"type": "object",
|
|
2161
2333
|
"required": ["name"],
|
|
2162
|
-
"properties": {
|
|
2163
|
-
"
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2334
|
+
"properties": {
|
|
2335
|
+
"name": {
|
|
2336
|
+
"type": "string",
|
|
2337
|
+
"minLength": 1,
|
|
2338
|
+
"maxLength": 64
|
|
2339
|
+
},
|
|
2340
|
+
"audience": {
|
|
2341
|
+
"type": "string",
|
|
2342
|
+
"enum": ["management", "mcp"],
|
|
2343
|
+
"default": "management",
|
|
2344
|
+
"description": "The surface the token may authenticate. `management` (the default)\nworks against `/api/v1`; `mcp` works against Native MCP and is\nrejected by every `/api/v1` operation.\n"
|
|
2345
|
+
}
|
|
2346
|
+
},
|
|
2167
2347
|
"additionalProperties": false
|
|
2168
2348
|
},
|
|
2349
|
+
"AccessTokenAudience": {
|
|
2350
|
+
"type": "string",
|
|
2351
|
+
"enum": ["management", "mcp"],
|
|
2352
|
+
"description": "The surface the token authenticates. Each surface rejects the other's\ntokens.\n"
|
|
2353
|
+
},
|
|
2169
2354
|
"AccessToken": {
|
|
2170
2355
|
"type": "object",
|
|
2171
2356
|
"required": [
|
|
2172
2357
|
"id",
|
|
2173
2358
|
"name",
|
|
2174
2359
|
"token_prefix",
|
|
2360
|
+
"audience",
|
|
2175
2361
|
"created_at",
|
|
2176
|
-
"last_used_at"
|
|
2362
|
+
"last_used_at",
|
|
2363
|
+
"expires_at",
|
|
2364
|
+
"client_name"
|
|
2177
2365
|
],
|
|
2178
2366
|
"properties": {
|
|
2179
2367
|
"id": {
|
|
@@ -2185,6 +2373,7 @@ const COMMANDS = deriveCommands({
|
|
|
2185
2373
|
"type": "string",
|
|
2186
2374
|
"description": "The first 8 characters after `hng_`, for recognising a token. Not a credential."
|
|
2187
2375
|
},
|
|
2376
|
+
"audience": { "$ref": "#/components/schemas/AccessTokenAudience" },
|
|
2188
2377
|
"created_at": {
|
|
2189
2378
|
"type": "string",
|
|
2190
2379
|
"format": "date-time"
|
|
@@ -2193,6 +2382,15 @@ const COMMANDS = deriveCommands({
|
|
|
2193
2382
|
"type": ["string", "null"],
|
|
2194
2383
|
"format": "date-time",
|
|
2195
2384
|
"description": "Recorded at most once every 5 minutes; null until first use."
|
|
2385
|
+
},
|
|
2386
|
+
"expires_at": {
|
|
2387
|
+
"type": ["string", "null"],
|
|
2388
|
+
"format": "date-time",
|
|
2389
|
+
"description": "Null for static tokens, which never expire and are revoked instead."
|
|
2390
|
+
},
|
|
2391
|
+
"client_name": {
|
|
2392
|
+
"type": ["string", "null"],
|
|
2393
|
+
"description": "The OAuth client that obtained the token; null for static tokens."
|
|
2196
2394
|
}
|
|
2197
2395
|
}
|
|
2198
2396
|
},
|
|
@@ -2204,6 +2402,192 @@ const COMMANDS = deriveCommands({
|
|
|
2204
2402
|
"items": { "$ref": "#/components/schemas/AccessToken" }
|
|
2205
2403
|
} }
|
|
2206
2404
|
},
|
|
2405
|
+
"OAuthClientRegistrationRequest": {
|
|
2406
|
+
"type": "object",
|
|
2407
|
+
"required": ["client_name", "redirect_uris"],
|
|
2408
|
+
"properties": {
|
|
2409
|
+
"client_name": {
|
|
2410
|
+
"type": "string",
|
|
2411
|
+
"minLength": 1,
|
|
2412
|
+
"maxLength": 100
|
|
2413
|
+
},
|
|
2414
|
+
"redirect_uris": {
|
|
2415
|
+
"type": "array",
|
|
2416
|
+
"minItems": 1,
|
|
2417
|
+
"maxItems": 10,
|
|
2418
|
+
"items": {
|
|
2419
|
+
"type": "string",
|
|
2420
|
+
"maxLength": 2048
|
|
2421
|
+
}
|
|
2422
|
+
},
|
|
2423
|
+
"token_endpoint_auth_method": {
|
|
2424
|
+
"type": "string",
|
|
2425
|
+
"enum": ["none"],
|
|
2426
|
+
"default": "none",
|
|
2427
|
+
"description": "The only accepted value; omitted means `none`."
|
|
2428
|
+
},
|
|
2429
|
+
"grant_types": {
|
|
2430
|
+
"type": "array",
|
|
2431
|
+
"items": {
|
|
2432
|
+
"type": "string",
|
|
2433
|
+
"enum": ["authorization_code", "refresh_token"]
|
|
2434
|
+
},
|
|
2435
|
+
"default": ["authorization_code"],
|
|
2436
|
+
"description": "Omitted means `[\"authorization_code\"]`; `refresh_token` may accompany it."
|
|
2437
|
+
},
|
|
2438
|
+
"response_types": {
|
|
2439
|
+
"type": "array",
|
|
2440
|
+
"items": {
|
|
2441
|
+
"type": "string",
|
|
2442
|
+
"enum": ["code"]
|
|
2443
|
+
},
|
|
2444
|
+
"default": ["code"],
|
|
2445
|
+
"description": "Omitted means `[\"code\"]`."
|
|
2446
|
+
}
|
|
2447
|
+
},
|
|
2448
|
+
"additionalProperties": false
|
|
2449
|
+
},
|
|
2450
|
+
"OAuthClientRegistration": {
|
|
2451
|
+
"type": "object",
|
|
2452
|
+
"required": [
|
|
2453
|
+
"client_id",
|
|
2454
|
+
"client_id_issued_at",
|
|
2455
|
+
"client_name",
|
|
2456
|
+
"redirect_uris",
|
|
2457
|
+
"token_endpoint_auth_method",
|
|
2458
|
+
"grant_types",
|
|
2459
|
+
"response_types"
|
|
2460
|
+
],
|
|
2461
|
+
"properties": {
|
|
2462
|
+
"client_id": {
|
|
2463
|
+
"type": "string",
|
|
2464
|
+
"description": "`hngc_` plus at least 20 characters."
|
|
2465
|
+
},
|
|
2466
|
+
"client_id_issued_at": {
|
|
2467
|
+
"type": "integer",
|
|
2468
|
+
"description": "Unix time in seconds."
|
|
2469
|
+
},
|
|
2470
|
+
"client_name": { "type": "string" },
|
|
2471
|
+
"redirect_uris": {
|
|
2472
|
+
"type": "array",
|
|
2473
|
+
"items": { "type": "string" }
|
|
2474
|
+
},
|
|
2475
|
+
"token_endpoint_auth_method": {
|
|
2476
|
+
"type": "string",
|
|
2477
|
+
"enum": ["none"]
|
|
2478
|
+
},
|
|
2479
|
+
"grant_types": {
|
|
2480
|
+
"type": "array",
|
|
2481
|
+
"items": { "type": "string" }
|
|
2482
|
+
},
|
|
2483
|
+
"response_types": {
|
|
2484
|
+
"type": "array",
|
|
2485
|
+
"items": { "type": "string" }
|
|
2486
|
+
}
|
|
2487
|
+
}
|
|
2488
|
+
},
|
|
2489
|
+
"ExchangeOAuthTokenRequest": {
|
|
2490
|
+
"type": "object",
|
|
2491
|
+
"properties": {
|
|
2492
|
+
"grant_type": {
|
|
2493
|
+
"type": "string",
|
|
2494
|
+
"enum": ["authorization_code", "refresh_token"]
|
|
2495
|
+
},
|
|
2496
|
+
"code": { "type": "string" },
|
|
2497
|
+
"redirect_uri": { "type": "string" },
|
|
2498
|
+
"client_id": { "type": "string" },
|
|
2499
|
+
"code_verifier": { "type": "string" },
|
|
2500
|
+
"refresh_token": { "type": "string" }
|
|
2501
|
+
}
|
|
2502
|
+
},
|
|
2503
|
+
"OAuthToken": {
|
|
2504
|
+
"type": "object",
|
|
2505
|
+
"required": [
|
|
2506
|
+
"access_token",
|
|
2507
|
+
"token_type",
|
|
2508
|
+
"expires_in",
|
|
2509
|
+
"refresh_token"
|
|
2510
|
+
],
|
|
2511
|
+
"properties": {
|
|
2512
|
+
"access_token": {
|
|
2513
|
+
"type": "string",
|
|
2514
|
+
"description": "An `hng_<value>` MCP token. Use it as `Authorization: Bearer <token>`."
|
|
2515
|
+
},
|
|
2516
|
+
"token_type": {
|
|
2517
|
+
"type": "string",
|
|
2518
|
+
"enum": ["Bearer"]
|
|
2519
|
+
},
|
|
2520
|
+
"expires_in": {
|
|
2521
|
+
"type": "integer",
|
|
2522
|
+
"description": "Access token lifetime in seconds."
|
|
2523
|
+
},
|
|
2524
|
+
"refresh_token": { "type": "string" }
|
|
2525
|
+
}
|
|
2526
|
+
},
|
|
2527
|
+
"OAuthError": {
|
|
2528
|
+
"type": "object",
|
|
2529
|
+
"required": ["error", "error_description"],
|
|
2530
|
+
"properties": {
|
|
2531
|
+
"error": {
|
|
2532
|
+
"type": "string",
|
|
2533
|
+
"enum": [
|
|
2534
|
+
"invalid_request",
|
|
2535
|
+
"invalid_grant",
|
|
2536
|
+
"unsupported_grant_type"
|
|
2537
|
+
]
|
|
2538
|
+
},
|
|
2539
|
+
"error_description": { "type": "string" }
|
|
2540
|
+
}
|
|
2541
|
+
},
|
|
2542
|
+
"OAuthAuthorizationPreview": {
|
|
2543
|
+
"type": "object",
|
|
2544
|
+
"required": ["client_name", "redirect_host"],
|
|
2545
|
+
"properties": {
|
|
2546
|
+
"client_name": { "type": "string" },
|
|
2547
|
+
"redirect_host": {
|
|
2548
|
+
"type": "string",
|
|
2549
|
+
"description": "The host of the registered redirect URI; the URI itself is never exposed."
|
|
2550
|
+
}
|
|
2551
|
+
}
|
|
2552
|
+
},
|
|
2553
|
+
"OAuthAuthorizationDecision": {
|
|
2554
|
+
"type": "object",
|
|
2555
|
+
"required": ["redirect_to"],
|
|
2556
|
+
"properties": { "redirect_to": {
|
|
2557
|
+
"type": "string",
|
|
2558
|
+
"description": "The client redirect carrying `code` or `error=access_denied`, plus `state`."
|
|
2559
|
+
} }
|
|
2560
|
+
},
|
|
2561
|
+
"DecideOAuthAuthorizationRequest": {
|
|
2562
|
+
"type": "object",
|
|
2563
|
+
"required": [
|
|
2564
|
+
"response_type",
|
|
2565
|
+
"client_id",
|
|
2566
|
+
"redirect_uri",
|
|
2567
|
+
"code_challenge",
|
|
2568
|
+
"code_challenge_method",
|
|
2569
|
+
"decision"
|
|
2570
|
+
],
|
|
2571
|
+
"properties": {
|
|
2572
|
+
"response_type": {
|
|
2573
|
+
"type": "string",
|
|
2574
|
+
"enum": ["code"]
|
|
2575
|
+
},
|
|
2576
|
+
"client_id": { "type": "string" },
|
|
2577
|
+
"redirect_uri": { "type": "string" },
|
|
2578
|
+
"state": { "type": "string" },
|
|
2579
|
+
"code_challenge": { "type": "string" },
|
|
2580
|
+
"code_challenge_method": {
|
|
2581
|
+
"type": "string",
|
|
2582
|
+
"enum": ["S256"]
|
|
2583
|
+
},
|
|
2584
|
+
"decision": {
|
|
2585
|
+
"type": "string",
|
|
2586
|
+
"enum": ["approve", "deny"]
|
|
2587
|
+
}
|
|
2588
|
+
},
|
|
2589
|
+
"additionalProperties": false
|
|
2590
|
+
},
|
|
2207
2591
|
"CreatedAccessToken": {
|
|
2208
2592
|
"type": "object",
|
|
2209
2593
|
"required": [
|
|
@@ -2774,7 +3158,7 @@ const COMMANDS = deriveCommands({
|
|
|
2774
3158
|
},
|
|
2775
3159
|
"Readme": {
|
|
2776
3160
|
"type": ["string", "null"],
|
|
2777
|
-
"description": "Markdown, at most 1 MiB of UTF-8. Omitted or null means no README; an
|
|
3161
|
+
"description": "Markdown, at most 1 MiB of UTF-8. Omitted or null means no README was\nattached; an empty string is kept as an empty README. A Skill Version\nwithout one reports its package's `SKILL.md` in place of a README.\n"
|
|
2778
3162
|
},
|
|
2779
3163
|
"MCPConfig": {
|
|
2780
3164
|
"type": "object",
|
|
@@ -2828,7 +3212,7 @@ const COMMANDS = deriveCommands({
|
|
|
2828
3212
|
"visibility": { "$ref": "#/components/schemas/Visibility" },
|
|
2829
3213
|
"readme": {
|
|
2830
3214
|
"type": "string",
|
|
2831
|
-
"description": "Markdown, at most 1 MiB of UTF-8. Omitted means
|
|
3215
|
+
"description": "Markdown, at most 1 MiB of UTF-8. Omitted means the package's\n`SKILL.md` is reported as the Version's README.\n"
|
|
2832
3216
|
},
|
|
2833
3217
|
"category": {
|
|
2834
3218
|
"type": "string",
|
|
@@ -2886,7 +3270,7 @@ const COMMANDS = deriveCommands({
|
|
|
2886
3270
|
"properties": {
|
|
2887
3271
|
"readme": {
|
|
2888
3272
|
"type": "string",
|
|
2889
|
-
"description": "Markdown, at most 1 MiB of UTF-8. Omitted means
|
|
3273
|
+
"description": "Markdown, at most 1 MiB of UTF-8. Omitted means the package's\n`SKILL.md` is reported as the Version's README.\n"
|
|
2890
3274
|
},
|
|
2891
3275
|
"package": {
|
|
2892
3276
|
"type": "string",
|
|
@@ -3122,7 +3506,11 @@ const COMMANDS = deriveCommands({
|
|
|
3122
3506
|
"version": { "$ref": "#/components/schemas/CalVer" },
|
|
3123
3507
|
"readme": {
|
|
3124
3508
|
"type": ["string", "null"],
|
|
3125
|
-
"description": "The Version's Markdown README
|
|
3509
|
+
"description": "The Version's Markdown README. A Skill Version that was published\nwithout one reports the package's `SKILL.md`, or null when the\nmanifest cannot be read as text within the README limit.\n"
|
|
3510
|
+
},
|
|
3511
|
+
"readme_from_manifest": {
|
|
3512
|
+
"type": "boolean",
|
|
3513
|
+
"description": "True when `readme` was derived from the package's `SKILL.md`\nbecause the Version has no attached README. Omitted otherwise.\n"
|
|
3126
3514
|
},
|
|
3127
3515
|
"created_at": {
|
|
3128
3516
|
"type": "string",
|
|
@@ -3567,7 +3955,7 @@ function workflowCatalog() {
|
|
|
3567
3955
|
},
|
|
3568
3956
|
{
|
|
3569
3957
|
name: "auth status",
|
|
3570
|
-
summary: "Show credential source, server, principal
|
|
3958
|
+
summary: "Show credential source, server, principal, token prefix and audience",
|
|
3571
3959
|
group: "auth",
|
|
3572
3960
|
args: [],
|
|
3573
3961
|
flags: [{
|
|
@@ -3583,7 +3971,11 @@ function workflowCatalog() {
|
|
|
3583
3971
|
summary: "Print the access token to stdout and nothing else",
|
|
3584
3972
|
group: "auth",
|
|
3585
3973
|
args: [],
|
|
3586
|
-
flags: [
|
|
3974
|
+
flags: [{
|
|
3975
|
+
name: "audience",
|
|
3976
|
+
type: "string",
|
|
3977
|
+
description: "management prints the stored token; mcp mints a new MCP token that can read and write the catalog content you have access to"
|
|
3978
|
+
}],
|
|
3587
3979
|
destructive: false,
|
|
3588
3980
|
dryRun: false
|
|
3589
3981
|
},
|
|
@@ -86,18 +86,16 @@ async function writeFileAtomically(path, content, options = {}) {
|
|
|
86
86
|
}
|
|
87
87
|
/**
|
|
88
88
|
* Writes a directory through "temp → rename": the destination is either
|
|
89
|
-
* absent or complete, never half-written.
|
|
90
|
-
*
|
|
89
|
+
* absent or complete, never half-written. An existing directory is moved
|
|
90
|
+
* aside before the rename so a failed swap puts it back.
|
|
91
91
|
*/
|
|
92
|
-
async function writeDirectoryAtomically(dest, writeTemp, options) {
|
|
92
|
+
async function writeDirectoryAtomically(dest, writeTemp, options = {}) {
|
|
93
93
|
const parent = dirname(dest);
|
|
94
94
|
await mkdir(parent, { recursive: true });
|
|
95
95
|
const temp = resolve(parent, `.hngr-${basename(dest)}-${randomUUID()}.tmp`);
|
|
96
96
|
try {
|
|
97
97
|
await writeTemp(temp);
|
|
98
|
-
|
|
99
|
-
if (exists && !options.force) throw options.existsError();
|
|
100
|
-
if (!exists) {
|
|
98
|
+
if (!await lstat(dest).then(() => true, () => false)) {
|
|
101
99
|
await rename(temp, dest);
|
|
102
100
|
return;
|
|
103
101
|
}
|
|
@@ -127,6 +125,14 @@ async function writeDirectoryAtomically(dest, writeTemp, options) {
|
|
|
127
125
|
}
|
|
128
126
|
}
|
|
129
127
|
// src/config.ts
|
|
128
|
+
/**
|
|
129
|
+
* The stored token's audience. Config files written before audiences existed
|
|
130
|
+
* hold a management token by definition, so a missing field reads as
|
|
131
|
+
* `management` instead of failing to load.
|
|
132
|
+
*/
|
|
133
|
+
function storedAudience(config) {
|
|
134
|
+
return config?.audience === "mcp" ? "mcp" : "management";
|
|
135
|
+
}
|
|
130
136
|
/** `$XDG_CONFIG_HOME/hngr/config.json`, falling back to `~/.config/hngr/config.json`. */
|
|
131
137
|
function configFilePath(env) {
|
|
132
138
|
const xdg = env.XDG_CONFIG_HOME;
|
|
@@ -456,4 +462,4 @@ async function apiFetch(connection, options) {
|
|
|
456
462
|
signal: options.signal
|
|
457
463
|
});
|
|
458
464
|
}
|
|
459
|
-
export {
|
|
465
|
+
export { HngrError as C, CancelledError as S, scrubCredentials as _, toHngrError as a, writeDirectoryAtomically as b, redactUrlForDisplay as c, configFilePath as d, deleteStoredConfig as f, resolveConnection as g, redactUrl as h, buildRequestUrl as i, apiErrorFromResponse as l, readStoredConfig as m, apiGet as n, redactQuery as o, normalizeServerUrl as p, apiRequest as r, redactSensitive as s, apiFetch as t, DEFAULT_SERVER_URL as u, storedAudience as v, renderError as w, writeFileAtomically as x, writeStoredConfig as y };
|