@cmmd-center/forge 0.13.124 → 0.13.125
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/bin.cjs +229 -26
- package/dist/bin.mjs +258 -55
- package/dist/client/assets/{DiffPanel-C1le0UaP.js → DiffPanel-BM-qanPT.js} +2 -2
- package/dist/client/assets/{DiffPanel.logic-DK4RDoiD.js → DiffPanel.logic-DS1mmUZB.js} +2 -2
- package/dist/client/assets/{DiffWorkerPoolProvider-orOnVCAZ.js → DiffWorkerPoolProvider-C2tnM5gq.js} +2 -2
- package/dist/client/assets/{PullRequestCodePanel-Br2r3lbU.js → PullRequestCodePanel-zxkVGUeq.js} +2 -2
- package/dist/client/assets/{Virtualizer-D5o0Becp.js → Virtualizer-Ds5-GzY3.js} +2 -2
- package/dist/client/assets/{WorkerPoolContext-CpM1XKxV.js → WorkerPoolContext-BWuPUnhX.js} +3 -3
- package/dist/client/assets/{index-DB8H2s1H.js → index-Rx6Z6FPS.js} +6 -6
- package/dist/client/index.html +4 -4
- package/dist/forge-build.json +1 -1
- package/dist/{open-2-reBrpv.mjs → open-yHTUkd2Y.mjs} +3 -3
- package/package.json +1 -1
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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.
|
|
66340
|
+
var version$1 = "0.13.125";
|
|
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
|
-
|
|
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
|
|
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
|
|
@@ -300407,7 +300572,7 @@ function resolveBuildCommitFromEnv(env) {
|
|
|
300407
300572
|
* environment descriptor down instead of reporting an honest "unknown".
|
|
300408
300573
|
*/
|
|
300409
300574
|
function readBakedBuildCommit() {
|
|
300410
|
-
return "
|
|
300575
|
+
return "2b217d6b85f7c98cf77e2a23bce2ef8102d3a0a5";
|
|
300411
300576
|
}
|
|
300412
300577
|
async function resolveServerBuildCommit(input) {
|
|
300413
300578
|
if (isFullCommitSha(input.baked)) return input.baked;
|
|
@@ -313335,6 +313500,44 @@ const cmmdUpdateProjectAgentMemberAvatarRouteLayer = registerProxyRoute({
|
|
|
313335
313500
|
responseShape: "unwrapData"
|
|
313336
313501
|
});
|
|
313337
313502
|
//#endregion
|
|
313503
|
+
//#region src/command/cmmdPushProxyRoutes.ts
|
|
313504
|
+
/**
|
|
313505
|
+
* The signed-in phone's push registration, forwarded to CMMD's push
|
|
313506
|
+
* device-token API on the owner's stored CMMD JWT (forge#3534).
|
|
313507
|
+
*
|
|
313508
|
+
* CMMD owns push delivery: the `device_push_tokens` table, the Expo transport,
|
|
313509
|
+
* and the `mobile_push_v1` delivery flag are all CMMD-side, and its
|
|
313510
|
+
* registration API lives at `/api/push`, not under `/api/forge/context`. The
|
|
313511
|
+
* mobile push delivery spec (docs/specs/2026-09-09-mobile-push-delivery.md,
|
|
313512
|
+
* stage 2) recommends reusing that API over adding a forge-context doorway
|
|
313513
|
+
* first: the Forge server already holds the phone's CMMD credential, and the
|
|
313514
|
+
* registry prologue here provides the same session auth, CMMD-connection
|
|
313515
|
+
* gate, and `CMMD_AUTH_REQUIRED` mapping every `/api/cmmd` route gets. The
|
|
313516
|
+
* deny-listed legacy owner control paths are untouched. If review wants a
|
|
313517
|
+
* forge-context doorway first, these two routes are the only call sites to
|
|
313518
|
+
* repoint.
|
|
313519
|
+
*
|
|
313520
|
+
* `orgIdSource` is `none` (the organization comes from the JWT, like the
|
|
313521
|
+
* settings and device-registry routes): a phone registers against its default
|
|
313522
|
+
* organization and has no workspace context to pass.
|
|
313523
|
+
*/
|
|
313524
|
+
const cmmdRegisterMobilePushDeviceRouteLayer = registerProxyRoute({
|
|
313525
|
+
forgePath: "/api/cmmd/mobile/push/registration",
|
|
313526
|
+
method: "POST",
|
|
313527
|
+
cmmdPath: "/api/push/device-tokens",
|
|
313528
|
+
orgIdSource: "none",
|
|
313529
|
+
responseShape: "unwrapData",
|
|
313530
|
+
successStatus: 201
|
|
313531
|
+
});
|
|
313532
|
+
const cmmdDeleteMobilePushDeviceRouteLayer = registerProxyRoute({
|
|
313533
|
+
forgePath: "/api/cmmd/mobile/push/registration",
|
|
313534
|
+
method: "DELETE",
|
|
313535
|
+
cmmdPath: "/api/push/device-tokens",
|
|
313536
|
+
orgIdSource: "none",
|
|
313537
|
+
responseShape: "unwrapData",
|
|
313538
|
+
requestBody: "json"
|
|
313539
|
+
});
|
|
313540
|
+
//#endregion
|
|
313338
313541
|
//#region src/command/runtimeProviderAuthorityUpgradeModel.ts
|
|
313339
313542
|
const AUTHORITY_UPGRADE_OPERATION_KEYS = new Set([
|
|
313340
313543
|
"operationId",
|
|
@@ -324954,7 +325157,7 @@ const MiscRuntimeLayerLive = require_Schema$1.mergeAll(AnalyticsServiceLayerLive
|
|
|
324954
325157
|
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
325158
|
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
325159
|
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));
|
|
325160
|
+
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
325161
|
const makeServerLayer = require_Schema$1.unwrap(require_Schema$1.gen(function* () {
|
|
324959
325162
|
const config = yield* ServerConfig;
|
|
324960
325163
|
fixPath({ preferProcessPath: config.forgeCmmdRuntimeMode === true });
|