@mycadet/credential-executor 0.12.10 → 0.13.0-dev.202610171617.9ea363e

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.
@@ -10,7 +10,7 @@ import {
10
10
  trimmedNonEmptyString,
11
11
  TUNNEL_PROVIDERS,
12
12
  type TunnelRecord,
13
- tunnelHostForPlatformHost,
13
+ tunnelHostForCloudHost,
14
14
  } from "../ingress.js";
15
15
  import {
16
16
  buildTwilioMediaStreamUrl,
@@ -19,27 +19,27 @@ import {
19
19
  resolveTwilioPublicBaseUrl,
20
20
  } from "../twilio-ingress.js";
21
21
 
22
- describe("tunnelHostForPlatformHost", () => {
22
+ describe("tunnelHostForCloudHost", () => {
23
23
  test("maps the prod platform host to the prod tunnel", () => {
24
- expect(tunnelHostForPlatformHost("cloud.mycadet.ai")).toBe(
24
+ expect(tunnelHostForCloudHost("cloud.mycadet.ai")).toBe(
25
25
  "tunnel.mycadet.ai",
26
26
  );
27
27
  });
28
28
 
29
29
  test("maps env-prefixed platform hosts to their env tunnel", () => {
30
- expect(tunnelHostForPlatformHost("staging-cloud.mycadet.ai")).toBe(
31
- "tunnel-staging.mycadet.ai",
30
+ expect(tunnelHostForCloudHost("staging-cloud.mycadet.ai")).toBe(
31
+ "staging-tunnel.mycadet.ai",
32
32
  );
33
- expect(tunnelHostForPlatformHost("dev-cloud.mycadet.ai")).toBe(
34
- "tunnel-dev.mycadet.ai",
33
+ expect(tunnelHostForCloudHost("dev-cloud.mycadet.ai")).toBe(
34
+ "dev-tunnel.mycadet.ai",
35
35
  );
36
36
  });
37
37
 
38
38
  test("returns null for hosts outside the deployment convention", () => {
39
- expect(tunnelHostForPlatformHost("localhost")).toBeNull();
40
- expect(tunnelHostForPlatformHost("platform.example.com")).toBeNull();
39
+ expect(tunnelHostForCloudHost("localhost")).toBeNull();
40
+ expect(tunnelHostForCloudHost("platform.example.com")).toBeNull();
41
41
  expect(
42
- tunnelHostForPlatformHost("staging-cloud.mycadet.ai.evil.com"),
42
+ tunnelHostForCloudHost("staging-cloud.mycadet.ai.evil.com"),
43
43
  ).toBeNull();
44
44
  });
45
45
  });
@@ -39,7 +39,6 @@ export const CHANNEL_IDS = [
39
39
  "slack",
40
40
  "email",
41
41
  "platform",
42
- "a2a",
43
42
  "discord",
44
43
  "plugin",
45
44
  // Internal id for conversations a guardian shares with a contact. It has no
@@ -1,17 +1,17 @@
1
1
  /**
2
2
  * Map a Cadet Cloud host onto its environment's tunnel host, following the
3
3
  * deployment naming convention (`cloud.mycadet.ai` → `tunnel.mycadet.ai`,
4
- * `{env}-cloud.mycadet.ai` → `tunnel-{env}.mycadet.ai`). Returns null for
4
+ * `{env}-cloud.mycadet.ai` → `{env}-tunnel.mycadet.ai`). Returns null for
5
5
  * hosts outside that convention (localhost, custom domains). Shared by the
6
6
  * gateway speech relay and the web live-voice client so the two ends of the
7
7
  * cloud-speech transport can't drift.
8
8
  */
9
- export function tunnelHostForPlatformHost(host: string): string | null {
9
+ export function tunnelHostForCloudHost(host: string): string | null {
10
10
  if (host === "cloud.mycadet.ai") {
11
11
  return "tunnel.mycadet.ai";
12
12
  }
13
13
  const match = /^([a-z0-9-]+)-cloud\.mycadet\.ai$/.exec(host);
14
- return match ? `tunnel-${match[1]}.mycadet.ai` : null;
14
+ return match ? `${match[1]}-tunnel.mycadet.ai` : null;
15
15
  }
16
16
 
17
17
  export function normalizePublicBaseUrl(value: unknown): string | undefined {
package/package.json CHANGED
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "name": "@mycadet/credential-executor",
3
- "version": "0.12.10",
3
+ "version": "0.13.0-dev.202610171617.9ea363e",
4
+ "repository": {
5
+ "type": "git",
6
+ "url": "git+https://github.com/vargasjr-dev/mycadet-temp.git"
7
+ },
4
8
  "license": "MIT",
5
9
  "type": "module",
6
10
  "exports": {
@@ -0,0 +1,29 @@
1
+ import { expect, test } from "bun:test";
2
+ import { withCesHttpErrorLogging } from "./request-errors.js";
3
+
4
+ test("CES HTTP failures log the exception and return a safe correlation id", async () => {
5
+ const captured: Array<Record<string, unknown>> = [];
6
+ const failure = new Error("backend unavailable");
7
+ const response = await withCesHttpErrorLogging(
8
+ new Request("http://127.0.0.1:8090/v1/credentials", {
9
+ headers: { Authorization: "Bearer fixture-secret" },
10
+ }),
11
+ async () => {
12
+ throw failure;
13
+ },
14
+ {
15
+ error: (context) => {
16
+ captured.push(context);
17
+ },
18
+ },
19
+ );
20
+ const body = await response.json();
21
+ expect(response.status).toBe(500);
22
+ expect(body.error).toBe("Internal server error");
23
+ expect(body.request_id).toBe(response.headers.get("x-ces-error-id"));
24
+ expect(captured[0]?.err).toBe(failure);
25
+ expect(captured[0]?.method).toBe("GET");
26
+ expect(captured[0]?.path).toBe("/v1/credentials");
27
+ expect(JSON.stringify(body)).not.toContain("fixture-secret");
28
+ expect(JSON.stringify(captured)).not.toContain("fixture-secret");
29
+ });
@@ -0,0 +1,34 @@
1
+ import { randomUUID } from "node:crypto";
2
+
3
+ type RequestLogger = {
4
+ error(context: Record<string, unknown>, message: string): void;
5
+ };
6
+
7
+ /** Keep exception diagnostics in CES logs and expose only a correlation id. */
8
+ export async function withCesHttpErrorLogging(
9
+ request: Request,
10
+ handle: () => Promise<Response>,
11
+ logger: RequestLogger,
12
+ ): Promise<Response> {
13
+ try {
14
+ return await handle();
15
+ } catch (err) {
16
+ const requestId = randomUUID();
17
+ logger.error(
18
+ {
19
+ err,
20
+ requestId,
21
+ method: request.method,
22
+ path: new URL(request.url).pathname,
23
+ },
24
+ "CES HTTP request failed",
25
+ );
26
+ return Response.json(
27
+ { error: "Internal server error", request_id: requestId },
28
+ {
29
+ status: 500,
30
+ headers: { "x-ces-error-id": requestId },
31
+ },
32
+ );
33
+ }
34
+ }
package/src/main.ts CHANGED
@@ -51,6 +51,7 @@ import {
51
51
  } from "./http/credential-routes.js";
52
52
  import { handleLogExportRoute } from "./http/log-export-routes.js";
53
53
  import { isLoopbackAddress } from "./http/loopback-address.js";
54
+ import { withCesHttpErrorLogging } from "./http/request-errors.js";
54
55
  import { handleMetadataRoute } from "./http/metadata-routes.js";
55
56
  import { CES_MIGRATIONS } from "./migrations/registry.js";
56
57
  import { runCesMigrations } from "./migrations/runner.js";
@@ -283,29 +284,35 @@ function startHealthServer(
283
284
  return new Response("Not Found", { status: 404 });
284
285
  }
285
286
 
286
- // Credential CRUD routes (only if service token is configured)
287
- if (credentialDeps) {
288
- const metadataResponse = await handleMetadataRoute(req);
289
- if (metadataResponse) {
290
- return metadataResponse;
291
- }
292
- const credentialResponse = await handleCredentialRoute(
293
- req,
294
- credentialDeps,
295
- );
296
- if (credentialResponse) {
297
- return credentialResponse;
298
- }
299
- }
300
-
301
- // Log export route
302
- const logExportResponse = await handleLogExportRoute(
287
+ return withCesHttpErrorLogging(
303
288
  req,
304
- getCesLogDir("managed"),
305
- );
306
- if (logExportResponse) return logExportResponse;
289
+ async () => {
290
+ // Credential CRUD routes (only if service token is configured)
291
+ if (credentialDeps) {
292
+ const metadataResponse = await handleMetadataRoute(req);
293
+ if (metadataResponse) {
294
+ return metadataResponse;
295
+ }
296
+ const credentialResponse = await handleCredentialRoute(
297
+ req,
298
+ credentialDeps,
299
+ );
300
+ if (credentialResponse) {
301
+ return credentialResponse;
302
+ }
303
+ }
307
304
 
308
- return new Response("Not Found", { status: 404 });
305
+ // Log export route
306
+ const logExportResponse = await handleLogExportRoute(
307
+ req,
308
+ getCesLogDir("managed"),
309
+ );
310
+ if (logExportResponse) return logExportResponse;
311
+
312
+ return new Response("Not Found", { status: 404 });
313
+ },
314
+ log,
315
+ );
309
316
  },
310
317
  });
311
318