@cmmd-center/forge 0.13.124 → 0.13.126

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.
Files changed (37) hide show
  1. package/dist/bin.cjs +365 -65
  2. package/dist/bin.mjs +394 -94
  3. package/dist/client/assets/{CanvasPanel-BOh2xUZv.js → CanvasPanel-Db1Iz4JN.js} +2 -2
  4. package/dist/client/assets/CompositeItem-BxPZ_9BJ.js +2 -0
  5. package/dist/client/assets/DiffPanel-CNil6OV-.js +64 -0
  6. package/dist/client/assets/{DiffPanel.logic-DK4RDoiD.js → DiffPanel.logic-yEOXCs0u.js} +2 -2
  7. package/dist/client/assets/{DiffWorkerPoolProvider-orOnVCAZ.js → DiffWorkerPoolProvider-BF-pZpUl.js} +11 -11
  8. package/dist/client/assets/PullRequestCodePanel-eldrSbZ3.js +5 -0
  9. package/dist/client/assets/{ThemeEditorPanel-v2mnY18G.js → ThemeEditorPanel-DTfrlPBc.js} +2 -2
  10. package/dist/client/assets/{Virtualizer-D5o0Becp.js → Virtualizer-BvsCWf4E.js} +2 -2
  11. package/dist/client/assets/WorkerPoolContext-CaivT_VA.js +1533 -0
  12. package/dist/client/assets/{canvasStateStore-xaZAenqH.js → canvasStateStore-DKtQcBVc.js} +2 -2
  13. package/dist/client/assets/cmmdForgeRuntimeListResource-CRVF6pin.js +1008 -0
  14. package/dist/client/assets/currentForgeWorkspaceStore-DH-DGTJm.js +2 -0
  15. package/dist/client/assets/{index-DB8H2s1H.js → index-CIM7fDFV.js} +68 -68
  16. package/dist/client/assets/popover-Cvb1Axgx.js +2 -0
  17. package/dist/client/assets/scroll-area-DzReJmNn.js +2 -0
  18. package/dist/client/assets/switch-RuRK1onH.js +2 -0
  19. package/dist/client/assets/{themePalette-eOA8OvYM.js → themePalette-gr6ZYaOx.js} +2 -2
  20. package/dist/client/assets/toggle-D8xaJAgP.js +2 -0
  21. package/dist/client/assets/toggle-group-CwSbXP07.js +2 -0
  22. package/dist/client/assets/useOpenInteractionType-B983ff5H.js +2 -0
  23. package/dist/client/assets/utils-B4G-Q0s7.js +2 -0
  24. package/dist/client/index.html +16 -13
  25. package/dist/forge-build.json +1 -1
  26. package/dist/{open-2-reBrpv.mjs → open-yHTUkd2Y.mjs} +3 -3
  27. package/package.json +1 -1
  28. package/dist/client/assets/CompositeItem-DNVaubQt.js +0 -2
  29. package/dist/client/assets/DiffPanel-C1le0UaP.js +0 -64
  30. package/dist/client/assets/PullRequestCodePanel-Br2r3lbU.js +0 -5
  31. package/dist/client/assets/WorkerPoolContext-CpM1XKxV.js +0 -2538
  32. package/dist/client/assets/popover-Cxnw5kve.js +0 -2
  33. package/dist/client/assets/scroll-area-DDcW71H4.js +0 -2
  34. package/dist/client/assets/switch-Wi-2Dkh9.js +0 -2
  35. package/dist/client/assets/toggle-group-Cqh7gA8I.js +0 -2
  36. package/dist/client/assets/toggle-m8ncTpRB.js +0 -2
  37. package/dist/client/assets/useOpenInteractionType-CN3hnnfp.js +0 -2
package/dist/bin.cjs CHANGED
@@ -30767,7 +30767,7 @@ function frameClassByte(frameClass) {
30767
30767
  if (frameClass === "application") return Uint8Array.of(1);
30768
30768
  return Uint8Array.of(2);
30769
30769
  }
30770
- function encodeBase64Url$1(value) {
30770
+ function encodeBase64Url$2(value) {
30771
30771
  let output = "";
30772
30772
  for (let index = 0; index < value.byteLength; index += 3) {
30773
30773
  const first = value[index] ?? 0;
@@ -30797,7 +30797,7 @@ function decodeBase64Url$1(value) {
30797
30797
  outputIndex += 1;
30798
30798
  }
30799
30799
  }
30800
- if (encodeBase64Url$1(output) !== value) throw new RelayPayloadChannelFailure("channel_authentication_failed", "Encrypted relay frame has a non-canonical base64url encoding.");
30800
+ if (encodeBase64Url$2(output) !== value) throw new RelayPayloadChannelFailure("channel_authentication_failed", "Encrypted relay frame has a non-canonical base64url encoding.");
30801
30801
  return output;
30802
30802
  }
30803
30803
  function isLowOrderX25519PublicKey(value) {
@@ -30836,7 +30836,7 @@ async function createRelayPayloadBinding(input, digestProvider = defaultDigestPr
30836
30836
  };
30837
30837
  }
30838
30838
  function relayPayloadTicketDigestBase64Url(binding) {
30839
- return encodeBase64Url$1(binding.ticketDigest);
30839
+ return encodeBase64Url$2(binding.ticketDigest);
30840
30840
  }
30841
30841
  function relayPayloadInfo(binding) {
30842
30842
  return concat([
@@ -30873,7 +30873,7 @@ async function generateRelayEndpointKeyPair(randomSource = defaultRandomSource)
30873
30873
  }
30874
30874
  async function serializeRelayEndpointPublicKey(publicKey) {
30875
30875
  try {
30876
- return encodeBase64Url$1(bytes(await suite.kem.serializePublicKey(publicKey)));
30876
+ return encodeBase64Url$2(bytes(await suite.kem.serializePublicKey(publicKey)));
30877
30877
  } catch (error) {
30878
30878
  throw new RelayPayloadChannelFailure("endpoint_key_invalid", error instanceof Error ? error.message : "Endpoint public-key serialization failed.");
30879
30879
  }
@@ -30928,7 +30928,7 @@ var HpkeRelayPayloadSender = class {
30928
30928
  constructor(binding, context) {
30929
30929
  this.binding = binding;
30930
30930
  this.context = context;
30931
- this.encapsulatedKey = encodeBase64Url$1(bytes(context.enc));
30931
+ this.encapsulatedKey = encodeBase64Url$2(bytes(context.enc));
30932
30932
  }
30933
30933
  seal(input) {
30934
30934
  return this.queue.run(async () => {
@@ -30958,7 +30958,7 @@ var HpkeRelayPayloadSender = class {
30958
30958
  direction: this.binding.direction,
30959
30959
  frameClass: input.frameClass,
30960
30960
  sequence,
30961
- ciphertext: encodeBase64Url$1(ciphertext)
30961
+ ciphertext: encodeBase64Url$2(ciphertext)
30962
30962
  };
30963
30963
  });
30964
30964
  }
@@ -66337,7 +66337,7 @@ function normalizeNumberish(value) {
66337
66337
  }
66338
66338
  //#endregion
66339
66339
  //#region package.json
66340
- var version$1 = "0.13.124";
66340
+ var version$1 = "0.13.126";
66341
66341
  //#endregion
66342
66342
  //#region src/sentry.ts
66343
66343
  const SERVER_APP_NAME = "forge-server";
@@ -71414,6 +71414,19 @@ function mintCmmdShortLivedToken(grantToken) {
71414
71414
  });
71415
71415
  }
71416
71416
  /**
71417
+ * The redemption request body. `popPublicKeyPem` is the server's PoP identity
71418
+ * (forge#3534): presented when bound sessions are enabled so CMMD stamps the
71419
+ * returned JWT with this server's key (`cnf.jkt`), omitted otherwise.
71420
+ */
71421
+ function buildSessionGrantRedemptionBody(grantToken, popPublicKeyPem) {
71422
+ return {
71423
+ grant_type: "forge_session_grant",
71424
+ grant_token: grantToken,
71425
+ client_id: "cmmd-forge-web",
71426
+ ...popPublicKeyPem ? { pop_public_key: popPublicKeyPem } : {}
71427
+ };
71428
+ }
71429
+ /**
71417
71430
  * Redeem a durable CMMD session grant for a fresh credential set.
71418
71431
  *
71419
71432
  * The grant is CMMD's designed recovery path for exactly the case a rotating
@@ -71425,15 +71438,11 @@ function mintCmmdShortLivedToken(grantToken) {
71425
71438
  * Returns the new MCP PAT alongside the JWT/refresh pair, because CMMD mints a
71426
71439
  * fresh PAT on redemption and the old one is not carried over.
71427
71440
  */
71428
- function redeemCmmdSessionGrant(grantToken) {
71441
+ function redeemCmmdSessionGrant(grantToken, popPublicKeyPem) {
71429
71442
  return fetch(new URL("/api/mcp-server/oauth/token", resolveCommandAppUrl()).toString(), {
71430
71443
  method: "POST",
71431
71444
  headers: { "Content-Type": "application/json" },
71432
- body: JSON.stringify({
71433
- grant_type: "forge_session_grant",
71434
- grant_token: grantToken,
71435
- client_id: "cmmd-forge-web"
71436
- })
71445
+ body: JSON.stringify(buildSessionGrantRedemptionBody(grantToken, popPublicKeyPem))
71437
71446
  }).then(async (response) => {
71438
71447
  if (!response.ok) return null;
71439
71448
  const payload = await response.json();
@@ -71504,6 +71513,140 @@ async function readOAuthErrorCode(response) {
71504
71513
  }
71505
71514
  }
71506
71515
  //#endregion
71516
+ //#region ../../packages/shared/src/cmmdSessionPop.ts
71517
+ /**
71518
+ * CMMD session proof-of-possession client protocol (forge#3517).
71519
+ *
71520
+ * A CMMD session issued to a Forge client can be bound to the client's Ed25519
71521
+ * device key: the public key travels with the login (`pop_public_key`), CMMD
71522
+ * stamps its thumbprint into the token (`cnf.jkt`), and every authenticated
71523
+ * request must then carry a detached Ed25519 signature over a canonical
71524
+ * request binding (method + path + timestamp + nonce). A stolen bearer token
71525
+ * alone is unusable.
71526
+ *
71527
+ * This module is the client half of the scheme CMMD verifies in
71528
+ * `server/services/auth/session-pop-binding.ts`. The protocol deliberately
71529
+ * reuses CMMD's existing crypto shapes rather than inventing a new scheme: the
71530
+ * thumbprint is the same sha256-over-SPKI-DER base64url digest as the forge
71531
+ * device registry and the desktop relay identity, and the signature covers
71532
+ * `<purpose>.<base64url payload>`, the forge relay challenge shape, with
71533
+ * Ed25519. It is not a DPoP port: proofs are plain headers, not JWTs.
71534
+ */
71535
+ const HEADER_KEY = "x-cmmd-pop-key";
71536
+ const HEADER_INPUT = "x-cmmd-pop-input";
71537
+ const HEADER_SIGNATURE = "x-cmmd-pop-signature";
71538
+ const CMMD_SESSION_POP_PURPOSE = "cmmd-session-pop-v1";
71539
+ function encodeBase64Url$1(bytes) {
71540
+ let binary = "";
71541
+ for (const byte of bytes) binary += String.fromCharCode(byte);
71542
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/u, "");
71543
+ }
71544
+ function buildCmmdSessionPopSigningInput(payload) {
71545
+ return `${CMMD_SESSION_POP_PURPOSE}.${encodeBase64Url$1(new TextEncoder().encode(JSON.stringify(payload)))}`;
71546
+ }
71547
+ function randomNonce() {
71548
+ const bytes = new Uint8Array(16);
71549
+ crypto.getRandomValues(bytes);
71550
+ return encodeBase64Url$1(bytes);
71551
+ }
71552
+ /**
71553
+ * Build the three proof headers for one request. Each call produces a fresh
71554
+ * nonce; callers must not reuse the returned headers across attempts
71555
+ * (retries included), because CMMD rejects replayed nonces.
71556
+ */
71557
+ async function buildCmmdSessionPopProofHeaders(input) {
71558
+ const payload = {
71559
+ version: 1,
71560
+ htm: input.method.toUpperCase(),
71561
+ htu: input.path,
71562
+ ts: input.nowSeconds ?? Math.floor(Date.now() / 1e3),
71563
+ nonce: input.nonce ?? randomNonce()
71564
+ };
71565
+ const signingInput = buildCmmdSessionPopSigningInput(payload);
71566
+ const signature = await input.signer.sign(signingInput);
71567
+ const spkiDer = decodePemSpkiDer(input.signer.publicKeyPem);
71568
+ return {
71569
+ [HEADER_KEY]: encodeBase64Url$1(spkiDer),
71570
+ [HEADER_INPUT]: encodeBase64Url$1(new TextEncoder().encode(JSON.stringify(payload))),
71571
+ [HEADER_SIGNATURE]: encodeBase64Url$1(signature)
71572
+ };
71573
+ }
71574
+ function decodePemSpkiDer(pem) {
71575
+ const base64 = pem.replace("-----BEGIN PUBLIC KEY-----", "").replace("-----END PUBLIC KEY-----", "").replace(/\s+/gu, "").replace(/-/g, "+").replace(/_/g, "/");
71576
+ const padded = base64 + "=".repeat((4 - base64.length % 4) % 4);
71577
+ const binary = atob(padded);
71578
+ const bytes = new Uint8Array(binary.length);
71579
+ for (let index = 0; index < binary.length; index += 1) bytes[index] = binary.charCodeAt(index);
71580
+ return bytes;
71581
+ }
71582
+ //#endregion
71583
+ //#region src/cmmd/cmmdSessionPopServer.ts
71584
+ /**
71585
+ * The Forge server's own CMMD session proof-of-possession identity (forge#3534,
71586
+ * D3): the server holds an Ed25519 key, presents its SPKI PEM as
71587
+ * `pop_public_key` when it redeems a CMMD session grant, and signs every
71588
+ * CMMD-origin REST call it makes with detached proofs, so a stolen bearer JWT
71589
+ * alone is unusable. CMMD verifies the scheme in
71590
+ * `server/services/auth/session-pop-binding.ts`.
71591
+ *
71592
+ * Rollout order (docs/forge-cmmd-auth-lanes.md): CMMD deploys first; Forge
71593
+ * flips `FORGE_CMMD_SESSION_POP_ENABLED` afterwards; CMMD's
71594
+ * `JWT_REQUIRE_BOUND_SESSIONS=true` is the contract phase. While the flag is
71595
+ * off this module produces no keys and no headers, and CMMD treats tokens as
71596
+ * plain bearers.
71597
+ *
71598
+ * The identity lives in memory for the process lifetime. After a restart the
71599
+ * server mints a new identity; CMMD then refuses proofs from the old one, and
71600
+ * the JWT resolver's recovery ladder re-mints the affected credentials. The
71601
+ * persistence duty (a stable identity across restarts) lands with the flag
71602
+ * rollout, not with this plumbing.
71603
+ */
71604
+ const ENABLED_FLAG = "FORGE_CMMD_SESSION_POP_ENABLED";
71605
+ /** Enables session proofs only when the rollout flag is exactly "true". */
71606
+ function isCmmdSessionPopEnabled() {
71607
+ return process.env[ENABLED_FLAG] === "true";
71608
+ }
71609
+ let cachedSigner = null;
71610
+ /** Creates an in-memory Ed25519 identity with a public SPKI PEM and a detached signer. */
71611
+ function createServerSigner() {
71612
+ const { publicKey, privateKey } = (0, node_crypto.generateKeyPairSync)("ed25519");
71613
+ const exported = publicKey.export({
71614
+ type: "spki",
71615
+ format: "pem"
71616
+ });
71617
+ return {
71618
+ publicKeyPem: typeof exported === "string" ? exported : exported.toString("utf8"),
71619
+ sign: async (signingInput) => new Uint8Array((0, node_crypto.sign)(null, Buffer.from(signingInput, "utf8"), privateKey))
71620
+ };
71621
+ }
71622
+ /**
71623
+ * The server's PoP identity, created on first use under the flag and stable
71624
+ * for the process lifetime. Null while the flag is off.
71625
+ */
71626
+ function resolveCmmdSessionPopSigner() {
71627
+ if (!isCmmdSessionPopEnabled()) return null;
71628
+ cachedSigner ??= createServerSigner();
71629
+ return cachedSigner;
71630
+ }
71631
+ /** The `pop_public_key` value to present at issuance, or null while disabled. */
71632
+ function cmmdSessionPopPublicKey() {
71633
+ return resolveCmmdSessionPopSigner()?.publicKeyPem ?? null;
71634
+ }
71635
+ /**
71636
+ * The three proof headers for one CMMD-origin request, or no headers while
71637
+ * disabled. Each call carries a fresh nonce; never reuse the result across
71638
+ * attempts, because CMMD rejects replayed nonces.
71639
+ */
71640
+ async function cmmdSessionPopRequestHeaders(input) {
71641
+ const signer = resolveCmmdSessionPopSigner();
71642
+ if (!signer) return {};
71643
+ return buildCmmdSessionPopProofHeaders({
71644
+ signer,
71645
+ method: input.method,
71646
+ path: input.path
71647
+ });
71648
+ }
71649
+ //#endregion
71507
71650
  //#region src/command/cmmdTokenMintLane.ts
71508
71651
  /**
71509
71652
  * The CMMD token-mint lane (forge#3518, expand phase).
@@ -71756,7 +71899,7 @@ function tryGrantRedemption(sessionId, context) {
71756
71899
  return require_Schema$1.gen(function* () {
71757
71900
  yield* logCmmdJwtRung(context, "grant-redemption", "attempted", "redeeming this session's durable CMMD session grant");
71758
71901
  yield* recordCmmdCredentialRefreshMetric("grant-redemption", "attempted");
71759
- const redeemed = yield* require_Schema$1.promise(() => redeemCmmdSessionGrant(grantToken)).pipe(require_Schema$1.orElseSucceed(() => null));
71902
+ const redeemed = yield* require_Schema$1.promise(() => redeemCmmdSessionGrant(grantToken, cmmdSessionPopPublicKey())).pipe(require_Schema$1.orElseSucceed(() => null));
71760
71903
  const accessToken = usableCmmdUserJwt(redeemed?.accessToken);
71761
71904
  if (!accessToken) {
71762
71905
  yield* logCmmdJwtRung(context, "grant-redemption", "failed", "session grant redemption did not return a usable user access JWT");
@@ -72045,11 +72188,25 @@ async function requireCmmdProxySuccess(response, signal) {
72045
72188
  * Uses the stored JWT (set after OAuth auth-code flow with post-update CMMD)
72046
72189
  * to make authenticated REST calls on behalf of the user. A missing delegated
72047
72190
  * JWT is an authentication-required state, not a CMMD availability failure.
72191
+ *
72192
+ * Every request this module sends can carry the server's session
72193
+ * proof-of-possession headers (forge#3534): while
72194
+ * `FORGE_CMMD_SESSION_POP_ENABLED` is true, `authHeaders` attaches detached
72195
+ * Ed25519 proofs over each request's method and path, which CMMD requires for
72196
+ * tokens bound with `cnf.jkt` and ignores otherwise. Each call gets a fresh
72197
+ * nonce, so retries re-sign rather than replay.
72048
72198
  */
72049
- function authHeaders(jwt, orgId) {
72199
+ /** Builds owner JWT and optional organization headers with fresh, flag-gated session proofs. */
72200
+ async function authHeaders(jwt, orgId, method, path) {
72050
72201
  const headers = { Authorization: `Bearer ${jwt}` };
72051
72202
  if (orgId) headers["X-Organization-ID"] = orgId;
72052
- return headers;
72203
+ return {
72204
+ ...headers,
72205
+ ...await cmmdSessionPopRequestHeaders({
72206
+ method,
72207
+ path
72208
+ })
72209
+ };
72053
72210
  }
72054
72211
  async function fetchResponse(url, init, options) {
72055
72212
  try {
@@ -72061,18 +72218,20 @@ async function fetchResponse(url, init, options) {
72061
72218
  rethrowCmmdProxyTimeout(error, options?.signal);
72062
72219
  }
72063
72220
  }
72221
+ /** Fetches a CMMD JSON resource with optional query parameters and caller cancellation. */
72064
72222
  async function proxyGet(jwt, path, searchParams, orgId, options) {
72065
72223
  const cmmdBase = resolveCommandAppUrl();
72066
72224
  const url = new URL(path, cmmdBase);
72067
72225
  if (searchParams) for (const [k, v] of Object.entries(searchParams)) url.searchParams.set(k, v);
72068
- return handleCmmdProxyResponse(await fetchResponse(url.toString(), { headers: authHeaders(jwt, orgId) }, options), options?.signal);
72226
+ return handleCmmdProxyResponse(await fetchResponse(url.toString(), { headers: await authHeaders(jwt, orgId, "GET", path) }, options), options?.signal);
72069
72227
  }
72228
+ /** Posts JSON to CMMD on the owner JWT lane and decodes the response or upstream error. */
72070
72229
  async function proxyPost(jwt, path, body, orgId, options) {
72071
72230
  const cmmdBase = resolveCommandAppUrl();
72072
72231
  return handleCmmdProxyResponse(await fetchResponse(new URL(path, cmmdBase).toString(), {
72073
72232
  method: "POST",
72074
72233
  headers: {
72075
- ...authHeaders(jwt, orgId),
72234
+ ...await authHeaders(jwt, orgId, "POST", path),
72076
72235
  "Content-Type": "application/json"
72077
72236
  },
72078
72237
  body: JSON.stringify(body)
@@ -72086,44 +72245,49 @@ async function responseWithMetadata(response, signal) {
72086
72245
  retryAfter: response.headers.get("retry-after")
72087
72246
  };
72088
72247
  }
72248
+ /** Fetches a CMMD resource while preserving status, Location, and Retry-After metadata. */
72089
72249
  async function proxyGetWithMetadata(jwt, path, orgId, options) {
72090
- return responseWithMetadata(await fetchResponse(new URL(path, resolveCommandAppUrl()).toString(), { headers: authHeaders(jwt, orgId) }, options), options?.signal);
72250
+ return responseWithMetadata(await fetchResponse(new URL(path, resolveCommandAppUrl()).toString(), { headers: await authHeaders(jwt, orgId, "GET", path) }, options), options?.signal);
72091
72251
  }
72252
+ /** Posts JSON to CMMD while preserving status, Location, and Retry-After metadata. */
72092
72253
  async function proxyPostWithMetadata(jwt, path, body, orgId, options) {
72093
72254
  const cmmdBase = resolveCommandAppUrl();
72094
72255
  return responseWithMetadata(await fetchResponse(new URL(path, cmmdBase).toString(), {
72095
72256
  method: "POST",
72096
72257
  headers: {
72097
- ...authHeaders(jwt, orgId),
72258
+ ...await authHeaders(jwt, orgId, "POST", path),
72098
72259
  "Content-Type": "application/json"
72099
72260
  },
72100
72261
  body: JSON.stringify(body)
72101
72262
  }, options), options?.signal);
72102
72263
  }
72264
+ /** Applies a JSON patch request to CMMD using the owner JWT and optional organization. */
72103
72265
  async function proxyPatch(jwt, path, body, orgId) {
72104
72266
  const cmmdBase = resolveCommandAppUrl();
72105
72267
  const url = new URL(path, cmmdBase);
72106
72268
  return handleCmmdProxyResponse(await fetch(url.toString(), {
72107
72269
  method: "PATCH",
72108
72270
  headers: {
72109
- ...authHeaders(jwt, orgId),
72271
+ ...await authHeaders(jwt, orgId, "PATCH", path),
72110
72272
  "Content-Type": "application/json"
72111
72273
  },
72112
72274
  body: JSON.stringify(body)
72113
72275
  }));
72114
72276
  }
72277
+ /** Sends a JSON replacement request to CMMD and decodes its response or upstream error. */
72115
72278
  async function proxyPut(jwt, path, body, orgId) {
72116
72279
  const cmmdBase = resolveCommandAppUrl();
72117
72280
  const url = new URL(path, cmmdBase);
72118
72281
  return handleCmmdProxyResponse(await fetch(url.toString(), {
72119
72282
  method: "PUT",
72120
72283
  headers: {
72121
- ...authHeaders(jwt, orgId),
72284
+ ...await authHeaders(jwt, orgId, "PUT", path),
72122
72285
  "Content-Type": "application/json"
72123
72286
  },
72124
72287
  body: JSON.stringify(body)
72125
72288
  }));
72126
72289
  }
72290
+ /** Deletes a CMMD resource with optional query parameters and JSON body; 204 returns undefined. */
72127
72291
  async function proxyDelete(jwt, path, searchParams, orgId, body) {
72128
72292
  const cmmdBase = resolveCommandAppUrl();
72129
72293
  const url = new URL(path, cmmdBase);
@@ -72131,7 +72295,7 @@ async function proxyDelete(jwt, path, searchParams, orgId, body) {
72131
72295
  const response = await fetch(url.toString(), {
72132
72296
  method: "DELETE",
72133
72297
  headers: {
72134
- ...authHeaders(jwt, orgId),
72298
+ ...await authHeaders(jwt, orgId, "DELETE", path),
72135
72299
  ...body === void 0 ? {} : { "Content-Type": "application/json" }
72136
72300
  },
72137
72301
  ...body === void 0 ? {} : { body: JSON.stringify(body) }
@@ -72139,13 +72303,14 @@ async function proxyDelete(jwt, path, searchParams, orgId, body) {
72139
72303
  if (!response.ok && response.status !== 204) await requireCmmdProxySuccess(response);
72140
72304
  return response.status === 204 ? void 0 : handleCmmdProxyResponse(response);
72141
72305
  }
72306
+ /** Forwards multipart bytes to CMMD, preserving the caller's content type and boundary. */
72142
72307
  async function proxyMultipart(jwt, path, body, contentType, orgId) {
72143
72308
  const cmmdBase = resolveCommandAppUrl();
72144
72309
  const url = new URL(path, cmmdBase);
72145
72310
  return handleCmmdProxyResponse(await fetch(url.toString(), {
72146
72311
  method: "POST",
72147
72312
  headers: {
72148
- ...authHeaders(jwt, orgId),
72313
+ ...await authHeaders(jwt, orgId, "POST", path),
72149
72314
  "Content-Type": contentType
72150
72315
  },
72151
72316
  body
@@ -72469,20 +72634,25 @@ const legacyCmmdToolBridge = {
72469
72634
  };
72470
72635
  //#endregion
72471
72636
  //#region src/command/workIntakeCache.ts
72472
- const cacheBySessionId = /* @__PURE__ */ new Map();
72637
+ const cacheByKey = /* @__PURE__ */ new Map();
72638
+ function cacheKeyFor(sessionId, orgId) {
72639
+ return `${sessionId}\u0000${orgId ?? ""}`;
72640
+ }
72473
72641
  function resolveCmmdWorkIntakeCacheIdentity(subject, cmmdUserId) {
72474
72642
  return cmmdUserId ? `cmmd-user:${cmmdUserId}` : `forge-subject:${subject}`;
72475
72643
  }
72476
72644
  function fingerprintToken(token) {
72477
72645
  return (0, node_crypto.createHash)("sha256").update(token).digest("hex");
72478
72646
  }
72479
- async function fetchBlockingSnapshot(input, identityKey, tokenFingerprint, existing) {
72647
+ async function fetchBlockingSnapshot(input, cacheKey, identityKey, tokenFingerprint, existing) {
72480
72648
  if (existing?.inFlight) return existing.inFlight;
72481
72649
  const nowMs = input.nowMs ?? Date.now;
72482
72650
  let promise;
72483
72651
  promise = input.fetchFresh().then((snapshot) => {
72484
- const current = cacheBySessionId.get(input.sessionId);
72485
- if (current?.tokenFingerprint === tokenFingerprint && current.inFlight === promise) cacheBySessionId.set(input.sessionId, {
72652
+ const current = cacheByKey.get(cacheKey);
72653
+ if (current?.tokenFingerprint === tokenFingerprint && current.inFlight === promise) cacheByKey.set(cacheKey, {
72654
+ sessionId: input.sessionId,
72655
+ orgId: input.orgId,
72486
72656
  identityKey,
72487
72657
  tokenFingerprint,
72488
72658
  snapshot,
@@ -72491,12 +72661,14 @@ async function fetchBlockingSnapshot(input, identityKey, tokenFingerprint, exist
72491
72661
  });
72492
72662
  return snapshot;
72493
72663
  }).catch((error) => {
72494
- const current = cacheBySessionId.get(input.sessionId);
72495
- if (current?.tokenFingerprint === tokenFingerprint && current.inFlight === promise) if (legacyCmmdToolBridge.isAuthError(error)) cacheBySessionId.delete(input.sessionId);
72664
+ const current = cacheByKey.get(cacheKey);
72665
+ if (current?.tokenFingerprint === tokenFingerprint && current.inFlight === promise) if (legacyCmmdToolBridge.isAuthError(error)) cacheByKey.delete(cacheKey);
72496
72666
  else current.inFlight = null;
72497
72667
  throw error;
72498
72668
  });
72499
- cacheBySessionId.set(input.sessionId, {
72669
+ cacheByKey.set(cacheKey, {
72670
+ sessionId: input.sessionId,
72671
+ orgId: input.orgId,
72500
72672
  identityKey,
72501
72673
  tokenFingerprint,
72502
72674
  snapshot: existing?.snapshot ?? null,
@@ -72512,9 +72684,9 @@ async function fetchBlockingSnapshot(input, identityKey, tokenFingerprint, exist
72512
72684
  * opportunistically on every read.
72513
72685
  */
72514
72686
  function sweepExpiredWorkIntakeEntries(nowMs) {
72515
- for (const [sessionId, entry] of cacheBySessionId) {
72687
+ for (const [key, entry] of cacheByKey) {
72516
72688
  if (entry.inFlight) continue;
72517
- if (nowMs - entry.cachedAtMs > 3e5) cacheBySessionId.delete(sessionId);
72689
+ if (nowMs - entry.cachedAtMs > 3e5) cacheByKey.delete(key);
72518
72690
  }
72519
72691
  }
72520
72692
  async function readCachedCmmdWorkIntakeForSession(input) {
@@ -72523,17 +72695,18 @@ async function readCachedCmmdWorkIntakeForSession(input) {
72523
72695
  const identityKey = input.identityKey ?? input.sessionId;
72524
72696
  if (input.forceFresh) invalidateCachedCmmdWorkIntakeForIdentity(identityKey);
72525
72697
  const tokenFingerprint = fingerprintToken(input.token);
72526
- const existing = cacheBySessionId.get(input.sessionId);
72527
- if (existing && (existing.identityKey !== identityKey || existing.tokenFingerprint !== tokenFingerprint)) cacheBySessionId.delete(input.sessionId);
72528
- const current = cacheBySessionId.get(input.sessionId);
72529
- if (!current?.snapshot) return fetchBlockingSnapshot(input, identityKey, tokenFingerprint, current);
72698
+ const cacheKey = cacheKeyFor(input.sessionId, input.orgId);
72699
+ const existing = cacheByKey.get(cacheKey);
72700
+ if (existing && (existing.identityKey !== identityKey || existing.tokenFingerprint !== tokenFingerprint)) cacheByKey.delete(cacheKey);
72701
+ const current = cacheByKey.get(cacheKey);
72702
+ if (!current?.snapshot) return fetchBlockingSnapshot(input, cacheKey, identityKey, tokenFingerprint, current);
72530
72703
  const ageMs = Math.max(0, nowMs() - current.cachedAtMs);
72531
72704
  if (ageMs <= 12e4) return current.snapshot;
72532
72705
  if (ageMs <= 3e5) {
72533
72706
  if (!current.inFlight) {
72534
72707
  let refreshPromise;
72535
72708
  refreshPromise = input.fetchFresh().then((snapshot) => {
72536
- const latest = cacheBySessionId.get(input.sessionId);
72709
+ const latest = cacheByKey.get(cacheKey);
72537
72710
  if (latest?.tokenFingerprint === tokenFingerprint && latest.inFlight === refreshPromise) {
72538
72711
  latest.snapshot = snapshot;
72539
72712
  latest.cachedAtMs = nowMs();
@@ -72541,25 +72714,25 @@ async function readCachedCmmdWorkIntakeForSession(input) {
72541
72714
  return snapshot;
72542
72715
  }).catch((error) => {
72543
72716
  if (legacyCmmdToolBridge.isAuthError(error)) {
72544
- const latest = cacheBySessionId.get(input.sessionId);
72545
- if (latest?.tokenFingerprint === tokenFingerprint && latest.inFlight === refreshPromise) cacheBySessionId.delete(input.sessionId);
72717
+ const latest = cacheByKey.get(cacheKey);
72718
+ if (latest?.tokenFingerprint === tokenFingerprint && latest.inFlight === refreshPromise) cacheByKey.delete(cacheKey);
72546
72719
  }
72547
72720
  return current.snapshot;
72548
72721
  }).finally(() => {
72549
- const latest = cacheBySessionId.get(input.sessionId);
72722
+ const latest = cacheByKey.get(cacheKey);
72550
72723
  if (latest?.tokenFingerprint === tokenFingerprint && latest.inFlight === refreshPromise) latest.inFlight = null;
72551
72724
  });
72552
72725
  current.inFlight = refreshPromise;
72553
72726
  }
72554
72727
  return current.snapshot;
72555
72728
  }
72556
- return fetchBlockingSnapshot(input, identityKey, tokenFingerprint, current);
72729
+ return fetchBlockingSnapshot(input, cacheKey, identityKey, tokenFingerprint, current);
72557
72730
  }
72558
72731
  function invalidateCachedCmmdWorkIntakeForSession(sessionId) {
72559
- cacheBySessionId.delete(sessionId);
72732
+ for (const [key, entry] of cacheByKey) if (entry.sessionId === sessionId) cacheByKey.delete(key);
72560
72733
  }
72561
72734
  function invalidateCachedCmmdWorkIntakeForIdentity(identityKey) {
72562
- for (const [sessionId, entry] of cacheBySessionId) if (entry.identityKey === identityKey) cacheBySessionId.delete(sessionId);
72735
+ for (const [key, entry] of cacheByKey) if (entry.identityKey === identityKey) cacheByKey.delete(key);
72563
72736
  }
72564
72737
  //#endregion
72565
72738
  //#region src/cmmdData/proxyRouteRegistry.ts
@@ -105066,6 +105239,29 @@ function warn$1(label, cause) {
105066
105239
  return `${label} fetch failed: ${cause instanceof Error ? cause.message : String(cause)}`;
105067
105240
  }
105068
105241
  /**
105242
+ * Reads the workspace catalog off a settled bootstrap call. No explicit org
105243
+ * means CMMD answered `activeWorkspace` with its own default-organization
105244
+ * guess, not a confirmed fact -- leave it null rather than publish it
105245
+ * (forge#3636).
105246
+ */
105247
+ function resolveWorkspacesFromBootstrap(bootstrapResult, orgId, warnings) {
105248
+ if (bootstrapResult.status !== "fulfilled") {
105249
+ warnings.push(warn$1("Workspaces (bootstrap)", bootstrapResult.reason));
105250
+ return {
105251
+ workspaces: [],
105252
+ activeWorkspace: null
105253
+ };
105254
+ }
105255
+ const bootstrap = unwrapRestObject(bootstrapResult.value);
105256
+ const rawWorkspaces = bootstrap?.["workspaces"];
105257
+ const workspaces = Array.isArray(rawWorkspaces) ? rawWorkspaces.map(adaptBootstrapWorkspace) : [];
105258
+ const rawActive = orgId !== void 0 ? bootstrap?.["activeWorkspace"] : void 0;
105259
+ return {
105260
+ workspaces,
105261
+ activeWorkspace: rawActive && typeof rawActive === "object" ? adaptBootstrapWorkspace(rawActive) : null
105262
+ };
105263
+ }
105264
+ /**
105069
105265
  * Build the CMMD work-intake snapshot via the JWT REST API:
105070
105266
  * bootstrap (workspaces) + campaigns + projects + center-board (tasks WITH deps).
105071
105267
  * `identity` is supplied by the session (resolved at login), matching the MCP builder.
@@ -105073,21 +105269,13 @@ function warn$1(label, cause) {
105073
105269
  async function fetchCmmdWorkIntakeViaRest(jwt, identity, deps = {}) {
105074
105270
  const get = deps.proxyGet ?? proxyGet;
105075
105271
  const [bootstrapResult, campaignsResult, projectsResult, boardResult] = await Promise.allSettled([
105076
- get(jwt, "/api/forge/context/bootstrap"),
105272
+ get(jwt, "/api/forge/context/bootstrap", void 0, deps.orgId),
105077
105273
  get(jwt, "/api/forge/context/campaigns"),
105078
105274
  fetchAllProjects(get, jwt),
105079
105275
  get(jwt, "/api/forge/context/work-items/board")
105080
105276
  ]);
105081
105277
  const warnings = [];
105082
- let workspaces = [];
105083
- let activeWorkspace = null;
105084
- if (bootstrapResult.status === "fulfilled") {
105085
- const bootstrap = unwrapRestObject(bootstrapResult.value);
105086
- const rawWorkspaces = bootstrap?.["workspaces"];
105087
- if (Array.isArray(rawWorkspaces)) workspaces = rawWorkspaces.map(adaptBootstrapWorkspace);
105088
- const rawActive = bootstrap?.["activeWorkspace"];
105089
- if (rawActive && typeof rawActive === "object") activeWorkspace = adaptBootstrapWorkspace(rawActive);
105090
- } else warnings.push(warn$1("Workspaces (bootstrap)", bootstrapResult.reason));
105278
+ const { workspaces, activeWorkspace } = resolveWorkspacesFromBootstrap(bootstrapResult, deps.orgId, warnings);
105091
105279
  if (projectsResult.status === "rejected") throw projectsResult.reason;
105092
105280
  if (campaignsResult.status === "rejected") warnings.push(warn$1("Campaigns", campaignsResult.reason));
105093
105281
  if (boardResult.status === "rejected") warnings.push(warn$1("Tasks (board)", boardResult.reason));
@@ -300407,7 +300595,7 @@ function resolveBuildCommitFromEnv(env) {
300407
300595
  * environment descriptor down instead of reporting an honest "unknown".
300408
300596
  */
300409
300597
  function readBakedBuildCommit() {
300410
- return "52e8c25f0de536143b44ff1ac86510aa93d35772";
300598
+ return "8a4057a4a58eeb014b73143ffbb1bb091089d750";
300411
300599
  }
300412
300600
  async function resolveServerBuildCommit(input) {
300413
300601
  if (isFullCommitSha(input.baked)) return input.baked;
@@ -310827,6 +311015,24 @@ function logWorkIntakeFailure(cause) {
310827
311015
  }
310828
311016
  console.error("[work-intake] fetch failed:", cause);
310829
311017
  }
311018
+ /**
311019
+ * `?orgId=` present but empty (a stripped or malformed query string) is not
311020
+ * an explicit org. Treat it the same as an absent param rather than sending
311021
+ * CMMD an empty `X-Organization-ID` header (forge#3636).
311022
+ */
311023
+ function normalizeOrgIdQueryParam(raw) {
311024
+ const trimmed = raw?.trim();
311025
+ return trimmed ? trimmed : void 0;
311026
+ }
311027
+ /**
311028
+ * `{ orgId }` when present, `{}` when not -- the `exactOptionalPropertyTypes`
311029
+ * shape every `orgId?: string` deps object here needs, since spreading
311030
+ * `{ orgId: possiblyUndefined }` inline assigns `undefined` to a property
311031
+ * the type says is only ever absent or a string.
311032
+ */
311033
+ function orgIdDeps(orgId) {
311034
+ return orgId !== void 0 ? { orgId } : {};
311035
+ }
310830
311036
  function fetchRestFallbackWorkIntakeForSession(session, dependencies = {}) {
310831
311037
  return require_Schema$1.gen(function* () {
310832
311038
  const jwt = yield* (dependencies.resolveJwt ?? resolveCmmdJwtForSession)({
@@ -310838,7 +311044,7 @@ function fetchRestFallbackWorkIntakeForSession(session, dependencies = {}) {
310838
311044
  const identity = (dependencies.readIdentity ?? readCmmdUserIdentityForSession)(session.sessionId);
310839
311045
  const fetchFallback = dependencies.fetchFallback ?? fetchCmmdWorkIntakeViaRest;
310840
311046
  return yield* require_Schema$1.tryPromise({
310841
- try: () => fetchFallback(jwt, identity),
311047
+ try: () => fetchFallback(jwt, identity, orgIdDeps(dependencies.orgId)),
310842
311048
  catch: (cause) => new WorkIntakeSnapshotReadError({
310843
311049
  detail: "Failed to build CMMD REST work-intake fallback.",
310844
311050
  cause
@@ -310950,7 +311156,9 @@ function unwrapForgeEnvelopeJsonText(text) {
310950
311156
  const cmmdWorkIntakeRouteLayer = add$1("GET", "/api/cmmd/work-intake", require_Schema$1.gen(function* () {
310951
311157
  const request = yield* require_HttpServer.HttpServerRequest;
310952
311158
  const session = yield* (yield* ServerAuth).authenticateHttpRequest(request);
310953
- const forceFresh = new URL(request.url, "http://localhost").searchParams.get("fresh") === "1";
311159
+ const requestUrl = new URL(request.url, "http://localhost");
311160
+ const forceFresh = requestUrl.searchParams.get("fresh") === "1";
311161
+ const orgId = normalizeOrgIdQueryParam(requestUrl.searchParams.get("orgId"));
310954
311162
  let accessToken = readCommandAccessTokenForSession(session.sessionId);
310955
311163
  if (!accessToken && session.role === "owner" && (session.subject === "owner-bootstrap" || session.subject === "desktop-bootstrap")) accessToken = (yield* promoteMostRecentOwnerCommandAuthToSession(session.sessionId).pipe(require_Schema$1.catch_((error) => require_Schema$1.logWarning("Failed to promote owner CMMD auth for work intake.", {
310956
311164
  cause: error,
@@ -310995,8 +311203,9 @@ const cmmdWorkIntakeRouteLayer = add$1("GET", "/api/cmmd/work-intake", require_S
310995
311203
  sessionId: session.sessionId,
310996
311204
  identityKey: workIntakeCacheIdentity,
310997
311205
  token: accessToken,
311206
+ ...orgIdDeps(orgId),
310998
311207
  forceFresh,
310999
- fetchFresh: () => cmmdJwt ? fetchCmmdWorkIntakeViaRest(cmmdJwt, sessionIdentity) : Promise.reject(/* @__PURE__ */ new Error("CMMD JWT unavailable for work-intake fetch."))
311208
+ fetchFresh: () => cmmdJwt ? fetchCmmdWorkIntakeViaRest(cmmdJwt, sessionIdentity, orgIdDeps(orgId)) : Promise.reject(/* @__PURE__ */ new Error("CMMD JWT unavailable for work-intake fetch."))
311000
311209
  }),
311001
311210
  catch: (cause) => new WorkIntakeSnapshotReadError({
311002
311211
  detail: "Failed to read CMMD work intake snapshot.",
@@ -311014,7 +311223,7 @@ const cmmdWorkIntakeRouteLayer = add$1("GET", "/api/cmmd/work-intake", require_S
311014
311223
  if (isCmmdMcpAuthError(cause)) {
311015
311224
  console.error("[WORK_INTAKE] CMMD_AUTH_EXPIRED mcp_error:", cause instanceof Error ? cause.message : String(cause));
311016
311225
  invalidateCachedCmmdWorkIntakeForSession(session.sessionId);
311017
- const fallback = yield* fetchRestFallbackWorkIntakeForSession(session);
311226
+ const fallback = yield* fetchRestFallbackWorkIntakeForSession(session, orgIdDeps(orgId));
311018
311227
  yield* clearCommandAccessTokenForSession(session.sessionId).pipe(require_Schema$1.catch_((error) => require_Schema$1.logWarning("Failed to clear expired CMMD token after MCP auth failure.", {
311019
311228
  cause: error,
311020
311229
  sessionId: session.sessionId
@@ -311039,7 +311248,7 @@ const cmmdWorkIntakeRouteLayer = add$1("GET", "/api/cmmd/work-intake", require_S
311039
311248
  connected: snapshot.connected
311040
311249
  });
311041
311250
  invalidateCachedCmmdWorkIntakeForSession(session.sessionId);
311042
- const fallback = yield* fetchRestFallbackWorkIntakeForSession(session);
311251
+ const fallback = yield* fetchRestFallbackWorkIntakeForSession(session, orgIdDeps(orgId));
311043
311252
  yield* clearCommandAccessTokenForSession(session.sessionId).pipe(require_Schema$1.catch_((error) => require_Schema$1.logWarning("Failed to clear expired CMMD token after auth warning.", {
311044
311253
  cause: error,
311045
311254
  sessionId: session.sessionId
@@ -312395,12 +312604,65 @@ registerProxyRoute({
312395
312604
  responseShape: "unwrapData",
312396
312605
  requestBody: "none"
312397
312606
  });
312398
- registerProxyRoute({
312607
+ function parseCmmdBootstrapRequest(request) {
312608
+ return require_Schema$1.sync(() => {
312609
+ return {
312610
+ ok: true,
312611
+ value: normalizeOrgIdQueryParam(new URL(request.url, "http://localhost").searchParams.get("orgId"))
312612
+ };
312613
+ });
312614
+ }
312615
+ /**
312616
+ * Nulls `activeWorkspace` on a bootstrap body, wherever it sits. CMMD answers
312617
+ * `GET /api/forge/context/bootstrap` either flat (`{ activeWorkspace, ... }`)
312618
+ * or enveloped (`{ data: { activeWorkspace, ... } }`) -- `workIntakeRest.ts`'s
312619
+ * `unwrapRestObject` already treats both as live shapes for this exact
312620
+ * endpoint. Nulling only the top level left `data.activeWorkspace` (CMMD's
312621
+ * guess) untouched on the enveloped shape.
312622
+ */
312623
+ function nullActiveWorkspace(body) {
312624
+ const data = body["data"];
312625
+ const nulledData = typeof data === "object" && data !== null && !Array.isArray(data) ? {
312626
+ ...data,
312627
+ activeWorkspace: null
312628
+ } : data;
312629
+ return {
312630
+ ...body,
312631
+ activeWorkspace: null,
312632
+ ...data !== void 0 ? { data: nulledData } : {}
312633
+ };
312634
+ }
312635
+ /**
312636
+ * CMMD resolves `activeWorkspace` to the caller's own default organization
312637
+ * whenever the request carries no `X-Organization-ID` header (forge#3636:
312638
+ * the owner working in org 2119 kept getting org 2142, CMMD's default, back).
312639
+ * Passing the client's explicit `?orgId=` scopes CMMD's answer to that
312640
+ * workspace; without one, CMMD still guesses, so `activeWorkspace` is nulled
312641
+ * here rather than forwarded as fact. Everything else in the bootstrap
312642
+ * response passes through untouched.
312643
+ */
312644
+ function runCmmdBootstrap(orgId, { session }) {
312645
+ return runCmmdRestProxyAsHandlerResult(session, (jwt) => cmmdGateway.request({
312646
+ method: "GET",
312647
+ jwt,
312648
+ path: "/api/forge/context/bootstrap",
312649
+ ...orgId !== void 0 ? { orgId } : {}
312650
+ }), (value) => {
312651
+ if (orgId !== void 0 || value === null || typeof value !== "object") return {
312652
+ status: 200,
312653
+ body: value
312654
+ };
312655
+ return {
312656
+ status: 200,
312657
+ body: nullActiveWorkspace(value)
312658
+ };
312659
+ });
312660
+ }
312661
+ registerProxyHandler({
312399
312662
  forgePath: "/api/cmmd/bootstrap",
312400
312663
  method: "GET",
312401
- cmmdPath: "/api/forge/context/bootstrap",
312402
- orgIdSource: "none",
312403
- responseShape: "passthrough"
312664
+ parse: parseCmmdBootstrapRequest,
312665
+ run: runCmmdBootstrap
312404
312666
  });
312405
312667
  registerProxyRoute({
312406
312668
  forgePath: "/api/cmmd/account/preferences",
@@ -313335,6 +313597,44 @@ const cmmdUpdateProjectAgentMemberAvatarRouteLayer = registerProxyRoute({
313335
313597
  responseShape: "unwrapData"
313336
313598
  });
313337
313599
  //#endregion
313600
+ //#region src/command/cmmdPushProxyRoutes.ts
313601
+ /**
313602
+ * The signed-in phone's push registration, forwarded to CMMD's push
313603
+ * device-token API on the owner's stored CMMD JWT (forge#3534).
313604
+ *
313605
+ * CMMD owns push delivery: the `device_push_tokens` table, the Expo transport,
313606
+ * and the `mobile_push_v1` delivery flag are all CMMD-side, and its
313607
+ * registration API lives at `/api/push`, not under `/api/forge/context`. The
313608
+ * mobile push delivery spec (docs/specs/2026-09-09-mobile-push-delivery.md,
313609
+ * stage 2) recommends reusing that API over adding a forge-context doorway
313610
+ * first: the Forge server already holds the phone's CMMD credential, and the
313611
+ * registry prologue here provides the same session auth, CMMD-connection
313612
+ * gate, and `CMMD_AUTH_REQUIRED` mapping every `/api/cmmd` route gets. The
313613
+ * deny-listed legacy owner control paths are untouched. If review wants a
313614
+ * forge-context doorway first, these two routes are the only call sites to
313615
+ * repoint.
313616
+ *
313617
+ * `orgIdSource` is `none` (the organization comes from the JWT, like the
313618
+ * settings and device-registry routes): a phone registers against its default
313619
+ * organization and has no workspace context to pass.
313620
+ */
313621
+ const cmmdRegisterMobilePushDeviceRouteLayer = registerProxyRoute({
313622
+ forgePath: "/api/cmmd/mobile/push/registration",
313623
+ method: "POST",
313624
+ cmmdPath: "/api/push/device-tokens",
313625
+ orgIdSource: "none",
313626
+ responseShape: "unwrapData",
313627
+ successStatus: 201
313628
+ });
313629
+ const cmmdDeleteMobilePushDeviceRouteLayer = registerProxyRoute({
313630
+ forgePath: "/api/cmmd/mobile/push/registration",
313631
+ method: "DELETE",
313632
+ cmmdPath: "/api/push/device-tokens",
313633
+ orgIdSource: "none",
313634
+ responseShape: "unwrapData",
313635
+ requestBody: "json"
313636
+ });
313637
+ //#endregion
313338
313638
  //#region src/command/runtimeProviderAuthorityUpgradeModel.ts
313339
313639
  const AUTHORITY_UPGRADE_OPERATION_KEYS = new Set([
313340
313640
  "operationId",
@@ -324954,7 +325254,7 @@ const MiscRuntimeLayerLive = require_Schema$1.mergeAll(AnalyticsServiceLayerLive
324954
325254
  const RuntimeDependenciesWithoutPullRequestsLive = ReactorLayerLive.pipe(require_Schema$1.provideMerge(CheckpointingLayerLive), require_Schema$1.provideMerge(GitLayerLive), require_Schema$1.provideMerge(OrchestrationLayerLive), require_Schema$1.provideMerge(ProviderLayerLive), require_Schema$1.provideMerge(TerminalLayerLive), require_Schema$1.provideMerge(AgentBrowserManagerLive), require_Schema$1.provideMerge(require_Schema$1.mergeAll(ThreadDevRuntimeLive, DevTunnelLive.pipe(require_Schema$1.provide$1(ThreadDevRuntimeLive)))), require_Schema$1.provideMerge(PersistenceLayerLive), require_Schema$1.provideMerge(KeybindingsLive), require_Schema$1.provideMerge(ProviderRegistryLive), require_Schema$1.provideMerge(require_Schema$1.mergeAll(ServerSettingsLive, UsageServiceLive.pipe(require_Schema$1.provide$1(ServerSettingsLive), require_Schema$1.provide$1(UsageSessionLookupLive), require_Schema$1.provide$1(UsageRecordedSourceLive.pipe(require_Schema$1.provide$1(OrchestrationLayerLive), require_Schema$1.provide$1(RuntimeReplicationOutboxLive), require_Schema$1.provide$1(ProviderSessionRuntimeRepositoryLive), require_Schema$1.provide$1(TextGenerationSessionRepositoryLive)))))), require_Schema$1.provideMerge(WorkspaceLayerLive), require_Schema$1.provideMerge(WorkspaceReadinessStateLive), require_Schema$1.provideMerge(ProjectFaviconResolverLive), require_Schema$1.provideMerge(RepositoryIdentityResolverLive), require_Schema$1.provideMerge(ServerEnvironmentLive), require_Schema$1.provideMerge(AuthLayerLive), require_Schema$1.provideMerge(ServerSecretStoreLayerLive), require_Schema$1.provideMerge(require_Schema$1.mergeAll(CloudRedisCoordinationLive, CloudStateSyncLive.pipe(require_Schema$1.provide$1(PersistenceLayerLive)))), require_Schema$1.provideMerge(MiscRuntimeLayerLive));
324955
325255
  const RuntimeDependenciesLive = pullRequestServiceFactoryLayer.pipe(require_Schema$1.provideMerge(RuntimeDependenciesWithoutPullRequestsLive), require_Schema$1.provideMerge(cmmdProjectIdentityResolverLive.pipe(require_Schema$1.provide$1(RuntimeDependenciesWithoutPullRequestsLive))), require_Schema$1.provideMerge(pullRequestProviderRuntimeLive));
324956
325256
  const RuntimeServicesLive = ServerRuntimeStartupLive.pipe(require_Schema$1.provideMerge(RuntimeDependenciesLive));
324957
- const makeRoutesLayer = require_Schema$1.mergeAll(attachmentUploadRouteLayer, authBearerBootstrapRouteLayer, authBootstrapRouteLayer, authCommandBearerBootstrapRouteLayer, authCommandBootstrapRouteLayer, authCmmdEmbedExchangeRouteLayer, authOAuthTokenExchangeRouteLayer, authWebSocketTicketRouteLayer, authCmmdOAuthDeviceRouteLayer, authCmmdOAuthTokenRouteLayer, authCmmdPasswordTokenRouteLayer, authClientsRevokeOthersRouteLayer, authClientsRevokeRouteLayer, authClientsRouteLayer, authLogoutRouteLayer, authPairingLinksRevokeRouteLayer, authPairingLinksRouteLayer, authPairingCredentialRouteLayer, authSessionRenewRouteLayer, authSessionRouteLayer, authWebSocketTokenRouteLayer, authWebLoginRouteLayer, authWebCallbackRouteLayer, cloudOwnerSessionRedeemRouteLayer, cloudOwnerSessionRevokeRouteLayer, desktopLocalWebBridgePublishRouteLayer, desktopLocalWebBridgeBootstrapRouteLayer, desktopRelayControlPlaneRouteLayer, apiDocsHtmlRouteLayer, apiDocsOpenApiRouteLayer, pulseSignalStateListRouteLayer, pulseSignalStateUpsertRouteLayer, cmmdProfileRouteLayer, ...cmmdProxyRouteLayers, cmmdWorkIntakeRouteLayer, cmmdWsTokenRouteLayer, cmmdGetProjectCommentsRouteLayer, cmmdAddProjectCommentRouteLayer, cmmdUpdateProjectRouteLayer, cmmdUploadTaskAttachmentRouteLayer, cmmdUpdateTaskCommentRouteLayer, cmmdListWorkspaceMembersRouteLayer, cmmdAddProjectMemberRouteLayer, cmmdUpdateProjectAgentMemberAvatarRouteLayer, runtimeReplicationStatusRouteLayer, cmmdRefreshProviderUsageRouteLayer, cmmdForgeCloudProjectsListRouteLayer, cmmdForgeCloudProjectsEventsRouteLayer, cmmdForgeEnvironmentsEventsRouteLayer, cmmdForgeRuntimesEnsureRouteLayer, cmmdForgeRuntimesConnectRouteLayer, ownerRuntimeCodexReadinessRouteLayer, cmmdForgeRuntimesStatusRouteLayer, cmmdForgeRuntimesStartRouteLayer, cmmdForgeRuntimesStopRouteLayer, cmmdForgeRuntimesRecycleRouteLayer, cmmdForgeRuntimesUpdateCommitRouteLayer, cmmdForgeRuntimesDestroyRouteLayer, mobileProviderCatalogRouteLayer, attachmentsRouteLayer, orchestrationDispatchRouteLayer, orchestrationCmmdWorkRunEventRouteLayer, orchestrationAutomationStartThreadRouteLayer, orchestrationAutomationRebindRouteLayer, orchestrationAutomationTerminalReconcileRouteLayer, mobileGitRunStackedActionRouteLayer, pullRequestDiffRouteLayer, orchestrationSnapshotRouteLayer, orchestrationThreadSnapshotRouteLayer, localAutomationListRouteLayer, localAutomationCreateRouteLayer, localAutomationUpdateRouteLayer, localAutomationDeleteRouteLayer, localAutomationRunRouteLayer, otlpTracesProxyRouteLayer, devTunnelProxyRouteLayer, devTunnelLocalPortProxyRouteLayer, projectFaviconRouteLayer, serverEnvironmentRouteLayer, browserWebRtcReadinessRouteLayer, staticAndDevRouteLayer, githubStatusRouteLayer, githubAuthorizeUrlRouteLayer, githubDisconnectRouteLayer, githubReposRouteLayer, githubCloneRouteLayer, githubCloneJobStartRouteLayer, githubCloneJobStatusRouteLayer, githubRefreshCredentialsRouteLayer, githubToolConfirmationApproveRouteLayer, projectLinkGetRouteLayer, projectLinkListRouteLayer, projectLinkSetRouteLayer, projectLinkDeleteRouteLayer, cmmdWorkspacesRouteLayer, cmmdCampaignsRouteLayer, cmmdProjectLinksRouteLayer, cmmdEnsureGitHubProjectContextRouteLayer, cmmdProjectRepositoryEnsureRouteLayer, cmmdProjectsGetRouteLayer, cmmdProjectsCreateRouteLayer, workspaceReadinessRouteLayer, runtimeLivenessRouteLayer, runtimeFilesystemHealthRouteLayer, claudeNativeGenerationProofRouteLayer, claudeUsageRouteLayer, codexRuntimeProviderReadinessRouteLayer, runtimeDiagnosticsRouteLayer, cloudFoldersRouteLayer, cloudProjectCreateRouteLayer, gitUrlCloneRouteLayer, websocketRpcRouteLayer).pipe(require_Schema$1.provide$1(browserApiCorsLayer));
325257
+ const makeRoutesLayer = require_Schema$1.mergeAll(attachmentUploadRouteLayer, authBearerBootstrapRouteLayer, authBootstrapRouteLayer, authCommandBearerBootstrapRouteLayer, authCommandBootstrapRouteLayer, authCmmdEmbedExchangeRouteLayer, authOAuthTokenExchangeRouteLayer, authWebSocketTicketRouteLayer, authCmmdOAuthDeviceRouteLayer, authCmmdOAuthTokenRouteLayer, authCmmdPasswordTokenRouteLayer, authClientsRevokeOthersRouteLayer, authClientsRevokeRouteLayer, authClientsRouteLayer, authLogoutRouteLayer, authPairingLinksRevokeRouteLayer, authPairingLinksRouteLayer, authPairingCredentialRouteLayer, authSessionRenewRouteLayer, authSessionRouteLayer, authWebSocketTokenRouteLayer, authWebLoginRouteLayer, authWebCallbackRouteLayer, cloudOwnerSessionRedeemRouteLayer, cloudOwnerSessionRevokeRouteLayer, desktopLocalWebBridgePublishRouteLayer, desktopLocalWebBridgeBootstrapRouteLayer, desktopRelayControlPlaneRouteLayer, apiDocsHtmlRouteLayer, apiDocsOpenApiRouteLayer, pulseSignalStateListRouteLayer, pulseSignalStateUpsertRouteLayer, cmmdProfileRouteLayer, ...cmmdProxyRouteLayers, cmmdWorkIntakeRouteLayer, cmmdWsTokenRouteLayer, cmmdGetProjectCommentsRouteLayer, cmmdAddProjectCommentRouteLayer, cmmdUpdateProjectRouteLayer, cmmdUploadTaskAttachmentRouteLayer, cmmdUpdateTaskCommentRouteLayer, cmmdListWorkspaceMembersRouteLayer, cmmdAddProjectMemberRouteLayer, cmmdUpdateProjectAgentMemberAvatarRouteLayer, runtimeReplicationStatusRouteLayer, cmmdRefreshProviderUsageRouteLayer, cmmdForgeCloudProjectsListRouteLayer, cmmdForgeCloudProjectsEventsRouteLayer, cmmdForgeEnvironmentsEventsRouteLayer, cmmdForgeRuntimesEnsureRouteLayer, cmmdForgeRuntimesConnectRouteLayer, ownerRuntimeCodexReadinessRouteLayer, cmmdRegisterMobilePushDeviceRouteLayer, cmmdDeleteMobilePushDeviceRouteLayer, cmmdForgeRuntimesStatusRouteLayer, cmmdForgeRuntimesStartRouteLayer, cmmdForgeRuntimesStopRouteLayer, cmmdForgeRuntimesRecycleRouteLayer, cmmdForgeRuntimesUpdateCommitRouteLayer, cmmdForgeRuntimesDestroyRouteLayer, mobileProviderCatalogRouteLayer, attachmentsRouteLayer, orchestrationDispatchRouteLayer, orchestrationCmmdWorkRunEventRouteLayer, orchestrationAutomationStartThreadRouteLayer, orchestrationAutomationRebindRouteLayer, orchestrationAutomationTerminalReconcileRouteLayer, mobileGitRunStackedActionRouteLayer, pullRequestDiffRouteLayer, orchestrationSnapshotRouteLayer, orchestrationThreadSnapshotRouteLayer, localAutomationListRouteLayer, localAutomationCreateRouteLayer, localAutomationUpdateRouteLayer, localAutomationDeleteRouteLayer, localAutomationRunRouteLayer, otlpTracesProxyRouteLayer, devTunnelProxyRouteLayer, devTunnelLocalPortProxyRouteLayer, projectFaviconRouteLayer, serverEnvironmentRouteLayer, browserWebRtcReadinessRouteLayer, staticAndDevRouteLayer, githubStatusRouteLayer, githubAuthorizeUrlRouteLayer, githubDisconnectRouteLayer, githubReposRouteLayer, githubCloneRouteLayer, githubCloneJobStartRouteLayer, githubCloneJobStatusRouteLayer, githubRefreshCredentialsRouteLayer, githubToolConfirmationApproveRouteLayer, projectLinkGetRouteLayer, projectLinkListRouteLayer, projectLinkSetRouteLayer, projectLinkDeleteRouteLayer, cmmdWorkspacesRouteLayer, cmmdCampaignsRouteLayer, cmmdProjectLinksRouteLayer, cmmdEnsureGitHubProjectContextRouteLayer, cmmdProjectRepositoryEnsureRouteLayer, cmmdProjectsGetRouteLayer, cmmdProjectsCreateRouteLayer, workspaceReadinessRouteLayer, runtimeLivenessRouteLayer, runtimeFilesystemHealthRouteLayer, claudeNativeGenerationProofRouteLayer, claudeUsageRouteLayer, codexRuntimeProviderReadinessRouteLayer, runtimeDiagnosticsRouteLayer, cloudFoldersRouteLayer, cloudProjectCreateRouteLayer, gitUrlCloneRouteLayer, websocketRpcRouteLayer).pipe(require_Schema$1.provide$1(browserApiCorsLayer));
324958
325258
  const makeServerLayer = require_Schema$1.unwrap(require_Schema$1.gen(function* () {
324959
325259
  const config = yield* ServerConfig;
324960
325260
  fixPath({ preferProcessPath: config.forgeCmmdRuntimeMode === true });