@opengeni/api-router 1.4.5-canary.38533733713001 → 2.0.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.
- package/dist/app.js +1 -1
- package/dist/attached-browser-viewer.d.ts +11 -0
- package/dist/auth/managed-auth-session-adapter.d.ts +6 -0
- package/dist/{chunk-D5WJ24GY.js → chunk-ZDTWXRFX.js} +137 -83
- package/dist/{chunk-D5WJ24GY.js.map → chunk-ZDTWXRFX.js.map} +1 -1
- package/dist/connection-ownership.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/integrations/marketplace-plugin.d.ts +43 -43
- package/dist/routes/native-app-auth.d.ts +5 -0
- package/dist/sandbox/screen-control.d.ts +1 -1
- package/package.json +23 -22
- package/src/app.ts +1 -0
- package/src/attached-browser-viewer.ts +26 -0
- package/src/auth/managed-auth-session-adapter.ts +38 -25
- package/src/connection-ownership.ts +2 -2
- package/src/integrations/oauth-client.ts +18 -19
- package/src/mcp/action-descriptions.ts +1 -1
- package/src/mcp/server.ts +6 -6
- package/src/routes/browser-sessions.ts +7 -11
- package/src/routes/codex-core.ts +5 -2
- package/src/routes/computer-sessions.ts +5 -7
- package/src/routes/machines.ts +1 -1
- package/src/routes/native-app-auth.ts +51 -4
- package/src/routes/sessions.ts +21 -2
- package/src/routes/subscription-pool-core.ts +2 -2
- package/src/routes/vault-providers.ts +3 -3
- package/src/sandbox/screen-control.ts +1 -1
package/dist/app.js
CHANGED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { AccessGrant } from "@opengeni/contracts";
|
|
2
|
+
import { type ApiRouteDeps } from "@opengeni/core";
|
|
3
|
+
import type { AttachedBrowserViewer } from "@opengeni/db";
|
|
4
|
+
/**
|
|
5
|
+
* The person whose machine access decides which attached browsers a caller
|
|
6
|
+
* may see and control. Personal (Only me) machines belong to one person, so a
|
|
7
|
+
* human sees and controls their own; an agent acts for the person who started
|
|
8
|
+
* its turn (never merely for the workspace). Other callers (keys, services)
|
|
9
|
+
* reach only machines that are not personal.
|
|
10
|
+
*/
|
|
11
|
+
export declare function attachedBrowserViewer(deps: Pick<ApiRouteDeps, "db">, grant: AccessGrant, workspaceId: string): Promise<AttachedBrowserViewer>;
|
|
@@ -4,3 +4,9 @@ import { type ManagedAuth } from "@opengeni/core";
|
|
|
4
4
|
/** Server-only proof for a product-owned endpoint; never returned to a browser. */
|
|
5
5
|
export declare function managedAuthSelectedProofHeaders(auth: ManagedAuth, token: string): Promise<Headers>;
|
|
6
6
|
export declare function createBetterAuthSessionAdapter(auth: ManagedAuth, db: Database): ManagedAuthSessionAdapter;
|
|
7
|
+
/**
|
|
8
|
+
* The provider's own session cookie for an exact session token (or its
|
|
9
|
+
* expiry when `token` is null), plus invalidation of stale provider cache
|
|
10
|
+
* cookies, exactly as a browser sign-in would set them.
|
|
11
|
+
*/
|
|
12
|
+
export declare function managedAuthSessionCookieHeaders(auth: ManagedAuth, token: string | null, currentCookieHeader?: string | null): Promise<string[]>;
|
|
@@ -385,7 +385,7 @@ async function coreCodexConnected(c, deps, input) {
|
|
|
385
385
|
);
|
|
386
386
|
default:
|
|
387
387
|
throw new HTTPException3(403, {
|
|
388
|
-
message: "only an organization administrator can connect a
|
|
388
|
+
message: input.workspaceId === null ? "only an organization administrator can connect a Codex account for the whole organization" : "only an administrator of this workspace or of the organization can connect a Codex account here"
|
|
389
389
|
});
|
|
390
390
|
}
|
|
391
391
|
}
|
|
@@ -407,7 +407,7 @@ async function coreCodexConnected(c, deps, input) {
|
|
|
407
407
|
function disconnectRefused(outcome) {
|
|
408
408
|
if (outcome === "forbidden") {
|
|
409
409
|
throw new HTTPException3(403, {
|
|
410
|
-
message: "only an organization administrator can disconnect a
|
|
410
|
+
message: "only an organization administrator can disconnect this Codex account; a workspace administrator can disconnect only an account that serves just their workspace"
|
|
411
411
|
});
|
|
412
412
|
}
|
|
413
413
|
if (outcome === "unresolved_redemption") {
|
|
@@ -2723,7 +2723,7 @@ function coreConnectRefused(reason, displayName, personal) {
|
|
|
2723
2723
|
});
|
|
2724
2724
|
default:
|
|
2725
2725
|
return new HTTPException7(personal ? 409 : 403, {
|
|
2726
|
-
message: personal ? `a private ${displayName} account is connected from your Personal workspace` : `only an organization
|
|
2726
|
+
message: personal ? `a private ${displayName} account is connected from your Personal workspace` : `only an administrator of this workspace or of the organization can connect a ${displayName} account here`
|
|
2727
2727
|
});
|
|
2728
2728
|
}
|
|
2729
2729
|
}
|
|
@@ -2732,7 +2732,7 @@ function disconnectRefused2(outcome, displayName) {
|
|
|
2732
2732
|
throw new HTTPException7(404, { message: `${displayName} account not found` });
|
|
2733
2733
|
if (outcome === "forbidden")
|
|
2734
2734
|
throw new HTTPException7(403, {
|
|
2735
|
-
message: `only an organization administrator can disconnect
|
|
2735
|
+
message: `only an organization administrator can disconnect this ${displayName} account; a workspace administrator can disconnect only an account that serves just their workspace`
|
|
2736
2736
|
});
|
|
2737
2737
|
if (outcome === "unresolved_redemption")
|
|
2738
2738
|
throw new HTTPException7(409, {
|
|
@@ -9521,7 +9521,7 @@ function registerVaultProviderRoutes(app, deps, options = {}) {
|
|
|
9521
9521
|
}
|
|
9522
9522
|
const created = await provider.createVault(routeProviderContext(connectionId), {
|
|
9523
9523
|
name: payload.name,
|
|
9524
|
-
description: "Managed by
|
|
9524
|
+
description: "Managed by Opengeni",
|
|
9525
9525
|
allowAdminsAccess: true,
|
|
9526
9526
|
...payload.groupId ? { groupId: payload.groupId } : {}
|
|
9527
9527
|
});
|
|
@@ -13869,29 +13869,26 @@ async function startMcpOAuthWithinDeadline(deps, context, deadline) {
|
|
|
13869
13869
|
...existing ? { connectionId: existing.id, connectionVersion: existing.version } : {}
|
|
13870
13870
|
});
|
|
13871
13871
|
const expiresAt = new Date(Date.now() + oauthStateTtlMs);
|
|
13872
|
-
|
|
13873
|
-
|
|
13874
|
-
|
|
13875
|
-
|
|
13876
|
-
|
|
13872
|
+
const stateId = randomUUID();
|
|
13873
|
+
const state = createSignedState2(requireIntegrationsStateSecret(settings), {
|
|
13874
|
+
kind: "mcp_oauth_reference",
|
|
13875
|
+
id: stateId,
|
|
13876
|
+
accountId: context.accountId,
|
|
13877
|
+
workspaceId: context.workspaceId
|
|
13878
|
+
});
|
|
13879
|
+
if (state.length > 512) {
|
|
13880
|
+
throw new Error("MCP OAuth reference state exceeded its provider-safe length");
|
|
13881
|
+
}
|
|
13882
|
+
await deadline.run(
|
|
13883
|
+
"state_persist",
|
|
13884
|
+
async () => storeIntegrationOAuthPendingState(db, {
|
|
13877
13885
|
id: stateId,
|
|
13878
13886
|
accountId: context.accountId,
|
|
13879
|
-
workspaceId: context.workspaceId
|
|
13880
|
-
|
|
13881
|
-
|
|
13882
|
-
|
|
13883
|
-
|
|
13884
|
-
await deadline.run(
|
|
13885
|
-
"state_persist",
|
|
13886
|
-
async () => storeIntegrationOAuthPendingState(db, {
|
|
13887
|
-
id: stateId,
|
|
13888
|
-
accountId: context.accountId,
|
|
13889
|
-
workspaceId: context.workspaceId,
|
|
13890
|
-
stateEncrypted: encryptEnvironmentValue4(key2, fullState),
|
|
13891
|
-
expiresAt
|
|
13892
|
-
})
|
|
13893
|
-
);
|
|
13894
|
-
}
|
|
13887
|
+
workspaceId: context.workspaceId,
|
|
13888
|
+
stateEncrypted: encryptEnvironmentValue4(key2, fullState),
|
|
13889
|
+
expiresAt
|
|
13890
|
+
})
|
|
13891
|
+
);
|
|
13895
13892
|
const authorizationUrl = buildAuthorizationUrl({
|
|
13896
13893
|
endpoint: discovery.as.authorizationEndpoint,
|
|
13897
13894
|
settings,
|
|
@@ -36115,7 +36112,7 @@ var RULES = [
|
|
|
36115
36112
|
{
|
|
36116
36113
|
method: "PATCH",
|
|
36117
36114
|
pattern: /\/(codex|claude|supergrok)\/accounts\/:accountId\/allocator$/,
|
|
36118
|
-
text: ({ provider }) => `Turn "
|
|
36115
|
+
text: ({ provider }) => `Turn "Enable account" on or off for a ${provider} account (enable or disable it, sometimes called pause or resume). Disabled accounts aren't picked for new chats or schedules; running work continues. Send the current allocator version.`
|
|
36119
36116
|
},
|
|
36120
36117
|
{
|
|
36121
36118
|
method: "PATCH",
|
|
@@ -39884,7 +39881,7 @@ function registerFleetTools(server, deps, grant, sessionId, json2) {
|
|
|
39884
39881
|
server.registerTool(
|
|
39885
39882
|
"sandbox_provision",
|
|
39886
39883
|
{
|
|
39887
|
-
description: "Provision a new sandbox for the fleet. kind=selfhosted posts the Connected Machine card in this chat: the person copies a one-line connect command, runs it on their own computer, and picks Use in this chat; the card also offers the
|
|
39884
|
+
description: "Provision a new sandbox for the fleet. kind=selfhosted posts the Connected Machine card in this chat: the person copies a one-line connect command, runs it on their own computer, and picks Use in this chat; the card also offers the Opengeni Browser Chrome extension once the machine is connected. When you can already run commands on the target machine, install it yourself with connected_machine_enroll_token instead. kind=modal creates a named Modal sandbox record, but it is NOT yet attachable as a swap target: routing a session onto a second Modal box is not supported yet, so sandbox_swap to its id is rejected. Use the session's own box (the default) or attach a Connected Machine instead.",
|
|
39888
39885
|
inputSchema: {
|
|
39889
39886
|
kind: z45.enum(["selfhosted", "modal"]),
|
|
39890
39887
|
name: z45.string().min(1).max(120).optional()
|
|
@@ -39919,7 +39916,7 @@ function registerFleetTools(server, deps, grant, sessionId, json2) {
|
|
|
39919
39916
|
kind: "selfhosted",
|
|
39920
39917
|
status: "card_posted",
|
|
39921
39918
|
eventId,
|
|
39922
|
-
message: `${setup.status === "ready" ? `${setup.detail} The Connected Machine card lists them with Use in this chat and can connect another.` : "The Connected Machine card was posted. The person copies its one-line connect command, runs it on their machine, and picks Use in this chat; nothing is connected yet."} Check sandboxes_list after they confirm. Chrome needs the
|
|
39919
|
+
message: `${setup.status === "ready" ? `${setup.detail} The Connected Machine card lists them with Use in this chat and can connect another.` : "The Connected Machine card was posted. The person copies its one-line connect command, runs it on their machine, and picks Use in this chat; nothing is connected yet."} Check sandboxes_list after they confirm. Chrome needs the Opengeni Browser extension (${OPENGENI_BROWSER_EXTENSION_URL}) on the connected machine.`
|
|
39923
39920
|
});
|
|
39924
39921
|
}
|
|
39925
39922
|
return json2(
|
|
@@ -39935,7 +39932,7 @@ function registerConnectedMachineTools(server, deps, grant, sessionId, json2) {
|
|
|
39935
39932
|
server.registerTool(
|
|
39936
39933
|
"connected_machine_enroll_token",
|
|
39937
39934
|
{
|
|
39938
|
-
description: "Create a single-use Connected Machine enrollment token for this workspace (expires in one hour) using existing enrollments:manage authority. Returns the token, expiry and Unix/PowerShell install commands. Use it when you can run commands on the target machine yourself (run_on, SSH, a VM you control): run the matching command there, then verify the machine with sandboxes_list. One token connects one machine; mint another for each additional machine (the same machine may rerun its command). When the person must run it on their own computer, post the Connected Machine card instead (capability_authorization_request with api:connected-machine), which mints the command in their browser; do not paste a token into the chat. Screen control is optional and defaults off; to turn it on for a machine that is already connected, use connected_machine_enable_screen_control instead of reconnecting. The token grants whole-machine access on enrollment; do not put it in public code or unrelated logs. Existing machine connections are preserved. For Chrome, the person installs the
|
|
39935
|
+
description: "Create a single-use Connected Machine enrollment token for this workspace (expires in one hour) using existing enrollments:manage authority. Returns the token, expiry and Unix/PowerShell install commands. Use it when you can run commands on the target machine yourself (run_on, SSH, a VM you control): run the matching command there, then verify the machine with sandboxes_list. One token connects one machine; mint another for each additional machine (the same machine may rerun its command). When the person must run it on their own computer, post the Connected Machine card instead (capability_authorization_request with api:connected-machine), which mints the command in their browser; do not paste a token into the chat. Screen control is optional and defaults off; to turn it on for a machine that is already connected, use connected_machine_enable_screen_control instead of reconnecting. The token grants whole-machine access on enrollment; do not put it in public code or unrelated logs. Existing machine connections are preserved. For Chrome, the person installs the Opengeni Browser extension (" + OPENGENI_BROWSER_EXTENSION_URL + ") after the machine is connected.",
|
|
39939
39936
|
inputSchema: { allowScreenControl: z45.boolean().optional() }
|
|
39940
39937
|
},
|
|
39941
39938
|
async ({ allowScreenControl }) => {
|
|
@@ -39963,7 +39960,7 @@ function registerConnectedMachineTools(server, deps, grant, sessionId, json2) {
|
|
|
39963
39960
|
server.registerTool(
|
|
39964
39961
|
"connected_machine_enable_screen_control",
|
|
39965
39962
|
{
|
|
39966
|
-
description: "Turn screen control on for a machine that is already connected, in place, so agents can see and use its screen (computer and attached-Chrome tools). `target` is a sandboxes_list id or an enrollment id. Needs no human click, token, terminal or reconnect:
|
|
39963
|
+
description: "Turn screen control on for a machine that is already connected, in place, so agents can see and use its screen (computer and attached-Chrome tools). `target` is a sandboxes_list id or an enrollment id. Needs no human click, token, terminal or reconnect: Opengeni records the consent on that machine and its agent refreshes its own credentials over the live connection. status=active means it is on now. status=pending means it is allowed and turns on by itself: reason=offline when the machine next connects; reason=agent_update_required after its agent is updated (update it with the machine update action, POST /v1/workspaces/{workspaceId}/machines/{enrollmentId}/update); reason=renewal_failed: call again shortly; reason=reconnect_required: the machine's connection predates in-place changes, so rerun the connect command on it (connected_machine_enroll_token with allowScreenControl=true). On a Mac the OS must also allow Screen Recording, Accessibility and Input Monitoring for Opengeni: open each pane on the Mac with run_on exec `open 'x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture'` (Privacy_Accessibility, Privacy_ListenEvent; also the privacy-settings machine action) and ask the person to switch Opengeni on there, choosing Quit & Reopen if macOS asks; the machine list reports runtime.macPermissions. Chrome additionally needs the Opengeni Browser extension (" + OPENGENI_BROWSER_EXTENSION_URL + ") on that machine.",
|
|
39967
39964
|
inputSchema: { target: z45.string().min(1) }
|
|
39968
39965
|
},
|
|
39969
39966
|
async ({ target }) => {
|
|
@@ -41817,7 +41814,7 @@ function registerCapabilityDiscoveryTools(server, deps, grant, sessionId, json2)
|
|
|
41817
41814
|
server.registerTool(
|
|
41818
41815
|
"capability_authorization_request",
|
|
41819
41816
|
{
|
|
41820
|
-
description: "Show a Connect card in this chat for a suitable capability returned by capability_catalog_search with setup.nextAction. Supply its capability ID and a brief rationale explaining how it helps the task; no separate confirmation is needed before showing the card. Requesting setup needs no integration-management permission and grants no access. The authenticated human completes setup through the card; never ask them to paste credentials into chat. Do not request another card for the same pending setup, or for a candidate reported ready or unavailable. GitHub App (api:github-app) and Connected Machine (api:connected-machine) are exceptions: when already connected their card is still shown, listing repositories or machines the person can use in this chat. Use api:connected-machine when the person should connect their own computer: the card gives them a one-line connect command and the
|
|
41817
|
+
description: "Show a Connect card in this chat for a suitable capability returned by capability_catalog_search with setup.nextAction. Supply its capability ID and a brief rationale explaining how it helps the task; no separate confirmation is needed before showing the card. Requesting setup needs no integration-management permission and grants no access. The authenticated human completes setup through the card; never ask them to paste credentials into chat. Do not request another card for the same pending setup, or for a candidate reported ready or unavailable. GitHub App (api:github-app) and Connected Machine (api:connected-machine) are exceptions: when already connected their card is still shown, listing repositories or machines the person can use in this chat. Use api:connected-machine when the person should connect their own computer: the card gives them a one-line connect command and the Opengeni Browser Chrome extension link; never paste an enroll token into the chat yourself." + (secretsSelected ? ' For a single password, API key or code for a service with no connector here, ask with a kind: "secret" question in request_human_input instead; you get a saved reference back.' : ""),
|
|
41821
41818
|
inputSchema: {
|
|
41822
41819
|
capabilityId: z45.string().min(1).max(512),
|
|
41823
41820
|
rationale: z45.string().min(1).max(2e3)
|
|
@@ -41863,7 +41860,7 @@ function registerCapabilityDiscoveryTools(server, deps, grant, sessionId, json2)
|
|
|
41863
41860
|
capabilityId: item.id,
|
|
41864
41861
|
status: "ready",
|
|
41865
41862
|
eventId,
|
|
41866
|
-
message: `${setup.detail} The Connected Machine card is in this chat: the person can pick a machine with "Use in this chat" or connect another. Chrome needs the
|
|
41863
|
+
message: `${setup.detail} The Connected Machine card is in this chat: the person can pick a machine with "Use in this chat" or connect another. Chrome needs the Opengeni Browser extension (${OPENGENI_BROWSER_EXTENSION_URL}) on a connected machine.`
|
|
41867
41864
|
});
|
|
41868
41865
|
}
|
|
41869
41866
|
if (githubCardWhenReady) {
|
|
@@ -51080,31 +51077,7 @@ function createBetterAuthSessionAdapter(auth, db) {
|
|
|
51080
51077
|
await db.execute(sql`delete from auth_sessions where id = ${input.authSessionId}`);
|
|
51081
51078
|
},
|
|
51082
51079
|
async createLegacySelectedSessionCookies(input, currentCookieHeader) {
|
|
51083
|
-
|
|
51084
|
-
const cookie = context.authCookies.sessionToken;
|
|
51085
|
-
const value = input ? signedCookieValue(input.token, context.secret) : "";
|
|
51086
|
-
const headers = [
|
|
51087
|
-
serializeCookieHeader(cookie.name, value, {
|
|
51088
|
-
...cookie.attributes,
|
|
51089
|
-
...input ? {} : { maxAge: 0, expires: /* @__PURE__ */ new Date(0) }
|
|
51090
|
-
})
|
|
51091
|
-
];
|
|
51092
|
-
for (const cacheCookie of [
|
|
51093
|
-
context.authCookies.sessionData,
|
|
51094
|
-
context.authCookies.accountData,
|
|
51095
|
-
context.authCookies.dontRememberToken
|
|
51096
|
-
]) {
|
|
51097
|
-
for (const name of cacheCookieNames(cacheCookie.name, currentCookieHeader)) {
|
|
51098
|
-
headers.push(
|
|
51099
|
-
serializeCookieHeader(name, "", {
|
|
51100
|
-
...cacheCookie.attributes,
|
|
51101
|
-
maxAge: 0,
|
|
51102
|
-
expires: /* @__PURE__ */ new Date(0)
|
|
51103
|
-
})
|
|
51104
|
-
);
|
|
51105
|
-
}
|
|
51106
|
-
}
|
|
51107
|
-
return headers;
|
|
51080
|
+
return await managedAuthSessionCookieHeaders(auth, input?.token ?? null, currentCookieHeader);
|
|
51108
51081
|
}
|
|
51109
51082
|
};
|
|
51110
51083
|
}
|
|
@@ -51116,6 +51089,33 @@ function liveResolvedSession(value) {
|
|
|
51116
51089
|
if (!Number.isFinite(expiryMillis) || expiryMillis <= Date.now()) return null;
|
|
51117
51090
|
return value;
|
|
51118
51091
|
}
|
|
51092
|
+
async function managedAuthSessionCookieHeaders(auth, token, currentCookieHeader) {
|
|
51093
|
+
const context = await auth.$context;
|
|
51094
|
+
const cookie = context.authCookies.sessionToken;
|
|
51095
|
+
const value = token ? signedCookieValue(token, context.secret) : "";
|
|
51096
|
+
const headers = [
|
|
51097
|
+
serializeCookieHeader(cookie.name, value, {
|
|
51098
|
+
...cookie.attributes,
|
|
51099
|
+
...token ? {} : { maxAge: 0, expires: /* @__PURE__ */ new Date(0) }
|
|
51100
|
+
})
|
|
51101
|
+
];
|
|
51102
|
+
for (const cacheCookie of [
|
|
51103
|
+
context.authCookies.sessionData,
|
|
51104
|
+
context.authCookies.accountData,
|
|
51105
|
+
context.authCookies.dontRememberToken
|
|
51106
|
+
]) {
|
|
51107
|
+
for (const name of cacheCookieNames(cacheCookie.name, currentCookieHeader)) {
|
|
51108
|
+
headers.push(
|
|
51109
|
+
serializeCookieHeader(name, "", {
|
|
51110
|
+
...cacheCookie.attributes,
|
|
51111
|
+
maxAge: 0,
|
|
51112
|
+
expires: /* @__PURE__ */ new Date(0)
|
|
51113
|
+
})
|
|
51114
|
+
);
|
|
51115
|
+
}
|
|
51116
|
+
}
|
|
51117
|
+
return headers;
|
|
51118
|
+
}
|
|
51119
51119
|
function cookieValue(header, name) {
|
|
51120
51120
|
if (!header) return null;
|
|
51121
51121
|
for (const part of header.split(";")) {
|
|
@@ -93652,6 +93652,20 @@ function browserIdentityRouteError(error) {
|
|
|
93652
93652
|
});
|
|
93653
93653
|
}
|
|
93654
93654
|
|
|
93655
|
+
// src/attached-browser-viewer.ts
|
|
93656
|
+
import { requireLiveAgentAttemptAuthorization as requireLiveAgentAttemptAuthorization6 } from "@opengeni/core";
|
|
93657
|
+
async function attachedBrowserViewer(deps, grant, workspaceId) {
|
|
93658
|
+
let subjectId = grant.subjectId;
|
|
93659
|
+
if (grant.principalKind === "agent_attempt") {
|
|
93660
|
+
const callerSessionId = grant.metadata?.["sessionId"];
|
|
93661
|
+
if (typeof callerSessionId === "string") {
|
|
93662
|
+
const actor = await requireLiveAgentAttemptAuthorization6(deps.db, grant, callerSessionId);
|
|
93663
|
+
subjectId = actor.initiatingHumanSubjectId ?? grant.subjectId;
|
|
93664
|
+
}
|
|
93665
|
+
}
|
|
93666
|
+
return { accountId: grant.accountId, workspaceId, subjectId };
|
|
93667
|
+
}
|
|
93668
|
+
|
|
93655
93669
|
// src/browser-ephemeral.ts
|
|
93656
93670
|
import { createHmac as createHmac10 } from "crypto";
|
|
93657
93671
|
import { BrowserControlUnsupportedError } from "@opengeni/runtime/sandbox";
|
|
@@ -94751,8 +94765,7 @@ function registerBrowserSessionRoutes(app, deps, options = {}) {
|
|
|
94751
94765
|
return context.json(
|
|
94752
94766
|
AttachedBrowserDeviceListResponse.parse(
|
|
94753
94767
|
await listAttachedBrowserDevices(deps.db, {
|
|
94754
|
-
|
|
94755
|
-
workspaceId,
|
|
94768
|
+
...await attachedBrowserViewer(deps, grant, workspaceId),
|
|
94756
94769
|
includeDisconnected
|
|
94757
94770
|
})
|
|
94758
94771
|
)
|
|
@@ -94766,8 +94779,7 @@ function registerBrowserSessionRoutes(app, deps, options = {}) {
|
|
|
94766
94779
|
return context.json(
|
|
94767
94780
|
AttachedBrowserDevice.parse(
|
|
94768
94781
|
await getAttachedBrowserDevice(deps.db, {
|
|
94769
|
-
|
|
94770
|
-
workspaceId,
|
|
94782
|
+
...await attachedBrowserViewer(deps, grant, workspaceId),
|
|
94771
94783
|
deviceId
|
|
94772
94784
|
})
|
|
94773
94785
|
)
|
|
@@ -97010,20 +97022,16 @@ function registerBrowserSessionRoutes(app, deps, options = {}) {
|
|
|
97010
97022
|
async function withBrowserPlacement(sourceSession, grant, expectedPlacement, expectedPlacementInstanceId, operation, waitSignal, callback, allowOperationReplay = true) {
|
|
97011
97023
|
if (expectedPlacement?.kind === "attached_device") {
|
|
97012
97024
|
waitSignal.throwIfAborted();
|
|
97025
|
+
const viewer = await attachedBrowserViewer(deps, grant, sourceSession.workspaceId);
|
|
97013
97026
|
const device = await getAttachedBrowserDevice(deps.db, {
|
|
97014
|
-
|
|
97015
|
-
workspaceId: sourceSession.workspaceId,
|
|
97027
|
+
...viewer,
|
|
97016
97028
|
deviceId: expectedPlacement.deviceId
|
|
97017
97029
|
});
|
|
97018
97030
|
if (device.state !== "connected") {
|
|
97019
97031
|
recordAttachedBrowserUnavailable(deps.observability, "disconnected");
|
|
97020
97032
|
throw new BrowserSessionStateError2("Attached browser is disconnected");
|
|
97021
97033
|
}
|
|
97022
|
-
const enrollment = await getLiveEnrollmentConnection6(
|
|
97023
|
-
deps.db,
|
|
97024
|
-
sourceSession.workspaceId,
|
|
97025
|
-
device.enrollmentId
|
|
97026
|
-
);
|
|
97034
|
+
const enrollment = await getLiveEnrollmentConnection6(deps.db, viewer, device.enrollmentId);
|
|
97027
97035
|
if (!enrollment || enrollment.status !== "active" || !enrollment.connectionInstanceId) {
|
|
97028
97036
|
recordAttachedBrowserUnavailable(deps.observability, "machine_unavailable");
|
|
97029
97037
|
throw new BrowserSessionStateError2("Attached browser machine is unavailable");
|
|
@@ -99744,19 +99752,15 @@ function registerComputerSessionRoutes(app, deps, options = {}) {
|
|
|
99744
99752
|
}
|
|
99745
99753
|
if (expectedPlacement?.kind === "attached_device") {
|
|
99746
99754
|
waitSignal.throwIfAborted();
|
|
99755
|
+
const viewer = await attachedBrowserViewer(deps, grant, sourceSession.workspaceId);
|
|
99747
99756
|
const device = await getAttachedBrowserDevice2(deps.db, {
|
|
99748
|
-
|
|
99749
|
-
workspaceId: sourceSession.workspaceId,
|
|
99757
|
+
...viewer,
|
|
99750
99758
|
deviceId: expectedPlacement.deviceId
|
|
99751
99759
|
});
|
|
99752
99760
|
if (device.state !== "connected") {
|
|
99753
99761
|
throw new ComputerSessionStateError("Attached browser machine is disconnected");
|
|
99754
99762
|
}
|
|
99755
|
-
const enrollment = await getLiveEnrollmentConnection7(
|
|
99756
|
-
deps.db,
|
|
99757
|
-
sourceSession.workspaceId,
|
|
99758
|
-
device.enrollmentId
|
|
99759
|
-
);
|
|
99763
|
+
const enrollment = await getLiveEnrollmentConnection7(deps.db, viewer, device.enrollmentId);
|
|
99760
99764
|
if (!enrollment || enrollment.status !== "active" || !enrollment.connectionInstanceId) {
|
|
99761
99765
|
throw new ComputerSessionStateError("Attached browser machine is unavailable");
|
|
99762
99766
|
}
|
|
@@ -115514,6 +115518,12 @@ function sessionAuthorizationHttpError(error) {
|
|
|
115514
115518
|
if (error instanceof HTTPException99) return error;
|
|
115515
115519
|
throw error;
|
|
115516
115520
|
}
|
|
115521
|
+
var SESSION_FORK_POINT_REFUSAL_MESSAGES = {
|
|
115522
|
+
message_unavailable: "Only your messages and the agent's replies can be fork points. Choose one of those, or fork the whole session.",
|
|
115523
|
+
boundary_not_retained: "This message is from before the conversation was compacted, so it can't be a fork point. Choose a more recent message, or fork the whole session.",
|
|
115524
|
+
history_compacted: "The conversation up to this message was compacted, so it can't be copied as it was then. Choose a more recent message, or fork the whole session.",
|
|
115525
|
+
unfinished_tool_exchange: "The agent was still waiting for a tool result at this message. Choose a later message, or fork the whole session."
|
|
115526
|
+
};
|
|
115517
115527
|
function sessionTenancyHttpError(error) {
|
|
115518
115528
|
if (error instanceof SessionTenancyManagedHumanRequiredError2) {
|
|
115519
115529
|
return new ApiHttpError(403, {
|
|
@@ -115565,6 +115575,14 @@ function sessionTenancyHttpError(error) {
|
|
|
115565
115575
|
});
|
|
115566
115576
|
}
|
|
115567
115577
|
if (error instanceof SessionTenancyInvalidRequestError) {
|
|
115578
|
+
if (error.forkPointRefusal) {
|
|
115579
|
+
return new ApiHttpError(422, {
|
|
115580
|
+
code: "validation_failed",
|
|
115581
|
+
message: SESSION_FORK_POINT_REFUSAL_MESSAGES[error.forkPointRefusal],
|
|
115582
|
+
retryable: false,
|
|
115583
|
+
details: { reason: `fork_point_${error.forkPointRefusal}` }
|
|
115584
|
+
});
|
|
115585
|
+
}
|
|
115568
115586
|
return new ApiHttpError(422, {
|
|
115569
115587
|
code: "validation_failed",
|
|
115570
115588
|
message: "Invalid session tenancy request.",
|
|
@@ -121038,7 +121056,7 @@ import {
|
|
|
121038
121056
|
grantHasAgentAttemptAuthority as grantHasAgentAttemptAuthority4,
|
|
121039
121057
|
hasPermission as hasPermission34,
|
|
121040
121058
|
requireAccessGrantAuthorization as requireAccessGrantAuthorization37,
|
|
121041
|
-
requireLiveAgentAttemptAuthorization as
|
|
121059
|
+
requireLiveAgentAttemptAuthorization as requireLiveAgentAttemptAuthorization7,
|
|
121042
121060
|
requireSessionAuthorization as requireSessionAuthorization18,
|
|
121043
121061
|
SessionAuthorizationDeniedError as SessionAuthorizationDeniedError17,
|
|
121044
121062
|
SessionAuthorizationUnavailableError as SessionAuthorizationUnavailableError15,
|
|
@@ -121228,7 +121246,7 @@ function registerArtifactCatalogRoutes(app, deps) {
|
|
|
121228
121246
|
try {
|
|
121229
121247
|
if (typeof callerSessionId !== "string")
|
|
121230
121248
|
throw new SessionAuthorizationDeniedError17("caller_stale");
|
|
121231
|
-
const attempt = await
|
|
121249
|
+
const attempt = await requireLiveAgentAttemptAuthorization7(deps.db, grant, callerSessionId);
|
|
121232
121250
|
actor.initiatingHumanSubjectId = attempt.initiatingHumanSubjectId;
|
|
121233
121251
|
} catch (error) {
|
|
121234
121252
|
if (error instanceof SessionAuthorizationDeniedError17)
|
|
@@ -127743,12 +127761,23 @@ var CODE_IDENTIFIER_PREFIX = "opengeni-native-app-code:";
|
|
|
127743
127761
|
var PKCE_VERIFIER2 = /^[A-Za-z0-9._~-]{43,128}$/u;
|
|
127744
127762
|
var PKCE_CHALLENGE2 = /^[A-Za-z0-9_-]{43}$/u;
|
|
127745
127763
|
var MANAGED_PROVIDERS = ["credential", "google", "github"];
|
|
127764
|
+
var NATIVE_APP_PLATFORMS = ["ios", "android", "macos", "windows", "linux"];
|
|
127765
|
+
var PLATFORM_LABELS = {
|
|
127766
|
+
ios: "iOS",
|
|
127767
|
+
android: "Android",
|
|
127768
|
+
macos: "macOS",
|
|
127769
|
+
windows: "Windows",
|
|
127770
|
+
linux: "Linux"
|
|
127771
|
+
};
|
|
127772
|
+
function nativeAppSessionUserAgent(platform, deviceName) {
|
|
127773
|
+
return `Opengeni app (${PLATFORM_LABELS[platform]}${deviceName ? `; ${deviceName}` : ""})`;
|
|
127774
|
+
}
|
|
127746
127775
|
var AuthorizeBody = z71.object({
|
|
127747
127776
|
redirectUri: z71.string().min(1).max(512),
|
|
127748
127777
|
codeChallenge: z71.string().regex(PKCE_CHALLENGE2),
|
|
127749
127778
|
codeChallengeMethod: z71.literal("S256"),
|
|
127750
127779
|
state: z71.string().min(8).max(256).optional(),
|
|
127751
|
-
platform: z71.enum(
|
|
127780
|
+
platform: z71.enum(NATIVE_APP_PLATFORMS),
|
|
127752
127781
|
deviceName: z71.string().trim().min(1).max(80).optional()
|
|
127753
127782
|
});
|
|
127754
127783
|
var TokenBody = z71.object({
|
|
@@ -127768,7 +127797,7 @@ var StoredGrant = z71.object({
|
|
|
127768
127797
|
providerId: z71.enum(MANAGED_PROVIDERS),
|
|
127769
127798
|
redirectUri: z71.string(),
|
|
127770
127799
|
codeChallenge: z71.string().regex(PKCE_CHALLENGE2),
|
|
127771
|
-
platform: z71.enum(
|
|
127800
|
+
platform: z71.enum(NATIVE_APP_PLATFORMS),
|
|
127772
127801
|
deviceName: z71.string().nullable()
|
|
127773
127802
|
});
|
|
127774
127803
|
function nativeAppRedirectAllowed(redirectUri, schemes) {
|
|
@@ -127867,11 +127896,10 @@ function registerNativeAppAuthRoutes(app, deps) {
|
|
|
127867
127896
|
if (!sameText(grant.redirectUri, body4.redirectUri) || !sameText(grant.codeChallenge, pkceS256Challenge(body4.codeVerifier))) {
|
|
127868
127897
|
return context.json({ error: "invalid_grant" }, 400);
|
|
127869
127898
|
}
|
|
127870
|
-
const platform = grant.platform === "ios" ? "iOS" : "Android";
|
|
127871
127899
|
const session = await runManagedAuthProvider(
|
|
127872
127900
|
grant.providerId,
|
|
127873
127901
|
() => authContext.internalAdapter.createSession(grant.userId, false, {
|
|
127874
|
-
userAgent:
|
|
127902
|
+
userAgent: nativeAppSessionUserAgent(grant.platform, grant.deviceName)
|
|
127875
127903
|
})
|
|
127876
127904
|
);
|
|
127877
127905
|
if (!session?.token || new Date(session.expiresAt).getTime() <= Date.now()) {
|
|
@@ -127897,6 +127925,31 @@ function registerNativeAppAuthRoutes(app, deps) {
|
|
|
127897
127925
|
}
|
|
127898
127926
|
return context.json({ signedOut: true });
|
|
127899
127927
|
});
|
|
127928
|
+
app.post(`${base}/web-session`, async (context) => {
|
|
127929
|
+
const auth = deps.managedAuth;
|
|
127930
|
+
if (!auth) throw new HTTPException126(404);
|
|
127931
|
+
if (deps.settings.managedAuthSessionSetMode === "broker") {
|
|
127932
|
+
throw new HTTPException126(409, {
|
|
127933
|
+
message: "This deployment signs browser windows in through login session sets"
|
|
127934
|
+
});
|
|
127935
|
+
}
|
|
127936
|
+
const credential = bearer(context.req.header("authorization"));
|
|
127937
|
+
if (!credential?.startsWith(NATIVE_APP_CREDENTIAL_PREFIX)) {
|
|
127938
|
+
throw new HTTPException126(401, { message: "Native app credential required" });
|
|
127939
|
+
}
|
|
127940
|
+
const session = await getNativeAppManagedSession(auth, credential, deps.db).catch(() => null);
|
|
127941
|
+
if (typeof session?.session?.id !== "string") {
|
|
127942
|
+
throw new HTTPException126(401, { message: "Sign in again" });
|
|
127943
|
+
}
|
|
127944
|
+
for (const cookie of await managedAuthSessionCookieHeaders(
|
|
127945
|
+
auth,
|
|
127946
|
+
credential.slice(NATIVE_APP_CREDENTIAL_PREFIX.length),
|
|
127947
|
+
context.req.header("cookie") ?? null
|
|
127948
|
+
)) {
|
|
127949
|
+
context.header("set-cookie", cookie, { append: true });
|
|
127950
|
+
}
|
|
127951
|
+
return context.json({ signedIn: true });
|
|
127952
|
+
});
|
|
127900
127953
|
const appSessionId = async (authorization) => {
|
|
127901
127954
|
const auth = deps.managedAuth;
|
|
127902
127955
|
if (!auth) throw new HTTPException126(404);
|
|
@@ -135416,6 +135469,7 @@ function createAppComposition(deps) {
|
|
|
135416
135469
|
ClientConfig.parse({
|
|
135417
135470
|
deploymentRevision: deps.settings.deploymentRevision,
|
|
135418
135471
|
claudeSubscriptionEnabled: deps.settings.claudeSubscriptionEnabled,
|
|
135472
|
+
connectedMachines: deps.settings.sandboxSelfhostedEnabled,
|
|
135419
135473
|
apiContractRevision: OPENGENI_API_CONTRACT_REVISION4,
|
|
135420
135474
|
...deps.settings.serverVersion ? { serverVersion: deps.settings.serverVersion } : {},
|
|
135421
135475
|
defaultModel,
|
|
@@ -137625,4 +137679,4 @@ export {
|
|
|
137625
137679
|
withDefaultEnabledCapabilityMcpTools,
|
|
137626
137680
|
workflowIdForSession4 as workflowIdForSession
|
|
137627
137681
|
};
|
|
137628
|
-
//# sourceMappingURL=chunk-
|
|
137682
|
+
//# sourceMappingURL=chunk-ZDTWXRFX.js.map
|