@opengeni/api-router 2.5.0 → 2.6.3-canary.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.
Files changed (59) hide show
  1. package/dist/app.js +1 -1
  2. package/dist/auth/managed-auth-attempt-context.d.ts +5 -1
  3. package/dist/auth/managed-auth.d.ts +24 -1
  4. package/dist/{chunk-QESX7HDK.js → chunk-XTLI3CBH.js} +2236 -387
  5. package/dist/chunk-XTLI3CBH.js.map +1 -0
  6. package/dist/http/sse.d.ts +9 -0
  7. package/dist/index.js +104 -4
  8. package/dist/index.js.map +1 -1
  9. package/dist/interaction-metrics.d.ts +2 -0
  10. package/dist/mcp/company-brain-governed-writes.d.ts +1 -1
  11. package/dist/mcp/company-profile-agent-admin.d.ts +6 -6
  12. package/dist/mcp/remember.d.ts +2 -2
  13. package/dist/mcp/server.d.ts +1 -0
  14. package/dist/mcp/session-view.d.ts +1 -0
  15. package/dist/mcp/session-wait.d.ts +19 -0
  16. package/dist/routes/api-keys.d.ts +2 -0
  17. package/dist/routes/browser-sessions.d.ts +1 -0
  18. package/dist/routes/computer-sessions.d.ts +12 -0
  19. package/dist/routes/managed-auth-session-sets.d.ts +7 -0
  20. package/dist/routes/workspaces.d.ts +1 -1
  21. package/dist/sandbox/metrics-ingestion.d.ts +16 -0
  22. package/dist/workspace-delete-observability.d.ts +10 -0
  23. package/package.json +19 -19
  24. package/src/app.ts +172 -20
  25. package/src/auth/managed-auth-attempt-context.ts +40 -3
  26. package/src/auth/managed-auth-session-adapter.ts +1 -0
  27. package/src/auth/managed-auth.ts +164 -4
  28. package/src/http/sse.ts +279 -45
  29. package/src/integrations/oauth-client.ts +8 -1
  30. package/src/integrations/provider-oauth.ts +12 -2
  31. package/src/interaction-metrics.ts +30 -0
  32. package/src/mcp/company-brain-governed-writes.ts +38 -23
  33. package/src/mcp/company-profile-agent-admin.ts +7 -7
  34. package/src/mcp/remember.ts +19 -8
  35. package/src/mcp/server.ts +318 -26
  36. package/src/mcp/session-wait.ts +56 -8
  37. package/src/routes/api-integrations.ts +2 -2
  38. package/src/routes/api-keys.ts +149 -7
  39. package/src/routes/browser-sessions.ts +10 -2
  40. package/src/routes/capabilities.ts +3 -3
  41. package/src/routes/codex.ts +483 -74
  42. package/src/routes/company-profile.ts +64 -0
  43. package/src/routes/computer-sessions.ts +103 -1
  44. package/src/routes/integration-facets.ts +8 -5
  45. package/src/routes/interaction-resources.ts +7 -1
  46. package/src/routes/managed-auth-session-sets.ts +199 -2
  47. package/src/routes/organization-memberships.ts +28 -8
  48. package/src/routes/packs.ts +5 -5
  49. package/src/routes/plugins.ts +2 -2
  50. package/src/routes/scheduled-tasks.ts +9 -0
  51. package/src/routes/sessions.ts +3 -6
  52. package/src/routes/skills.ts +3 -3
  53. package/src/routes/workspaces.ts +293 -54
  54. package/src/sandbox/channel-a.ts +10 -4
  55. package/src/sandbox/machines.ts +13 -6
  56. package/src/sandbox/metrics-ingestion.ts +157 -3
  57. package/src/sandbox/viewer.ts +20 -1
  58. package/src/workspace-delete-observability.ts +75 -0
  59. package/dist/chunk-QESX7HDK.js.map +0 -1
@@ -33,6 +33,7 @@ import {
33
33
  import {
34
34
  getManagedSession,
35
35
  organizationMembershipHttpStatus,
36
+ requireCanonicalLocalAccountAdministrator,
36
37
  type ApiRouteDeps,
37
38
  } from "@opengeni/core";
38
39
  import {
@@ -100,6 +101,25 @@ async function requireManagedHuman(context: Context, deps: ApiRouteDeps) {
100
101
  return { session, subjectId: `user:${session.user.id}` };
101
102
  }
102
103
 
104
+ async function requireOrganizationAdministrator(
105
+ context: Context,
106
+ deps: ApiRouteDeps,
107
+ organizationId: string,
108
+ ): Promise<{ subjectId: string }> {
109
+ if (deps.settings.productAccessMode === "managed") {
110
+ return await requireManagedHuman(context, deps);
111
+ }
112
+ if (deps.settings.productAccessMode === "local") {
113
+ const { subjectId } = await requireCanonicalLocalAccountAdministrator(
114
+ context,
115
+ deps,
116
+ organizationId,
117
+ );
118
+ return { subjectId };
119
+ }
120
+ throw new HTTPException(401, { message: "organization administrator session required" });
121
+ }
122
+
103
123
  async function parseBody<S extends z.ZodType>(context: Context, schema: S): Promise<z.infer<S>> {
104
124
  const parsed = schema.safeParse(await context.req.json().catch(() => null));
105
125
  if (!parsed.success) {
@@ -213,12 +233,12 @@ export function registerOrganizationMembershipRoutes(app: Hono, deps: ApiRouteDe
213
233
  });
214
234
 
215
235
  app.get("/v1/organizations/:organizationId/overview", async (context) => {
216
- const { subjectId } = await requireManagedHuman(context, deps);
217
236
  const organizationId = parseId(
218
237
  OrganizationId,
219
238
  context.req.param("organizationId"),
220
239
  "organization id",
221
240
  );
241
+ const { subjectId } = await requireOrganizationAdministrator(context, deps, organizationId);
222
242
  try {
223
243
  return context.json(
224
244
  OrganizationAdministrationOverview.parse(
@@ -234,12 +254,12 @@ export function registerOrganizationMembershipRoutes(app: Hono, deps: ApiRouteDe
234
254
  });
235
255
 
236
256
  app.patch("/v1/organizations/:organizationId", async (context) => {
237
- const { subjectId } = await requireManagedHuman(context, deps);
238
257
  const organizationId = parseId(
239
258
  OrganizationId,
240
259
  context.req.param("organizationId"),
241
260
  "organization id",
242
261
  );
262
+ const { subjectId } = await requireOrganizationAdministrator(context, deps, organizationId);
243
263
  const payload = await parseBody(context, UpdateOrganizationNameRequest);
244
264
  try {
245
265
  return context.json(
@@ -257,12 +277,12 @@ export function registerOrganizationMembershipRoutes(app: Hono, deps: ApiRouteDe
257
277
  });
258
278
 
259
279
  app.patch("/v1/organizations/:organizationId/workspaces/:workspaceId", async (context) => {
260
- const { subjectId } = await requireManagedHuman(context, deps);
261
280
  const organizationId = parseId(
262
281
  OrganizationId,
263
282
  context.req.param("organizationId"),
264
283
  "organization id",
265
284
  );
285
+ const { subjectId } = await requireOrganizationAdministrator(context, deps, organizationId);
266
286
  const workspaceId = parseId(WorkspaceId, context.req.param("workspaceId"), "workspace id");
267
287
  const payload = await parseBody(context, UpdateOrganizationWorkspaceRequest);
268
288
  try {
@@ -284,12 +304,12 @@ export function registerOrganizationMembershipRoutes(app: Hono, deps: ApiRouteDe
284
304
  });
285
305
 
286
306
  app.post("/v1/organizations/:organizationId/workspaces", async (context) => {
287
- const { subjectId } = await requireManagedHuman(context, deps);
288
307
  const organizationId = parseId(
289
308
  OrganizationId,
290
309
  context.req.param("organizationId"),
291
310
  "organization id",
292
311
  );
312
+ const { subjectId } = await requireOrganizationAdministrator(context, deps, organizationId);
293
313
  const payload = await parseBody(context, CreateOrganizationWorkspaceRequest);
294
314
  try {
295
315
  return context.json(
@@ -311,12 +331,12 @@ export function registerOrganizationMembershipRoutes(app: Hono, deps: ApiRouteDe
311
331
  app.patch(
312
332
  "/v1/organizations/:organizationId/workspaces/:workspaceId/settings",
313
333
  async (context) => {
314
- const { subjectId } = await requireManagedHuman(context, deps);
315
334
  const organizationId = parseId(
316
335
  OrganizationId,
317
336
  context.req.param("organizationId"),
318
337
  "organization id",
319
338
  );
339
+ const { subjectId } = await requireOrganizationAdministrator(context, deps, organizationId);
320
340
  const workspaceId = parseId(WorkspaceId, context.req.param("workspaceId"), "workspace id");
321
341
  const payload = await parseBody(context, UpdateWorkspaceSettingsRequest);
322
342
  try {
@@ -642,12 +662,12 @@ export function registerOrganizationMembershipRoutes(app: Hono, deps: ApiRouteDe
642
662
  );
643
663
 
644
664
  app.get("/v1/organizations/:organizationId/members", async (context) => {
645
- const { subjectId } = await requireManagedHuman(context, deps);
646
665
  const organizationId = parseId(
647
666
  OrganizationId,
648
667
  context.req.param("organizationId"),
649
668
  "organization id",
650
669
  );
670
+ const { subjectId } = await requireOrganizationAdministrator(context, deps, organizationId);
651
671
  try {
652
672
  return context.json(
653
673
  ListOrganizationAdministrationMembersResponse.parse({
@@ -689,12 +709,12 @@ export function registerOrganizationMembershipRoutes(app: Hono, deps: ApiRouteDe
689
709
  });
690
710
 
691
711
  app.get("/v1/organizations/:organizationId/retention-policy", async (context) => {
692
- const { subjectId } = await requireManagedHuman(context, deps);
693
712
  const organizationId = parseId(
694
713
  OrganizationId,
695
714
  context.req.param("organizationId"),
696
715
  "organization id",
697
716
  );
717
+ const { subjectId } = await requireOrganizationAdministrator(context, deps, organizationId);
698
718
  try {
699
719
  return context.json(
700
720
  OrganizationRetentionPolicy.parse(
@@ -710,12 +730,12 @@ export function registerOrganizationMembershipRoutes(app: Hono, deps: ApiRouteDe
710
730
  });
711
731
 
712
732
  app.patch("/v1/organizations/:organizationId/retention-policy", async (context) => {
713
- const { subjectId } = await requireManagedHuman(context, deps);
714
733
  const organizationId = parseId(
715
734
  OrganizationId,
716
735
  context.req.param("organizationId"),
717
736
  "organization id",
718
737
  );
738
+ const { subjectId } = await requireOrganizationAdministrator(context, deps, organizationId);
719
739
  const payload = await parseBody(context, UpdateOrganizationRetentionPolicyRequest);
720
740
  try {
721
741
  return context.json(
@@ -83,7 +83,7 @@ export function registerPackRoutes(app: Hono, deps: ApiRouteDeps): void {
83
83
  // Built-in pack ids stay reserved so a registration can never shadow them.
84
84
  app.post("/v1/workspaces/:workspaceId/packs", async (c) => {
85
85
  const workspaceId = c.req.param("workspaceId");
86
- const grant = await requireAccessGrant(c, deps, workspaceId, "workspace:admin");
86
+ const grant = await requireAccessGrant(c, deps, workspaceId, "capabilities:manage");
87
87
  const manifest = RegisterCapabilityPackRequest.parse(await c.req.json());
88
88
  if (isBuiltInCapabilityPack(manifest.id)) {
89
89
  throw new HTTPException(409, {
@@ -100,7 +100,7 @@ export function registerPackRoutes(app: Hono, deps: ApiRouteDeps): void {
100
100
 
101
101
  app.delete("/v1/workspaces/:workspaceId/packs/:packId", async (c) => {
102
102
  const workspaceId = c.req.param("workspaceId");
103
- await requireAccessGrant(c, deps, workspaceId, "workspace:admin");
103
+ await requireAccessGrant(c, deps, workspaceId, "capabilities:manage");
104
104
  const packId = c.req.param("packId");
105
105
  if (isBuiltInCapabilityPack(packId)) {
106
106
  throw new HTTPException(409, {
@@ -172,7 +172,7 @@ export function registerPackRoutes(app: Hono, deps: ApiRouteDeps): void {
172
172
 
173
173
  app.post("/v1/workspaces/:workspaceId/packs/:packId/install", async (c) => {
174
174
  const workspaceId = c.req.param("workspaceId");
175
- const grant = await requireAccessGrant(c, deps, workspaceId, "workspace:admin");
175
+ const grant = await requireAccessGrant(c, deps, workspaceId, "capabilities:manage");
176
176
  const pack = await requirePack(db, workspaceId, c.req.param("packId"));
177
177
  const payload = InstallPackRequest.parse(await c.req.json());
178
178
  const preview = PackInstallationPreview.parse(
@@ -393,7 +393,7 @@ export function registerPackRoutes(app: Hono, deps: ApiRouteDeps): void {
393
393
 
394
394
  app.delete("/v1/workspaces/:workspaceId/packs/:packId/installation", async (c) => {
395
395
  const workspaceId = c.req.param("workspaceId");
396
- const grant = await requireAccessGrant(c, deps, workspaceId, "workspace:admin");
396
+ const grant = await requireAccessGrant(c, deps, workspaceId, "capabilities:manage");
397
397
  const pack = await requirePack(db, workspaceId, c.req.param("packId"));
398
398
  const payload = UninstallPackRequest.parse(await c.req.json());
399
399
  const requestDigest = sha256(
@@ -483,7 +483,7 @@ export function registerPackRoutes(app: Hono, deps: ApiRouteDeps): void {
483
483
 
484
484
  app.post("/v1/workspaces/:workspaceId/packs/:packId/enable", async (c) => {
485
485
  const workspaceId = c.req.param("workspaceId");
486
- const grant = await requireAccessGrant(c, deps, workspaceId, "workspace:admin");
486
+ const grant = await requireAccessGrant(c, deps, workspaceId, "capabilities:manage");
487
487
  const pack = await requirePack(db, workspaceId, c.req.param("packId"));
488
488
  if (capabilityPackRequiresInstallationPlan(pack)) {
489
489
  throw new HTTPException(409, {
@@ -120,7 +120,7 @@ export function registerPluginRoutes(
120
120
 
121
121
  app.post("/v1/workspaces/:workspaceId/plugins/install", async (c) => {
122
122
  const workspaceId = c.req.param("workspaceId");
123
- const grant = await requireAccessGrant(c, deps, workspaceId, "workspace:admin");
123
+ const grant = await requireAccessGrant(c, deps, workspaceId, "capabilities:manage");
124
124
  const payload = InstallPluginRequest.parse(await c.req.json());
125
125
  const resolved = await resolvePluginPackage({
126
126
  deps,
@@ -253,7 +253,7 @@ export function registerPluginRoutes(
253
253
 
254
254
  app.delete("/v1/workspaces/:workspaceId/plugins/:pluginKey", async (c) => {
255
255
  const workspaceId = c.req.param("workspaceId");
256
- const grant = await requireAccessGrant(c, deps, workspaceId, "workspace:admin");
256
+ const grant = await requireAccessGrant(c, deps, workspaceId, "capabilities:manage");
257
257
  const pluginKey = decodeURIComponent(c.req.param("pluginKey"));
258
258
  const payload = UninstallPluginRequest.parse(await c.req.json());
259
259
  try {
@@ -22,6 +22,7 @@ import {
22
22
  syncCreatedScheduledTask,
23
23
  syncUpdatedScheduledTask,
24
24
  updateScheduledTaskForApi,
25
+ validateScheduledTaskMachineTarget,
25
26
  validateScheduledTaskTarget,
26
27
  validatedScheduledTaskUpdate,
27
28
  } from "@opengeni/core";
@@ -157,6 +158,14 @@ export function registerScheduledTaskRoutes(app: Hono, deps: ApiRouteDeps): void
157
158
  agentConfig: task.agentConfig,
158
159
  missingTargetStatus: 404,
159
160
  });
161
+ await validateScheduledTaskMachineTarget({
162
+ settings,
163
+ db,
164
+ grant,
165
+ runMode: task.runMode,
166
+ agentConfig: task.agentConfig,
167
+ requireOnline: true,
168
+ });
160
169
  await requireLimit(deps, {
161
170
  accountId: grant.accountId,
162
171
  workspaceId,
@@ -284,7 +284,7 @@ import {
284
284
  validateVariableSetAttachment,
285
285
  } from "@opengeni/core";
286
286
  import { assertSessionExists, boundedLimit } from "../http/common";
287
- import { sseSessionStream } from "../http/sse";
287
+ import { browserSseDeliveryOptions, sseSessionStream } from "../http/sse";
288
288
  import {
289
289
  serveWorkspaceCapture,
290
290
  serveWorkspaceCaptureFile,
@@ -2659,6 +2659,7 @@ export function registerSessionRoutes(app: Hono, deps: SessionRouteDeps): void {
2659
2659
  Number.isFinite(after) ? after : 0,
2660
2660
  c.req.raw.signal,
2661
2661
  {
2662
+ ...browserSseDeliveryOptions(c.req.query("transport")),
2662
2663
  observability: deps.observability,
2663
2664
  actorEpoch: getManagedAuthRequestActorEpoch(c.req.raw) ?? undefined,
2664
2665
  reauthorizeAfterMs:
@@ -3201,12 +3202,8 @@ export function registerSessionRoutes(app: Hono, deps: SessionRouteDeps): void {
3201
3202
  // Human take-control: when the desktop is available + this policy is on
3202
3203
  // (default), the cell is mode "interactive" — the noVNC viewer drives :0
3203
3204
  // (x11vnc runs without -viewonly). Off → mode "read-only" (client disables
3204
- // take-control). Independent of the agent's computerUseReadOnly.
3205
+ // take-control). Agent interaction uses managed ComputerSession tools.
3205
3206
  desktopInteractive: settings.sandboxDesktopInteractive,
3206
- // P4.3 computer-use: the agent drives the active display; availability
3207
- // tracks the desktop tier + a desktop-capable backend.
3208
- computerUseEnabled: settings.computerUseEnabled,
3209
- computerUseReadOnly: settings.computerUseReadOnly,
3210
3207
  // Graceful degrade when scoped stream credentials cannot be minted.
3211
3208
  streamTokenSecretAvailable: !streamTokenDegraded(settings),
3212
3209
  desktopAcknowledged: acknowledged,
@@ -55,7 +55,7 @@ export function registerSkillRoutes(
55
55
 
56
56
  app.post("/v1/workspaces/:workspaceId/skills/library/:libraryId/install", async (c) => {
57
57
  const workspaceId = c.req.param("workspaceId");
58
- const grant = await requireAccessGrant(c, deps, workspaceId, "workspace:admin");
58
+ const grant = await requireAccessGrant(c, deps, workspaceId, "capabilities:manage");
59
59
  const libraryId = decodeURIComponent(c.req.param("libraryId"));
60
60
  const payload = InstallLibrarySkillRequest.parse(await c.req.json());
61
61
  let loaded: ReturnType<typeof loadSkillLibrarySkill>;
@@ -136,7 +136,7 @@ export function registerSkillRoutes(
136
136
 
137
137
  app.post("/v1/workspaces/:workspaceId/skills/install", async (c) => {
138
138
  const workspaceId = c.req.param("workspaceId");
139
- const grant = await requireAccessGrant(c, deps, workspaceId, "workspace:admin");
139
+ const grant = await requireAccessGrant(c, deps, workspaceId, "capabilities:manage");
140
140
  const payload = InstallSkillRequest.parse(await c.req.json());
141
141
  const resolved = await resolveForRoute(payload.url, github);
142
142
  if (
@@ -204,7 +204,7 @@ export function registerSkillRoutes(
204
204
 
205
205
  app.delete("/v1/workspaces/:workspaceId/skills/:capabilityId", async (c) => {
206
206
  const workspaceId = c.req.param("workspaceId");
207
- const grant = await requireAccessGrant(c, deps, workspaceId, "workspace:admin");
207
+ const grant = await requireAccessGrant(c, deps, workspaceId, "capabilities:manage");
208
208
  const capabilityId = decodeURIComponent(c.req.param("capabilityId"));
209
209
  const payload = UninstallSkillRequest.parse(await c.req.json());
210
210
  try {