@executor-js/emulate 0.6.0 → 0.7.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.
@@ -9,11 +9,7 @@ function getWorkosStore(store) {
9
9
  return {
10
10
  users: store.collection("workos.users", ["workos_id", "email"]),
11
11
  organizations: store.collection("workos.organizations", ["workos_id"]),
12
- memberships: store.collection("workos.memberships", [
13
- "workos_id",
14
- "user_id",
15
- "organization_id"
16
- ]),
12
+ memberships: store.collection("workos.memberships", ["workos_id", "user_id", "organization_id"]),
17
13
  invitations: store.collection("workos.invitations", [
18
14
  "workos_id",
19
15
  "email",
@@ -23,12 +19,10 @@ function getWorkosStore(store) {
23
19
  apiKeys: store.collection("workos.api_keys", ["workos_id", "value", "user_id"]),
24
20
  authCodes: store.collection("workos.auth_codes", ["code"]),
25
21
  sessions: store.collection("workos.sessions", ["refresh_token", "workos_id"]),
26
- vaultObjects: store.collection("workos.vault_objects", [
27
- "workos_id",
28
- "name"
29
- ]),
22
+ vaultObjects: store.collection("workos.vault_objects", ["workos_id", "name"]),
30
23
  oauthClients: store.collection("workos.oauth_clients", ["client_id"]),
31
- oauthCodes: store.collection("workos.oauth_codes", ["code"])
24
+ oauthCodes: store.collection("workos.oauth_codes", ["code"]),
25
+ oauthSettings: store.collection("workos.oauth_settings", [])
32
26
  };
33
27
  }
34
28
  function createErrorHandler(documentationUrl) {
@@ -527,7 +521,8 @@ async function authenticationResponse(c, ws, baseUrl, user, organizationId, clie
527
521
  user_id: user.workos_id,
528
522
  organization_id: organizationId,
529
523
  client_id: clientId,
530
- revoked: false
524
+ revoked: false,
525
+ scope: null
531
526
  });
532
527
  const membership = organizationId ? ws.memberships.findBy("user_id", user.workos_id).find((m) => m.organization_id === organizationId) : void 0;
533
528
  const accessToken = await signAccessToken(
@@ -970,7 +965,8 @@ function oauthRoutes(ctx) {
970
965
  client_id: workosId("client"),
971
966
  client_secret: null,
972
967
  redirect_uris: Array.isArray(body.redirect_uris) ? body.redirect_uris : [],
973
- name: typeof body.client_name === "string" ? body.client_name : null
968
+ name: typeof body.client_name === "string" ? body.client_name : null,
969
+ access_token_ttl_seconds: typeof body.access_token_ttl_seconds === "number" && body.access_token_ttl_seconds > 0 ? Math.floor(body.access_token_ttl_seconds) : null
974
970
  });
975
971
  return c.json(
976
972
  {
@@ -989,6 +985,7 @@ function oauthRoutes(ctx) {
989
985
  const redirectUri = c.req.query("redirect_uri") ?? "";
990
986
  const state = c.req.query("state") ?? "";
991
987
  const codeChallenge = c.req.query("code_challenge") ?? "";
988
+ const scope = c.req.query("scope") ?? "";
992
989
  const loginHint = c.req.query("login_hint");
993
990
  if (!redirectUri) return workosError(c, 422, "invalid_request", "redirect_uri is required");
994
991
  const issue = (email) => {
@@ -1002,6 +999,7 @@ function oauthRoutes(ctx) {
1002
999
  client_id: clientId,
1003
1000
  redirect_uri: redirectUri,
1004
1001
  code_challenge: codeChallenge || null,
1002
+ scope: scope || null,
1005
1003
  used: false
1006
1004
  });
1007
1005
  const target = new URL(redirectUri);
@@ -1023,7 +1021,8 @@ function oauthRoutes(ctx) {
1023
1021
  client_id: clientId,
1024
1022
  redirect_uri: redirectUri,
1025
1023
  state,
1026
- code_challenge: codeChallenge
1024
+ code_challenge: codeChallenge,
1025
+ scope
1027
1026
  }
1028
1027
  })
1029
1028
  ).join("\n");
@@ -1033,6 +1032,7 @@ function oauthRoutes(ctx) {
1033
1032
  <input type="hidden" name="redirect_uri" value="${escapeHtml(redirectUri)}" />
1034
1033
  <input type="hidden" name="state" value="${escapeHtml(state)}" />
1035
1034
  <input type="hidden" name="code_challenge" value="${escapeHtml(codeChallenge)}" />
1035
+ <input type="hidden" name="scope" value="${escapeHtml(scope)}" />
1036
1036
  <input type="email" name="email" class="checkout-input" placeholder="new-user@example.com" required />
1037
1037
  <button type="submit" class="checkout-pay-btn">Continue as new user</button>
1038
1038
  </form>`;
@@ -1058,6 +1058,7 @@ function oauthRoutes(ctx) {
1058
1058
  client_id: String(form.client_id ?? ""),
1059
1059
  redirect_uri: redirectUri,
1060
1060
  code_challenge: String(form.code_challenge ?? "") || null,
1061
+ scope: String(form.scope ?? "") || null,
1061
1062
  used: false
1062
1063
  });
1063
1064
  const target = new URL(redirectUri);
@@ -1070,6 +1071,7 @@ function oauthRoutes(ctx) {
1070
1071
  const contentType = c.req.header("content-type") ?? "";
1071
1072
  const body = contentType.includes("json") ? await c.req.json().catch(() => ({})) : await c.req.parseBody();
1072
1073
  const grantType = String(body.grant_type ?? "");
1074
+ const ttlFor = (clientId) => ws().oauthClients.findOneBy("client_id", clientId)?.access_token_ttl_seconds ?? ws().oauthSettings.all()[0]?.default_access_token_ttl_seconds ?? 3600;
1073
1075
  if (grantType === "refresh_token") {
1074
1076
  const refreshToken = String(body.refresh_token ?? "");
1075
1077
  const session2 = ws().sessions.findOneBy("refresh_token", refreshToken);
@@ -1083,9 +1085,11 @@ function oauthRoutes(ctx) {
1083
1085
  user_id: session2.user_id,
1084
1086
  organization_id: session2.organization_id,
1085
1087
  client_id: session2.client_id,
1086
- revoked: false
1088
+ revoked: false,
1089
+ scope: session2.scope
1087
1090
  });
1088
1091
  const audience2 = process.env.EMULATE_WORKOS_AUDIENCE ?? session2.client_id;
1092
+ const expiresIn2 = ttlFor(session2.client_id);
1089
1093
  const accessToken2 = await signAccessToken(
1090
1094
  {
1091
1095
  sub: session2.user_id,
@@ -1093,13 +1097,14 @@ function oauthRoutes(ctx) {
1093
1097
  ...session2.organization_id ? { org_id: session2.organization_id } : {},
1094
1098
  permissions: []
1095
1099
  },
1096
- { issuer: baseUrl, audience: audience2 }
1100
+ { issuer: baseUrl, audience: audience2, expiresIn: `${expiresIn2}s` }
1097
1101
  );
1098
1102
  return c.json({
1099
1103
  access_token: accessToken2,
1100
1104
  token_type: "Bearer",
1101
- expires_in: 3600,
1102
- refresh_token: rotated.refresh_token
1105
+ expires_in: expiresIn2,
1106
+ refresh_token: rotated.refresh_token,
1107
+ ...session2.scope ? { scope: session2.scope } : {}
1103
1108
  });
1104
1109
  }
1105
1110
  if (grantType !== "authorization_code") {
@@ -1112,31 +1117,442 @@ function oauthRoutes(ctx) {
1112
1117
  }
1113
1118
  ws().oauthCodes.update(oauthCode.id, { used: true });
1114
1119
  const audience = process.env.EMULATE_WORKOS_AUDIENCE ?? oauthCode.client_id;
1115
- const session = ws().sessions.insert({
1120
+ const grantedScopes = (oauthCode.scope ?? "").split(" ").filter(Boolean);
1121
+ const offline = grantedScopes.includes("offline_access");
1122
+ const session = offline ? ws().sessions.insert({
1116
1123
  workos_id: workosId("session"),
1117
1124
  refresh_token: randomToken("rt"),
1118
1125
  user_id: oauthCode.user_id,
1119
1126
  organization_id: oauthCode.organization_id,
1120
1127
  client_id: oauthCode.client_id,
1121
- revoked: false
1122
- });
1128
+ revoked: false,
1129
+ scope: oauthCode.scope
1130
+ }) : null;
1131
+ const expiresIn = ttlFor(oauthCode.client_id);
1123
1132
  const accessToken = await signAccessToken(
1124
1133
  {
1125
1134
  sub: oauthCode.user_id,
1126
- sid: session.workos_id,
1135
+ sid: session?.workos_id ?? workosId("session"),
1127
1136
  ...oauthCode.organization_id ? { org_id: oauthCode.organization_id } : {},
1128
1137
  permissions: []
1129
1138
  },
1130
- { issuer: baseUrl, audience }
1139
+ { issuer: baseUrl, audience, expiresIn: `${expiresIn}s` }
1131
1140
  );
1132
1141
  return c.json({
1133
1142
  access_token: accessToken,
1134
1143
  token_type: "Bearer",
1135
- expires_in: 3600,
1136
- refresh_token: session.refresh_token
1144
+ expires_in: expiresIn,
1145
+ ...session ? { refresh_token: session.refresh_token } : {},
1146
+ ...grantedScopes.length > 0 ? { scope: grantedScopes.join(" ") } : {}
1137
1147
  });
1138
1148
  });
1139
1149
  }
1150
+ function openapiRoutes({ app, baseUrl }) {
1151
+ app.get("/openapi.json", (c) => c.json(buildSpec(baseUrl)));
1152
+ }
1153
+ var ok = (description) => ({
1154
+ description,
1155
+ content: { "application/json": { schema: { type: "object" } } }
1156
+ });
1157
+ var noContent = (description) => ({ description });
1158
+ var id = { name: "id", in: "path", required: true, schema: { type: "string" } };
1159
+ var query = (name, description) => ({
1160
+ name,
1161
+ in: "query",
1162
+ required: false,
1163
+ schema: { type: "string" },
1164
+ description
1165
+ });
1166
+ var jsonBody = (properties, required, description) => ({
1167
+ required: true,
1168
+ description,
1169
+ content: {
1170
+ "application/json": {
1171
+ schema: { type: "object", properties, required: [...required] }
1172
+ }
1173
+ }
1174
+ });
1175
+ function buildSpec(baseUrl) {
1176
+ return {
1177
+ openapi: "3.1.0",
1178
+ info: {
1179
+ title: "WorkOS API (Emulated)",
1180
+ version: "1.0.0",
1181
+ description: "Emulated subset of the WorkOS REST API: user management, organization memberships, invitations, API keys, Vault KV, and the OAuth token surface. Authenticate with a bearer secret key (mint one at POST /_emulate/credentials)."
1182
+ },
1183
+ servers: [{ url: baseUrl }],
1184
+ components: {
1185
+ securitySchemes: {
1186
+ bearerAuth: {
1187
+ type: "http",
1188
+ scheme: "bearer",
1189
+ description: "WorkOS secret API key, sent as `Authorization: Bearer sk_\u2026`."
1190
+ }
1191
+ }
1192
+ },
1193
+ security: [{ bearerAuth: [] }],
1194
+ paths: {
1195
+ "/user_management/authenticate": {
1196
+ post: {
1197
+ operationId: "userManagement.authenticate",
1198
+ tags: ["user-management"],
1199
+ summary: "Exchange an authorization code or refresh token for a session",
1200
+ security: [],
1201
+ requestBody: jsonBody(
1202
+ {
1203
+ grant_type: { type: "string", enum: ["authorization_code", "refresh_token"] },
1204
+ client_id: { type: "string" },
1205
+ code: { type: "string" },
1206
+ refresh_token: { type: "string" },
1207
+ organization_id: { type: "string" }
1208
+ },
1209
+ ["grant_type"],
1210
+ "authorization_code grants need `code`; refresh_token grants need `refresh_token` (optionally switching `organization_id`)."
1211
+ ),
1212
+ responses: {
1213
+ "200": ok("User, access token, and refresh token."),
1214
+ "400": ok("Invalid or used grant.")
1215
+ }
1216
+ }
1217
+ },
1218
+ "/user_management/users/{id}": {
1219
+ get: {
1220
+ operationId: "userManagement.getUser",
1221
+ tags: ["user-management"],
1222
+ summary: "Retrieve a user",
1223
+ parameters: [id],
1224
+ responses: { "200": ok("The user."), "404": ok("Not found.") }
1225
+ }
1226
+ },
1227
+ "/user_management/organization_memberships": {
1228
+ get: {
1229
+ operationId: "memberships.list",
1230
+ tags: ["memberships"],
1231
+ summary: "List organization memberships",
1232
+ parameters: [
1233
+ query("user_id", "Filter by user."),
1234
+ query("organization_id", "Filter by organization."),
1235
+ query("statuses", "Comma-separated membership statuses.")
1236
+ ],
1237
+ responses: { "200": ok("Membership list.") }
1238
+ },
1239
+ post: {
1240
+ operationId: "memberships.create",
1241
+ tags: ["memberships"],
1242
+ summary: "Add a user to an organization",
1243
+ requestBody: jsonBody(
1244
+ {
1245
+ user_id: { type: "string" },
1246
+ organization_id: { type: "string" },
1247
+ role_slug: { type: "string" }
1248
+ },
1249
+ ["user_id", "organization_id"],
1250
+ "The membership to create."
1251
+ ),
1252
+ responses: {
1253
+ "201": ok("The created membership."),
1254
+ "404": ok("User or organization not found."),
1255
+ "409": ok("Already a member.")
1256
+ }
1257
+ }
1258
+ },
1259
+ "/user_management/organization_memberships/{id}": {
1260
+ get: {
1261
+ operationId: "memberships.get",
1262
+ tags: ["memberships"],
1263
+ summary: "Retrieve a membership",
1264
+ parameters: [id],
1265
+ responses: { "200": ok("The membership."), "404": ok("Not found.") }
1266
+ },
1267
+ put: {
1268
+ operationId: "memberships.update",
1269
+ tags: ["memberships"],
1270
+ summary: "Update a membership's role",
1271
+ parameters: [id],
1272
+ requestBody: jsonBody({ role_slug: { type: "string" } }, [], "Fields to update."),
1273
+ responses: { "200": ok("The updated membership."), "404": ok("Not found.") }
1274
+ },
1275
+ delete: {
1276
+ operationId: "memberships.delete",
1277
+ tags: ["memberships"],
1278
+ summary: "Remove a membership",
1279
+ parameters: [id],
1280
+ responses: { "204": noContent("Deleted."), "404": ok("Not found.") }
1281
+ }
1282
+ },
1283
+ "/user_management/invitations": {
1284
+ get: {
1285
+ operationId: "invitations.list",
1286
+ tags: ["invitations"],
1287
+ summary: "List invitations",
1288
+ parameters: [query("email", "Filter by invitee email."), query("organization_id", "Filter by organization.")],
1289
+ responses: { "200": ok("Invitation list.") }
1290
+ },
1291
+ post: {
1292
+ operationId: "invitations.send",
1293
+ tags: ["invitations"],
1294
+ summary: "Send an invitation",
1295
+ requestBody: jsonBody(
1296
+ {
1297
+ email: { type: "string" },
1298
+ organization_id: { type: "string" },
1299
+ inviter_user_id: { type: "string" },
1300
+ role_slug: { type: "string" }
1301
+ },
1302
+ ["email", "organization_id"],
1303
+ "The invitation to send."
1304
+ ),
1305
+ responses: {
1306
+ "201": ok("The created invitation."),
1307
+ "404": ok("Organization not found."),
1308
+ "422": ok("Validation error.")
1309
+ }
1310
+ }
1311
+ },
1312
+ "/user_management/invitations/{id}/accept": {
1313
+ post: {
1314
+ operationId: "invitations.accept",
1315
+ tags: ["invitations"],
1316
+ summary: "Accept a pending invitation",
1317
+ parameters: [id],
1318
+ responses: {
1319
+ "200": ok("The accepted invitation."),
1320
+ "400": ok("Invitation is not pending."),
1321
+ "404": ok("Not found.")
1322
+ }
1323
+ }
1324
+ },
1325
+ "/user_management/users/{id}/api_keys": {
1326
+ get: {
1327
+ operationId: "userApiKeys.list",
1328
+ tags: ["api-keys"],
1329
+ summary: "List a user's API keys",
1330
+ parameters: [id, query("organization_id", "Filter by organization.")],
1331
+ responses: { "200": ok("API key list.") }
1332
+ },
1333
+ post: {
1334
+ operationId: "userApiKeys.create",
1335
+ tags: ["api-keys"],
1336
+ summary: "Create an API key for a user",
1337
+ parameters: [id],
1338
+ requestBody: jsonBody(
1339
+ { name: { type: "string" }, organization_id: { type: "string" } },
1340
+ [],
1341
+ "The API key to create."
1342
+ ),
1343
+ responses: {
1344
+ "201": ok("The created key (value shown once)."),
1345
+ "404": ok("User not found.")
1346
+ }
1347
+ }
1348
+ },
1349
+ "/api_keys/validations": {
1350
+ post: {
1351
+ operationId: "apiKeys.validate",
1352
+ tags: ["api-keys"],
1353
+ summary: "Validate an API key value",
1354
+ requestBody: jsonBody({ value: { type: "string" } }, ["value"], "The API key value to validate."),
1355
+ responses: { "200": ok("The matching key."), "404": ok("Invalid API key.") }
1356
+ }
1357
+ },
1358
+ "/api_keys/{id}": {
1359
+ delete: {
1360
+ operationId: "apiKeys.delete",
1361
+ tags: ["api-keys"],
1362
+ summary: "Delete an API key",
1363
+ parameters: [id],
1364
+ responses: { "204": noContent("Deleted."), "404": ok("Not found.") }
1365
+ }
1366
+ },
1367
+ "/organizations": {
1368
+ post: {
1369
+ operationId: "organizations.create",
1370
+ tags: ["organizations"],
1371
+ summary: "Create an organization",
1372
+ requestBody: jsonBody(
1373
+ { name: { type: "string" }, external_id: { type: "string" } },
1374
+ ["name"],
1375
+ "The organization to create."
1376
+ ),
1377
+ responses: { "201": ok("The created organization."), "422": ok("Validation error.") }
1378
+ }
1379
+ },
1380
+ "/organizations/{id}": {
1381
+ get: {
1382
+ operationId: "organizations.get",
1383
+ tags: ["organizations"],
1384
+ summary: "Retrieve an organization",
1385
+ parameters: [id],
1386
+ responses: { "200": ok("The organization."), "404": ok("Not found.") }
1387
+ },
1388
+ put: {
1389
+ operationId: "organizations.update",
1390
+ tags: ["organizations"],
1391
+ summary: "Update an organization",
1392
+ parameters: [id],
1393
+ requestBody: jsonBody({ name: { type: "string" } }, [], "Fields to update."),
1394
+ responses: { "200": ok("The updated organization."), "404": ok("Not found.") }
1395
+ }
1396
+ },
1397
+ "/organizations/{id}/roles": {
1398
+ get: {
1399
+ operationId: "organizations.roles",
1400
+ tags: ["organizations"],
1401
+ summary: "List an organization's roles",
1402
+ parameters: [id],
1403
+ responses: { "200": ok("Role list."), "404": ok("Not found.") }
1404
+ }
1405
+ },
1406
+ "/sso/jwks/{clientId}": {
1407
+ get: {
1408
+ operationId: "sso.jwks",
1409
+ tags: ["oauth"],
1410
+ summary: "JWKS for verifying issued access tokens",
1411
+ security: [],
1412
+ parameters: [{ name: "clientId", in: "path", required: true, schema: { type: "string" } }],
1413
+ responses: { "200": ok("JSON Web Key Set.") }
1414
+ }
1415
+ },
1416
+ "/.well-known/oauth-authorization-server": {
1417
+ get: {
1418
+ operationId: "oauth.metadata",
1419
+ tags: ["oauth"],
1420
+ summary: "OAuth authorization-server metadata",
1421
+ security: [],
1422
+ responses: { "200": ok("Authorization-server metadata.") }
1423
+ }
1424
+ },
1425
+ "/oauth2/register": {
1426
+ post: {
1427
+ operationId: "oauth.register",
1428
+ tags: ["oauth"],
1429
+ summary: "Dynamically register an OAuth client",
1430
+ security: [],
1431
+ requestBody: jsonBody(
1432
+ {
1433
+ redirect_uris: { type: "array", items: { type: "string" } },
1434
+ client_name: { type: "string" }
1435
+ },
1436
+ [],
1437
+ "The client to register."
1438
+ ),
1439
+ responses: { "201": ok("The registered client.") }
1440
+ }
1441
+ },
1442
+ "/oauth2/token": {
1443
+ post: {
1444
+ operationId: "oauth.token",
1445
+ tags: ["oauth"],
1446
+ summary: "Exchange an authorization code or refresh token for tokens",
1447
+ security: [],
1448
+ requestBody: {
1449
+ required: true,
1450
+ description: "Form-encoded or JSON. authorization_code grants need `code`; refresh_token grants need `refresh_token`.",
1451
+ content: {
1452
+ "application/x-www-form-urlencoded": {
1453
+ schema: {
1454
+ type: "object",
1455
+ properties: {
1456
+ grant_type: {
1457
+ type: "string",
1458
+ enum: ["authorization_code", "refresh_token"]
1459
+ },
1460
+ code: { type: "string" },
1461
+ refresh_token: { type: "string" }
1462
+ },
1463
+ required: ["grant_type"]
1464
+ }
1465
+ },
1466
+ "application/json": {
1467
+ schema: {
1468
+ type: "object",
1469
+ properties: {
1470
+ grant_type: {
1471
+ type: "string",
1472
+ enum: ["authorization_code", "refresh_token"]
1473
+ },
1474
+ code: { type: "string" },
1475
+ refresh_token: { type: "string" }
1476
+ },
1477
+ required: ["grant_type"]
1478
+ }
1479
+ }
1480
+ }
1481
+ },
1482
+ responses: { "200": ok("Access and refresh tokens."), "400": ok("Invalid grant.") }
1483
+ }
1484
+ },
1485
+ "/vault/v1/kv": {
1486
+ post: {
1487
+ operationId: "vault.create",
1488
+ tags: ["vault"],
1489
+ summary: "Create a Vault KV object",
1490
+ requestBody: jsonBody(
1491
+ {
1492
+ name: { type: "string" },
1493
+ value: { type: "string" },
1494
+ key_context: { type: "object" }
1495
+ },
1496
+ ["name"],
1497
+ "The object to create."
1498
+ ),
1499
+ responses: {
1500
+ "201": ok("The created object's metadata."),
1501
+ "400": ok("Validation error."),
1502
+ "409": ok("Name already exists.")
1503
+ }
1504
+ },
1505
+ get: {
1506
+ operationId: "vault.list",
1507
+ tags: ["vault"],
1508
+ summary: "List Vault KV objects",
1509
+ responses: { "200": ok("Object list (id + name).") }
1510
+ }
1511
+ },
1512
+ "/vault/v1/kv/name/{name}": {
1513
+ get: {
1514
+ operationId: "vault.readByName",
1515
+ tags: ["vault"],
1516
+ summary: "Read a Vault KV object by name",
1517
+ parameters: [{ name: "name", in: "path", required: true, schema: { type: "string" } }],
1518
+ responses: { "200": ok("The object."), "404": ok("Not found.") }
1519
+ }
1520
+ },
1521
+ "/vault/v1/kv/{id}": {
1522
+ get: {
1523
+ operationId: "vault.read",
1524
+ tags: ["vault"],
1525
+ summary: "Read a Vault KV object",
1526
+ parameters: [id],
1527
+ responses: { "200": ok("The object."), "404": ok("Not found.") }
1528
+ },
1529
+ put: {
1530
+ operationId: "vault.update",
1531
+ tags: ["vault"],
1532
+ summary: "Update a Vault KV object's value",
1533
+ parameters: [id],
1534
+ requestBody: jsonBody(
1535
+ { value: { type: "string" }, version_check: { type: "string" } },
1536
+ [],
1537
+ "The new value; pass `version_check` for optimistic concurrency."
1538
+ ),
1539
+ responses: {
1540
+ "200": ok("The updated object."),
1541
+ "404": ok("Not found."),
1542
+ "409": ok("Version check failed.")
1543
+ }
1544
+ },
1545
+ delete: {
1546
+ operationId: "vault.delete",
1547
+ tags: ["vault"],
1548
+ summary: "Delete a Vault KV object",
1549
+ parameters: [id],
1550
+ responses: { "204": noContent("Deleted."), "404": ok("Not found.") }
1551
+ }
1552
+ }
1553
+ }
1554
+ };
1555
+ }
1140
1556
  var manifest = {
1141
1557
  id: "workos",
1142
1558
  name: "WorkOS",
@@ -1144,43 +1560,120 @@ var manifest = {
1144
1560
  docsUrl: "https://docs.emulators.dev/workos",
1145
1561
  surfaces: [
1146
1562
  { id: "rest", kind: "rest", title: "WorkOS REST API", status: "partial", basePath: "/" },
1147
- { id: "authkit", kind: "ui", title: "Hosted AuthKit login", status: "supported", basePath: "/user_management/authorize" },
1563
+ {
1564
+ id: "authkit",
1565
+ kind: "ui",
1566
+ title: "Hosted AuthKit login",
1567
+ status: "supported",
1568
+ basePath: "/user_management/authorize"
1569
+ },
1148
1570
  { id: "oauth", kind: "rest", title: "OAuth authorization server (MCP)", status: "supported", basePath: "/oauth2" },
1149
1571
  { id: "vault", kind: "rest", title: "Vault KV", status: "supported", basePath: "/vault/v1/kv" }
1150
1572
  ],
1151
1573
  auth: [{ id: "api-key", title: "WorkOS secret key", type: "api-key", status: "supported" }],
1152
1574
  specs: [
1153
1575
  {
1154
- kind: "manual",
1576
+ kind: "openapi",
1155
1577
  title: "WorkOS User Management + Organizations subset",
1156
1578
  coverage: "hand-authored",
1579
+ url: "/openapi.json",
1157
1580
  operations: [
1158
- { operationId: "userManagement.authorize", method: "GET", path: "/user_management/authorize", status: "hand-authored" },
1159
- { operationId: "userManagement.authenticate", method: "POST", path: "/user_management/authenticate", status: "hand-authored" },
1160
- { operationId: "userManagement.getUser", method: "GET", path: "/user_management/users/:id", status: "hand-authored" },
1161
- { operationId: "memberships.list", method: "GET", path: "/user_management/organization_memberships", status: "hand-authored" },
1162
- { operationId: "memberships.create", method: "POST", path: "/user_management/organization_memberships", status: "hand-authored" },
1163
- { operationId: "memberships.get", method: "GET", path: "/user_management/organization_memberships/:id", status: "hand-authored" },
1164
- { operationId: "memberships.update", method: "PUT", path: "/user_management/organization_memberships/:id", status: "hand-authored" },
1165
- { operationId: "memberships.delete", method: "DELETE", path: "/user_management/organization_memberships/:id", status: "hand-authored" },
1166
- { operationId: "invitations.list", method: "GET", path: "/user_management/invitations", status: "hand-authored" },
1167
- { operationId: "invitations.send", method: "POST", path: "/user_management/invitations", status: "hand-authored" },
1168
- { operationId: "invitations.accept", method: "POST", path: "/user_management/invitations/:id/accept", status: "hand-authored" },
1169
- { operationId: "userApiKeys.list", method: "GET", path: "/user_management/users/:id/api_keys", status: "hand-authored" },
1170
- { operationId: "userApiKeys.create", method: "POST", path: "/user_management/users/:id/api_keys", status: "hand-authored" },
1581
+ {
1582
+ operationId: "userManagement.authenticate",
1583
+ method: "POST",
1584
+ path: "/user_management/authenticate",
1585
+ status: "hand-authored"
1586
+ },
1587
+ {
1588
+ operationId: "userManagement.getUser",
1589
+ method: "GET",
1590
+ path: "/user_management/users/:id",
1591
+ status: "hand-authored"
1592
+ },
1593
+ {
1594
+ operationId: "memberships.list",
1595
+ method: "GET",
1596
+ path: "/user_management/organization_memberships",
1597
+ status: "hand-authored"
1598
+ },
1599
+ {
1600
+ operationId: "memberships.create",
1601
+ method: "POST",
1602
+ path: "/user_management/organization_memberships",
1603
+ status: "hand-authored"
1604
+ },
1605
+ {
1606
+ operationId: "memberships.get",
1607
+ method: "GET",
1608
+ path: "/user_management/organization_memberships/:id",
1609
+ status: "hand-authored"
1610
+ },
1611
+ {
1612
+ operationId: "memberships.update",
1613
+ method: "PUT",
1614
+ path: "/user_management/organization_memberships/:id",
1615
+ status: "hand-authored"
1616
+ },
1617
+ {
1618
+ operationId: "memberships.delete",
1619
+ method: "DELETE",
1620
+ path: "/user_management/organization_memberships/:id",
1621
+ status: "hand-authored"
1622
+ },
1623
+ {
1624
+ operationId: "invitations.list",
1625
+ method: "GET",
1626
+ path: "/user_management/invitations",
1627
+ status: "hand-authored"
1628
+ },
1629
+ {
1630
+ operationId: "invitations.send",
1631
+ method: "POST",
1632
+ path: "/user_management/invitations",
1633
+ status: "hand-authored"
1634
+ },
1635
+ {
1636
+ operationId: "invitations.accept",
1637
+ method: "POST",
1638
+ path: "/user_management/invitations/:id/accept",
1639
+ status: "hand-authored"
1640
+ },
1641
+ {
1642
+ operationId: "userApiKeys.list",
1643
+ method: "GET",
1644
+ path: "/user_management/users/:id/api_keys",
1645
+ status: "hand-authored"
1646
+ },
1647
+ {
1648
+ operationId: "userApiKeys.create",
1649
+ method: "POST",
1650
+ path: "/user_management/users/:id/api_keys",
1651
+ status: "hand-authored"
1652
+ },
1171
1653
  { operationId: "apiKeys.validate", method: "POST", path: "/api_keys/validations", status: "hand-authored" },
1172
1654
  { operationId: "apiKeys.delete", method: "DELETE", path: "/api_keys/:id", status: "hand-authored" },
1173
1655
  { operationId: "organizations.create", method: "POST", path: "/organizations", status: "hand-authored" },
1174
1656
  { operationId: "organizations.get", method: "GET", path: "/organizations/:id", status: "hand-authored" },
1175
1657
  { operationId: "organizations.update", method: "PUT", path: "/organizations/:id", status: "hand-authored" },
1176
- { operationId: "organizations.roles", method: "GET", path: "/organizations/:id/roles", status: "hand-authored" },
1658
+ {
1659
+ operationId: "organizations.roles",
1660
+ method: "GET",
1661
+ path: "/organizations/:id/roles",
1662
+ status: "hand-authored"
1663
+ },
1177
1664
  { operationId: "sso.jwks", method: "GET", path: "/sso/jwks/:clientId", status: "hand-authored" },
1178
- { operationId: "oauth.metadata", method: "GET", path: "/.well-known/oauth-authorization-server", status: "hand-authored" },
1665
+ {
1666
+ operationId: "oauth.metadata",
1667
+ method: "GET",
1668
+ path: "/.well-known/oauth-authorization-server",
1669
+ status: "hand-authored"
1670
+ },
1179
1671
  { operationId: "oauth.register", method: "POST", path: "/oauth2/register", status: "hand-authored" },
1180
- { operationId: "oauth.authorize", method: "GET", path: "/oauth2/authorize", status: "hand-authored" },
1181
1672
  { operationId: "oauth.token", method: "POST", path: "/oauth2/token", status: "hand-authored" },
1182
1673
  { operationId: "vault.create", method: "POST", path: "/vault/v1/kv", status: "hand-authored" },
1674
+ { operationId: "vault.list", method: "GET", path: "/vault/v1/kv", status: "hand-authored" },
1183
1675
  { operationId: "vault.readByName", method: "GET", path: "/vault/v1/kv/name/:name", status: "hand-authored" },
1676
+ { operationId: "vault.read", method: "GET", path: "/vault/v1/kv/:id", status: "hand-authored" },
1184
1677
  { operationId: "vault.update", method: "PUT", path: "/vault/v1/kv/:id", status: "hand-authored" },
1185
1678
  { operationId: "vault.delete", method: "DELETE", path: "/vault/v1/kv/:id", status: "hand-authored" }
1186
1679
  ]
@@ -1233,6 +1726,12 @@ var manifest = {
1233
1726
  };
1234
1727
  function seedFromConfig(store, _baseUrl, config) {
1235
1728
  const ws = getWorkosStore(store);
1729
+ if (config.oauth !== void 0) {
1730
+ const ttl = config.oauth.default_access_token_ttl_seconds ?? null;
1731
+ const existing = ws.oauthSettings.all()[0];
1732
+ if (existing) ws.oauthSettings.update(existing.id, { default_access_token_ttl_seconds: ttl });
1733
+ else ws.oauthSettings.insert({ default_access_token_ttl_seconds: ttl });
1734
+ }
1236
1735
  for (const user of config.users ?? []) {
1237
1736
  const created = ensureUserByEmail(ws, user.email);
1238
1737
  if (user.first_name || user.last_name) {
@@ -1269,6 +1768,7 @@ var workosPlugin = {
1269
1768
  organizationRoutes(ctx);
1270
1769
  apiKeyRoutes(ctx);
1271
1770
  vaultRoutes(ctx);
1771
+ openapiRoutes(ctx);
1272
1772
  },
1273
1773
  seed(_store, _baseUrl) {
1274
1774
  }
@@ -1281,4 +1781,4 @@ export {
1281
1781
  seedFromConfig,
1282
1782
  workosPlugin
1283
1783
  };
1284
- //# sourceMappingURL=dist-IYZPDKJW.js.map
1784
+ //# sourceMappingURL=dist-S47YJ552.js.map