@cmmd-center/forge 0.13.123 → 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 +347 -37
- package/dist/bin.mjs +352 -42
- package/dist/client/assets/{DiffPanel-2DWWEN5v.js → DiffPanel-BM-qanPT.js} +2 -2
- package/dist/client/assets/{DiffPanel.logic-CVgetEpI.js → DiffPanel.logic-DS1mmUZB.js} +2 -2
- package/dist/client/assets/{DiffWorkerPoolProvider-DKnxI3A2.js → DiffWorkerPoolProvider-C2tnM5gq.js} +2 -2
- package/dist/client/assets/{PullRequestCodePanel-Cy6TVn_H.js → PullRequestCodePanel-zxkVGUeq.js} +2 -2
- package/dist/client/assets/{Virtualizer-nadxEIQ2.js → Virtualizer-Ds5-GzY3.js} +2 -2
- package/dist/client/assets/{WorkerPoolContext-PB5UPru-.js → WorkerPoolContext-BWuPUnhX.js} +3 -3
- package/dist/client/assets/{index-C7UfrmO7.js → index-Rx6Z6FPS.js} +6 -6
- package/dist/client/index.html +4 -4
- package/dist/forge-build.json +1 -1
- 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",
|
|
@@ -315066,9 +315269,17 @@ const RuntimeReplicationLive = require_Schema$1.effect(RuntimeReplication, requi
|
|
|
315066
315269
|
concurrency: 1,
|
|
315067
315270
|
discard: true
|
|
315068
315271
|
});
|
|
315272
|
+
const retryStuckThreadStreams = (streamKeys) => runtimeId ? require_Schema$1.forEach(streamKeys, (streamKey) => outbox.resetStuckThreadGeneration({
|
|
315273
|
+
runtimeId,
|
|
315274
|
+
streamKey
|
|
315275
|
+
}).pipe(require_Schema$1.flatMap((reset) => reset ? enqueueThread(threadIdFromStreamKey(streamKey)) : require_Schema$1.void_)), {
|
|
315276
|
+
concurrency: 1,
|
|
315277
|
+
discard: true
|
|
315278
|
+
}) : require_Schema$1.void_;
|
|
315069
315279
|
return RuntimeReplication.of({
|
|
315070
315280
|
start,
|
|
315071
|
-
retryReprojectionPending
|
|
315281
|
+
retryReprojectionPending,
|
|
315282
|
+
retryStuckThreadStreams
|
|
315072
315283
|
});
|
|
315073
315284
|
}));
|
|
315074
315285
|
//#endregion
|
|
@@ -315415,6 +315626,31 @@ const THREAD_STREAM_PAYLOAD_HASH_MISMATCH_CODE = "THREAD_STREAM_PAYLOAD_HASH_MIS
|
|
|
315415
315626
|
*/
|
|
315416
315627
|
const THREAD_STREAM_REMOTE_CONFLICT_REPROJECTION_PENDING_CODE = "THREAD_STREAM_REMOTE_CONFLICT_REPROJECTION_PENDING";
|
|
315417
315628
|
/**
|
|
315629
|
+
* Terminal park code for a thread-events reset `refusedResetIsStuck` judges
|
|
315630
|
+
* genuinely stuck: the exact canonical reset shape was already sent once
|
|
315631
|
+
* under this generation and CMMD refused it again with zero acknowledged
|
|
315632
|
+
* progress (forge#3635). Before this code existed, that case fell through to
|
|
315633
|
+
* `failGeneric`, which recorded the raw `THREAD_STREAM_PAYLOAD_CONFLICT` /
|
|
315634
|
+
* `THREAD_STREAM_HEAD_MISMATCH` code -- a code neither
|
|
315635
|
+
* `readReprojectionPendingThreadStreamKeys` nor
|
|
315636
|
+
* `MANUALLY_RECOVERABLE_BLOCKED_CODES` selects, so the manual retry endpoint
|
|
315637
|
+
* could never reach the row.
|
|
315638
|
+
*
|
|
315639
|
+
* Deliberately excluded from `CLAIMABLE_BLOCKED_CODES`,
|
|
315640
|
+
* `STARTUP_RECOVERABLE_BLOCKED_CODES`, and `MANUALLY_RECOVERABLE_BLOCKED_CODES`:
|
|
315641
|
+
* a plain status flip back to `pending` would resend the exact batch CMMD
|
|
315642
|
+
* already refused twice. `enqueueThread` also leaves a row parked under this
|
|
315643
|
+
* code alone on new domain events and the heartbeat (see the stuck check
|
|
315644
|
+
* inside `enqueueThread` below) instead of re-projecting it back to `pending`
|
|
315645
|
+
* the way it does for every other generation, so a stuck row only clears once
|
|
315646
|
+
* the operator retry endpoint calls `RuntimeReplicationOutboxShape`'s
|
|
315647
|
+
* `resetStuckThreadGeneration` (which mints a fresh, never-before-sent repair
|
|
315648
|
+
* generation before re-projecting) or
|
|
315649
|
+
* a genuinely new generation id supersedes it (e.g. the thread archives and
|
|
315650
|
+
* gets a `:final:` id).
|
|
315651
|
+
*/
|
|
315652
|
+
const THREAD_STREAM_REMOTE_CONFLICT_STUCK_CODE = "THREAD_STREAM_REMOTE_CONFLICT_STUCK";
|
|
315653
|
+
/**
|
|
315418
315654
|
* Whether a refused batch is the exact canonical reset shape (`fromSequence
|
|
315419
315655
|
* === 1`, empty previous head, deterministic content) under a generation the
|
|
315420
315656
|
* local cursor already holds with zero acknowledged progress -- i.e. this
|
|
@@ -315692,6 +315928,23 @@ const readReprojectionPendingThreadStreamKeys = require_Schema$1.gen(function* (
|
|
|
315692
315928
|
AND last_error_code = ${THREAD_STREAM_REMOTE_CONFLICT_REPROJECTION_PENDING_CODE}
|
|
315693
315929
|
`).map((row) => row.streamKey);
|
|
315694
315930
|
}).pipe(require_Schema$1.orDie);
|
|
315931
|
+
/**
|
|
315932
|
+
* Thread streams parked under THREAD_STREAM_REMOTE_CONFLICT_STUCK_CODE, for the
|
|
315933
|
+
* manual `/api/runtime-replication/retry` endpoint (forge#3635). Deliberately absent
|
|
315934
|
+
* from MANUALLY_RECOVERABLE_BLOCKED_CODES for the same reason
|
|
315935
|
+
* `readReprojectionPendingThreadStreamKeys` is: a plain status flip back to `pending`
|
|
315936
|
+
* would resend the exact reset CMMD already refused twice. `http.ts`'s retry handler
|
|
315937
|
+
* passes these to `RuntimeReplication`'s `retryStuckThreadStreams`, which calls
|
|
315938
|
+
* `resetStuckThreadGeneration` for each of these before re-projecting, so the retry
|
|
315939
|
+
* always sends a fresh, never-before-refused generation.
|
|
315940
|
+
*/
|
|
315941
|
+
const readStuckThreadStreamKeys = require_Schema$1.gen(function* () {
|
|
315942
|
+
return (yield* (yield* require_SqlError.SqlClient)`
|
|
315943
|
+
SELECT stream_key AS "streamKey" FROM runtime_replication_outbox
|
|
315944
|
+
WHERE operation = 'thread-events' AND status = 'blocked'
|
|
315945
|
+
AND last_error_code = ${THREAD_STREAM_REMOTE_CONFLICT_STUCK_CODE}
|
|
315946
|
+
`).map((row) => row.streamKey);
|
|
315947
|
+
}).pipe(require_Schema$1.orDie);
|
|
315695
315948
|
const RuntimeReplicationOutboxLive = require_Schema$1.effect(RuntimeReplicationOutbox, require_Schema$1.gen(function* () {
|
|
315696
315949
|
const sql = yield* require_SqlError.SqlClient;
|
|
315697
315950
|
const upsertPayload = (input) => {
|
|
@@ -315912,6 +316165,12 @@ const RuntimeReplicationOutboxLive = require_Schema$1.effect(RuntimeReplicationO
|
|
|
315912
316165
|
now
|
|
315913
316166
|
}).pipe(require_Schema$1.orDie);
|
|
315914
316167
|
};
|
|
316168
|
+
const isThreadStreamParkedStuck = (input) => sql`
|
|
316169
|
+
SELECT runtime_id AS "runtimeId" FROM runtime_replication_outbox
|
|
316170
|
+
WHERE runtime_id = ${input.runtimeId} AND stream_key = ${input.streamKey}
|
|
316171
|
+
AND operation = 'thread-events' AND status = 'blocked'
|
|
316172
|
+
AND last_error_code = ${THREAD_STREAM_REMOTE_CONFLICT_STUCK_CODE}
|
|
316173
|
+
`.pipe(require_Schema$1.map((rows) => rows.length === 1));
|
|
315915
316174
|
const enqueueThread = (input) => {
|
|
315916
316175
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
315917
316176
|
const baseGenerationId = runtimeThreadGenerationId(input.runtimeId, input.streamKey);
|
|
@@ -315920,6 +316179,7 @@ const RuntimeReplicationOutboxLive = require_Schema$1.effect(RuntimeReplicationO
|
|
|
315920
316179
|
const previousActiveGeneration = activeRuntimeThreadGenerationId(baseGenerationId, previousCursor?.generationId);
|
|
315921
316180
|
const generationId = input.historyFinalized ? runtimeThreadFinalGenerationId(input.runtimeId, input.streamKey, input.events) : previousActiveGeneration;
|
|
315922
316181
|
const replacesGeneration = previousCursor !== void 0 && previousCursor.generationId !== generationId;
|
|
316182
|
+
if (!replacesGeneration && (yield* isThreadStreamParkedStuck(input))) return;
|
|
315923
316183
|
yield* initializeThreadCursor({
|
|
315924
316184
|
runtimeId: input.runtimeId,
|
|
315925
316185
|
streamKey: input.streamKey,
|
|
@@ -316221,12 +316481,16 @@ const RuntimeReplicationOutboxLive = require_Schema$1.effect(RuntimeReplicationO
|
|
|
316221
316481
|
})).pipe(require_Schema$1.orDie);
|
|
316222
316482
|
/**
|
|
316223
316483
|
* Recovers from a CMMD-reported thread stream conflict (see
|
|
316224
|
-
* THREAD_STREAM_REMOTE_CONFLICT_CODES). Returns `null` when this
|
|
316225
|
-
*
|
|
316226
|
-
* says the exact reset shape
|
|
316227
|
-
*
|
|
316228
|
-
*
|
|
316229
|
-
*
|
|
316484
|
+
* THREAD_STREAM_REMOTE_CONFLICT_CODES). Returns `null` only when this
|
|
316485
|
+
* item's payload does not decode, in which case the caller falls back to
|
|
316486
|
+
* `failGeneric`. When `refusedResetIsStuck` says the exact reset shape
|
|
316487
|
+
* already failed once with zero progress, parks the row under
|
|
316488
|
+
* `THREAD_STREAM_REMOTE_CONFLICT_STUCK_CODE` directly (see that code's
|
|
316489
|
+
* doc comment) and returns `true`/`false` the same way the normal repair
|
|
316490
|
+
* path below does -- never `null` -- so the caller never falls through to
|
|
316491
|
+
* `failGeneric`'s raw conflict code (forge#3635). `false` means the row
|
|
316492
|
+
* was superseded before this could apply, mirroring `fail`'s own
|
|
316493
|
+
* supersession signal. Otherwise replaces the local cursor with a brand-new repair generation
|
|
316230
316494
|
* from sequence 1, empty previous head -- the shape CMMD's reset rule
|
|
316231
316495
|
* accepts unconditionally -- and parks the row blocked under
|
|
316232
316496
|
* `THREAD_STREAM_REMOTE_CONFLICT_REPROJECTION_PENDING_CODE`, deferring the
|
|
@@ -316245,13 +316509,30 @@ const RuntimeReplicationOutboxLive = require_Schema$1.effect(RuntimeReplicationO
|
|
|
316245
316509
|
runtimeId: item.runtimeId,
|
|
316246
316510
|
streamKey: item.streamKey
|
|
316247
316511
|
});
|
|
316512
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
316248
316513
|
if (refusedResetIsStuck({
|
|
316249
316514
|
baseGenerationId,
|
|
316250
316515
|
refusedGenerationId: parsedBatch.generationId,
|
|
316251
316516
|
refusedFromSequence: parsedBatch.fromSequence,
|
|
316252
316517
|
cursor
|
|
316253
|
-
}))
|
|
316254
|
-
|
|
316518
|
+
})) {
|
|
316519
|
+
if ((yield* sql`
|
|
316520
|
+
UPDATE runtime_replication_outbox SET
|
|
316521
|
+
status = 'blocked', next_attempt_at = '9999-12-31T23:59:59.999Z',
|
|
316522
|
+
last_error_code = ${"THREAD_STREAM_REMOTE_CONFLICT_STUCK"},
|
|
316523
|
+
last_error = ${`Runtime replication conflict (${failure.code}) refused a thread-events reset that had already been refused once with zero acknowledged progress under this generation. Parked as stuck rather than resent; use the manual retry endpoint to reset it to a fresh generation.`}, updated_at = ${now}
|
|
316524
|
+
WHERE runtime_id = ${item.runtimeId} AND stream_key = ${item.streamKey}
|
|
316525
|
+
AND payload_hash = ${item.payloadHash}
|
|
316526
|
+
RETURNING runtime_id AS "runtimeId"
|
|
316527
|
+
`).length !== 1) return false;
|
|
316528
|
+
yield* require_Schema$1.logWarning("Runtime replication parked a thread-stream repair as stuck after its reset was refused with zero progress.", {
|
|
316529
|
+
runtimeId: item.runtimeId,
|
|
316530
|
+
streamKey: item.streamKey,
|
|
316531
|
+
errorCode: failure.code,
|
|
316532
|
+
refusedGenerationId: parsedBatch.generationId
|
|
316533
|
+
});
|
|
316534
|
+
return true;
|
|
316535
|
+
}
|
|
316255
316536
|
const repairGenerationId = `${baseGenerationId}:repair:${node_crypto.randomUUID()}`;
|
|
316256
316537
|
const message = `Runtime replication conflict (${failure.code}) refused a thread-events batch; deferring to a full-history re-projection under a new repair generation.`;
|
|
316257
316538
|
if (!(yield* sql.withTransaction(require_Schema$1.gen(function* () {
|
|
@@ -316319,6 +316600,30 @@ const RuntimeReplicationOutboxLive = require_Schema$1.effect(RuntimeReplicationO
|
|
|
316319
316600
|
AND status = 'blocked'
|
|
316320
316601
|
AND last_error_code = 'RUNTIME_REVOKED'
|
|
316321
316602
|
`.pipe(require_Schema$1.asVoid, require_Schema$1.orDie);
|
|
316603
|
+
const resetStuckThreadGeneration = (input) => {
|
|
316604
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
316605
|
+
const repairGenerationId = `${runtimeThreadGenerationId(input.runtimeId, input.streamKey)}:repair:${node_crypto.randomUUID()}`;
|
|
316606
|
+
const message = "Runtime replication operator retry reset a stuck thread-stream repair to a new generation.";
|
|
316607
|
+
return sql.withTransaction(require_Schema$1.gen(function* () {
|
|
316608
|
+
if ((yield* sql`
|
|
316609
|
+
UPDATE runtime_replication_outbox SET
|
|
316610
|
+
status = 'blocked', next_attempt_at = '9999-12-31T23:59:59.999Z',
|
|
316611
|
+
last_error_code = ${"THREAD_STREAM_REMOTE_CONFLICT_REPROJECTION_PENDING"},
|
|
316612
|
+
last_error = ${message}, updated_at = ${now}
|
|
316613
|
+
WHERE runtime_id = ${input.runtimeId} AND stream_key = ${input.streamKey}
|
|
316614
|
+
AND operation = 'thread-events'
|
|
316615
|
+
AND last_error_code = ${"THREAD_STREAM_REMOTE_CONFLICT_STUCK"}
|
|
316616
|
+
RETURNING runtime_id AS "runtimeId"
|
|
316617
|
+
`).length !== 1) return false;
|
|
316618
|
+
yield* replaceThreadCursor({
|
|
316619
|
+
runtimeId: input.runtimeId,
|
|
316620
|
+
streamKey: input.streamKey,
|
|
316621
|
+
generationId: repairGenerationId,
|
|
316622
|
+
now
|
|
316623
|
+
});
|
|
316624
|
+
return true;
|
|
316625
|
+
})).pipe(require_Schema$1.orDie);
|
|
316626
|
+
};
|
|
316322
316627
|
const getStatus = () => readRuntimeReplicationOutboxStatus.pipe(require_Schema$1.provideService(require_SqlError.SqlClient, sql));
|
|
316323
316628
|
const getDiagnostics = () => require_Schema$1.gen(function* () {
|
|
316324
316629
|
const [diagnostics] = decodeDiagnostics(yield* sql`
|
|
@@ -316349,6 +316654,7 @@ const RuntimeReplicationOutboxLive = require_Schema$1.effect(RuntimeReplicationO
|
|
|
316349
316654
|
acknowledge,
|
|
316350
316655
|
fail,
|
|
316351
316656
|
retryBlocked,
|
|
316657
|
+
resetStuckThreadGeneration,
|
|
316352
316658
|
recover,
|
|
316353
316659
|
recoverLegacyRevokedInventory,
|
|
316354
316660
|
getStatus,
|
|
@@ -316481,10 +316787,14 @@ const retryHandler = require_Schema$1.gen(function* () {
|
|
|
316481
316787
|
const replication = yield* require_Schema$1.serviceOption(RuntimeReplication);
|
|
316482
316788
|
const requeued = yield* retryBlockedRuntimeReplicationOutbox;
|
|
316483
316789
|
const reprojectionPendingStreamKeys = yield* readReprojectionPendingThreadStreamKeys;
|
|
316484
|
-
|
|
316790
|
+
const stuckStreamKeys = yield* readStuckThreadStreamKeys;
|
|
316791
|
+
if (require_Schema$1.isSome(replication)) {
|
|
316792
|
+
yield* replication.value.retryReprojectionPending(reprojectionPendingStreamKeys);
|
|
316793
|
+
yield* replication.value.retryStuckThreadStreams(stuckStreamKeys);
|
|
316794
|
+
}
|
|
316485
316795
|
const status = yield* readRuntimeReplicationOutboxStatus;
|
|
316486
316796
|
return require_HttpServer.jsonUnsafe({
|
|
316487
|
-
requeued: requeued + (require_Schema$1.isSome(replication) ? reprojectionPendingStreamKeys.length : 0),
|
|
316797
|
+
requeued: requeued + (require_Schema$1.isSome(replication) ? reprojectionPendingStreamKeys.length + stuckStreamKeys.length : 0),
|
|
316488
316798
|
...responseFromStatus(status)
|
|
316489
316799
|
});
|
|
316490
316800
|
}).pipe(require_Schema$1.catchTag("AuthError", respondToAuthError));
|
|
@@ -324847,7 +325157,7 @@ const MiscRuntimeLayerLive = require_Schema$1.mergeAll(AnalyticsServiceLayerLive
|
|
|
324847
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));
|
|
324848
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));
|
|
324849
325159
|
const RuntimeServicesLive = ServerRuntimeStartupLive.pipe(require_Schema$1.provideMerge(RuntimeDependenciesLive));
|
|
324850
|
-
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));
|
|
324851
325161
|
const makeServerLayer = require_Schema$1.unwrap(require_Schema$1.gen(function* () {
|
|
324852
325162
|
const config = yield* ServerConfig;
|
|
324853
325163
|
fixPath({ preferProcessPath: config.forgeCmmdRuntimeMode === true });
|