@opengeni/api-router 0.30.3 → 2.2.0-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.
- package/dist/api-websocket.d.ts +18 -0
- package/dist/app.js +1 -1
- package/dist/auth/managed-auth.d.ts +6 -0
- package/dist/{chunk-WCXHI3FF.js → chunk-3TP54PPX.js} +26692 -15389
- package/dist/chunk-3TP54PPX.js.map +1 -0
- package/dist/codemode.d.ts +8 -1
- package/dist/company-brain-okf.d.ts +24 -0
- package/dist/connected-machine-computer-access.d.ts +11 -0
- package/dist/connection-authority-owner.d.ts +59 -0
- package/dist/connection-ownership.d.ts +47 -0
- package/dist/controller-data-plane.d.ts +16 -0
- package/dist/editable-artifact-websocket.d.ts +5 -15
- package/dist/editable-artifact-workspace-files.d.ts +1 -1
- package/dist/http/api-error.d.ts +10 -0
- package/dist/http/cors.d.ts +5 -0
- package/dist/http/interaction-control-error.d.ts +13 -0
- package/dist/http/sse.d.ts +2 -3
- package/dist/index.d.ts +5 -2
- package/dist/index.js +326 -82
- package/dist/index.js.map +1 -1
- package/dist/integrations/atlassian.d.ts +17 -0
- package/dist/integrations/fiken.d.ts +7 -2
- package/dist/integrations/google-drive.d.ts +18 -0
- package/dist/integrations/oauth-client.d.ts +22 -3
- package/dist/integrations/oauth-profiles.d.ts +195 -0
- package/dist/integrations/personal-github-repositories.d.ts +45 -0
- package/dist/integrations/personal-github.d.ts +40 -0
- package/dist/integrations/pr-review-provider.d.ts +22 -0
- package/dist/integrations/provider-oauth.d.ts +8 -0
- package/dist/integrations/slack-app-home.d.ts +24 -0
- package/dist/integrations/slack-bot.d.ts +157 -9
- package/dist/integrations/slack-interactions.d.ts +39 -5
- package/dist/integrations/slack-routing.d.ts +120 -0
- package/dist/integrations/social-oauth.d.ts +5 -0
- package/dist/interaction-frame-proxy.d.ts +68 -0
- package/dist/mcp/company-brain-governed-writes.d.ts +26 -0
- package/dist/mcp/company-profile-agent-admin.d.ts +44 -0
- package/dist/mcp/documents.d.ts +1 -0
- package/dist/mcp/remember.d.ts +28 -0
- package/dist/mcp/request-abort.d.ts +19 -0
- package/dist/mcp/scheduled-task-view.d.ts +4 -4
- package/dist/mcp/server.d.ts +15 -2
- package/dist/mcp/session-view.d.ts +4 -0
- package/dist/mcp/session-wait.d.ts +137 -0
- package/dist/routes/automations.d.ts +4 -0
- package/dist/routes/billing.d.ts +5 -0
- package/dist/routes/browser-sessions.d.ts +4 -1
- package/dist/routes/company-brain.d.ts +3 -0
- package/dist/routes/company-profile.d.ts +2 -1
- package/dist/routes/connection-authorities.d.ts +3 -0
- package/dist/routes/personal-github-git-broker.d.ts +29 -0
- package/dist/routes/personal-github.d.ts +3 -0
- package/dist/routes/pr-review.d.ts +3 -0
- package/dist/routes/sessions.d.ts +1 -0
- package/dist/routes/user-resource-authorities.d.ts +3 -0
- package/dist/routes/workspace-capture.d.ts +10 -2
- package/dist/routes/workspace-learning.d.ts +3 -0
- package/dist/routes/workspaces.d.ts +19 -0
- package/dist/sandbox/channel-a.d.ts +11 -1
- package/dist/sandbox/connection-authority.d.ts +3 -0
- package/dist/sandbox/enrollment.d.ts +2 -0
- package/dist/sandbox/machines.d.ts +2 -2
- package/dist/sandbox/metrics-ingestion.d.ts +10 -20
- package/dist/sandbox/viewer.d.ts +24 -1
- package/dist/sandbox-file-artifacts.d.ts +11 -0
- package/dist/scheduled-task-deletion.d.ts +14 -0
- package/dist/slack-reaction-files.d.ts +1 -1
- package/dist/temporal-schedule-cleanup.d.ts +1 -0
- package/package.json +20 -18
- package/src/api-websocket.ts +23 -0
- package/src/app.ts +161 -23
- package/src/auth/managed-auth.ts +25 -3
- package/src/codemode.ts +53 -23
- package/src/codex-realtime.ts +8 -2
- package/src/company-brain-okf.ts +340 -0
- package/src/connected-machine-computer-access.ts +33 -0
- package/src/connection-authority-owner.ts +61 -0
- package/src/connection-ownership.ts +180 -0
- package/src/controller-data-plane.ts +47 -0
- package/src/editable-artifact-native-kernel.ts +26 -15
- package/src/editable-artifact-office-import.ts +28 -3
- package/src/editable-artifact-production.ts +13 -7
- package/src/editable-artifact-websocket.ts +11 -18
- package/src/editable-artifact-workspace-files.ts +31 -6
- package/src/http/api-error.ts +28 -0
- package/src/http/auth.ts +4 -0
- package/src/http/cors.ts +35 -0
- package/src/http/interaction-control-error.ts +164 -0
- package/src/http/sse.ts +112 -66
- package/src/index.ts +46 -6
- package/src/integrations/atlassian.ts +146 -35
- package/src/integrations/fiken.ts +102 -22
- package/src/integrations/google-drive.ts +301 -92
- package/src/integrations/oauth-client.ts +272 -143
- package/src/integrations/oauth-profiles.ts +477 -0
- package/src/integrations/personal-github-repositories.ts +445 -0
- package/src/integrations/personal-github.ts +705 -0
- package/src/integrations/pr-review-provider.ts +246 -0
- package/src/integrations/provider-oauth.ts +121 -5
- package/src/integrations/slack-app-home.ts +300 -0
- package/src/integrations/slack-bot.ts +342 -57
- package/src/integrations/slack-interactions.ts +1898 -259
- package/src/integrations/slack-routing.ts +337 -0
- package/src/integrations/social-oauth.ts +25 -0
- package/src/interaction-frame-proxy.ts +409 -0
- package/src/mcp/company-brain-governed-writes.ts +262 -0
- package/src/mcp/company-profile-agent-admin.ts +205 -0
- package/src/mcp/documents.ts +37 -6
- package/src/mcp/files.ts +16 -1
- package/src/mcp/remember.ts +181 -0
- package/src/mcp/request-abort.ts +44 -0
- package/src/mcp/scheduled-task-view.ts +1 -0
- package/src/mcp/server.ts +719 -369
- package/src/mcp/session-view.ts +54 -0
- package/src/mcp/session-wait.ts +554 -0
- package/src/model-catalog.ts +20 -12
- package/src/routes/api-integrations.ts +4 -0
- package/src/routes/api-keys.ts +1 -0
- package/src/routes/automations.ts +534 -0
- package/src/routes/billing.ts +57 -1
- package/src/routes/browser-sessions.ts +329 -79
- package/src/routes/channels.ts +17 -1
- package/src/routes/codex.ts +50 -0
- package/src/routes/company-brain.ts +367 -0
- package/src/routes/company-profile.ts +1 -1
- package/src/routes/computer-sessions.ts +320 -93
- package/src/routes/connection-authorities.ts +139 -0
- package/src/routes/connections.ts +144 -34
- package/src/routes/documents.ts +353 -4
- package/src/routes/editable-artifacts.ts +11 -3
- package/src/routes/enrollments.ts +116 -26
- package/src/routes/environments.ts +127 -55
- package/src/routes/files.ts +72 -11
- package/src/routes/install.ts +82 -28
- package/src/routes/integration-facets.ts +29 -0
- package/src/routes/interaction-resources.ts +20 -2
- package/src/routes/machines.ts +275 -13
- package/src/routes/organization-memberships.ts +717 -28
- package/src/routes/packs.ts +4 -3
- package/src/routes/personal-github-git-broker.ts +785 -0
- package/src/routes/personal-github.ts +319 -0
- package/src/routes/pr-review.ts +531 -0
- package/src/routes/rigs.ts +104 -39
- package/src/routes/scheduled-tasks.ts +19 -21
- package/src/routes/sessions.ts +775 -38
- package/src/routes/social.ts +14 -2
- package/src/routes/supergrok.ts +23 -4
- package/src/routes/transcription-recordings.ts +8 -2
- package/src/routes/user-resource-authorities.ts +138 -0
- package/src/routes/workspace-artifacts.ts +4 -1
- package/src/routes/workspace-capture.ts +21 -0
- package/src/routes/workspace-learning.ts +228 -0
- package/src/routes/workspaces.ts +59 -6
- package/src/sandbox/auth-callout.ts +38 -12
- package/src/sandbox/channel-a.ts +242 -19
- package/src/sandbox/connection-authority.ts +3 -0
- package/src/sandbox/enrollment.ts +15 -3
- package/src/sandbox/machines.ts +186 -64
- package/src/sandbox/metrics-ingestion.ts +220 -58
- package/src/sandbox/viewer.ts +243 -19
- package/src/sandbox-file-artifacts.ts +329 -0
- package/src/scheduled-task-deletion.ts +127 -0
- package/src/slack-reaction-files.ts +16 -5
- package/src/temporal-schedule-cleanup.ts +13 -0
- package/dist/chunk-WCXHI3FF.js.map +0 -1
package/src/routes/workspaces.ts
CHANGED
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
workspaceControlUtf8Bytes,
|
|
17
17
|
type AccessContext,
|
|
18
18
|
type Permission,
|
|
19
|
+
type WorkspaceMember as WorkspaceMemberValue,
|
|
19
20
|
} from "@opengeni/contracts";
|
|
20
21
|
import {
|
|
21
22
|
allWorkspacePermissions,
|
|
@@ -25,6 +26,7 @@ import {
|
|
|
25
26
|
getWorkspaceModelPolicy,
|
|
26
27
|
grantWorkspaceAccess,
|
|
27
28
|
listWorkspaceMembers,
|
|
29
|
+
normalizeWorkspaceMembershipPermissions,
|
|
28
30
|
listWorkspaceControlEvents,
|
|
29
31
|
listWorkspacesForSubject,
|
|
30
32
|
removeWorkspaceMember,
|
|
@@ -35,13 +37,19 @@ import {
|
|
|
35
37
|
updateWorkspaceSettings,
|
|
36
38
|
upsertWorkspaceModelPolicy,
|
|
37
39
|
workspaceCodexSubscriptionActive,
|
|
40
|
+
workspaceControlRequestLockTimeoutMs,
|
|
38
41
|
workspaceXaiSubscriptionActive,
|
|
39
42
|
workspaceVercelAiGatewayConnectionActive,
|
|
40
43
|
} from "@opengeni/db";
|
|
41
44
|
import { boundWorkspaceControlHttpPage } from "@opengeni/events";
|
|
42
45
|
import type { Hono } from "hono";
|
|
43
46
|
import { HTTPException } from "hono/http-exception";
|
|
44
|
-
import {
|
|
47
|
+
import {
|
|
48
|
+
hasPermission,
|
|
49
|
+
requireAccessContext,
|
|
50
|
+
requireAccessGrant,
|
|
51
|
+
requireFreshAccessGrant,
|
|
52
|
+
} from "@opengeni/core";
|
|
45
53
|
import { requireLimit } from "@opengeni/core";
|
|
46
54
|
import type { ApiRouteDeps } from "@opengeni/core";
|
|
47
55
|
import {
|
|
@@ -71,6 +79,24 @@ export function canonicalWorkspacePolicyModelIds(
|
|
|
71
79
|
return [...new Set(modelIds.map((modelId) => canonicalizeConfiguredModelId(settings, modelId)))];
|
|
72
80
|
}
|
|
73
81
|
|
|
82
|
+
type WorkspaceMemberProjectionInput = Omit<WorkspaceMemberValue, "permissions"> & {
|
|
83
|
+
permissions: unknown;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Keep the roster readable across stored-permission contract changes. Unknown
|
|
88
|
+
* values are never re-authorized: they are omitted from the public projection,
|
|
89
|
+
* while every currently recognized permission is preserved in stored order.
|
|
90
|
+
*/
|
|
91
|
+
export function workspaceMembersResponse(members: readonly WorkspaceMemberProjectionInput[]) {
|
|
92
|
+
return ListWorkspaceMembersResponse.parse({
|
|
93
|
+
members: members.map((member) => ({
|
|
94
|
+
...member,
|
|
95
|
+
permissions: normalizeWorkspaceMembershipPermissions(member.permissions),
|
|
96
|
+
})),
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
74
100
|
export function registerWorkspaceRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
75
101
|
app.get("/v1/access/me", async (c) => {
|
|
76
102
|
return c.json(await requireAccessContext(c, deps));
|
|
@@ -157,7 +183,11 @@ export function registerWorkspaceRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
157
183
|
if (!parsed.success) {
|
|
158
184
|
throw new HTTPException(400, { message: "invalid workspace settings patch" });
|
|
159
185
|
}
|
|
160
|
-
|
|
186
|
+
// Request-scoped: bound the exclusive control-prefix wait so a busy
|
|
187
|
+
// workspace yields the retryable 503 instead of parking this request.
|
|
188
|
+
const workspace = await updateWorkspaceSettings(deps.db, workspaceId, parsed.data, {
|
|
189
|
+
controlLockTimeoutMs: workspaceControlRequestLockTimeoutMs(),
|
|
190
|
+
});
|
|
161
191
|
return c.json(Workspace.parse(workspace));
|
|
162
192
|
});
|
|
163
193
|
|
|
@@ -290,7 +320,14 @@ export function registerWorkspaceRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
290
320
|
}
|
|
291
321
|
return c.json(
|
|
292
322
|
await controlHumanWorkspace(
|
|
293
|
-
{
|
|
323
|
+
{
|
|
324
|
+
db: deps.db,
|
|
325
|
+
bus: deps.bus,
|
|
326
|
+
workflowClient: deps.workflowClient,
|
|
327
|
+
...(deps.schedulePromptPostCommit
|
|
328
|
+
? { schedulePromptPostCommit: deps.schedulePromptPostCommit }
|
|
329
|
+
: {}),
|
|
330
|
+
},
|
|
294
331
|
{ accountId: grant.accountId, workspaceId, subjectId: grant.subjectId },
|
|
295
332
|
parsed.data,
|
|
296
333
|
),
|
|
@@ -324,7 +361,12 @@ export function registerWorkspaceRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
324
361
|
workspaceId,
|
|
325
362
|
after,
|
|
326
363
|
c.req.raw.signal,
|
|
327
|
-
{
|
|
364
|
+
{
|
|
365
|
+
observability: deps.observability,
|
|
366
|
+
reauthorize: async () => {
|
|
367
|
+
await requireFreshAccessGrant(c, deps, workspaceId, "workspace:read");
|
|
368
|
+
},
|
|
369
|
+
},
|
|
328
370
|
);
|
|
329
371
|
});
|
|
330
372
|
|
|
@@ -369,6 +411,11 @@ export function registerWorkspaceRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
369
411
|
message: "wait for the workspace's active video generations to finish before deleting it",
|
|
370
412
|
});
|
|
371
413
|
}
|
|
414
|
+
if (deleted.status === "active_background_commands") {
|
|
415
|
+
throw new HTTPException(409, {
|
|
416
|
+
message: "pause or cancel the workspace's background commands before deleting it",
|
|
417
|
+
});
|
|
418
|
+
}
|
|
372
419
|
if (deleted.status === "live_sandboxes") {
|
|
373
420
|
throw new HTTPException(409, {
|
|
374
421
|
message: "wait for the workspace's active sandboxes to finish draining before deleting it",
|
|
@@ -399,7 +446,7 @@ export function registerWorkspaceRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
399
446
|
const workspaceId = c.req.param("workspaceId");
|
|
400
447
|
await requireAccessGrant(c, deps, workspaceId, "workspace:read");
|
|
401
448
|
const members = await listWorkspaceMembers(deps.db, workspaceId);
|
|
402
|
-
return c.json(
|
|
449
|
+
return c.json(workspaceMembersResponse(members));
|
|
403
450
|
});
|
|
404
451
|
|
|
405
452
|
app.post("/v1/workspaces/:workspaceId/members", async (c) => {
|
|
@@ -458,8 +505,14 @@ export function registerWorkspaceRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
458
505
|
const subjectId = decodeURIComponent(c.req.param("subjectId"));
|
|
459
506
|
const members = await listWorkspaceMembers(deps.db, workspaceId);
|
|
460
507
|
// Never remove yourself, and never remove the last administering member.
|
|
508
|
+
// The fenced removal command re-enforces both guards fail-closed.
|
|
461
509
|
assertWorkspaceMemberRemovable({ members, subjectId, callerSubjectId: grant.subjectId });
|
|
462
|
-
await removeWorkspaceMember(deps.db,
|
|
510
|
+
await removeWorkspaceMember(deps.db, {
|
|
511
|
+
accountId: grant.accountId,
|
|
512
|
+
workspaceId,
|
|
513
|
+
actorSubjectId: grant.subjectId,
|
|
514
|
+
targetSubjectId: subjectId,
|
|
515
|
+
});
|
|
463
516
|
return c.body(null, 204);
|
|
464
517
|
});
|
|
465
518
|
}
|
|
@@ -12,15 +12,15 @@
|
|
|
12
12
|
// resolveEnrollmentSigningSecret) — an invalid/expired/forged bearer is denied;
|
|
13
13
|
// 3. confirms the enrollment is still ACTIVE in the DB at the exact credential
|
|
14
14
|
// generation (a revoked or re-enrolled machine denies an old bearer);
|
|
15
|
-
// 4.
|
|
16
|
-
//
|
|
15
|
+
// 4. atomically claims the daemon instance named in CONNECT;
|
|
16
|
+
// 5. signs a NATS user JWT granting pub/sub ONLY that exact process subtree +
|
|
17
|
+
// `_INBOX.>` (deny-all-else by an allow-list) and returns it inside a signed
|
|
17
18
|
// authorization-response JWT.
|
|
18
19
|
//
|
|
19
|
-
// That
|
|
20
|
-
//
|
|
21
|
-
// signed permission set; the boundary does not rely
|
|
22
|
-
//
|
|
23
|
-
// refuses cross-workspace access).
|
|
20
|
+
// That exact scope is both the tenancy boundary and routing fence: workspace A
|
|
21
|
+
// cannot reach B, and an old/duplicate process cannot subscribe to its successor's
|
|
22
|
+
// work. nats-server enforces the signed permission set; the boundary does not rely
|
|
23
|
+
// on naming alone.
|
|
24
24
|
//
|
|
25
25
|
// SECURITY (§18): the bearer value + the account signing seed are NEVER logged. A
|
|
26
26
|
// validation failure → a DENIAL response (the server refuses the connection); a
|
|
@@ -34,23 +34,24 @@ import {
|
|
|
34
34
|
type Settings,
|
|
35
35
|
} from "@opengeni/config";
|
|
36
36
|
import { verifyEnrollmentBearer } from "@opengeni/contracts";
|
|
37
|
-
import { getEnrollment, type Database } from "@opengeni/db";
|
|
37
|
+
import { claimEnrollmentConnection, getEnrollment, type Database } from "@opengeni/db";
|
|
38
38
|
import {
|
|
39
39
|
createResponderConnection,
|
|
40
40
|
decodeAuthRequest,
|
|
41
41
|
mintAuthResponse,
|
|
42
42
|
mintUserJwt,
|
|
43
|
+
parseAgentConnectionName,
|
|
43
44
|
workspaceAgentPermissions,
|
|
44
45
|
type ResponderConnection,
|
|
45
46
|
} from "@opengeni/events";
|
|
46
47
|
import type { Observability } from "@opengeni/observability";
|
|
47
48
|
import { observabilityEventLogger } from "../observability";
|
|
49
|
+
import { AGENT_CONNECTION_LEASE_MS } from "./connection-authority";
|
|
48
50
|
|
|
49
51
|
/** The NATS subject nats-server publishes authorization requests on (ADR-26). */
|
|
50
52
|
export const AUTH_CALLOUT_SUBJECT = "$SYS.REQ.USER.AUTH";
|
|
51
53
|
/** Keep live NATS credentials short-lived while never outliving the bearer. */
|
|
52
54
|
export const NATS_USER_JWT_TTL_SECONDS = 5 * 60;
|
|
53
|
-
|
|
54
55
|
export interface AuthCalloutDeps {
|
|
55
56
|
db: Database;
|
|
56
57
|
settings: Settings;
|
|
@@ -138,9 +139,33 @@ export async function handleAuthorizationRequest(
|
|
|
138
139
|
return deny("enrollment credential generation mismatch");
|
|
139
140
|
}
|
|
140
141
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
142
|
+
const connectionInstanceId = parseAgentConnectionName(decoded.name);
|
|
143
|
+
if (!connectionInstanceId) {
|
|
144
|
+
return deny("missing or invalid agent connection instance");
|
|
145
|
+
}
|
|
146
|
+
const claim = await claimEnrollmentConnection(deps.db, {
|
|
147
|
+
workspaceId: claims.workspaceId,
|
|
148
|
+
enrollmentId: claims.enrollmentId,
|
|
149
|
+
credentialGeneration: claims.credentialGeneration,
|
|
150
|
+
connectionInstanceId,
|
|
151
|
+
leaseMs: AGENT_CONNECTION_LEASE_MS,
|
|
152
|
+
});
|
|
153
|
+
if (!claim.claimed) {
|
|
154
|
+
deps.observability?.warn?.("auth-callout: denied a duplicate live runner", {
|
|
155
|
+
workspaceId: claims.workspaceId,
|
|
156
|
+
agentId: claims.agentId,
|
|
157
|
+
});
|
|
158
|
+
return deny("another runner instance currently owns this enrollment");
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// GRANT: the operational subject includes the exact claimed process instance.
|
|
162
|
+
// A previous socket may remain open until its short JWT expires, but no control
|
|
163
|
+
// request is routed to its old subject after a successor claims authority.
|
|
164
|
+
const permissions = workspaceAgentPermissions(
|
|
165
|
+
claims.workspaceId,
|
|
166
|
+
claims.agentId,
|
|
167
|
+
connectionInstanceId,
|
|
168
|
+
);
|
|
144
169
|
const nowSeconds = Math.floor(Date.now() / 1000);
|
|
145
170
|
const userJwt = mintUserJwt({
|
|
146
171
|
userPublicKey: decoded.userNkey,
|
|
@@ -165,6 +190,7 @@ export async function handleAuthorizationRequest(
|
|
|
165
190
|
deps.observability?.info?.("auth-callout: granted a workspace-scoped NATS credential", {
|
|
166
191
|
workspaceId: claims.workspaceId,
|
|
167
192
|
agentId: claims.agentId,
|
|
193
|
+
connectionGeneration: claim.connectionGeneration,
|
|
168
194
|
});
|
|
169
195
|
return Buffer.from(response, "utf8");
|
|
170
196
|
}
|