@opengeni/api-router 0.26.1 → 0.30.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/dist/app.d.ts +1 -0
  2. package/dist/app.js +5 -3
  3. package/dist/auth/canonical-human-session-admission.d.ts +27 -0
  4. package/dist/browser-auth-broker.d.ts +20 -0
  5. package/dist/browser-controller-authority.d.ts +19 -1
  6. package/dist/browser-network-route.d.ts +8 -0
  7. package/dist/{chunk-JIKNR5YL.js → chunk-2PQMSRCB.js} +15518 -6863
  8. package/dist/chunk-2PQMSRCB.js.map +1 -0
  9. package/dist/controller-data-plane.d.ts +12 -0
  10. package/dist/http/auth.d.ts +1 -1
  11. package/dist/http/sse.d.ts +18 -1
  12. package/dist/index.js +1 -1
  13. package/dist/integrations/api-integrations.d.ts +2 -2
  14. package/dist/integrations/fiken.d.ts +233 -0
  15. package/dist/integrations/google-drive.d.ts +6 -6
  16. package/dist/integrations/provider-oauth.d.ts +0 -2
  17. package/dist/integrations/slack-bot.d.ts +171 -1
  18. package/dist/integrations/slack-interactions.d.ts +17 -5
  19. package/dist/interaction-holder-heartbeat.d.ts +17 -0
  20. package/dist/interaction-metrics.d.ts +11 -0
  21. package/dist/mcp/server.d.ts +3 -2
  22. package/dist/model-catalog.d.ts +1 -0
  23. package/dist/routes/canonical-human-identities.d.ts +3 -0
  24. package/dist/routes/channels.d.ts +3 -0
  25. package/dist/routes/integration-facets.d.ts +3 -0
  26. package/dist/routes/interaction-resources.d.ts +5 -0
  27. package/dist/routes/organization-memberships.d.ts +3 -0
  28. package/dist/routes/sessions.d.ts +2 -1
  29. package/dist/routes/slack-task-policy.d.ts +3 -0
  30. package/dist/routes/supergrok.d.ts +3 -0
  31. package/dist/sandbox/channel-a.d.ts +8 -1
  32. package/dist/sandbox/machines.d.ts +2 -2
  33. package/dist/sandbox/viewer.d.ts +3 -1
  34. package/dist/slack-reaction-files.d.ts +27 -0
  35. package/dist/transcription/providers/xai-subscription.d.ts +8 -0
  36. package/dist/xai-realtime.d.ts +26 -0
  37. package/dist/xai-subscription-auth.d.ts +23 -0
  38. package/package.json +17 -16
  39. package/src/app.ts +352 -49
  40. package/src/auth/canonical-human-session-admission.ts +116 -0
  41. package/src/auth/managed-auth.ts +101 -0
  42. package/src/browser-auth-broker.ts +155 -0
  43. package/src/browser-controller-authority.ts +50 -1
  44. package/src/browser-network-route.ts +34 -0
  45. package/src/controller-data-plane.ts +35 -0
  46. package/src/http/auth.ts +27 -9
  47. package/src/http/sse.ts +229 -5
  48. package/src/integrations/api-integrations.ts +57 -42
  49. package/src/integrations/fiken.ts +1230 -0
  50. package/src/integrations/google-drive.ts +31 -26
  51. package/src/integrations/provider-oauth.ts +70 -91
  52. package/src/integrations/slack-bot.ts +638 -9
  53. package/src/integrations/slack-interactions.ts +1583 -73
  54. package/src/interaction-holder-heartbeat.ts +95 -0
  55. package/src/interaction-metrics.ts +159 -0
  56. package/src/mcp/documents.ts +115 -6
  57. package/src/mcp/server.ts +865 -242
  58. package/src/model-catalog.ts +19 -6
  59. package/src/routes/api-integrations.ts +32 -29
  60. package/src/routes/browser-identities.ts +24 -0
  61. package/src/routes/browser-sessions.ts +2122 -183
  62. package/src/routes/canonical-human-identities.ts +156 -0
  63. package/src/routes/channels.ts +90 -0
  64. package/src/routes/computer-sessions.ts +441 -86
  65. package/src/routes/connections.ts +141 -4
  66. package/src/routes/{integration-features.ts → integration-facets.ts} +63 -63
  67. package/src/routes/interaction-resources.ts +595 -0
  68. package/src/routes/organization-memberships.ts +53 -0
  69. package/src/routes/plugins.ts +2 -1
  70. package/src/routes/sessions.ts +419 -43
  71. package/src/routes/skills.ts +92 -10
  72. package/src/routes/slack-task-policy.ts +115 -0
  73. package/src/routes/supergrok.ts +717 -0
  74. package/src/routes/transcription-recordings.ts +9 -2
  75. package/src/routes/transcriptions.ts +2 -1
  76. package/src/routes/video-generation.ts +34 -4
  77. package/src/routes/workspaces.ts +22 -4
  78. package/src/sandbox/channel-a.ts +29 -2
  79. package/src/sandbox/machines.ts +5 -5
  80. package/src/sandbox/viewer.ts +10 -3
  81. package/src/slack-reaction-files.ts +181 -0
  82. package/src/transcription/providers/xai-subscription.ts +110 -0
  83. package/src/transcription/service.ts +17 -2
  84. package/src/xai-realtime.ts +216 -0
  85. package/src/xai-subscription-auth.ts +132 -0
  86. package/dist/chunk-JIKNR5YL.js.map +0 -1
  87. package/dist/routes/integration-features.d.ts +0 -3
@@ -12,6 +12,12 @@ import {
12
12
  ApiIntegrationOAuthStartRequest,
13
13
  ConnectionResponse,
14
14
  CreateConnectionRequest,
15
+ FIKEN_CREDENTIAL_LABEL,
16
+ FIKEN_CREDENTIAL_ROLE,
17
+ FIKEN_PROVIDER_DOMAIN,
18
+ FikenInstallRequest,
19
+ FikenOAuthStartRequest,
20
+ FikenOAuthStartResponse,
15
21
  IntegrationClientMetadata,
16
22
  ListSlackInstallationBindingsResponse,
17
23
  ListConnectionsResponse,
@@ -34,13 +40,17 @@ import {
34
40
  GoogleDriveOAuthStartResponse,
35
41
  } from "@opengeni/contracts/google-drive";
36
42
  import {
43
+ fikenConnectionMetadata,
37
44
  hasPermission,
45
+ hasReservedFikenMetadata,
38
46
  hasReservedOpenGeniSlackBotMetadata,
47
+ isFikenConnection,
39
48
  isOpenGeniSlackBotConnection,
40
49
  openGeniSlackBotMetadata,
41
50
  requireAccessGrant,
42
51
  requireAccessGrantAuthorization,
43
52
  requireEnvironmentEncryption,
53
+ resolveFikenDefaultCompanySlug,
44
54
  } from "@opengeni/core";
45
55
  import {
46
56
  consumeIntegrationOAuthStateNonce,
@@ -74,7 +84,6 @@ import {
74
84
  } from "../integrations/google-drive";
75
85
  import {
76
86
  completeApiIntegrationProviderOAuth,
77
- isApiIntegrationProviderOAuthState,
78
87
  startApiIntegrationProviderOAuth,
79
88
  } from "../integrations/provider-oauth";
80
89
  import {
@@ -96,6 +105,12 @@ import {
96
105
  SlackBotCredentialVerificationError,
97
106
  verifyOpenGeniSlackBotCredential,
98
107
  } from "../integrations/slack-bot";
108
+ import {
109
+ completeFikenOAuthCallback,
110
+ fikenCredentialBundle,
111
+ startFikenOAuth,
112
+ verifyFikenApiToken,
113
+ } from "../integrations/fiken";
99
114
  import {
100
115
  OPENGENI_SLACK_BOT_CREDENTIAL_LABEL,
101
116
  OPENGENI_SLACK_BOT_CREDENTIAL_ROLE,
@@ -152,6 +167,7 @@ export function registerConnectionRoutes(app: Hono, deps: ApiRouteDeps): void {
152
167
  const grant = await requireAccessGrant(c, deps, workspaceId, "connections:write");
153
168
  const payload = CreateConnectionRequest.parse(await c.req.json());
154
169
  assertNotReservedSlackBotMetadata(payload.metadata);
170
+ assertNotReservedFikenMetadata(payload.metadata);
155
171
  assertNotReservedApiIntegrationOAuthMetadata(payload.metadata);
156
172
  const key = requireEnvironmentEncryption(settings);
157
173
  const subjectId = createConnectionSubjectId(payload, grant.subjectId);
@@ -306,6 +322,120 @@ export function registerConnectionRoutes(app: Hono, deps: ApiRouteDeps): void {
306
322
  }
307
323
  });
308
324
 
325
+ // Verified paste-a-token install for the first-party Fiken connector. The
326
+ // token is validated against Fiken (and its accessible companies discovered)
327
+ // before it enters encrypted storage. Workspace-owned only.
328
+ app.post("/v1/workspaces/:workspaceId/connections/fiken/install", async (c) => {
329
+ assertIntegrationsEnabled();
330
+ const workspaceId = c.req.param("workspaceId");
331
+ const grant = await requireAccessGrant(c, deps, workspaceId, "connections:write");
332
+ const payload = FikenInstallRequest.parse(await c.req.json());
333
+ const key = requireEnvironmentEncryption(settings);
334
+ const existing = payload.connectionId
335
+ ? await getConnectionMetadata(db, workspaceId, payload.connectionId, null)
336
+ : null;
337
+ if (payload.connectionId && !existing) {
338
+ throw new HTTPException(404, { message: "connection not found" });
339
+ }
340
+ if (existing && !isFikenConnection(existing)) {
341
+ throw new HTTPException(422, { message: "connectionId is not a Fiken connection" });
342
+ }
343
+ const verified = await verifyFikenApiToken(payload.apiToken, deps.fikenFetch ?? fetch);
344
+ const previousDefault = existing
345
+ ? (fikenConnectionMetadata(existing.metadata)?.defaultCompanySlug ?? null)
346
+ : null;
347
+ const defaultCompanySlug = resolveFikenDefaultCompanySlug({
348
+ requested: payload.defaultCompanySlug ?? null,
349
+ previous: previousDefault,
350
+ companies: verified.companies,
351
+ });
352
+ if (payload.defaultCompanySlug && defaultCompanySlug !== payload.defaultCompanySlug) {
353
+ throw new HTTPException(422, {
354
+ message: `defaultCompanySlug is not among the companies this token can access: ${verified.companies
355
+ .map((company) => company.slug)
356
+ .join(", ")}`,
357
+ });
358
+ }
359
+ const metadata = {
360
+ credentialRole: FIKEN_CREDENTIAL_ROLE,
361
+ credentialLabel: FIKEN_CREDENTIAL_LABEL,
362
+ companies: verified.companies,
363
+ defaultCompanySlug,
364
+ verifiedAt: new Date().toISOString(),
365
+ };
366
+ const credentialEncrypted = encryptCredentialBundle(
367
+ key,
368
+ fikenCredentialBundle(payload.apiToken),
369
+ );
370
+ if (existing) {
371
+ // Rewrites the whole credential identity: a token pasted over an OAuth
372
+ // row must also flip kind and clear the OAuth expiry, or the broker
373
+ // keeps treating the api_key bundle as a refreshable oauth2 credential.
374
+ const updated = await updateConnection(db, {
375
+ workspaceId,
376
+ connectionId: existing.id,
377
+ visibleToSubjectId: null,
378
+ expectedVersion: existing.version,
379
+ kind: "api_key",
380
+ status: "active",
381
+ credentialEncrypted,
382
+ grantedScopes: [],
383
+ expiresAt: null,
384
+ metadata,
385
+ updatedBySubjectId: grant.subjectId,
386
+ });
387
+ if (!updated) {
388
+ throw new HTTPException(409, { message: "the Fiken connection changed; retry" });
389
+ }
390
+ return c.json(ConnectionResponse.parse({ connection: updated }));
391
+ }
392
+ const connection = await createConnection(db, {
393
+ accountId: grant.accountId,
394
+ workspaceId,
395
+ subjectId: null,
396
+ providerDomain: FIKEN_PROVIDER_DOMAIN,
397
+ kind: "api_key",
398
+ credentialEncrypted,
399
+ grantedScopes: [],
400
+ expiresAt: null,
401
+ metadata,
402
+ createdBySubjectId: grant.subjectId,
403
+ });
404
+ return c.json(ConnectionResponse.parse({ connection }), 201);
405
+ });
406
+
407
+ // Fiken OAuth (registered app) start. Both Fiken lanes produce the same
408
+ // workspace-owned connection shape; this one refreshes through the broker.
409
+ app.post("/v1/workspaces/:workspaceId/connections/fiken/oauth/start", async (c) => {
410
+ assertIntegrationsEnabled();
411
+ const workspaceId = c.req.param("workspaceId");
412
+ const grant = await requireAccessGrant(c, deps, workspaceId, "connections:write");
413
+ const payload = FikenOAuthStartRequest.parse(await c.req.json());
414
+ requireEnvironmentEncryption(settings);
415
+ return c.json(
416
+ FikenOAuthStartResponse.parse(
417
+ await startFikenOAuth(deps, {
418
+ accountId: grant.accountId,
419
+ workspaceId,
420
+ subjectId: grant.subjectId,
421
+ requestUrl: c.req.url,
422
+ payload,
423
+ }),
424
+ ),
425
+ );
426
+ });
427
+
428
+ app.get("/v1/integrations/fiken/callback", async (c) => {
429
+ assertIntegrationsEnabled();
430
+ const result = await completeFikenOAuthCallback(deps, {
431
+ ...(c.req.query("code") ? { code: c.req.query("code") } : {}),
432
+ ...(c.req.query("state") ? { state: c.req.query("state") } : {}),
433
+ ...(c.req.query("error") ? { error: c.req.query("error") } : {}),
434
+ requestUrl: c.req.url,
435
+ });
436
+ return c.redirect(result.redirectTo, 302);
437
+ });
438
+
309
439
  app.post("/v1/workspaces/:workspaceId/connections/google-drive/install", async (c) => {
310
440
  assertIntegrationsEnabled();
311
441
  const workspaceId = c.req.param("workspaceId");
@@ -429,9 +559,7 @@ export function registerConnectionRoutes(app: Hono, deps: ApiRouteDeps): void {
429
559
  ...(c.req.query("error") ? { error: c.req.query("error") } : {}),
430
560
  requestUrl: c.req.url,
431
561
  };
432
- const result = isApiIntegrationProviderOAuthState(input.state, deps.settings)
433
- ? await completeApiIntegrationProviderOAuth(deps, input)
434
- : await completeGoogleDriveOAuthCallback(deps, input);
562
+ const result = await completeGoogleDriveOAuthCallback(deps, input);
435
563
  return c.redirect(result.redirectTo, 302);
436
564
  });
437
565
 
@@ -532,6 +660,7 @@ export function registerConnectionRoutes(app: Hono, deps: ApiRouteDeps): void {
532
660
  const { grant } = authorization;
533
661
  const payload = UpdateConnectionRequest.parse(await c.req.json());
534
662
  assertNotReservedSlackBotMetadata(payload.metadata);
663
+ assertNotReservedFikenMetadata(payload.metadata);
535
664
  assertNotReservedApiIntegrationOAuthMetadata(payload.metadata);
536
665
  const existing = await getConnectionMetadata(
537
666
  db,
@@ -1135,6 +1264,14 @@ function assertNotReservedSlackBotMetadata(metadata: Record<string, unknown> | u
1135
1264
  }
1136
1265
  }
1137
1266
 
1267
+ function assertNotReservedFikenMetadata(metadata: Record<string, unknown> | undefined): void {
1268
+ if (hasReservedFikenMetadata(metadata)) {
1269
+ throw new HTTPException(422, {
1270
+ message: "Fiken connection metadata is reserved for the verified Fiken connect flows",
1271
+ });
1272
+ }
1273
+ }
1274
+
1138
1275
  function assertNotReservedApiIntegrationOAuthMetadata(
1139
1276
  metadata: Record<string, unknown> | undefined,
1140
1277
  ): void {
@@ -1,9 +1,9 @@
1
1
  import {
2
- IntegrationFeatureMutationResult,
3
- IntegrationFeatureRemovalResult,
4
- IntegrationInstanceFeaturesResponse,
5
- MutateIntegrationFeatureRequest,
6
- UpsertIntegrationFeatureRequest,
2
+ IntegrationFacetMutationResult,
3
+ IntegrationFacetRemovalResult,
4
+ IntegrationInstanceFacetsResponse,
5
+ MutateIntegrationFacetRequest,
6
+ UpsertIntegrationFacetRequest,
7
7
  } from "@opengeni/contracts";
8
8
  import {
9
9
  hasPermission,
@@ -12,52 +12,52 @@ import {
12
12
  type ApiRouteDeps,
13
13
  } from "@opengeni/core";
14
14
  import {
15
- configureIntegrationFeature,
16
- IntegrationFeatureBindingOwnershipConflictError,
17
- IntegrationFeatureBindingVersionConflictError,
18
- IntegrationFeatureBindingVersionRequiredError,
19
- IntegrationFeatureConfigError,
20
- IntegrationFeatureConnectionError,
21
- IntegrationFeatureNotFoundError,
22
- IntegrationFeatureOperationIdempotencyError,
23
- listIntegrationInstanceFeatures,
24
- removeIntegrationFeature,
25
- setIntegrationFeatureLifecycle,
15
+ configureIntegrationFacet,
16
+ IntegrationFacetBindingOwnershipConflictError,
17
+ IntegrationFacetBindingVersionConflictError,
18
+ IntegrationFacetBindingVersionRequiredError,
19
+ IntegrationFacetConfigError,
20
+ IntegrationFacetConnectionError,
21
+ IntegrationFacetNotFoundError,
22
+ IntegrationFacetOperationIdempotencyError,
23
+ listIntegrationInstanceFacets,
24
+ removeIntegrationFacet,
25
+ setIntegrationFacetLifecycle,
26
26
  } from "@opengeni/db";
27
27
  import type { Hono } from "hono";
28
28
  import { HTTPException } from "hono/http-exception";
29
29
 
30
30
  import {
31
- browseGoogleDriveIntegrationSource,
32
- saveGoogleDriveIntegrationSource,
31
+ browseGoogleDriveFacetSource,
32
+ saveGoogleDriveFacetSource,
33
33
  } from "../integrations/google-drive";
34
34
 
35
- export function registerIntegrationFeatureRoutes(app: Hono, deps: ApiRouteDeps): void {
35
+ export function registerIntegrationFacetRoutes(app: Hono, deps: ApiRouteDeps): void {
36
36
  app.get(
37
- "/v1/workspaces/:workspaceId/integrations/:capabilityId/instances/:instanceKey/features/:featureKey/browse",
37
+ "/v1/workspaces/:workspaceId/integrations/:capabilityId/instances/:instanceKey/facets/:facetKey/browse",
38
38
  async (c) => {
39
39
  const workspaceId = c.req.param("workspaceId");
40
40
  const grant = await requireAccessGrant(c, deps, workspaceId, "connections:read");
41
41
  try {
42
42
  return c.json(
43
- await browseGoogleDriveIntegrationSource(deps, {
43
+ await browseGoogleDriveFacetSource(deps, {
44
44
  workspaceId,
45
45
  subjectId: grant.subjectId,
46
46
  capabilityId: decoded(c.req.param("capabilityId")),
47
47
  instanceKey: decoded(c.req.param("instanceKey")),
48
- featureKey: decoded(c.req.param("featureKey")),
48
+ facetKey: decoded(c.req.param("facetKey")),
49
49
  parentId: c.req.query("parentId") ?? "root",
50
50
  ...(c.req.query("pageToken") ? { pageToken: c.req.query("pageToken") } : {}),
51
51
  }),
52
52
  );
53
53
  } catch (error) {
54
- throw featureHttpError(error);
54
+ throw facetHttpError(error);
55
55
  }
56
56
  },
57
57
  );
58
58
 
59
59
  app.put(
60
- "/v1/workspaces/:workspaceId/integrations/:capabilityId/instances/:instanceKey/features/:featureKey/source",
60
+ "/v1/workspaces/:workspaceId/integrations/:capabilityId/instances/:instanceKey/facets/:facetKey/source",
61
61
  async (c) => {
62
62
  const workspaceId = c.req.param("workspaceId");
63
63
  const authorization = await requireAccessGrantAuthorization(
@@ -69,14 +69,14 @@ export function registerIntegrationFeatureRoutes(app: Hono, deps: ApiRouteDeps):
69
69
  const { grant } = authorization;
70
70
  try {
71
71
  return c.json(
72
- IntegrationFeatureMutationResult.parse(
73
- await saveGoogleDriveIntegrationSource(deps, {
72
+ IntegrationFacetMutationResult.parse(
73
+ await saveGoogleDriveFacetSource(deps, {
74
74
  accountId: grant.accountId,
75
75
  workspaceId,
76
76
  subjectId: grant.subjectId,
77
77
  capabilityId: decoded(c.req.param("capabilityId")),
78
78
  instanceKey: decoded(c.req.param("instanceKey")),
79
- featureKey: decoded(c.req.param("featureKey")),
79
+ facetKey: decoded(c.req.param("facetKey")),
80
80
  payload: await c.req.json(),
81
81
  canManageOrganizationDestination:
82
82
  authorization.accountGrant?.permissions.includes("account:admin") === true,
@@ -88,20 +88,20 @@ export function registerIntegrationFeatureRoutes(app: Hono, deps: ApiRouteDeps):
88
88
  ),
89
89
  );
90
90
  } catch (error) {
91
- throw featureHttpError(error);
91
+ throw facetHttpError(error);
92
92
  }
93
93
  },
94
94
  );
95
95
 
96
96
  app.get(
97
- "/v1/workspaces/:workspaceId/integrations/:capabilityId/instances/:instanceKey/features",
97
+ "/v1/workspaces/:workspaceId/integrations/:capabilityId/instances/:instanceKey/facets",
98
98
  async (c) => {
99
99
  const workspaceId = c.req.param("workspaceId");
100
100
  const grant = await requireAccessGrant(c, deps, workspaceId, "workspace:read");
101
101
  try {
102
102
  return c.json(
103
- IntegrationInstanceFeaturesResponse.parse(
104
- await listIntegrationInstanceFeatures(
103
+ IntegrationInstanceFacetsResponse.parse(
104
+ await listIntegrationInstanceFacets(
105
105
  deps.db,
106
106
  workspaceId,
107
107
  grant.subjectId,
@@ -111,30 +111,30 @@ export function registerIntegrationFeatureRoutes(app: Hono, deps: ApiRouteDeps):
111
111
  ),
112
112
  );
113
113
  } catch (error) {
114
- throw featureHttpError(error);
114
+ throw facetHttpError(error);
115
115
  }
116
116
  },
117
117
  );
118
118
 
119
119
  app.put(
120
- "/v1/workspaces/:workspaceId/integrations/:capabilityId/instances/:instanceKey/features/:featureKey",
120
+ "/v1/workspaces/:workspaceId/integrations/:capabilityId/instances/:instanceKey/facets/:facetKey",
121
121
  async (c) => {
122
122
  const workspaceId = c.req.param("workspaceId");
123
123
  const grant = await requireAccessGrant(c, deps, workspaceId, "workspace:admin");
124
- const payload = UpsertIntegrationFeatureRequest.parse(await c.req.json());
124
+ const payload = UpsertIntegrationFacetRequest.parse(await c.req.json());
125
125
  const capabilityId = decoded(c.req.param("capabilityId"));
126
126
  const instanceKey = decoded(c.req.param("instanceKey"));
127
- const featureKey = decoded(c.req.param("featureKey"));
127
+ const facetKey = decoded(c.req.param("facetKey"));
128
128
  try {
129
- const instance = await listIntegrationInstanceFeatures(
129
+ const instance = await listIntegrationInstanceFacets(
130
130
  deps.db,
131
131
  workspaceId,
132
132
  grant.subjectId,
133
133
  capabilityId,
134
134
  instanceKey,
135
135
  );
136
- const definition = instance.features.find(
137
- (feature) => feature.definition.featureKey === featureKey,
136
+ const definition = instance.facets.find(
137
+ (facet) => facet.definition.facetKey === facetKey,
138
138
  )?.definition;
139
139
  if (
140
140
  definition?.kind === "knowledge_source" &&
@@ -146,14 +146,14 @@ export function registerIntegrationFeatureRoutes(app: Hono, deps: ApiRouteDeps):
146
146
  });
147
147
  }
148
148
  return c.json(
149
- IntegrationFeatureMutationResult.parse(
150
- await configureIntegrationFeature(deps.db, {
149
+ IntegrationFacetMutationResult.parse(
150
+ await configureIntegrationFacet(deps.db, {
151
151
  accountId: grant.accountId,
152
152
  workspaceId,
153
153
  subjectId: grant.subjectId,
154
154
  capabilityId,
155
155
  instanceKey,
156
- featureKey,
156
+ facetKey,
157
157
  displayName: payload.displayName,
158
158
  config: payload.config,
159
159
  ...(payload.expectedVersion !== undefined
@@ -165,28 +165,28 @@ export function registerIntegrationFeatureRoutes(app: Hono, deps: ApiRouteDeps):
165
165
  payload.expectedVersion === undefined ? 201 : 200,
166
166
  );
167
167
  } catch (error) {
168
- throw featureHttpError(error);
168
+ throw facetHttpError(error);
169
169
  }
170
170
  },
171
171
  );
172
172
 
173
173
  for (const action of ["pause", "resume"] as const) {
174
174
  app.post(
175
- `/v1/workspaces/:workspaceId/integrations/:capabilityId/instances/:instanceKey/features/:featureKey/${action}`,
175
+ `/v1/workspaces/:workspaceId/integrations/:capabilityId/instances/:instanceKey/facets/:facetKey/${action}`,
176
176
  async (c) => {
177
177
  const workspaceId = c.req.param("workspaceId");
178
178
  const grant = await requireAccessGrant(c, deps, workspaceId, "workspace:admin");
179
- const payload = MutateIntegrationFeatureRequest.parse(await c.req.json());
179
+ const payload = MutateIntegrationFacetRequest.parse(await c.req.json());
180
180
  try {
181
181
  return c.json(
182
- IntegrationFeatureMutationResult.parse(
183
- await setIntegrationFeatureLifecycle(deps.db, {
182
+ IntegrationFacetMutationResult.parse(
183
+ await setIntegrationFacetLifecycle(deps.db, {
184
184
  accountId: grant.accountId,
185
185
  workspaceId,
186
186
  subjectId: grant.subjectId,
187
187
  capabilityId: decoded(c.req.param("capabilityId")),
188
188
  instanceKey: decoded(c.req.param("instanceKey")),
189
- featureKey: decoded(c.req.param("featureKey")),
189
+ facetKey: decoded(c.req.param("facetKey")),
190
190
  action,
191
191
  expectedVersion: payload.expectedVersion,
192
192
  idempotencyKey: payload.idempotencyKey,
@@ -194,35 +194,35 @@ export function registerIntegrationFeatureRoutes(app: Hono, deps: ApiRouteDeps):
194
194
  ),
195
195
  );
196
196
  } catch (error) {
197
- throw featureHttpError(error);
197
+ throw facetHttpError(error);
198
198
  }
199
199
  },
200
200
  );
201
201
  }
202
202
 
203
203
  app.delete(
204
- "/v1/workspaces/:workspaceId/integrations/:capabilityId/instances/:instanceKey/features/:featureKey",
204
+ "/v1/workspaces/:workspaceId/integrations/:capabilityId/instances/:instanceKey/facets/:facetKey",
205
205
  async (c) => {
206
206
  const workspaceId = c.req.param("workspaceId");
207
207
  const grant = await requireAccessGrant(c, deps, workspaceId, "workspace:admin");
208
- const payload = MutateIntegrationFeatureRequest.parse(await c.req.json());
208
+ const payload = MutateIntegrationFacetRequest.parse(await c.req.json());
209
209
  try {
210
210
  return c.json(
211
- IntegrationFeatureRemovalResult.parse(
212
- await removeIntegrationFeature(deps.db, {
211
+ IntegrationFacetRemovalResult.parse(
212
+ await removeIntegrationFacet(deps.db, {
213
213
  accountId: grant.accountId,
214
214
  workspaceId,
215
215
  subjectId: grant.subjectId,
216
216
  capabilityId: decoded(c.req.param("capabilityId")),
217
217
  instanceKey: decoded(c.req.param("instanceKey")),
218
- featureKey: decoded(c.req.param("featureKey")),
218
+ facetKey: decoded(c.req.param("facetKey")),
219
219
  expectedVersion: payload.expectedVersion,
220
220
  idempotencyKey: payload.idempotencyKey,
221
221
  }),
222
222
  ),
223
223
  );
224
224
  } catch (error) {
225
- throw featureHttpError(error);
225
+ throw facetHttpError(error);
226
226
  }
227
227
  },
228
228
  );
@@ -232,25 +232,25 @@ function decoded(value: string): string {
232
232
  return decodeURIComponent(value);
233
233
  }
234
234
 
235
- function featureHttpError(error: unknown): HTTPException {
235
+ function facetHttpError(error: unknown): HTTPException {
236
236
  if (error instanceof HTTPException) return error;
237
- if (error instanceof IntegrationFeatureNotFoundError) {
237
+ if (error instanceof IntegrationFacetNotFoundError) {
238
238
  return new HTTPException(404, { message: error.message });
239
239
  }
240
240
  if (
241
- error instanceof IntegrationFeatureBindingVersionConflictError ||
242
- error instanceof IntegrationFeatureBindingVersionRequiredError ||
243
- error instanceof IntegrationFeatureBindingOwnershipConflictError ||
244
- error instanceof IntegrationFeatureOperationIdempotencyError
241
+ error instanceof IntegrationFacetBindingVersionConflictError ||
242
+ error instanceof IntegrationFacetBindingVersionRequiredError ||
243
+ error instanceof IntegrationFacetBindingOwnershipConflictError ||
244
+ error instanceof IntegrationFacetOperationIdempotencyError
245
245
  ) {
246
246
  return new HTTPException(409, {
247
247
  message:
248
- "The Integration feature changed, is shared, or reused an idempotency key. Refresh before retrying.",
248
+ "The Integration facet changed, is shared, or reused an idempotency key. Refresh before retrying.",
249
249
  });
250
250
  }
251
251
  if (
252
- error instanceof IntegrationFeatureConfigError ||
253
- error instanceof IntegrationFeatureConnectionError
252
+ error instanceof IntegrationFacetConfigError ||
253
+ error instanceof IntegrationFacetConnectionError
254
254
  ) {
255
255
  return new HTTPException(422, { message: error.message });
256
256
  }