@agent-native/dispatch 0.27.9 → 0.27.10
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/actions/create_workspace_app_embed_session.d.ts.map +1 -1
- package/dist/actions/create_workspace_app_embed_session.js +6 -3
- package/dist/actions/create_workspace_app_embed_session.js.map +1 -1
- package/dist/server/lib/app-creation-store.d.ts.map +1 -1
- package/dist/server/lib/app-creation-store.js +5 -0
- package/dist/server/lib/app-creation-store.js.map +1 -1
- package/dist/server/lib/mcp-gateway.d.ts.map +1 -1
- package/dist/server/lib/mcp-gateway.js +144 -47
- package/dist/server/lib/mcp-gateway.js.map +1 -1
- package/package.json +2 -2
- package/src/actions/create_workspace_app_embed_session.spec.ts +19 -3
- package/src/actions/create_workspace_app_embed_session.ts +6 -5
- package/src/server/lib/app-creation-store.ts +5 -0
- package/src/server/lib/mcp-gateway.spec.ts +94 -17
- package/src/server/lib/mcp-gateway.ts +185 -56
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-gateway.d.ts","sourceRoot":"","sources":["../../../src/server/lib/mcp-gateway.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,IAAI,EACV,MAAM,wBAAwB,CAAC;AA6BhC,OAAO,EAGL,KAAK,4BAA4B,EAClC,MAAM,uBAAuB,CAAC;AA4B/B,MAAM,WAAW,wBAAwB;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;CAClB;AAkCD,KAAK,iCAAiC,GAClC,WAAW,GACX,SAAS,GACT,cAAc,GACd,cAAc,CAAC;AAEnB,KAAK,wBAAwB,GAAG;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,KAAK,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,aAAa,CAAC;IAC3B,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,aAAa,CAAC,EAAE,iCAAiC,CAAC;IAClD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE;QAAE,IAAI,EAAE,gBAAgB,CAAC;QAAC,SAAS,EAAE;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC;IAC9E,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,iBAAS,wBAAwB,CAC/B,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,IAAI,GACT,wBAAwB,CAyC1B;AA+XD,wBAAsB,mBAAmB,IAAI,OAAO,CAAC;IACnD,QAAQ,EAAE,4BAA4B,CAAC;IACvC,IAAI,EAAE,wBAAwB,EAAE,CAAC;CAClC,CAAC,CAcD;AAED,wBAAsB,0BAA0B,IAAI,OAAO,CACzD,wBAAwB,EAAE,CAC3B,CAGA;AAED,wBAAsB,gCAAgC,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAG1E;AAED,wBAAsB,4BAA4B,CAChD,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,wBAAwB,CAAC,CAwBnC;
|
|
1
|
+
{"version":3,"file":"mcp-gateway.d.ts","sourceRoot":"","sources":["../../../src/server/lib/mcp-gateway.ts"],"names":[],"mappings":"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"}
|
|
@@ -462,18 +462,6 @@ function workspaceSsoOriginForApp(appId) {
|
|
|
462
462
|
const custom = parseWorkspaceSsoAppRegistrations(process.env.IDENTITY_SSO_APP_REGISTRY_JSON).find((registration) => registration.appId === appId);
|
|
463
463
|
return custom?.origin ?? null;
|
|
464
464
|
}
|
|
465
|
-
function workspaceSsoGatewayOrigin() {
|
|
466
|
-
const configured = process.env.WORKSPACE_GATEWAY_URL?.trim();
|
|
467
|
-
if (!configured)
|
|
468
|
-
return null;
|
|
469
|
-
try {
|
|
470
|
-
return new URL(configured).origin;
|
|
471
|
-
}
|
|
472
|
-
catch {
|
|
473
|
-
// coercion-ok: malformed deployment configuration is not a trusted origin.
|
|
474
|
-
return null;
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
465
|
function isWorkspaceSsoCanonicalApp(app) {
|
|
478
466
|
const origin = safeAppOrigin(app);
|
|
479
467
|
if (!origin)
|
|
@@ -488,12 +476,7 @@ function isWorkspaceSsoCanonicalApp(app) {
|
|
|
488
476
|
? Object.prototype.hasOwnProperty.call(CANONICAL_WORKSPACE_SSO_APP_ORIGINS, app.id)
|
|
489
477
|
: false;
|
|
490
478
|
}
|
|
491
|
-
function
|
|
492
|
-
if (candidate.id !== mounted.id || !mounted.url)
|
|
493
|
-
return false;
|
|
494
|
-
return candidate.url.replace(/\/+$/, "") === mounted.url.replace(/\/+$/, "");
|
|
495
|
-
}
|
|
496
|
-
async function isEligibleWorkspaceSsoApp(candidate, mountedApps) {
|
|
479
|
+
async function isEligibleWorkspaceSsoApp(candidate) {
|
|
497
480
|
const origin = safeAppOrigin(candidate);
|
|
498
481
|
if (!origin)
|
|
499
482
|
return false;
|
|
@@ -502,18 +485,7 @@ async function isEligibleWorkspaceSsoApp(candidate, mountedApps) {
|
|
|
502
485
|
const customOrigin = workspaceSsoOriginForApp(candidate.id);
|
|
503
486
|
if (customOrigin === origin)
|
|
504
487
|
return true;
|
|
505
|
-
|
|
506
|
-
id: DISPATCH_APP_ID,
|
|
507
|
-
name: DISPATCH_NAME,
|
|
508
|
-
description: DISPATCH_DESCRIPTION,
|
|
509
|
-
url: dispatchSelfBaseUrl(),
|
|
510
|
-
color: DISPATCH_COLOR,
|
|
511
|
-
granted: true,
|
|
512
|
-
});
|
|
513
|
-
const trustedMountedOrigin = workspaceSsoGatewayOrigin() ?? dispatchOrigin;
|
|
514
|
-
if (origin !== trustedMountedOrigin)
|
|
515
|
-
return false;
|
|
516
|
-
return mountedApps.some((mounted) => sameWorkspaceApp(candidate, mounted));
|
|
488
|
+
return false;
|
|
517
489
|
}
|
|
518
490
|
async function listWorkspaceSsoApps() {
|
|
519
491
|
const [agents, mountedApps] = await Promise.all([
|
|
@@ -533,8 +505,8 @@ async function listWorkspaceSsoApps() {
|
|
|
533
505
|
});
|
|
534
506
|
}
|
|
535
507
|
// The hosted Dispatch database is separate from the shared Workspace
|
|
536
|
-
// database. Overlay the live mounted registry so custom apps
|
|
537
|
-
// without copying a stale app list into Dispatch configuration.
|
|
508
|
+
// database. Overlay the live mounted registry so custom apps remain
|
|
509
|
+
// discoverable without copying a stale app list into Dispatch configuration.
|
|
538
510
|
for (const app of mountedApps) {
|
|
539
511
|
if (app.isDispatch || !app.url)
|
|
540
512
|
continue;
|
|
@@ -550,7 +522,7 @@ async function listWorkspaceSsoApps() {
|
|
|
550
522
|
const candidates = [...candidatesById.values()];
|
|
551
523
|
const eligible = [];
|
|
552
524
|
for (const candidate of candidates) {
|
|
553
|
-
if (await isEligibleWorkspaceSsoApp(candidate
|
|
525
|
+
if (await isEligibleWorkspaceSsoApp(candidate)) {
|
|
554
526
|
eligible.push(candidate);
|
|
555
527
|
}
|
|
556
528
|
}
|
|
@@ -750,6 +722,31 @@ function isRetryableTargetMcpError(error) {
|
|
|
750
722
|
}
|
|
751
723
|
return /streamable http|handshake|failed to fetch|fetch failed|networkerror|econnrefused|enotfound|timed out|timeout|502|503|504/i.test(message);
|
|
752
724
|
}
|
|
725
|
+
function isTargetMcpAuthError(error) {
|
|
726
|
+
const message = error instanceof Error
|
|
727
|
+
? error.message
|
|
728
|
+
: typeof error === "string"
|
|
729
|
+
? error
|
|
730
|
+
: String(error ?? "");
|
|
731
|
+
return /\b401\b|\b403\b|unauthorized|forbidden|invalid(?: or expired)? (?:a2a )?token|authentication required/i.test(message);
|
|
732
|
+
}
|
|
733
|
+
function targetMcpErrorStatus(error) {
|
|
734
|
+
const message = error instanceof Error
|
|
735
|
+
? error.message
|
|
736
|
+
: typeof error === "string"
|
|
737
|
+
? error
|
|
738
|
+
: String(error ?? "");
|
|
739
|
+
const status = message.match(/\b([45]\d{2})\b/)?.[1];
|
|
740
|
+
return status ? Number(status) : undefined;
|
|
741
|
+
}
|
|
742
|
+
function targetMcpRequestDetails(input) {
|
|
743
|
+
const targetUrl = new URL(input.url);
|
|
744
|
+
return {
|
|
745
|
+
app: input.app.id,
|
|
746
|
+
targetOrigin: targetUrl.origin,
|
|
747
|
+
targetPath: targetUrl.pathname,
|
|
748
|
+
};
|
|
749
|
+
}
|
|
753
750
|
function targetMcpRetryDelay(attempt) {
|
|
754
751
|
const base = TARGET_EMBED_SESSION_RETRY_BASE_MS * Math.pow(2, Math.max(0, attempt - 1));
|
|
755
752
|
return base + Math.floor(Math.random() * 100);
|
|
@@ -789,6 +786,42 @@ async function callTargetCreateEmbedSession(input) {
|
|
|
789
786
|
}
|
|
790
787
|
}
|
|
791
788
|
}
|
|
789
|
+
async function createTargetMcpTokenAttempts(input) {
|
|
790
|
+
const attempts = [];
|
|
791
|
+
const addAttempt = async (tokenInput) => {
|
|
792
|
+
const token = await signA2AToken(input.ownerEmail, input.orgDomain, tokenInput.secret, {
|
|
793
|
+
expiresIn: "5m",
|
|
794
|
+
audience: canonicalA2AAudience(appBaseUrl(input.target)),
|
|
795
|
+
preferGlobalSecret: tokenInput.preferGlobalSecret,
|
|
796
|
+
});
|
|
797
|
+
if (!attempts.some((attempt) => attempt.token === token)) {
|
|
798
|
+
attempts.push({ token, strategy: tokenInput.strategy });
|
|
799
|
+
}
|
|
800
|
+
};
|
|
801
|
+
if (input.orgDomain && input.orgSecret) {
|
|
802
|
+
await addAttempt({
|
|
803
|
+
strategy: "org",
|
|
804
|
+
secret: input.orgSecret,
|
|
805
|
+
preferGlobalSecret: false,
|
|
806
|
+
});
|
|
807
|
+
// A target app may not have the org secret synced yet. The shared secret
|
|
808
|
+
// is a bounded compatibility fallback, used only after the target rejects
|
|
809
|
+
// the org-signed request and never after a non-authentication failure.
|
|
810
|
+
if (process.env.A2A_SECRET?.trim()) {
|
|
811
|
+
await addAttempt({
|
|
812
|
+
strategy: "global",
|
|
813
|
+
preferGlobalSecret: true,
|
|
814
|
+
});
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
else {
|
|
818
|
+
await addAttempt({
|
|
819
|
+
strategy: "global",
|
|
820
|
+
preferGlobalSecret: true,
|
|
821
|
+
});
|
|
822
|
+
}
|
|
823
|
+
return attempts;
|
|
824
|
+
}
|
|
792
825
|
async function resolveEmbedTarget(input, options) {
|
|
793
826
|
const explicitApp = input.app?.trim()
|
|
794
827
|
? await options.resolveApp(input.app)
|
|
@@ -897,28 +930,75 @@ async function createEmbedSessionForResolvedApp(input) {
|
|
|
897
930
|
: [null, null];
|
|
898
931
|
const usableOrgSecret = typeof orgSecret === "string" && orgSecret.trim().length > 0;
|
|
899
932
|
const usableOrgDomain = typeof orgDomain === "string" && orgDomain.trim().length > 0;
|
|
900
|
-
const useOrgSigning = usableOrgDomain && usableOrgSecret;
|
|
901
933
|
const signedOrgDomain = usableOrgDomain ? orgDomain.trim() : undefined;
|
|
902
|
-
const
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
// Fall back to the global A2A_SECRET for orgs that have not synced yet.
|
|
908
|
-
preferGlobalSecret: !useOrgSigning,
|
|
934
|
+
const tokenAttempts = await createTargetMcpTokenAttempts({
|
|
935
|
+
ownerEmail,
|
|
936
|
+
orgDomain: signedOrgDomain,
|
|
937
|
+
orgSecret: usableOrgSecret ? orgSecret.trim() : undefined,
|
|
938
|
+
target: target.app,
|
|
909
939
|
});
|
|
910
|
-
const
|
|
940
|
+
const targetDetails = targetMcpRequestDetails({
|
|
911
941
|
app: target.app,
|
|
912
|
-
token,
|
|
913
942
|
url: target.url,
|
|
914
|
-
chrome,
|
|
915
943
|
});
|
|
916
|
-
|
|
944
|
+
let parsed = null;
|
|
945
|
+
let lastError;
|
|
946
|
+
for (let attemptIndex = 0; attemptIndex < tokenAttempts.length; attemptIndex++) {
|
|
947
|
+
const tokenAttempt = tokenAttempts[attemptIndex];
|
|
948
|
+
console.info("[dispatch] workspace embed target request", {
|
|
949
|
+
...targetDetails,
|
|
950
|
+
authStrategy: tokenAttempt.strategy,
|
|
951
|
+
attempt: attemptIndex + 1,
|
|
952
|
+
attempts: tokenAttempts.length,
|
|
953
|
+
});
|
|
954
|
+
try {
|
|
955
|
+
const result = await callTargetCreateEmbedSession({
|
|
956
|
+
app: target.app,
|
|
957
|
+
token: tokenAttempt.token,
|
|
958
|
+
url: target.url,
|
|
959
|
+
chrome,
|
|
960
|
+
});
|
|
961
|
+
parsed = parseMcpToolTextResult(result);
|
|
962
|
+
break;
|
|
963
|
+
}
|
|
964
|
+
catch (error) {
|
|
965
|
+
lastError = error;
|
|
966
|
+
console.warn("[dispatch] workspace embed target response", {
|
|
967
|
+
...targetDetails,
|
|
968
|
+
authStrategy: tokenAttempt.strategy,
|
|
969
|
+
attempt: attemptIndex + 1,
|
|
970
|
+
status: targetMcpErrorStatus(error),
|
|
971
|
+
category: isTargetMcpAuthError(error)
|
|
972
|
+
? "authentication"
|
|
973
|
+
: isRetryableTargetMcpError(error)
|
|
974
|
+
? "transient"
|
|
975
|
+
: "permanent",
|
|
976
|
+
});
|
|
977
|
+
if (!isTargetMcpAuthError(error) ||
|
|
978
|
+
attemptIndex >= tokenAttempts.length - 1) {
|
|
979
|
+
throw error;
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
if (!parsed) {
|
|
984
|
+
throw lastError instanceof Error
|
|
985
|
+
? lastError
|
|
986
|
+
: new Error("Target app did not return an embed session.");
|
|
987
|
+
}
|
|
917
988
|
if (!parsed.startUrl) {
|
|
918
989
|
throw new Error("Target app did not return an embed start URL.");
|
|
919
990
|
}
|
|
991
|
+
const startUrl = resolveGrantedAppEmbedStartUrl(target.app, parsed.startUrl);
|
|
992
|
+
const startUrlDetails = new URL(startUrl);
|
|
993
|
+
console.info("[dispatch] workspace embed target minted session", {
|
|
994
|
+
...targetDetails,
|
|
995
|
+
startPath: startUrlDetails.pathname,
|
|
996
|
+
returnedTicket: startUrlDetails.searchParams.has("ticket"),
|
|
997
|
+
returnedTargetPath: typeof parsed.targetPath === "string",
|
|
998
|
+
returnedExpiry: typeof parsed.expiresAt === "number",
|
|
999
|
+
});
|
|
920
1000
|
const output = {
|
|
921
|
-
startUrl
|
|
1001
|
+
startUrl,
|
|
922
1002
|
app: target.app.id,
|
|
923
1003
|
};
|
|
924
1004
|
if (parsed.targetPath)
|
|
@@ -929,14 +1009,31 @@ async function createEmbedSessionForResolvedApp(input) {
|
|
|
929
1009
|
}
|
|
930
1010
|
export async function createWorkspaceSsoEmbedSession(input) {
|
|
931
1011
|
const ownerEmail = getRequestUserEmail();
|
|
932
|
-
if (!ownerEmail)
|
|
1012
|
+
if (!ownerEmail) {
|
|
1013
|
+
console.warn("[dispatch] workspace embed mint rejected", {
|
|
1014
|
+
phase: "dispatch-auth",
|
|
1015
|
+
requestedApp: input.app ?? null,
|
|
1016
|
+
hasPath: typeof input.path === "string",
|
|
1017
|
+
hasUrl: typeof input.url === "string",
|
|
1018
|
+
ownerResolved: false,
|
|
1019
|
+
orgResolved: Boolean(getRequestOrgId()),
|
|
1020
|
+
});
|
|
933
1021
|
throw new Error("no authenticated user");
|
|
1022
|
+
}
|
|
934
1023
|
const enabled = await isFeatureFlagEnabled(DISPATCH_WORKSPACE_SSO_FLAG, {
|
|
935
1024
|
userEmail: ownerEmail,
|
|
936
1025
|
userKey: ownerEmail,
|
|
937
1026
|
orgId: getRequestOrgId(),
|
|
938
1027
|
});
|
|
939
1028
|
if (!enabled) {
|
|
1029
|
+
console.warn("[dispatch] workspace embed mint rejected", {
|
|
1030
|
+
phase: "feature-flag",
|
|
1031
|
+
requestedApp: input.app ?? null,
|
|
1032
|
+
hasPath: typeof input.path === "string",
|
|
1033
|
+
hasUrl: typeof input.url === "string",
|
|
1034
|
+
ownerResolved: true,
|
|
1035
|
+
orgResolved: Boolean(getRequestOrgId()),
|
|
1036
|
+
});
|
|
940
1037
|
throw new Error("Dispatch workspace sign-in is not enabled.");
|
|
941
1038
|
}
|
|
942
1039
|
const target = await resolveWorkspaceSsoEmbedTarget(input);
|