@mycadet/gateway 0.12.7 → 0.12.9
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/README.md +3 -3
- package/node_modules/@mycadet/ces-client/node_modules/@mycadet/service-contracts/package.json +2 -2
- package/node_modules/@mycadet/ces-client/node_modules/@mycadet/service-contracts/src/{platform-credential.ts → cloud-credential.ts} +9 -9
- package/node_modules/@mycadet/ces-client/node_modules/@mycadet/service-contracts/src/handles.ts +3 -3
- package/node_modules/@mycadet/ces-client/node_modules/@mycadet/service-contracts/src/index.ts +1 -1
- package/node_modules/@mycadet/ces-client/node_modules/@mycadet/service-contracts/src/ingress.ts +11 -5
- package/node_modules/@mycadet/ces-client/node_modules/@mycadet/service-contracts/src/remote-web-pairing.ts +1 -1
- package/node_modules/@mycadet/ces-client/node_modules/@mycadet/service-contracts/src/rpc.ts +14 -14
- package/node_modules/@mycadet/ces-client/node_modules/@mycadet/service-contracts/src/trust-rules.ts +5 -5
- package/node_modules/@mycadet/ces-client/package.json +1 -1
- package/node_modules/@mycadet/ces-client/src/__tests__/ces-client.test.ts +14 -14
- package/node_modules/@mycadet/ces-client/src/rpc-client.ts +1 -1
- package/node_modules/@mycadet/environments/src/seeds.ts +6 -6
- package/node_modules/@mycadet/environments/src/shell.test.ts +1 -1
- package/node_modules/@mycadet/environments/src/types.ts +6 -6
- package/node_modules/@mycadet/gateway-client/node_modules/@mycadet/service-contracts/package.json +2 -2
- package/node_modules/@mycadet/gateway-client/node_modules/@mycadet/service-contracts/src/{platform-credential.ts → cloud-credential.ts} +9 -9
- package/node_modules/@mycadet/gateway-client/node_modules/@mycadet/service-contracts/src/handles.ts +3 -3
- package/node_modules/@mycadet/gateway-client/node_modules/@mycadet/service-contracts/src/index.ts +1 -1
- package/node_modules/@mycadet/gateway-client/node_modules/@mycadet/service-contracts/src/ingress.ts +11 -5
- package/node_modules/@mycadet/gateway-client/node_modules/@mycadet/service-contracts/src/remote-web-pairing.ts +1 -1
- package/node_modules/@mycadet/gateway-client/node_modules/@mycadet/service-contracts/src/rpc.ts +14 -14
- package/node_modules/@mycadet/gateway-client/node_modules/@mycadet/service-contracts/src/trust-rules.ts +5 -5
- package/node_modules/@mycadet/gateway-client/src/binding-strength-contract.ts +5 -5
- package/node_modules/@mycadet/gateway-client/src/gateway-ipc-contracts.ts +1 -1
- package/node_modules/@mycadet/gateway-client/src/inbound-contract.ts +2 -2
- package/node_modules/@mycadet/gateway-client/src/outbound-contract.ts +1 -1
- package/node_modules/@mycadet/service-contracts/package.json +2 -2
- package/node_modules/@mycadet/service-contracts/src/{platform-credential.ts → cloud-credential.ts} +9 -9
- package/node_modules/@mycadet/service-contracts/src/handles.ts +3 -3
- package/node_modules/@mycadet/service-contracts/src/index.ts +1 -1
- package/node_modules/@mycadet/service-contracts/src/ingress.ts +11 -5
- package/node_modules/@mycadet/service-contracts/src/remote-web-pairing.ts +1 -1
- package/node_modules/@mycadet/service-contracts/src/rpc.ts +14 -14
- package/node_modules/@mycadet/service-contracts/src/trust-rules.ts +5 -5
- package/openapi.json +17 -17
- package/package.json +1 -1
- package/scripts/generate-openapi.ts +1 -1
- package/src/__tests__/actor-token-revocation.test.ts +0 -1
- package/src/__tests__/{backup-worker-platform-mode.test.ts → backup-worker-cloud-mode.test.ts} +4 -4
- package/src/__tests__/channel-verification-session-proxy.test.ts +0 -1
- package/src/__tests__/{platform-identity.test.ts → cloud-identity.test.ts} +72 -86
- package/src/__tests__/config.test.ts +0 -24
- package/src/__tests__/contacts-control-plane-proxy.test.ts +0 -1
- package/src/__tests__/credential-requests.test.ts +6 -6
- package/src/__tests__/{credential-watcher-managed-bootstrap.test.ts → credential-watcher-cloud-bootstrap.test.ts} +1 -1
- package/src/__tests__/edge-auth.test.ts +18 -18
- package/src/__tests__/edge-guardian-auth.test.ts +10 -10
- package/src/__tests__/guardian-init-lockfile.test.ts +6 -8
- package/src/__tests__/guardian-pin.test.ts +8 -8
- package/src/__tests__/handle-inbound-admission.test.ts +0 -1
- package/src/__tests__/handle-inbound-app-context.test.ts +0 -1
- package/src/__tests__/handle-inbound-invite-intercept.test.ts +0 -1
- package/src/__tests__/handle-inbound-trust-verdict.test.ts +0 -1
- package/src/__tests__/inbound-contact-seed.test.ts +1 -1
- package/src/__tests__/live-voice-websocket.test.ts +12 -12
- package/src/__tests__/load-guards.test.ts +0 -1
- package/src/__tests__/migration-teleport-gcs-proxy.test.ts +0 -1
- package/src/__tests__/oauth-callback.test.ts +0 -1
- package/src/__tests__/remote-feature-flag-sync.test.ts +35 -45
- package/src/__tests__/resolve-assistant.test.ts +0 -1
- package/src/__tests__/route-schema-guard.test.ts +4 -1
- package/src/__tests__/runtime-client.test.ts +1 -2
- package/src/__tests__/runtime-health-proxy.test.ts +0 -1
- package/src/__tests__/runtime-proxy-auth.test.ts +3 -2
- package/src/__tests__/runtime-proxy-device-stamp.test.ts +0 -1
- package/src/__tests__/runtime-proxy-error-body.test.ts +0 -1
- package/src/__tests__/runtime-proxy.test.ts +0 -1
- package/src/__tests__/runtime-stream-test-utils.ts +0 -1
- package/src/__tests__/shared-invite-registration.test.ts +15 -32
- package/src/__tests__/slack-control-plane-proxy.test.ts +0 -1
- package/src/__tests__/slack-display-name.test.ts +0 -1
- package/src/__tests__/slack-normalize.test.ts +0 -1
- package/src/__tests__/slack-reaction-normalize.test.ts +0 -1
- package/src/__tests__/slack-socket-mode-catchup.test.ts +0 -1
- package/src/__tests__/slack-socket-mode-group-dm.test.ts +0 -1
- package/src/__tests__/slack-socket-mode-liveness.test.ts +0 -1
- package/src/__tests__/slack-socket-mode-outbound-root.test.ts +0 -1
- package/src/__tests__/slack-socket-mode-thread-tracking.test.ts +0 -1
- package/src/__tests__/speech-relay-websocket.test.ts +16 -14
- package/src/__tests__/stt-stream-websocket.test.ts +5 -5
- package/src/__tests__/telegram-control-plane-proxy.test.ts +0 -1
- package/src/__tests__/telegram-webhook-drop-visibility.test.ts +0 -1
- package/src/__tests__/telegram-webhook-handler.test.ts +0 -1
- package/src/__tests__/telegram-webhook-manager.test.ts +79 -80
- package/src/__tests__/twilio-media-websocket.test.ts +0 -1
- package/src/__tests__/twilio-webhooks.test.ts +6 -7
- package/src/__tests__/whatsapp-download.test.ts +0 -1
- package/src/__tests__/whatsapp-webhook.test.ts +0 -1
- package/src/attachments/ingest.test.ts +0 -1
- package/src/auth/guardian-bootstrap.ts +16 -15
- package/src/auth/scopes.ts +1 -1
- package/src/auth-fallback-reporter.ts +1 -1
- package/src/backup/backup-worker.ts +2 -2
- package/src/channels/inbound-event.ts +3 -3
- package/src/channels/ingress-inbound-vendors.test.ts +2 -2
- package/src/channels/ingress-inbound.ts +1 -1
- package/src/channels/ingress-verification.test.ts +2 -2
- package/src/channels/ingress-verification.ts +4 -4
- package/src/channels/plugin-ingress-approvals.test.ts +1 -1
- package/src/channels/plugin-ingress-approvals.ts +1 -1
- package/src/channels/plugin-ingress.test.ts +3 -3
- package/src/channels/plugin-ingress.ts +3 -3
- package/src/channels/room-admission.ts +2 -2
- package/src/cli/contacts.ts +3 -1
- package/src/cli/index.ts +1 -1
- package/src/{platform-identity.ts → cloud-identity.ts} +60 -68
- package/src/cloud-url.ts +25 -0
- package/src/{platform-user-id.ts → cloud-user-id.ts} +4 -4
- package/src/config.ts +2 -4
- package/src/credential-reader.ts +2 -2
- package/src/db/assistant-db-proxy.ts +1 -1
- package/src/discord/forward.ts +1 -1
- package/src/email/normalize.ts +4 -4
- package/src/email/register-callback.test.ts +27 -34
- package/src/email/register-callback.ts +21 -26
- package/src/feature-flag-registry.json +5 -5
- package/src/feature-flag-remote-store.ts +2 -2
- package/src/feature-flag-resolver.ts +5 -5
- package/src/http/cadet-signature.ts +1 -1
- package/src/http/edge-forwarded-header.ts +1 -1
- package/src/http/loopback-guard.ts +1 -1
- package/src/http/middleware/auth.ts +22 -22
- package/src/http/router.ts +1 -1
- package/src/http/routes/auto-approve-thresholds-routes.ts +1 -1
- package/src/http/routes/cadet-identity.ts +2 -2
- package/src/http/routes/channel-admission-policy-routes.ts +2 -2
- package/src/http/routes/channel-ingress-routes.ts +3 -3
- package/src/http/routes/channel-ingress.test.ts +13 -2
- package/src/http/routes/channel-permission-overrides-routes.ts +3 -3
- package/src/http/routes/channel-verification-session-proxy.ts +8 -22
- package/src/http/routes/{platform-push-proxy.test.ts → cloud-push-proxy.test.ts} +21 -22
- package/src/http/routes/{platform-push-proxy.ts → cloud-push-proxy.ts} +13 -14
- package/src/http/routes/contacts-control-plane-proxy.ts +3 -3
- package/src/http/routes/contacts-control-plane-routes.ts +5 -5
- package/src/http/routes/email-webhook.test.ts +2 -3
- package/src/http/routes/email-webhook.ts +4 -4
- package/src/http/routes/feature-flags.ts +1 -1
- package/src/http/routes/guardian-channel-create.ts +3 -3
- package/src/http/routes/guardian-pin.ts +9 -9
- package/src/http/routes/inbound-register.ts +2 -2
- package/src/http/routes/ingress-domains.test.ts +129 -0
- package/src/http/routes/ingress-domains.ts +73 -0
- package/src/http/routes/live-voice-websocket.ts +2 -2
- package/src/http/routes/log-export.test.ts +0 -1
- package/src/http/routes/log-tail.test.ts +0 -1
- package/src/http/routes/migration-proxy.ts +1 -1
- package/src/http/routes/plugin-webhook-websocket.test.ts +3 -3
- package/src/http/routes/plugin-webhook.test.ts +9 -8
- package/src/http/routes/plugin-webhook.ts +1 -1
- package/src/http/routes/speech-relay-websocket.ts +8 -11
- package/src/http/routes/stt-stream-websocket.ts +1 -1
- package/src/http/routes/telegram-webhook.test.ts +0 -1
- package/src/http/routes/trust-rules-routes.ts +2 -2
- package/src/http/routes/twilio-voice-verify-callback.ts +3 -3
- package/src/http/routes/twilio-voice-webhook.test.ts +3 -4
- package/src/http/routes/twilio-voice-webhook.ts +3 -3
- package/src/http/routes/whatsapp-webhook.test.ts +0 -1
- package/src/index.ts +57 -56
- package/src/ipc/credential-request-handlers.ts +4 -4
- package/src/ipc/debug-export-handlers.test.ts +0 -1
- package/src/ipc/log-tail-handlers.test.ts +0 -1
- package/src/ipc/risk-classification-handlers.test.ts +4 -4
- package/src/ipc/risk-classification-handlers.ts +2 -2
- package/src/remote-feature-flag-sync.ts +17 -19
- package/src/risk/command-registry/commands/assistant.ts +2 -2
- package/src/risk/skill-risk-classifier.test.ts +24 -24
- package/src/risk/skill-risk-classifier.ts +10 -10
- package/src/runtime/client.ts +17 -17
- package/src/schema.ts +29 -29
- package/src/telegram/send.test.ts +0 -1
- package/src/telegram/webhook-manager.ts +33 -38
- package/src/tunnel/allowed-paths.test.ts +3 -3
- package/src/tunnel/allowed-paths.ts +1 -1
- package/src/tunnel/client.test.ts +20 -21
- package/src/tunnel/client.ts +16 -17
- package/src/verification/shared-invite-registration.ts +22 -17
- package/src/version.ts +1 -1
- package/src/platform-url.ts +0 -24
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loopback-guarded recording of a tunnel's public URL.
|
|
3
|
+
*
|
|
4
|
+
* POST /v1/ingress/domains — the CLI (its ngrok agent, today) tells the
|
|
5
|
+
* gateway the public URL its tunnel published; the gateway writes
|
|
6
|
+
* `ingress.publicBaseUrl` + `publicBaseUrlManagedBy: "tunnel"` in its own
|
|
7
|
+
* config via the same machinery its own tunnel uses, so both publishers
|
|
8
|
+
* record through one path and the gateway's existing loss handling clears
|
|
9
|
+
* it uniformly. Write-only: reads keep flowing through the config the
|
|
10
|
+
* gateway already serves. While we ship one bundle the gateway still hosts
|
|
11
|
+
* the ngrok-related logic; once the bundle splits, the gateway manages its
|
|
12
|
+
* own tunnel (mycadet.ai vs <assistant>.mycadet.bot) and this route is what
|
|
13
|
+
* the CLI-side publishers migrate onto.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { z } from "zod";
|
|
17
|
+
|
|
18
|
+
import { normalizeHttpPublicBaseUrlWithoutTrailingSlash } from "@mycadet/service-contracts/ingress";
|
|
19
|
+
|
|
20
|
+
import type { ConfigFileCache } from "../../config-file-cache.js";
|
|
21
|
+
import { writeManagedPublicBaseUrl } from "../../tunnel/client.js";
|
|
22
|
+
import {
|
|
23
|
+
enforceLoopbackOnly,
|
|
24
|
+
errorResponse,
|
|
25
|
+
rejectBrowserOrigin,
|
|
26
|
+
} from "../loopback-guard.js";
|
|
27
|
+
|
|
28
|
+
const SaveIngressDomainRequestSchema = z.object({
|
|
29
|
+
publicBaseUrl: z.string(),
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
export async function handleSaveIngressDomain(
|
|
33
|
+
req: Request,
|
|
34
|
+
clientIp: string,
|
|
35
|
+
configFile: ConfigFileCache,
|
|
36
|
+
): Promise<Response> {
|
|
37
|
+
if (req.method !== "POST") {
|
|
38
|
+
return new Response("method not allowed", {
|
|
39
|
+
status: 405,
|
|
40
|
+
headers: { Allow: "POST" },
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const guardError = enforceLoopbackOnly(req, clientIp, "ingress-domains");
|
|
45
|
+
if (guardError) return guardError;
|
|
46
|
+
const originError = rejectBrowserOrigin(req, clientIp, "ingress-domains");
|
|
47
|
+
if (originError) return originError;
|
|
48
|
+
|
|
49
|
+
let body: unknown;
|
|
50
|
+
try {
|
|
51
|
+
body = await req.json();
|
|
52
|
+
} catch {
|
|
53
|
+
body = {};
|
|
54
|
+
}
|
|
55
|
+
const parsed = SaveIngressDomainRequestSchema.safeParse(body);
|
|
56
|
+
if (!parsed.success) {
|
|
57
|
+
return errorResponse("BAD_REQUEST", "publicBaseUrl is required", 400);
|
|
58
|
+
}
|
|
59
|
+
const publicUrl = normalizeHttpPublicBaseUrlWithoutTrailingSlash(
|
|
60
|
+
parsed.data.publicBaseUrl,
|
|
61
|
+
);
|
|
62
|
+
if (!publicUrl) {
|
|
63
|
+
return errorResponse(
|
|
64
|
+
"BAD_REQUEST",
|
|
65
|
+
"publicBaseUrl must be an absolute http(s) URL",
|
|
66
|
+
400,
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// No-op (no write) when the URL is already recorded as tunnel-managed.
|
|
71
|
+
await writeManagedPublicBaseUrl(publicUrl, configFile);
|
|
72
|
+
return Response.json({ ok: true });
|
|
73
|
+
}
|
|
@@ -119,7 +119,7 @@ async function checkLiveVoiceAuth(
|
|
|
119
119
|
// the caller is *a* platform user who traversed tunnel, not that they
|
|
120
120
|
// are THIS assistant's guardian, so cross-check the tunnel user id
|
|
121
121
|
// against the bound platform user id (the same guardian check the
|
|
122
|
-
// edge-auth middleware applies to guardian routes under the
|
|
122
|
+
// edge-auth middleware applies to guardian routes under the cloud
|
|
123
123
|
// bypass). Without it, any tunnel-authorized org user reaching this
|
|
124
124
|
// assistant would be stamped guardian downstream.
|
|
125
125
|
const guardianError = await requireManagedGuardian(
|
|
@@ -127,7 +127,7 @@ async function checkLiveVoiceAuth(
|
|
|
127
127
|
log,
|
|
128
128
|
);
|
|
129
129
|
if (guardianError) return { response: guardianError };
|
|
130
|
-
// Tunnel attests a
|
|
130
|
+
// Tunnel attests a cloud user, not an actor principal, so the
|
|
131
131
|
// principal comes from the gateway's own binding rather than from
|
|
132
132
|
// anything the request carried.
|
|
133
133
|
const guardian = await findCadetGuardian();
|
|
@@ -452,7 +452,7 @@ async function handleSyncBytesImport(
|
|
|
452
452
|
*
|
|
453
453
|
* Returns the job's current status on 200, or 404 if the jobId is unknown
|
|
454
454
|
* (never issued, or pruned after the TTL). The response shape deliberately
|
|
455
|
-
* mirrors the
|
|
455
|
+
* mirrors the cloud's existing `/v1/migrations/import/:jobId/status/`
|
|
456
456
|
* endpoint so `PlatformMigrationClient.pollImportStatus` works against
|
|
457
457
|
* either surface with no changes.
|
|
458
458
|
*/
|
|
@@ -176,7 +176,7 @@ describe("upgrade gate", () => {
|
|
|
176
176
|
});
|
|
177
177
|
|
|
178
178
|
it("upgrades a cadet-signed declaration nobody approved", async () => {
|
|
179
|
-
// Only a caller holding the
|
|
179
|
+
// Only a caller holding the cloud secret can open it, and the user
|
|
180
180
|
// extended that trust when they connected their account.
|
|
181
181
|
const handle = makeHandler({
|
|
182
182
|
resolve: () =>
|
|
@@ -203,7 +203,7 @@ describe("upgrade gate", () => {
|
|
|
203
203
|
expect(upgrades).toHaveLength(1);
|
|
204
204
|
});
|
|
205
205
|
|
|
206
|
-
it("still demands the
|
|
206
|
+
it("still demands the cloud signature on an unapproved cadet route", async () => {
|
|
207
207
|
// Skipping approval did not skip authentication.
|
|
208
208
|
const handle = makeHandler({
|
|
209
209
|
resolve: () =>
|
|
@@ -378,7 +378,7 @@ describe("handshake signature", () => {
|
|
|
378
378
|
expect(upgrades).toEqual([]);
|
|
379
379
|
});
|
|
380
380
|
|
|
381
|
-
it("verifies a cadet-signed route against the
|
|
381
|
+
it("verifies a cadet-signed route against the cloud secret", async () => {
|
|
382
382
|
const handle = makeHandler({
|
|
383
383
|
resolve: () => approvedWith([{ ...ROUTE, signer: "cadet" as const }]),
|
|
384
384
|
});
|
|
@@ -335,7 +335,7 @@ describe("the gate", () => {
|
|
|
335
335
|
});
|
|
336
336
|
|
|
337
337
|
it("serves a cadet-signed declaration nobody approved", async () => {
|
|
338
|
-
// Only a caller holding the
|
|
338
|
+
// Only a caller holding the cloud secret can reach it, and the user
|
|
339
339
|
// extended that trust when they connected their account.
|
|
340
340
|
const { calls, fetchImpl } = recordingFetch();
|
|
341
341
|
const handle = createPluginWebhookHandler({
|
|
@@ -364,7 +364,7 @@ describe("the gate", () => {
|
|
|
364
364
|
expect(calls).toHaveLength(1);
|
|
365
365
|
});
|
|
366
366
|
|
|
367
|
-
it("still demands the
|
|
367
|
+
it("still demands the cloud signature on an unapproved cadet route", async () => {
|
|
368
368
|
// Skipping approval did not skip authentication.
|
|
369
369
|
const { calls, fetchImpl } = recordingFetch();
|
|
370
370
|
const handle = createPluginWebhookHandler({
|
|
@@ -605,7 +605,7 @@ describe("signature verification", () => {
|
|
|
605
605
|
expect(calls).toEqual([]);
|
|
606
606
|
});
|
|
607
607
|
|
|
608
|
-
it("verifies a cadet-signed route against the
|
|
608
|
+
it("verifies a cadet-signed route against the cloud secret", async () => {
|
|
609
609
|
const { calls, fetchImpl } = recordingFetch();
|
|
610
610
|
const handle = createPluginWebhookHandler({
|
|
611
611
|
config: CONFIG,
|
|
@@ -645,7 +645,7 @@ describe("signature verification", () => {
|
|
|
645
645
|
expect(calls).toEqual([]);
|
|
646
646
|
});
|
|
647
647
|
|
|
648
|
-
it("does not accept the
|
|
648
|
+
it("does not accept the cloud secret on a plugin-signed route", async () => {
|
|
649
649
|
const { calls, fetchImpl } = recordingFetch();
|
|
650
650
|
const handle = createPluginWebhookHandler({
|
|
651
651
|
config: CONFIG,
|
|
@@ -826,7 +826,7 @@ describe("declared verification", () => {
|
|
|
826
826
|
expect(calls).toEqual([]);
|
|
827
827
|
});
|
|
828
828
|
|
|
829
|
-
it("stops accepting the
|
|
829
|
+
it("stops accepting the cloud scheme once a descriptor is declared", async () => {
|
|
830
830
|
// Otherwise a declared route would have two ways in, and the weaker one
|
|
831
831
|
// would decide.
|
|
832
832
|
const { calls, fetchImpl } = recordingFetch();
|
|
@@ -1807,7 +1807,7 @@ describe("HMAC URL and form-encoded inbound", () => {
|
|
|
1807
1807
|
expect(gateCalls.events).toEqual([]);
|
|
1808
1808
|
});
|
|
1809
1809
|
|
|
1810
|
-
it("verifies against the
|
|
1810
|
+
it("verifies against the cloud-injected URL when the raw URL differs", async () => {
|
|
1811
1811
|
const { calls, fetchImpl } = recordingFetch();
|
|
1812
1812
|
const handle = createPluginWebhookHandler({
|
|
1813
1813
|
config: CONFIG,
|
|
@@ -1816,10 +1816,11 @@ describe("HMAC URL and form-encoded inbound", () => {
|
|
|
1816
1816
|
fetchImpl,
|
|
1817
1817
|
});
|
|
1818
1818
|
|
|
1819
|
-
// Twilio signed the
|
|
1819
|
+
// Twilio signed the cloud callback URL; the gateway sees localhost.
|
|
1820
1820
|
// The proxy injects the original URL it handed the vendor, and the
|
|
1821
1821
|
// signature covers that spelling rather than the one on the wire here.
|
|
1822
|
-
const injected =
|
|
1822
|
+
const injected =
|
|
1823
|
+
"https://test-cloud.mycadet.ai.test/v1/gateway/callbacks/cb-1";
|
|
1823
1824
|
const params: Record<string, string> = {
|
|
1824
1825
|
MessageSid: "SM9004",
|
|
1825
1826
|
From: "+15555550101",
|
|
@@ -76,7 +76,7 @@ const log = getLogger("plugin-webhook");
|
|
|
76
76
|
/**
|
|
77
77
|
* Credential holding the secret that signs this route.
|
|
78
78
|
*
|
|
79
|
-
* `cadet` routes verify against the
|
|
79
|
+
* `cadet` routes verify against the cloud's own webhook secret — the
|
|
80
80
|
* same credential the email webhook uses — so a plugin that only ever hears
|
|
81
81
|
* from Cadet needs no secret of its own. Everything else verifies against
|
|
82
82
|
* the plugin's, stored under its own service name.
|
|
@@ -30,7 +30,7 @@ import type { GatewayConfig } from "../../config.js";
|
|
|
30
30
|
import type { CredentialCache } from "../../credential-cache.js";
|
|
31
31
|
import { credentialKey } from "../../credential-key.js";
|
|
32
32
|
import { getLogger } from "../../logger.js";
|
|
33
|
-
import {
|
|
33
|
+
import { getCloudBaseUrl } from "../../cloud-url.js";
|
|
34
34
|
import { TUNNEL_FORWARDED_HEADER } from "../../tunnel/bridge-utils.js";
|
|
35
35
|
import { requestHasTunnelBridgeAuth } from "../../tunnel/bridge-auth.js";
|
|
36
36
|
|
|
@@ -172,18 +172,15 @@ export function tunnelOriginForPlatformHost(host: string): string | null {
|
|
|
172
172
|
* assistant's key is presented to its own environment's tunnel rather than
|
|
173
173
|
* prod (which would reject it with `invalid_key`).
|
|
174
174
|
*/
|
|
175
|
-
async function resolveTunnelBaseUrl(
|
|
176
|
-
config: GatewayConfig,
|
|
177
|
-
deps: SpeechRelayDeps,
|
|
178
|
-
): Promise<string> {
|
|
175
|
+
async function resolveTunnelBaseUrl(config: GatewayConfig): Promise<string> {
|
|
179
176
|
if (config.tunnelBaseUrl) {
|
|
180
177
|
return config.tunnelBaseUrl;
|
|
181
178
|
}
|
|
182
|
-
const
|
|
183
|
-
if (
|
|
179
|
+
const cloudBaseUrl = getCloudBaseUrl();
|
|
180
|
+
if (cloudBaseUrl) {
|
|
184
181
|
try {
|
|
185
182
|
const derived = tunnelOriginForPlatformHost(
|
|
186
|
-
new URL(
|
|
183
|
+
new URL(cloudBaseUrl).hostname,
|
|
187
184
|
);
|
|
188
185
|
if (derived) {
|
|
189
186
|
return derived;
|
|
@@ -320,7 +317,7 @@ export function createSpeechRelayUpgradeHandler(
|
|
|
320
317
|
}
|
|
321
318
|
}
|
|
322
319
|
const { wsUrl, httpUrl } = tunnelSpeechUrls(
|
|
323
|
-
await resolveTunnelBaseUrl(config
|
|
320
|
+
await resolveTunnelBaseUrl(config),
|
|
324
321
|
operation,
|
|
325
322
|
params,
|
|
326
323
|
version,
|
|
@@ -334,7 +331,7 @@ export function createSpeechRelayUpgradeHandler(
|
|
|
334
331
|
return jsonError(
|
|
335
332
|
401,
|
|
336
333
|
"missing_platform_connection",
|
|
337
|
-
"no Cadet assistant API key is stored — connect the
|
|
334
|
+
"no Cadet assistant API key is stored — connect the cloud account",
|
|
338
335
|
);
|
|
339
336
|
}
|
|
340
337
|
const rejection = await probeTunnel(
|
|
@@ -408,7 +405,7 @@ export function getSpeechRelayWebsocketHandlers() {
|
|
|
408
405
|
sendRelayError(
|
|
409
406
|
ws,
|
|
410
407
|
"missing_platform_connection",
|
|
411
|
-
"no Cadet assistant API key is stored — connect the
|
|
408
|
+
"no Cadet assistant API key is stored — connect the cloud account",
|
|
412
409
|
);
|
|
413
410
|
ws.close(1011, "missing_platform_connection");
|
|
414
411
|
return;
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* the binding, and dictation takes any valid actor, which is why that pin is a
|
|
10
10
|
* route opt-in rather than part of the shared gate.
|
|
11
11
|
*
|
|
12
|
-
* A
|
|
12
|
+
* A cloud assistant is reached through tunnel, which validates the browser's
|
|
13
13
|
* own token and attests the caller to the bridge; there is no edge JWT on that
|
|
14
14
|
* path to validate. So a tunnel-attested caller with the bridge proof is
|
|
15
15
|
* admitted here the way live voice and watch admit one, minus their guardian
|
|
@@ -47,7 +47,7 @@ const CreateTrustRuleRequestSchema = z.object({
|
|
|
47
47
|
.literal("everywhere")
|
|
48
48
|
.optional()
|
|
49
49
|
.describe(
|
|
50
|
-
'Compatibility field. Trust rules apply workspace-wide: the engine matches on
|
|
50
|
+
'Compatibility field. Trust rules apply workspace-wide: the engine matches on tool and pattern only, so a narrower scope cannot be honored and any value other than "everywhere" is rejected rather than stored broader than the consent it records.',
|
|
51
51
|
),
|
|
52
52
|
});
|
|
53
53
|
|
|
@@ -116,7 +116,7 @@ export const ROUTES: GatewayRouteDefinition[] = [
|
|
|
116
116
|
queryParameters: [
|
|
117
117
|
{
|
|
118
118
|
name: "origin",
|
|
119
|
-
description: "Filter by origin
|
|
119
|
+
description: "Filter by origin: default or user_defined",
|
|
120
120
|
},
|
|
121
121
|
{ name: "tool", description: "Filter by tool name" },
|
|
122
122
|
{
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
import type { GatewayConfig } from "../../config.js";
|
|
16
16
|
import { getLogger } from "../../logger.js";
|
|
17
|
-
import {
|
|
17
|
+
import { resolveCloudAssistantIdOrUndefined } from "../../cloud-identity.js";
|
|
18
18
|
import {
|
|
19
19
|
CircuitBreakerOpenError,
|
|
20
20
|
forwardTwilioVoiceWebhook,
|
|
@@ -239,7 +239,7 @@ async function forwardToAssistant(
|
|
|
239
239
|
caches?: TwilioValidationCaches,
|
|
240
240
|
): Promise<Response> {
|
|
241
241
|
try {
|
|
242
|
-
const
|
|
242
|
+
const cloudAssistantId = await resolveCloudAssistantIdOrUndefined();
|
|
243
243
|
const runtimeResponse = await forwardTwilioVoiceWebhook(
|
|
244
244
|
config,
|
|
245
245
|
params,
|
|
@@ -247,7 +247,7 @@ async function forwardToAssistant(
|
|
|
247
247
|
resolvePublicBaseWssUrl(
|
|
248
248
|
config,
|
|
249
249
|
caches?.configFile,
|
|
250
|
-
|
|
250
|
+
cloudAssistantId,
|
|
251
251
|
validatedPublicUrl,
|
|
252
252
|
),
|
|
253
253
|
);
|
|
@@ -112,7 +112,6 @@ const baseConfig: GatewayConfig = {
|
|
|
112
112
|
runtimeMaxRetries: 2,
|
|
113
113
|
runtimeProxyRequireAuth: true,
|
|
114
114
|
runtimeTimeoutMs: 30000,
|
|
115
|
-
shutdownDrainMs: 5000,
|
|
116
115
|
trustProxy: false,
|
|
117
116
|
};
|
|
118
117
|
|
|
@@ -392,7 +391,7 @@ describe("resolvePublicBaseWssUrl", () => {
|
|
|
392
391
|
expect(resolvePublicBaseWssUrl(baseConfig)).toBeUndefined();
|
|
393
392
|
});
|
|
394
393
|
|
|
395
|
-
test("uses tunnelBaseUrl +
|
|
394
|
+
test("uses tunnelBaseUrl + cloudAssistantId when both are present", () => {
|
|
396
395
|
const config = {
|
|
397
396
|
...baseConfig,
|
|
398
397
|
tunnelBaseUrl: "https://tunnel-dev.mycadet.ai",
|
|
@@ -428,7 +427,7 @@ describe("resolvePublicBaseWssUrl", () => {
|
|
|
428
427
|
);
|
|
429
428
|
});
|
|
430
429
|
|
|
431
|
-
test("falls back to configFile publicBaseUrl when
|
|
430
|
+
test("falls back to configFile publicBaseUrl when cloudAssistantId is missing", () => {
|
|
432
431
|
const config = {
|
|
433
432
|
...baseConfig,
|
|
434
433
|
tunnelBaseUrl: "https://tunnel-dev.mycadet.ai",
|
|
@@ -483,7 +482,7 @@ describe("resolvePublicBaseWssUrl", () => {
|
|
|
483
482
|
);
|
|
484
483
|
});
|
|
485
484
|
|
|
486
|
-
test("does not use a
|
|
485
|
+
test("does not use a cloud callback URL as the websocket base", () => {
|
|
487
486
|
const config = {
|
|
488
487
|
...baseConfig,
|
|
489
488
|
tunnelBaseUrl: "https://tunnel-staging.mycadet.ai",
|
|
@@ -2,7 +2,7 @@ import { LOCAL_ASSISTANT_ID } from "../../assistant-id.js";
|
|
|
2
2
|
import type { ConfigFileCache } from "../../config-file-cache.js";
|
|
3
3
|
import type { GatewayConfig } from "../../config.js";
|
|
4
4
|
import { getLogger } from "../../logger.js";
|
|
5
|
-
import {
|
|
5
|
+
import { resolveCloudAssistantIdOrUndefined } from "../../cloud-identity.js";
|
|
6
6
|
import { resolveAdmissionPolicy } from "../../risk/admission-policy-cache.js";
|
|
7
7
|
import {
|
|
8
8
|
CircuitBreakerOpenError,
|
|
@@ -204,7 +204,7 @@ export function createTwilioVoiceWebhookHandler(
|
|
|
204
204
|
}
|
|
205
205
|
|
|
206
206
|
try {
|
|
207
|
-
const
|
|
207
|
+
const platformCloudId = await resolveCloudAssistantIdOrUndefined();
|
|
208
208
|
const runtimeResponse = await forwardTwilioVoiceWebhook(
|
|
209
209
|
config,
|
|
210
210
|
params,
|
|
@@ -212,7 +212,7 @@ export function createTwilioVoiceWebhookHandler(
|
|
|
212
212
|
resolvePublicBaseWssUrl(
|
|
213
213
|
config,
|
|
214
214
|
caches?.configFile,
|
|
215
|
-
|
|
215
|
+
platformCloudId,
|
|
216
216
|
validation.validatedCandidateUrl,
|
|
217
217
|
),
|
|
218
218
|
);
|