@agent-native/dispatch 0.40.30 → 0.40.33
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/CHANGELOG.md +28 -25
- package/dist/actions/connect-external-agent.d.ts.map +1 -1
- package/dist/actions/connect-external-agent.js +2 -9
- package/dist/actions/connect-external-agent.js.map +1 -1
- package/dist/actions/list-connected-agents.js +7 -3
- package/dist/actions/list-connected-agents.js.map +1 -1
- package/dist/actions/list-dispatch-usage-metrics.js +1 -1
- package/dist/actions/list-dispatch-usage-metrics.js.map +1 -1
- package/dist/actions/list-workspace-connections.d.ts.map +1 -1
- package/dist/actions/list-workspace-connections.js +13 -18
- package/dist/actions/list-workspace-connections.js.map +1 -1
- package/dist/actions/provider-api-register.d.ts +8 -8
- package/dist/actions/provider-api-register.js +2 -14
- package/dist/actions/provider-api-register.js.map +1 -1
- package/dist/components/action-query-error.d.ts +2 -1
- package/dist/components/action-query-error.d.ts.map +1 -1
- package/dist/components/action-query-error.js +25 -2
- package/dist/components/action-query-error.js.map +1 -1
- package/dist/components/chat-home-apps.d.ts.map +1 -1
- package/dist/components/chat-home-apps.js +2 -2
- package/dist/components/chat-home-apps.js.map +1 -1
- package/dist/components/dispatch-control-plane.d.ts.map +1 -1
- package/dist/components/dispatch-control-plane.js +1 -1
- package/dist/components/dispatch-control-plane.js.map +1 -1
- package/dist/components/layout/Layout.d.ts +1 -0
- package/dist/components/layout/Layout.d.ts.map +1 -1
- package/dist/components/layout/Layout.js +7 -0
- package/dist/components/layout/Layout.js.map +1 -1
- package/dist/db/index.d.ts.map +1 -1
- package/dist/db/index.js +4 -9
- package/dist/db/index.js.map +1 -1
- package/dist/routes/pages/$appId.d.ts.map +1 -1
- package/dist/routes/pages/$appId.js +1 -1
- package/dist/routes/pages/$appId.js.map +1 -1
- package/dist/routes/pages/apps.js +1 -1
- package/dist/routes/pages/apps.js.map +1 -1
- package/dist/server/lib/admin-config.d.ts.map +1 -1
- package/dist/server/lib/admin-config.js +3 -1
- package/dist/server/lib/admin-config.js.map +1 -1
- package/dist/server/lib/app-creation-store.d.ts.map +1 -1
- package/dist/server/lib/app-creation-store.js +124 -65
- package/dist/server/lib/app-creation-store.js.map +1 -1
- package/dist/server/lib/community-apps.js +1 -1
- package/dist/server/lib/community-apps.js.map +1 -1
- package/dist/server/lib/mcp-access-store.d.ts.map +1 -1
- package/dist/server/lib/mcp-access-store.js +2 -10
- package/dist/server/lib/mcp-access-store.js.map +1 -1
- package/dist/server/lib/thread-debug-store.d.ts.map +1 -1
- package/dist/server/lib/thread-debug-store.js +2 -6
- package/dist/server/lib/thread-debug-store.js.map +1 -1
- package/dist/server/lib/usage-metrics-store.d.ts.map +1 -1
- package/dist/server/lib/usage-metrics-store.js +2 -11
- package/dist/server/lib/usage-metrics-store.js.map +1 -1
- package/dist/server/lib/vault-store.d.ts.map +1 -1
- package/dist/server/lib/vault-store.js +2 -9
- package/dist/server/lib/vault-store.js.map +1 -1
- package/dist/server/lib/workspace-app-action-auth.d.ts.map +1 -1
- package/dist/server/lib/workspace-app-action-auth.js +7 -2
- package/dist/server/lib/workspace-app-action-auth.js.map +1 -1
- package/dist/server/plugins/agent-chat.js +1 -1
- package/dist/server/plugins/agent-chat.js.map +1 -1
- package/package.json +3 -3
- package/src/actions/connect-external-agent.spec.ts +25 -11
- package/src/actions/connect-external-agent.ts +2 -9
- package/src/actions/index.spec.ts +3 -0
- package/src/actions/list-connected-agents.spec.ts +17 -1
- package/src/actions/list-connected-agents.ts +7 -2
- package/src/actions/list-dispatch-usage-metrics.ts +1 -1
- package/src/actions/list-workspace-connections.spec.ts +14 -3
- package/src/actions/list-workspace-connections.ts +13 -17
- package/src/actions/provider-api-register.spec.ts +15 -9
- package/src/actions/provider-api-register.ts +5 -13
- package/src/components/action-query-error.spec.tsx +20 -0
- package/src/components/action-query-error.tsx +40 -0
- package/src/components/chat-home-apps.spec.tsx +32 -1
- package/src/components/chat-home-apps.tsx +6 -1
- package/src/components/dispatch-control-plane.tsx +1 -0
- package/src/components/layout/Layout.tsx +9 -0
- package/src/db/index.spec.ts +74 -0
- package/src/db/index.ts +4 -9
- package/src/routes/pages/$appId.tsx +1 -0
- package/src/routes/pages/apps.spec.tsx +26 -2
- package/src/routes/pages/apps.tsx +2 -0
- package/src/server/lib/admin-config.spec.ts +51 -0
- package/src/server/lib/admin-config.ts +2 -1
- package/src/server/lib/app-creation-store.spec.ts +308 -10
- package/src/server/lib/app-creation-store.ts +174 -70
- package/src/server/lib/community-apps.ts +1 -1
- package/src/server/lib/mcp-access-store.spec.ts +39 -12
- package/src/server/lib/mcp-access-store.ts +3 -10
- package/src/server/lib/thread-debug-store.spec.ts +64 -0
- package/src/server/lib/thread-debug-store.ts +2 -9
- package/src/server/lib/usage-metrics-store.spec.ts +64 -0
- package/src/server/lib/usage-metrics-store.ts +2 -17
- package/src/server/lib/vault-store.spec.ts +26 -9
- package/src/server/lib/vault-store.ts +3 -10
- package/src/server/lib/workspace-app-action-auth.spec.ts +37 -6
- package/src/server/lib/workspace-app-action-auth.ts +12 -2
- package/src/server/plugins/agent-chat.spec.ts +14 -0
- package/src/server/plugins/agent-chat.ts +1 -1
|
@@ -2,13 +2,13 @@ import { spawn } from "node:child_process";
|
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
-
import { canonicalA2AAudience, signA2AToken } from "@agent-native/core/a2a";
|
|
6
|
-
import { isActionContractError } from "@agent-native/core/action";
|
|
5
|
+
import { canonicalA2AAudience, getGlobalA2ASecret, signA2AToken, } from "@agent-native/core/a2a";
|
|
6
|
+
import { ActionContractError, isActionContractError, } from "@agent-native/core/action";
|
|
7
7
|
import { describeErrorWithCauses } from "@agent-native/core/agent/engine/error-detail";
|
|
8
8
|
import { getDbExec } from "@agent-native/core/db";
|
|
9
9
|
import { and, eq, inArray } from "@agent-native/core/db/schema";
|
|
10
|
-
import { getOrgDomain, isWorkspaceAppAccessAllowed, workspaceApps, } from "@agent-native/core/org";
|
|
11
|
-
import { CredentialStoreUnavailableError, FeatureNotConfiguredError, createBuilderProject, getBuilderBranchProjectId, getConfiguredAppBasePath, getRequestContext, inferWorkspaceAppRootHomePath, isIntegrationCallerRequest, readConfiguredWorkspaceAppHomePath, resolveAppRuntimeUrl, resolveVercelDeploymentProtectionHeaders, normalizeBuilderAgentAttachments, runBuilderAgent, } from "@agent-native/core/server";
|
|
10
|
+
import { getOrgA2ASecret, getOrgDomain, isWorkspaceAppAccessAllowed, workspaceApps, } from "@agent-native/core/org";
|
|
11
|
+
import { CredentialStoreUnavailableError, FeatureNotConfiguredError, createBuilderProject, getBuilderBranchProjectId, getConfiguredAppBasePath, getRequestContext, inferWorkspaceAppRootHomePath, isIntegrationCallerRequest, readConfiguredWorkspaceAppHomePath, readOrgMemberRole, resolveAppRuntimeUrl, resolveVercelDeploymentProtectionHeaders, normalizeBuilderAgentAttachments, runBuilderAgent, } from "@agent-native/core/server";
|
|
12
12
|
import { getOrgSetting } from "@agent-native/core/settings";
|
|
13
13
|
import { getSetting, mutateSetting, putSetting, } from "@agent-native/core/settings";
|
|
14
14
|
import { BUILDER_CONNECT_PROVIDER, BUILDER_CONNECT_PROVIDER_LABEL, assertValidWorkspaceAppId, connectRequiredResult, normalizeWorkspaceAppHomePath, parseAgentProfileMetadata, } from "@agent-native/core/shared";
|
|
@@ -89,13 +89,11 @@ function requireWorkspaceAppOrgId() {
|
|
|
89
89
|
throw new WorkspaceAppOrgRequiredError();
|
|
90
90
|
return orgId;
|
|
91
91
|
}
|
|
92
|
-
class WorkspaceAppsGatewayAuthorizationError extends
|
|
93
|
-
constructor(statusCode, message = `Workspace apps gateway rejected the request with HTTP ${statusCode}.`) {
|
|
94
|
-
super(message);
|
|
92
|
+
class WorkspaceAppsGatewayAuthorizationError extends ActionContractError {
|
|
93
|
+
constructor(statusCode, errorCode = "workspace_gateway_rejected", message = `Workspace apps gateway rejected the request with HTTP ${statusCode}.`) {
|
|
94
|
+
super(message, { statusCode, errorCode });
|
|
95
95
|
this.name = "WorkspaceAppsGatewayAuthorizationError";
|
|
96
|
-
this.statusCode = statusCode;
|
|
97
96
|
}
|
|
98
|
-
statusCode;
|
|
99
97
|
}
|
|
100
98
|
function warnWorkspaceAppsGatewayDenial(denial, source) {
|
|
101
99
|
if (!denial)
|
|
@@ -581,6 +579,20 @@ function parseWorkspaceAppsManifest(parsed) {
|
|
|
581
579
|
.sort(sortWorkspaceApps);
|
|
582
580
|
return apps.length ? apps : null;
|
|
583
581
|
}
|
|
582
|
+
function parseWorkspaceAppsGatewayResponse(parsed) {
|
|
583
|
+
const rawApps = parsed && typeof parsed === "object" && !Array.isArray(parsed)
|
|
584
|
+
? parsed.apps
|
|
585
|
+
: parsed;
|
|
586
|
+
if (!Array.isArray(rawApps))
|
|
587
|
+
return null;
|
|
588
|
+
if (rawApps.length === 0)
|
|
589
|
+
return [];
|
|
590
|
+
const apps = parseWorkspaceAppsManifest(parsed);
|
|
591
|
+
if (!apps || apps.length !== rawApps.length)
|
|
592
|
+
return null;
|
|
593
|
+
const ids = new Set(apps.map((app) => app.id.toLowerCase()));
|
|
594
|
+
return ids.size === apps.length ? apps : null;
|
|
595
|
+
}
|
|
584
596
|
function parseDateMs(value) {
|
|
585
597
|
if (!value)
|
|
586
598
|
return null;
|
|
@@ -1371,27 +1383,64 @@ async function readWorkspaceAppsFromGateway() {
|
|
|
1371
1383
|
return null;
|
|
1372
1384
|
}
|
|
1373
1385
|
const requestContext = getRequestContext();
|
|
1386
|
+
const isLocalGateway = isLocalWorkspaceGateway(baseUrl);
|
|
1387
|
+
const requestOrigin = requestContext?.requestOrigin;
|
|
1388
|
+
const isSameOriginGateway = requestOrigin
|
|
1389
|
+
? (() => {
|
|
1390
|
+
try {
|
|
1391
|
+
return new URL(requestOrigin).origin === baseUrl.origin;
|
|
1392
|
+
}
|
|
1393
|
+
catch {
|
|
1394
|
+
// coercion-ok: an invalid request origin cannot prove a self-fetch.
|
|
1395
|
+
return false;
|
|
1396
|
+
}
|
|
1397
|
+
})()
|
|
1398
|
+
: false;
|
|
1399
|
+
if (isSameOriginGateway && !isLocalGateway)
|
|
1400
|
+
return null;
|
|
1374
1401
|
const authHeaders = {};
|
|
1375
1402
|
if (requestContext?.userEmail) {
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1403
|
+
let orgDomain;
|
|
1404
|
+
let orgSecret;
|
|
1405
|
+
if (requestContext.orgId) {
|
|
1406
|
+
try {
|
|
1407
|
+
orgDomain =
|
|
1408
|
+
(await getOrgDomain(requestContext.orgId))?.trim().toLowerCase() ||
|
|
1409
|
+
undefined;
|
|
1410
|
+
}
|
|
1411
|
+
catch {
|
|
1412
|
+
throw new WorkspaceAppsGatewayAuthorizationError(503, "workspace_gateway_org_domain_unavailable", "Workspace apps gateway organization domain could not be read.");
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1379
1415
|
if (requestContext.orgId && !orgDomain) {
|
|
1380
|
-
throw new WorkspaceAppsGatewayAuthorizationError(401, "Workspace apps gateway cannot authenticate without a resolved organization domain.");
|
|
1416
|
+
throw new WorkspaceAppsGatewayAuthorizationError(401, "workspace_gateway_org_domain_missing", "Workspace apps gateway cannot authenticate without a resolved organization domain.");
|
|
1381
1417
|
}
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1418
|
+
if (requestContext.orgId) {
|
|
1419
|
+
try {
|
|
1420
|
+
orgSecret =
|
|
1421
|
+
(await getOrgA2ASecret(requestContext.orgId))?.trim() || undefined;
|
|
1422
|
+
}
|
|
1423
|
+
catch {
|
|
1424
|
+
throw new WorkspaceAppsGatewayAuthorizationError(503, "workspace_gateway_org_a2a_secret_unavailable", "Workspace apps gateway organization A2A secret could not be read.");
|
|
1425
|
+
}
|
|
1389
1426
|
}
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1427
|
+
const globalSecret = getGlobalA2ASecret();
|
|
1428
|
+
const allowLocalUnauthenticatedRequest = isLocalGateway && !orgSecret && !globalSecret;
|
|
1429
|
+
if (!orgSecret && !globalSecret && !allowLocalUnauthenticatedRequest) {
|
|
1430
|
+
throw new WorkspaceAppsGatewayAuthorizationError(503, "workspace_gateway_a2a_secret_missing", "Workspace apps gateway cannot authenticate because no A2A secret is configured.");
|
|
1431
|
+
}
|
|
1432
|
+
if (!allowLocalUnauthenticatedRequest) {
|
|
1433
|
+
try {
|
|
1434
|
+
const token = await signA2AToken(requestContext.userEmail, orgDomain, orgSecret, {
|
|
1435
|
+
expiresIn: "1m",
|
|
1436
|
+
preferGlobalSecret: false,
|
|
1437
|
+
audience: canonicalA2AAudience(baseUrl.toString(), getConfiguredAppBasePath() || undefined),
|
|
1438
|
+
});
|
|
1439
|
+
authHeaders.Authorization = `Bearer ${token}`;
|
|
1440
|
+
}
|
|
1441
|
+
catch {
|
|
1442
|
+
throw new WorkspaceAppsGatewayAuthorizationError(503, "workspace_gateway_token_signing_failed", "Workspace apps gateway could not sign the authenticated request.");
|
|
1443
|
+
}
|
|
1395
1444
|
}
|
|
1396
1445
|
}
|
|
1397
1446
|
const controller = new AbortController();
|
|
@@ -1413,36 +1462,46 @@ async function readWorkspaceAppsFromGateway() {
|
|
|
1413
1462
|
const protectedRedirect = protectionHeaders["x-vercel-protection-bypass"]
|
|
1414
1463
|
? { redirect: "manual" }
|
|
1415
1464
|
: {};
|
|
1465
|
+
const readAppsResponse = async (response) => {
|
|
1466
|
+
let payload;
|
|
1467
|
+
try {
|
|
1468
|
+
payload = await response.json();
|
|
1469
|
+
}
|
|
1470
|
+
catch {
|
|
1471
|
+
throw new ActionContractError("Workspace apps gateway returned an unreadable response.", {
|
|
1472
|
+
statusCode: 503,
|
|
1473
|
+
errorCode: "workspace_gateway_invalid_response",
|
|
1474
|
+
});
|
|
1475
|
+
}
|
|
1476
|
+
const apps = parseWorkspaceAppsGatewayResponse(payload);
|
|
1477
|
+
if (!apps) {
|
|
1478
|
+
throw new ActionContractError("Workspace apps gateway returned an invalid response.", {
|
|
1479
|
+
statusCode: 503,
|
|
1480
|
+
errorCode: "workspace_gateway_invalid_response",
|
|
1481
|
+
});
|
|
1482
|
+
}
|
|
1483
|
+
return { apps };
|
|
1484
|
+
};
|
|
1416
1485
|
try {
|
|
1417
|
-
if (
|
|
1486
|
+
if (isLocalGateway) {
|
|
1418
1487
|
const localResponse = await fetch(gatewayUrl(WORKSPACE_APPS_GATEWAY_PATH), {
|
|
1419
1488
|
headers,
|
|
1420
1489
|
...protectedRedirect,
|
|
1421
1490
|
signal: controller.signal,
|
|
1422
1491
|
});
|
|
1423
1492
|
if (localResponse.status === 401 || localResponse.status === 403) {
|
|
1424
|
-
throw new WorkspaceAppsGatewayAuthorizationError(localResponse.status);
|
|
1493
|
+
throw new WorkspaceAppsGatewayAuthorizationError(localResponse.status, "workspace_gateway_rejected");
|
|
1494
|
+
}
|
|
1495
|
+
if (localResponse.status !== 404 && localResponse.status !== 200) {
|
|
1496
|
+
throw new ActionContractError(`Workspace apps gateway returned HTTP ${localResponse.status}.`, {
|
|
1497
|
+
statusCode: 503,
|
|
1498
|
+
errorCode: `workspace_gateway_http_${localResponse.status}`,
|
|
1499
|
+
});
|
|
1425
1500
|
}
|
|
1426
|
-
if (localResponse.
|
|
1427
|
-
|
|
1428
|
-
// coercion-ok: malformed gateway JSON is an unavailable registry and
|
|
1429
|
-
// must fall through to the local manifest sources.
|
|
1430
|
-
await localResponse.json().catch(() => null));
|
|
1431
|
-
return apps ? { apps } : null;
|
|
1501
|
+
if (localResponse.status === 200) {
|
|
1502
|
+
return await readAppsResponse(localResponse);
|
|
1432
1503
|
}
|
|
1433
1504
|
}
|
|
1434
|
-
const requestOrigin = requestContext?.requestOrigin;
|
|
1435
|
-
const isSameOriginGateway = requestOrigin
|
|
1436
|
-
? (() => {
|
|
1437
|
-
try {
|
|
1438
|
-
return new URL(requestOrigin).origin === baseUrl.origin;
|
|
1439
|
-
}
|
|
1440
|
-
catch {
|
|
1441
|
-
// coercion-ok: an invalid request origin cannot prove a self-fetch.
|
|
1442
|
-
return false;
|
|
1443
|
-
}
|
|
1444
|
-
})()
|
|
1445
|
-
: false;
|
|
1446
1505
|
if (isSameOriginGateway || !authHeaders.Authorization)
|
|
1447
1506
|
return null;
|
|
1448
1507
|
const actionUrl = gatewayUrl(WORKSPACE_APPS_ACTION_PATH);
|
|
@@ -1454,20 +1513,25 @@ async function readWorkspaceAppsFromGateway() {
|
|
|
1454
1513
|
signal: controller.signal,
|
|
1455
1514
|
});
|
|
1456
1515
|
if (actionResponse.status === 401 || actionResponse.status === 403) {
|
|
1457
|
-
throw new WorkspaceAppsGatewayAuthorizationError(actionResponse.status);
|
|
1516
|
+
throw new WorkspaceAppsGatewayAuthorizationError(actionResponse.status, "workspace_gateway_rejected");
|
|
1458
1517
|
}
|
|
1459
|
-
if (
|
|
1518
|
+
if (actionResponse.status === 404)
|
|
1460
1519
|
return null;
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1520
|
+
if (actionResponse.status !== 200) {
|
|
1521
|
+
throw new ActionContractError(`Workspace apps gateway returned HTTP ${actionResponse.status}.`, {
|
|
1522
|
+
statusCode: 503,
|
|
1523
|
+
errorCode: `workspace_gateway_http_${actionResponse.status}`,
|
|
1524
|
+
});
|
|
1525
|
+
}
|
|
1526
|
+
return await readAppsResponse(actionResponse);
|
|
1466
1527
|
}
|
|
1467
1528
|
catch (error) {
|
|
1468
|
-
if (error instanceof
|
|
1529
|
+
if (error instanceof ActionContractError)
|
|
1469
1530
|
throw error;
|
|
1470
|
-
|
|
1531
|
+
throw new ActionContractError("Workspace apps gateway could not be reached.", {
|
|
1532
|
+
statusCode: 503,
|
|
1533
|
+
errorCode: "workspace_gateway_unavailable",
|
|
1534
|
+
});
|
|
1471
1535
|
}
|
|
1472
1536
|
finally {
|
|
1473
1537
|
clearTimeout(timeout);
|
|
@@ -1698,6 +1762,9 @@ async function listWorkspaceAppsWithPrincipal(options = {}, principal = "user")
|
|
|
1698
1762
|
if (gatewayApps) {
|
|
1699
1763
|
return finalize(gatewayApps.apps);
|
|
1700
1764
|
}
|
|
1765
|
+
if (gatewayDenial && gatewayDenial.statusCode >= 500) {
|
|
1766
|
+
throw gatewayDenial;
|
|
1767
|
+
}
|
|
1701
1768
|
const unverified = gatewayDenial !== null;
|
|
1702
1769
|
const workspaceRoot = findWorkspaceRoot();
|
|
1703
1770
|
const localFilesystemApps = workspaceRoot && isLocalAppCreationRuntime()
|
|
@@ -1717,8 +1784,9 @@ async function listWorkspaceAppsWithPrincipal(options = {}, principal = "user")
|
|
|
1717
1784
|
// means the registry will not show this reader its apps, so they get the
|
|
1718
1785
|
// deployment's own list instead, and none of it is recorded. (A manifest
|
|
1719
1786
|
// answers earlier for both, with the warning above.)
|
|
1720
|
-
if (gatewayDenial
|
|
1787
|
+
if (gatewayDenial && gatewayDenial.statusCode !== 403) {
|
|
1721
1788
|
throw gatewayDenial;
|
|
1789
|
+
}
|
|
1722
1790
|
warnWorkspaceAppsGatewayDenial(gatewayDenial, "deployment's own app list");
|
|
1723
1791
|
if (!workspaceRoot) {
|
|
1724
1792
|
return finalize([
|
|
@@ -2085,19 +2153,10 @@ function isSyntheticIntegrationOwner(ownerEmail) {
|
|
|
2085
2153
|
}
|
|
2086
2154
|
async function requestOwnerRole() {
|
|
2087
2155
|
const orgId = currentOrgId();
|
|
2088
|
-
const ownerEmail = currentOwnerEmail();
|
|
2089
2156
|
if (!orgId)
|
|
2090
2157
|
return null;
|
|
2091
2158
|
try {
|
|
2092
|
-
|
|
2093
|
-
sql: `SELECT role FROM org_members
|
|
2094
|
-
WHERE org_id = ? AND LOWER(email) = ?
|
|
2095
|
-
AND federation_removal_pending_at IS NULL
|
|
2096
|
-
LIMIT 1`,
|
|
2097
|
-
args: [orgId, ownerEmail.toLowerCase()],
|
|
2098
|
-
});
|
|
2099
|
-
const role = rows[0]?.role;
|
|
2100
|
-
return typeof role === "string" ? role : null;
|
|
2159
|
+
return await readOrgMemberRole(orgId, currentOwnerEmail());
|
|
2101
2160
|
}
|
|
2102
2161
|
catch (error) {
|
|
2103
2162
|
throw new AppCreationOwnerRoleUnavailableError(error);
|