@cmmd-center/forge 0.13.70 → 0.13.71
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 +164 -6
- package/dist/bin.mjs +164 -6
- package/dist/client/assets/{DiffPanel-DILjIwCG.js → DiffPanel-7EXLivet.js} +2 -2
- package/dist/client/assets/{DiffPanel.logic-CtEl6NVr.js → DiffPanel.logic-QEKd4_Au.js} +2 -2
- package/dist/client/assets/{DiffPanelShell-BBd3OHCC.js → DiffPanelShell-DrmtqFA3.js} +3 -3
- package/dist/client/assets/{DiffWorkerPoolProvider-LxVtbvAD.js → DiffWorkerPoolProvider-GZjymiy_.js} +2 -2
- package/dist/client/assets/{PullRequestCodePanel-BNdxlP1v.js → PullRequestCodePanel-BcwwSZJE.js} +2 -2
- package/dist/client/assets/{Virtualizer-B5hUOqvZ.js → Virtualizer-BN2Oq2PR.js} +2 -2
- package/dist/client/assets/{index-CkwHuHiF.js → index-C2gYHhE5.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
|
@@ -66275,7 +66275,7 @@ function normalizeNumberish(value) {
|
|
|
66275
66275
|
}
|
|
66276
66276
|
//#endregion
|
|
66277
66277
|
//#region package.json
|
|
66278
|
-
var version$1 = "0.13.
|
|
66278
|
+
var version$1 = "0.13.71";
|
|
66279
66279
|
//#endregion
|
|
66280
66280
|
//#region src/sentry.ts
|
|
66281
66281
|
const SERVER_APP_NAME = "forge-server";
|
|
@@ -67235,6 +67235,7 @@ const runtimeReplicationPublicationAttemptsTotal = require_Schema$1.counter("t3_
|
|
|
67235
67235
|
const runtimeReplicationPublicationRetriesTotal = require_Schema$1.counter("t3_runtime_replication_publication_retries_total", { description: "Total runtime replication retries scheduled after recoverable failures." });
|
|
67236
67236
|
const runtimeReplicationPublicationLatency = require_Schema$1.timer("t3_runtime_replication_publication_latency", { description: "Time from durable enqueue to canonical CMMD acknowledgement." });
|
|
67237
67237
|
const cmmdCredentialRefreshTotal = require_Schema$1.counter("t3_cmmd_credential_refresh_total", { description: "CMMD JWT refresh attempts for a Forge session, by rung (self-refresh | grant-redemption) and outcome (attempted | succeeded)." });
|
|
67238
|
+
const cmmdTokenMintTotal = require_Schema$1.counter("t3_cmmd_token_mint_total", { description: "CMMD short-lived forge-token mints through the mint lane (forge#3518), by outcome (attempted | succeeded). The legacy rotation ladder keeps reporting t3_cmmd_credential_refresh_total, so the two lanes stay separable in every dashboard." });
|
|
67238
67239
|
const cmmdRotationFenceAppliedTotal = require_Schema$1.counter("t3_cmmd_rotation_fence_applied_total", { description: "Sessions Forge refused to rotate CMMD credentials for, because the rotation fence applied." });
|
|
67239
67240
|
const cmmdCredentialRowDeletedTotal = require_Schema$1.counter("t3_cmmd_credential_row_deleted_total", { description: "command_access_tokens rows actually deleted by clearUnrecoverableCmmdAuthForSession, by reason." });
|
|
67240
67241
|
const metricAttributes = (attributes) => Object.entries(compactMetricAttributes(attributes));
|
|
@@ -71265,6 +71266,63 @@ function parseCmmdOAuthRefreshResponse(value) {
|
|
|
71265
71266
|
};
|
|
71266
71267
|
}
|
|
71267
71268
|
/**
|
|
71269
|
+
* Read the one field the mint lane consumes from CMMD's mint response.
|
|
71270
|
+
* Returns null unless `access_token` is present and non-empty.
|
|
71271
|
+
*/
|
|
71272
|
+
function parseCmmdTokenMintResponse(value) {
|
|
71273
|
+
if (!value || typeof value !== "object") return null;
|
|
71274
|
+
const accessToken = value["access_token"];
|
|
71275
|
+
if (typeof accessToken !== "string" || accessToken.trim().length === 0) return null;
|
|
71276
|
+
return { accessToken: accessToken.trim() };
|
|
71277
|
+
}
|
|
71278
|
+
/**
|
|
71279
|
+
* How long the mint may take before the lane gives up on it. Minting is one
|
|
71280
|
+
* grant lookup and one signature on CMMD's side; anything slower is treated
|
|
71281
|
+
* like a failure and the resolver falls through to the legacy ladder.
|
|
71282
|
+
*/
|
|
71283
|
+
const MINT_TIMEOUT_MS = 1e4;
|
|
71284
|
+
/**
|
|
71285
|
+
* Mint a short-lived forge-scoped access token from a durable session grant
|
|
71286
|
+
* (forge#3518 expand phase).
|
|
71287
|
+
*
|
|
71288
|
+
* The grant is non-rotating and redeemable on demand, so this is the mint
|
|
71289
|
+
* lane's entire refresh story: call it again when the short-lived token
|
|
71290
|
+
* expires. CMMD answers with `access_token` only (no refresh token, no MCP
|
|
71291
|
+
* PAT, nothing durable), so a session that lives entirely on this lane never
|
|
71292
|
+
* accumulates long-lived credential state on Forge's side.
|
|
71293
|
+
*
|
|
71294
|
+
* Returns null for every failure because the resolver wants a value, not a
|
|
71295
|
+
* thrown error; the failure modes are logged by status and OAuth error code
|
|
71296
|
+
* the same way `refreshCmmdJwt` logs them. A rejection here is never fed to
|
|
71297
|
+
* the credential-rejection ledger: the ledger exists to prove a session's
|
|
71298
|
+
* *rotation* credential is dead, and the grant dying says nothing about the
|
|
71299
|
+
* refresh token the legacy fallback still owns.
|
|
71300
|
+
*/
|
|
71301
|
+
function mintCmmdShortLivedToken(grantToken) {
|
|
71302
|
+
return fetch(new URL("/api/mcp-server/oauth/token", resolveCommandAppUrl()).toString(), {
|
|
71303
|
+
method: "POST",
|
|
71304
|
+
headers: { "Content-Type": "application/json" },
|
|
71305
|
+
body: JSON.stringify({
|
|
71306
|
+
grant_type: "forge_token_mint",
|
|
71307
|
+
grant_token: grantToken,
|
|
71308
|
+
client_id: "cmmd-forge-web"
|
|
71309
|
+
}),
|
|
71310
|
+
signal: AbortSignal.timeout(MINT_TIMEOUT_MS)
|
|
71311
|
+
}).then(async (response) => {
|
|
71312
|
+
if (!response.ok) {
|
|
71313
|
+
const errorCode = await readOAuthErrorCode(response);
|
|
71314
|
+
console.warn(`[cmmd-auth] CMMD refused a forge token mint with ${response.status}${errorCode === null ? "" : ` (${errorCode})`}`);
|
|
71315
|
+
return null;
|
|
71316
|
+
}
|
|
71317
|
+
const parsed = parseCmmdTokenMintResponse(await response.json());
|
|
71318
|
+
if (!parsed) console.warn("[cmmd-auth] CMMD accepted a forge token mint but returned no usable access token");
|
|
71319
|
+
return parsed;
|
|
71320
|
+
}).catch((cause) => {
|
|
71321
|
+
console.warn(`[cmmd-auth] CMMD forge token mint could not be completed: ${String(cause)}`);
|
|
71322
|
+
return null;
|
|
71323
|
+
});
|
|
71324
|
+
}
|
|
71325
|
+
/**
|
|
71268
71326
|
* Redeem a durable CMMD session grant for a fresh credential set.
|
|
71269
71327
|
*
|
|
71270
71328
|
* The grant is CMMD's designed recovery path for exactly the case a rotating
|
|
@@ -71355,6 +71413,42 @@ async function readOAuthErrorCode(response) {
|
|
|
71355
71413
|
}
|
|
71356
71414
|
}
|
|
71357
71415
|
//#endregion
|
|
71416
|
+
//#region src/command/cmmdTokenMintLane.ts
|
|
71417
|
+
/**
|
|
71418
|
+
* The CMMD token-mint lane (forge#3518, expand phase).
|
|
71419
|
+
*
|
|
71420
|
+
* A new FIRST rung beside the legacy credential ladder: instead of rotating
|
|
71421
|
+
* CMMD's single-use refresh tokens, the resolver mints a short-lived
|
|
71422
|
+
* forge-scoped access token from the session's durable, non-rotating session
|
|
71423
|
+
* grant (`grant_type: forge_token_mint` on CMMD's token endpoint). The mint
|
|
71424
|
+
* spends nothing that cannot be spent again, so it cannot race itself into
|
|
71425
|
+
* `invalid_grant` the way a rotation can.
|
|
71426
|
+
*
|
|
71427
|
+
* Behind a config flag that is OFF unless explicitly enabled: production
|
|
71428
|
+
* keeps the legacy ladder as the only lane until this is switched on
|
|
71429
|
+
* deliberately. When enabled, the lane is tried before the legacy ladder;
|
|
71430
|
+
* every legacy rung (rotation fence, self-refresh, grant redemption,
|
|
71431
|
+
* bootstrap promotion, subject fallback) still exists below it unchanged and
|
|
71432
|
+
* still guards itself with the rejection ledger.
|
|
71433
|
+
*/
|
|
71434
|
+
/** Env var that enables the mint lane. Unset and anything but an affirmative value is off. */
|
|
71435
|
+
const CMMD_TOKEN_MINT_LANE_ENV = "FORGE_CMMD_TOKEN_MINT_LANE";
|
|
71436
|
+
const AFFIRMATIVE_VALUES = new Set([
|
|
71437
|
+
"1",
|
|
71438
|
+
"on",
|
|
71439
|
+
"true",
|
|
71440
|
+
"yes"
|
|
71441
|
+
]);
|
|
71442
|
+
/**
|
|
71443
|
+
* Whether the mint lane may run. Default off everywhere: a missing env var is
|
|
71444
|
+
* the production posture, and an opt-in flag must not half-default to on in
|
|
71445
|
+
* any environment a misconfigured deploy might land in.
|
|
71446
|
+
*/
|
|
71447
|
+
function isCmmdTokenMintLaneEnabled(env = process.env) {
|
|
71448
|
+
const raw = env[CMMD_TOKEN_MINT_LANE_ENV]?.trim().toLowerCase();
|
|
71449
|
+
return raw !== void 0 && AFFIRMATIVE_VALUES.has(raw);
|
|
71450
|
+
}
|
|
71451
|
+
//#endregion
|
|
71358
71452
|
//#region src/command/cmmdRotationFence.ts
|
|
71359
71453
|
/**
|
|
71360
71454
|
* Log and count a fence application, without changing the verdict it reports.
|
|
@@ -71443,12 +71537,13 @@ function usableCmmdUserJwt(token) {
|
|
|
71443
71537
|
function storeResolvedJwt(sessionId, accessToken, refreshToken) {
|
|
71444
71538
|
return setCommandJwtForSession(sessionId, accessToken, refreshToken ? { refreshToken } : {}).pipe(require_Schema$1.ignore({ log: true }));
|
|
71445
71539
|
}
|
|
71446
|
-
function logCmmdJwtRung(context, rung, outcome, reason) {
|
|
71540
|
+
function logCmmdJwtRung(context, rung, outcome, reason, lane = "legacy") {
|
|
71447
71541
|
return (outcome === "failed" ? require_Schema$1.logWarning : require_Schema$1.logInfo)("CMMD JWT resolver rung.", {
|
|
71448
71542
|
sessionId: context.sessionId,
|
|
71449
71543
|
subject: context.subject,
|
|
71450
71544
|
deviceType: context.deviceType,
|
|
71451
71545
|
caller: context.caller,
|
|
71546
|
+
lane,
|
|
71452
71547
|
rung,
|
|
71453
71548
|
outcome,
|
|
71454
71549
|
reason
|
|
@@ -71461,6 +71556,25 @@ function recordCmmdCredentialRefreshMetric(rung, outcome) {
|
|
|
71461
71556
|
})), 1);
|
|
71462
71557
|
}
|
|
71463
71558
|
const inFlightRefreshes = /* @__PURE__ */ new Map();
|
|
71559
|
+
/** In-flight mints, keyed by session, same shape as `inFlightRefreshes`. */
|
|
71560
|
+
const inFlightMints = /* @__PURE__ */ new Map();
|
|
71561
|
+
/**
|
|
71562
|
+
* Collapse concurrent mints for one session into a single upstream call. A
|
|
71563
|
+
* mint cannot race itself into `invalid_grant` (the grant is non-rotating),
|
|
71564
|
+
* so this is call-rate hygiene, not correctness: two tabs observing the same
|
|
71565
|
+
* expiry must not both hit CMMD when one mint would do.
|
|
71566
|
+
*/
|
|
71567
|
+
function mintOnceForSession(sessionId, grantToken) {
|
|
71568
|
+
return require_Schema$1.promise(() => {
|
|
71569
|
+
const existing = inFlightMints.get(sessionId);
|
|
71570
|
+
if (existing) return existing;
|
|
71571
|
+
const pending = mintCmmdShortLivedToken(grantToken).catch(() => null).finally(() => {
|
|
71572
|
+
inFlightMints.delete(sessionId);
|
|
71573
|
+
});
|
|
71574
|
+
inFlightMints.set(sessionId, pending);
|
|
71575
|
+
return pending;
|
|
71576
|
+
});
|
|
71577
|
+
}
|
|
71464
71578
|
function refreshOnceForSession(sessionId, refreshToken) {
|
|
71465
71579
|
return require_Schema$1.promise(() => {
|
|
71466
71580
|
const existing = inFlightRefreshes.get(sessionId);
|
|
@@ -71500,6 +71614,39 @@ function trySelfRefresh(sessionId, context) {
|
|
|
71500
71614
|
});
|
|
71501
71615
|
}
|
|
71502
71616
|
/**
|
|
71617
|
+
* The mint lane's rung (forge#3518, expand phase): mint a short-lived
|
|
71618
|
+
* forge-scoped access token from this session's durable session grant.
|
|
71619
|
+
*
|
|
71620
|
+
* Tried FIRST, before the rotation fence and the whole legacy ladder, when
|
|
71621
|
+
* `FORGE_CMMD_TOKEN_MINT_LANE` is on. Minting spends nothing that cannot be
|
|
71622
|
+
* spent again (the grant is non-rotating), so unlike the legacy rungs it is
|
|
71623
|
+
* safe for a session the rotation fence fences off, and it never feeds the
|
|
71624
|
+
* credential-rejection ledger: this rung failing says nothing about whether
|
|
71625
|
+
* the legacy rungs below it can still succeed.
|
|
71626
|
+
*
|
|
71627
|
+
* Only the JWT is persisted (`setCommandJwtForSession`, no refresh token):
|
|
71628
|
+
* the short lifetime is the expiry story, and re-minting is the refresh
|
|
71629
|
+
* story. A session that lives entirely on this lane accumulates no new
|
|
71630
|
+
* long-lived credential state beyond the grant it already had.
|
|
71631
|
+
*/
|
|
71632
|
+
function tryMintShortLivedToken(sessionId, context) {
|
|
71633
|
+
const grantToken = readCommandSessionGrantForSession(sessionId);
|
|
71634
|
+
if (!grantToken) return logCmmdJwtRung(context, "mint", "skipped", "no session grant stored for this session", "mint").pipe(require_Schema$1.as(null));
|
|
71635
|
+
return require_Schema$1.gen(function* () {
|
|
71636
|
+
yield* logCmmdJwtRung(context, "mint", "attempted", "minting a short-lived forge-scoped token from this session's grant", "mint");
|
|
71637
|
+
yield* require_Schema$1.update(require_Schema$1.withAttributes(cmmdTokenMintTotal, metricAttributes({ outcome: "attempted" })), 1);
|
|
71638
|
+
const accessToken = usableCmmdUserJwt((yield* mintOnceForSession(sessionId, grantToken))?.accessToken);
|
|
71639
|
+
if (!accessToken) {
|
|
71640
|
+
yield* logCmmdJwtRung(context, "mint", "failed", "CMMD token mint did not return a usable user access JWT", "mint");
|
|
71641
|
+
return null;
|
|
71642
|
+
}
|
|
71643
|
+
yield* setCommandJwtForSession(sessionId, accessToken, {}).pipe(require_Schema$1.ignore({ log: true }));
|
|
71644
|
+
yield* require_Schema$1.update(require_Schema$1.withAttributes(cmmdTokenMintTotal, metricAttributes({ outcome: "succeeded" })), 1);
|
|
71645
|
+
yield* logCmmdJwtRung(context, "mint", "succeeded", "CMMD minted a short-lived forge-scoped token", "mint");
|
|
71646
|
+
return accessToken;
|
|
71647
|
+
});
|
|
71648
|
+
}
|
|
71649
|
+
/**
|
|
71503
71650
|
* Last-resort recovery: redeem this session's durable CMMD session grant.
|
|
71504
71651
|
*
|
|
71505
71652
|
* A refresh token is single-use, so a session that lost a rotation — raced, or
|
|
@@ -71619,8 +71766,10 @@ function runRecoveryLadder(sessionId, normalizedSubject, forceRefresh, context)
|
|
|
71619
71766
|
* Resolve the CMMD REST JWT for a Forge session.
|
|
71620
71767
|
*
|
|
71621
71768
|
* Forge session auth is long-lived compared with the short-lived CMMD JWT.
|
|
71622
|
-
* When the session JWT expires, recover by
|
|
71623
|
-
*
|
|
71769
|
+
* When the session JWT expires, recover by minting a fresh short-lived token
|
|
71770
|
+
* from the session grant (the mint lane; default-off, forge#3518), or, on
|
|
71771
|
+
* the legacy lane, by promoting a valid subject-level JWT or rotating the
|
|
71772
|
+
* stored CMMD refresh token.
|
|
71624
71773
|
*
|
|
71625
71774
|
* Fail-closed: never returns a non-user / malformed Bearer. Callers must map
|
|
71626
71775
|
* null to CMMD_AUTH_REQUIRED and must not call CMMD upstream with a bad token.
|
|
@@ -71640,7 +71789,8 @@ function resolveCmmdJwtForSession({ sessionId, subject, logPrefix = "cmmd-auth",
|
|
|
71640
71789
|
})));
|
|
71641
71790
|
}
|
|
71642
71791
|
const normalizedSubject = subject?.trim() || null;
|
|
71643
|
-
|
|
71792
|
+
const mintLaneEnabled = isCmmdTokenMintLaneEnabled();
|
|
71793
|
+
if (!normalizedSubject && !forceRefresh && !mintLaneEnabled) return require_Schema$1.succeed(null);
|
|
71644
71794
|
return require_Schema$1.gen(function* () {
|
|
71645
71795
|
const context = {
|
|
71646
71796
|
sessionId,
|
|
@@ -71648,6 +71798,14 @@ function resolveCmmdJwtForSession({ sessionId, subject, logPrefix = "cmmd-auth",
|
|
|
71648
71798
|
deviceType: yield* readClientDeviceTypeForSession(sessionId),
|
|
71649
71799
|
caller: logPrefix
|
|
71650
71800
|
};
|
|
71801
|
+
if (mintLaneEnabled) {
|
|
71802
|
+
const minted = yield* tryMintShortLivedToken(sessionId, context);
|
|
71803
|
+
if (minted) return minted;
|
|
71804
|
+
if (!normalizedSubject && !forceRefresh) {
|
|
71805
|
+
yield* logCmmdJwtRung(context, "mint", "failed", "no subject to resolve and the mint lane produced nothing", "mint");
|
|
71806
|
+
return null;
|
|
71807
|
+
}
|
|
71808
|
+
}
|
|
71651
71809
|
if (yield* isRotationFencedSession(sessionId)) {
|
|
71652
71810
|
yield* logCmmdJwtRung(context, "rotation-fence", "failed", "session is fenced from rotating CMMD credentials");
|
|
71653
71811
|
return null;
|
|
@@ -298491,7 +298649,7 @@ function resolveBuildCommitFromEnv(env) {
|
|
|
298491
298649
|
* environment descriptor down instead of reporting an honest "unknown".
|
|
298492
298650
|
*/
|
|
298493
298651
|
function readBakedBuildCommit() {
|
|
298494
|
-
return "
|
|
298652
|
+
return "8e9fcb0318612dd80655f7c9662dd9128b8f005c";
|
|
298495
298653
|
}
|
|
298496
298654
|
async function resolveServerBuildCommit(input) {
|
|
298497
298655
|
if (isFullCommitSha(input.baked)) return input.baked;
|
package/dist/bin.mjs
CHANGED
|
@@ -65979,7 +65979,7 @@ function normalizeNumberish(value) {
|
|
|
65979
65979
|
}
|
|
65980
65980
|
//#endregion
|
|
65981
65981
|
//#region package.json
|
|
65982
|
-
var version$1 = "0.13.
|
|
65982
|
+
var version$1 = "0.13.71";
|
|
65983
65983
|
//#endregion
|
|
65984
65984
|
//#region src/sentry.ts
|
|
65985
65985
|
const SERVER_APP_NAME = "forge-server";
|
|
@@ -66939,6 +66939,7 @@ const runtimeReplicationPublicationAttemptsTotal = counter("t3_runtime_replicati
|
|
|
66939
66939
|
const runtimeReplicationPublicationRetriesTotal = counter("t3_runtime_replication_publication_retries_total", { description: "Total runtime replication retries scheduled after recoverable failures." });
|
|
66940
66940
|
const runtimeReplicationPublicationLatency = timer("t3_runtime_replication_publication_latency", { description: "Time from durable enqueue to canonical CMMD acknowledgement." });
|
|
66941
66941
|
const cmmdCredentialRefreshTotal = counter("t3_cmmd_credential_refresh_total", { description: "CMMD JWT refresh attempts for a Forge session, by rung (self-refresh | grant-redemption) and outcome (attempted | succeeded)." });
|
|
66942
|
+
const cmmdTokenMintTotal = counter("t3_cmmd_token_mint_total", { description: "CMMD short-lived forge-token mints through the mint lane (forge#3518), by outcome (attempted | succeeded). The legacy rotation ladder keeps reporting t3_cmmd_credential_refresh_total, so the two lanes stay separable in every dashboard." });
|
|
66942
66943
|
const cmmdRotationFenceAppliedTotal = counter("t3_cmmd_rotation_fence_applied_total", { description: "Sessions Forge refused to rotate CMMD credentials for, because the rotation fence applied." });
|
|
66943
66944
|
const cmmdCredentialRowDeletedTotal = counter("t3_cmmd_credential_row_deleted_total", { description: "command_access_tokens rows actually deleted by clearUnrecoverableCmmdAuthForSession, by reason." });
|
|
66944
66945
|
const metricAttributes = (attributes) => Object.entries(compactMetricAttributes(attributes));
|
|
@@ -70944,6 +70945,63 @@ function parseCmmdOAuthRefreshResponse(value) {
|
|
|
70944
70945
|
};
|
|
70945
70946
|
}
|
|
70946
70947
|
/**
|
|
70948
|
+
* Read the one field the mint lane consumes from CMMD's mint response.
|
|
70949
|
+
* Returns null unless `access_token` is present and non-empty.
|
|
70950
|
+
*/
|
|
70951
|
+
function parseCmmdTokenMintResponse(value) {
|
|
70952
|
+
if (!value || typeof value !== "object") return null;
|
|
70953
|
+
const accessToken = value["access_token"];
|
|
70954
|
+
if (typeof accessToken !== "string" || accessToken.trim().length === 0) return null;
|
|
70955
|
+
return { accessToken: accessToken.trim() };
|
|
70956
|
+
}
|
|
70957
|
+
/**
|
|
70958
|
+
* How long the mint may take before the lane gives up on it. Minting is one
|
|
70959
|
+
* grant lookup and one signature on CMMD's side; anything slower is treated
|
|
70960
|
+
* like a failure and the resolver falls through to the legacy ladder.
|
|
70961
|
+
*/
|
|
70962
|
+
const MINT_TIMEOUT_MS = 1e4;
|
|
70963
|
+
/**
|
|
70964
|
+
* Mint a short-lived forge-scoped access token from a durable session grant
|
|
70965
|
+
* (forge#3518 expand phase).
|
|
70966
|
+
*
|
|
70967
|
+
* The grant is non-rotating and redeemable on demand, so this is the mint
|
|
70968
|
+
* lane's entire refresh story: call it again when the short-lived token
|
|
70969
|
+
* expires. CMMD answers with `access_token` only (no refresh token, no MCP
|
|
70970
|
+
* PAT, nothing durable), so a session that lives entirely on this lane never
|
|
70971
|
+
* accumulates long-lived credential state on Forge's side.
|
|
70972
|
+
*
|
|
70973
|
+
* Returns null for every failure because the resolver wants a value, not a
|
|
70974
|
+
* thrown error; the failure modes are logged by status and OAuth error code
|
|
70975
|
+
* the same way `refreshCmmdJwt` logs them. A rejection here is never fed to
|
|
70976
|
+
* the credential-rejection ledger: the ledger exists to prove a session's
|
|
70977
|
+
* *rotation* credential is dead, and the grant dying says nothing about the
|
|
70978
|
+
* refresh token the legacy fallback still owns.
|
|
70979
|
+
*/
|
|
70980
|
+
function mintCmmdShortLivedToken(grantToken) {
|
|
70981
|
+
return fetch(new URL("/api/mcp-server/oauth/token", resolveCommandAppUrl()).toString(), {
|
|
70982
|
+
method: "POST",
|
|
70983
|
+
headers: { "Content-Type": "application/json" },
|
|
70984
|
+
body: JSON.stringify({
|
|
70985
|
+
grant_type: "forge_token_mint",
|
|
70986
|
+
grant_token: grantToken,
|
|
70987
|
+
client_id: "cmmd-forge-web"
|
|
70988
|
+
}),
|
|
70989
|
+
signal: AbortSignal.timeout(MINT_TIMEOUT_MS)
|
|
70990
|
+
}).then(async (response) => {
|
|
70991
|
+
if (!response.ok) {
|
|
70992
|
+
const errorCode = await readOAuthErrorCode(response);
|
|
70993
|
+
console.warn(`[cmmd-auth] CMMD refused a forge token mint with ${response.status}${errorCode === null ? "" : ` (${errorCode})`}`);
|
|
70994
|
+
return null;
|
|
70995
|
+
}
|
|
70996
|
+
const parsed = parseCmmdTokenMintResponse(await response.json());
|
|
70997
|
+
if (!parsed) console.warn("[cmmd-auth] CMMD accepted a forge token mint but returned no usable access token");
|
|
70998
|
+
return parsed;
|
|
70999
|
+
}).catch((cause) => {
|
|
71000
|
+
console.warn(`[cmmd-auth] CMMD forge token mint could not be completed: ${String(cause)}`);
|
|
71001
|
+
return null;
|
|
71002
|
+
});
|
|
71003
|
+
}
|
|
71004
|
+
/**
|
|
70947
71005
|
* Redeem a durable CMMD session grant for a fresh credential set.
|
|
70948
71006
|
*
|
|
70949
71007
|
* The grant is CMMD's designed recovery path for exactly the case a rotating
|
|
@@ -71034,6 +71092,42 @@ async function readOAuthErrorCode(response) {
|
|
|
71034
71092
|
}
|
|
71035
71093
|
}
|
|
71036
71094
|
//#endregion
|
|
71095
|
+
//#region src/command/cmmdTokenMintLane.ts
|
|
71096
|
+
/**
|
|
71097
|
+
* The CMMD token-mint lane (forge#3518, expand phase).
|
|
71098
|
+
*
|
|
71099
|
+
* A new FIRST rung beside the legacy credential ladder: instead of rotating
|
|
71100
|
+
* CMMD's single-use refresh tokens, the resolver mints a short-lived
|
|
71101
|
+
* forge-scoped access token from the session's durable, non-rotating session
|
|
71102
|
+
* grant (`grant_type: forge_token_mint` on CMMD's token endpoint). The mint
|
|
71103
|
+
* spends nothing that cannot be spent again, so it cannot race itself into
|
|
71104
|
+
* `invalid_grant` the way a rotation can.
|
|
71105
|
+
*
|
|
71106
|
+
* Behind a config flag that is OFF unless explicitly enabled: production
|
|
71107
|
+
* keeps the legacy ladder as the only lane until this is switched on
|
|
71108
|
+
* deliberately. When enabled, the lane is tried before the legacy ladder;
|
|
71109
|
+
* every legacy rung (rotation fence, self-refresh, grant redemption,
|
|
71110
|
+
* bootstrap promotion, subject fallback) still exists below it unchanged and
|
|
71111
|
+
* still guards itself with the rejection ledger.
|
|
71112
|
+
*/
|
|
71113
|
+
/** Env var that enables the mint lane. Unset and anything but an affirmative value is off. */
|
|
71114
|
+
const CMMD_TOKEN_MINT_LANE_ENV = "FORGE_CMMD_TOKEN_MINT_LANE";
|
|
71115
|
+
const AFFIRMATIVE_VALUES = new Set([
|
|
71116
|
+
"1",
|
|
71117
|
+
"on",
|
|
71118
|
+
"true",
|
|
71119
|
+
"yes"
|
|
71120
|
+
]);
|
|
71121
|
+
/**
|
|
71122
|
+
* Whether the mint lane may run. Default off everywhere: a missing env var is
|
|
71123
|
+
* the production posture, and an opt-in flag must not half-default to on in
|
|
71124
|
+
* any environment a misconfigured deploy might land in.
|
|
71125
|
+
*/
|
|
71126
|
+
function isCmmdTokenMintLaneEnabled(env = process.env) {
|
|
71127
|
+
const raw = env[CMMD_TOKEN_MINT_LANE_ENV]?.trim().toLowerCase();
|
|
71128
|
+
return raw !== void 0 && AFFIRMATIVE_VALUES.has(raw);
|
|
71129
|
+
}
|
|
71130
|
+
//#endregion
|
|
71037
71131
|
//#region src/command/cmmdRotationFence.ts
|
|
71038
71132
|
/**
|
|
71039
71133
|
* Log and count a fence application, without changing the verdict it reports.
|
|
@@ -71122,12 +71216,13 @@ function usableCmmdUserJwt(token) {
|
|
|
71122
71216
|
function storeResolvedJwt(sessionId, accessToken, refreshToken) {
|
|
71123
71217
|
return setCommandJwtForSession(sessionId, accessToken, refreshToken ? { refreshToken } : {}).pipe(ignore({ log: true }));
|
|
71124
71218
|
}
|
|
71125
|
-
function logCmmdJwtRung(context, rung, outcome, reason) {
|
|
71219
|
+
function logCmmdJwtRung(context, rung, outcome, reason, lane = "legacy") {
|
|
71126
71220
|
return (outcome === "failed" ? logWarning$1 : logInfo)("CMMD JWT resolver rung.", {
|
|
71127
71221
|
sessionId: context.sessionId,
|
|
71128
71222
|
subject: context.subject,
|
|
71129
71223
|
deviceType: context.deviceType,
|
|
71130
71224
|
caller: context.caller,
|
|
71225
|
+
lane,
|
|
71131
71226
|
rung,
|
|
71132
71227
|
outcome,
|
|
71133
71228
|
reason
|
|
@@ -71140,6 +71235,25 @@ function recordCmmdCredentialRefreshMetric(rung, outcome) {
|
|
|
71140
71235
|
})), 1);
|
|
71141
71236
|
}
|
|
71142
71237
|
const inFlightRefreshes = /* @__PURE__ */ new Map();
|
|
71238
|
+
/** In-flight mints, keyed by session, same shape as `inFlightRefreshes`. */
|
|
71239
|
+
const inFlightMints = /* @__PURE__ */ new Map();
|
|
71240
|
+
/**
|
|
71241
|
+
* Collapse concurrent mints for one session into a single upstream call. A
|
|
71242
|
+
* mint cannot race itself into `invalid_grant` (the grant is non-rotating),
|
|
71243
|
+
* so this is call-rate hygiene, not correctness: two tabs observing the same
|
|
71244
|
+
* expiry must not both hit CMMD when one mint would do.
|
|
71245
|
+
*/
|
|
71246
|
+
function mintOnceForSession(sessionId, grantToken) {
|
|
71247
|
+
return promise(() => {
|
|
71248
|
+
const existing = inFlightMints.get(sessionId);
|
|
71249
|
+
if (existing) return existing;
|
|
71250
|
+
const pending = mintCmmdShortLivedToken(grantToken).catch(() => null).finally(() => {
|
|
71251
|
+
inFlightMints.delete(sessionId);
|
|
71252
|
+
});
|
|
71253
|
+
inFlightMints.set(sessionId, pending);
|
|
71254
|
+
return pending;
|
|
71255
|
+
});
|
|
71256
|
+
}
|
|
71143
71257
|
function refreshOnceForSession(sessionId, refreshToken) {
|
|
71144
71258
|
return promise(() => {
|
|
71145
71259
|
const existing = inFlightRefreshes.get(sessionId);
|
|
@@ -71179,6 +71293,39 @@ function trySelfRefresh(sessionId, context) {
|
|
|
71179
71293
|
});
|
|
71180
71294
|
}
|
|
71181
71295
|
/**
|
|
71296
|
+
* The mint lane's rung (forge#3518, expand phase): mint a short-lived
|
|
71297
|
+
* forge-scoped access token from this session's durable session grant.
|
|
71298
|
+
*
|
|
71299
|
+
* Tried FIRST, before the rotation fence and the whole legacy ladder, when
|
|
71300
|
+
* `FORGE_CMMD_TOKEN_MINT_LANE` is on. Minting spends nothing that cannot be
|
|
71301
|
+
* spent again (the grant is non-rotating), so unlike the legacy rungs it is
|
|
71302
|
+
* safe for a session the rotation fence fences off, and it never feeds the
|
|
71303
|
+
* credential-rejection ledger: this rung failing says nothing about whether
|
|
71304
|
+
* the legacy rungs below it can still succeed.
|
|
71305
|
+
*
|
|
71306
|
+
* Only the JWT is persisted (`setCommandJwtForSession`, no refresh token):
|
|
71307
|
+
* the short lifetime is the expiry story, and re-minting is the refresh
|
|
71308
|
+
* story. A session that lives entirely on this lane accumulates no new
|
|
71309
|
+
* long-lived credential state beyond the grant it already had.
|
|
71310
|
+
*/
|
|
71311
|
+
function tryMintShortLivedToken(sessionId, context) {
|
|
71312
|
+
const grantToken = readCommandSessionGrantForSession(sessionId);
|
|
71313
|
+
if (!grantToken) return logCmmdJwtRung(context, "mint", "skipped", "no session grant stored for this session", "mint").pipe(as$3(null));
|
|
71314
|
+
return gen(function* () {
|
|
71315
|
+
yield* logCmmdJwtRung(context, "mint", "attempted", "minting a short-lived forge-scoped token from this session's grant", "mint");
|
|
71316
|
+
yield* update$3(withAttributes(cmmdTokenMintTotal, metricAttributes({ outcome: "attempted" })), 1);
|
|
71317
|
+
const accessToken = usableCmmdUserJwt((yield* mintOnceForSession(sessionId, grantToken))?.accessToken);
|
|
71318
|
+
if (!accessToken) {
|
|
71319
|
+
yield* logCmmdJwtRung(context, "mint", "failed", "CMMD token mint did not return a usable user access JWT", "mint");
|
|
71320
|
+
return null;
|
|
71321
|
+
}
|
|
71322
|
+
yield* setCommandJwtForSession(sessionId, accessToken, {}).pipe(ignore({ log: true }));
|
|
71323
|
+
yield* update$3(withAttributes(cmmdTokenMintTotal, metricAttributes({ outcome: "succeeded" })), 1);
|
|
71324
|
+
yield* logCmmdJwtRung(context, "mint", "succeeded", "CMMD minted a short-lived forge-scoped token", "mint");
|
|
71325
|
+
return accessToken;
|
|
71326
|
+
});
|
|
71327
|
+
}
|
|
71328
|
+
/**
|
|
71182
71329
|
* Last-resort recovery: redeem this session's durable CMMD session grant.
|
|
71183
71330
|
*
|
|
71184
71331
|
* A refresh token is single-use, so a session that lost a rotation — raced, or
|
|
@@ -71298,8 +71445,10 @@ function runRecoveryLadder(sessionId, normalizedSubject, forceRefresh, context)
|
|
|
71298
71445
|
* Resolve the CMMD REST JWT for a Forge session.
|
|
71299
71446
|
*
|
|
71300
71447
|
* Forge session auth is long-lived compared with the short-lived CMMD JWT.
|
|
71301
|
-
* When the session JWT expires, recover by
|
|
71302
|
-
*
|
|
71448
|
+
* When the session JWT expires, recover by minting a fresh short-lived token
|
|
71449
|
+
* from the session grant (the mint lane; default-off, forge#3518), or, on
|
|
71450
|
+
* the legacy lane, by promoting a valid subject-level JWT or rotating the
|
|
71451
|
+
* stored CMMD refresh token.
|
|
71303
71452
|
*
|
|
71304
71453
|
* Fail-closed: never returns a non-user / malformed Bearer. Callers must map
|
|
71305
71454
|
* null to CMMD_AUTH_REQUIRED and must not call CMMD upstream with a bad token.
|
|
@@ -71319,7 +71468,8 @@ function resolveCmmdJwtForSession({ sessionId, subject, logPrefix = "cmmd-auth",
|
|
|
71319
71468
|
})));
|
|
71320
71469
|
}
|
|
71321
71470
|
const normalizedSubject = subject?.trim() || null;
|
|
71322
|
-
|
|
71471
|
+
const mintLaneEnabled = isCmmdTokenMintLaneEnabled();
|
|
71472
|
+
if (!normalizedSubject && !forceRefresh && !mintLaneEnabled) return succeed(null);
|
|
71323
71473
|
return gen(function* () {
|
|
71324
71474
|
const context = {
|
|
71325
71475
|
sessionId,
|
|
@@ -71327,6 +71477,14 @@ function resolveCmmdJwtForSession({ sessionId, subject, logPrefix = "cmmd-auth",
|
|
|
71327
71477
|
deviceType: yield* readClientDeviceTypeForSession(sessionId),
|
|
71328
71478
|
caller: logPrefix
|
|
71329
71479
|
};
|
|
71480
|
+
if (mintLaneEnabled) {
|
|
71481
|
+
const minted = yield* tryMintShortLivedToken(sessionId, context);
|
|
71482
|
+
if (minted) return minted;
|
|
71483
|
+
if (!normalizedSubject && !forceRefresh) {
|
|
71484
|
+
yield* logCmmdJwtRung(context, "mint", "failed", "no subject to resolve and the mint lane produced nothing", "mint");
|
|
71485
|
+
return null;
|
|
71486
|
+
}
|
|
71487
|
+
}
|
|
71330
71488
|
if (yield* isRotationFencedSession(sessionId)) {
|
|
71331
71489
|
yield* logCmmdJwtRung(context, "rotation-fence", "failed", "session is fenced from rotating CMMD credentials");
|
|
71332
71490
|
return null;
|
|
@@ -297936,7 +298094,7 @@ function resolveBuildCommitFromEnv(env) {
|
|
|
297936
298094
|
* environment descriptor down instead of reporting an honest "unknown".
|
|
297937
298095
|
*/
|
|
297938
298096
|
function readBakedBuildCommit() {
|
|
297939
|
-
return "
|
|
298097
|
+
return "8e9fcb0318612dd80655f7c9662dd9128b8f005c";
|
|
297940
298098
|
}
|
|
297941
298099
|
async function resolveServerBuildCommit(input) {
|
|
297942
298100
|
if (isFullCommitSha(input.baked)) return input.baked;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{r as e}from"./rolldown-runtime-hePW80VL.js";import{Ha as t,Mi as n,Ua as r,Wa as i}from"./Stream-mUTFnSY9.js";import{Al as a,Fn as o,Gc as s,Hr as c,Jc as l,Qr as u,Rr as d,Sr as f,Tn as p,Uc as m,Xr as h,_r as g,d as _,fn as v,fr as y,gr as b,i as x,is as S,jn as C,l as w,m as T,ms as E,n as D,qc as O,qt as k,r as A,u as j,za as M,zr as N}from"./DiffPanelShell-
|
|
1
|
+
import{r as e}from"./rolldown-runtime-hePW80VL.js";import{Ha as t,Mi as n,Ua as r,Wa as i}from"./Stream-mUTFnSY9.js";import{Al as a,Fn as o,Gc as s,Hr as c,Jc as l,Qr as u,Rr as d,Sr as f,Tn as p,Uc as m,Xr as h,_r as g,d as _,fn as v,fr as y,gr as b,i as x,is as S,jn as C,l as w,m as T,ms as E,n as D,qc as O,qt as k,r as A,u as j,za as M,zr as N}from"./DiffPanelShell-DrmtqFA3.js";import{hn as P}from"./CompositeItem-BsSeqHOM.js";import{a as F,c as I,f as L,i as R,l as z,m as B,n as V,o as H,p as U,r as W,s as G,t as K}from"./Virtualizer-BN2Oq2PR.js";import{Dn as q,at as J,it as ee}from"./src-IM0FnQax.js";import{a as te,c as ne,d as re,h as ie,i as ae,l as oe,m as se,n as ce,o as Y,p as le,r as ue,s as de,t as fe,u as pe}from"./DiffPanel.logic-QEKd4_Au.js";var X=e(i(),1),me=typeof window>`u`?X.useEffect:X.useLayoutEffect;function he({fileDiff:e,options:t,editorOptions:n,lineAnnotations:r,selectedLines:i,prerenderedHTML:a,metrics:o,hasGutterRenderUtility:s,hasCustomHeader:c,disableWorkerPool:l,edit:u}){let d=V(),f=i!==void 0,p=(0,X.useContext)(x),m=j(),h=(0,X.useRef)(null),g=_(n=>{if(n!=null){if(h.current!=null)throw Error(`useFileDiffInstance: An instance should not already exist when a node is created`);d==null?h.current=new re(Z({controlledSelection:f,hasCustomHeader:c,hasGutterRenderUtility:s,options:t}),l?void 0:p,!0):h.current=new pe(Z({controlledSelection:f,hasCustomHeader:c,hasGutterRenderUtility:s,options:t}),d,o,l?void 0:p,!0),h.current.hydrate({fileDiff:e,fileContainer:n,lineAnnotations:r,prerenderedHTML:a})}else{if(h.current==null)throw Error(`useFileDiffInstance: A FileDiff instance should exist when unmounting`);h.current.cleanUp(),h.current=null}});return me(()=>{let{current:n}=h;if(n==null)return;let a=Z({controlledSelection:f,hasCustomHeader:c,hasGutterRenderUtility:s,options:t}),o=a!==void 0&&!k(n.options,a);n.setOptions(a),n.render({forceRender:o,fileDiff:e,lineAnnotations:r}),i!==void 0&&n.setSelectedLines(i)}),me(()=>{if(u&&h.current!=null){if(m===void 0)throw Error(`FileDiff: EditContext is not attached`);let e=m(n??{});if(e==null)throw Error(`FileDiff: EditProvider.createEditor must return an editor instance`);try{return e.edit(h.current)}catch(t){throw e.cleanUp(),t}}},[u]),{ref:g,getHoveredLine:(0,X.useCallback)(()=>h.current?.getHoveredLine(),[])}}function Z({options:e,controlledSelection:t,hasCustomHeader:n,hasGutterRenderUtility:r}){return t||r||n?{...e,controlledSelection:t,renderCustomHeader:n?w:e?.renderCustomHeader,renderGutterUtility:r?w:e?.renderGutterUtility}:e}var Q=r();function ge({fileDiff:e,options:t,editorOptions:n,metrics:r,lineAnnotations:i,selectedLines:a,className:o,style:s,prerenderedHTML:c,renderAnnotation:l,renderCustomHeader:u,renderHeaderPrefix:d,renderHeaderFilenameSuffix:f,renderHeaderMetadata:p,renderGutterUtility:m,disableWorkerPool:h=!1,edit:g=!1}){let{ref:_,getHoveredLine:y}=he({fileDiff:e,options:t,editorOptions:n,metrics:r,lineAnnotations:i,selectedLines:a,prerenderedHTML:c,hasGutterRenderUtility:m!=null,hasCustomHeader:u!=null,disableWorkerPool:h,edit:g});return(0,Q.jsx)(v,{ref:_,className:o,style:s,children:W(oe({fileDiff:e,renderCustomHeader:u,renderHeaderPrefix:d,renderHeaderFilenameSuffix:f,renderHeaderMetadata:p,renderAnnotation:l,renderGutterUtility:m,lineAnnotations:i,getHoveredLine:y}),c)})}var $=t();function _e(e){return e.fromTurnCount===0?q(ee)({threadId:e.threadId,toTurnCount:e.toTurnCount}).pipe(n(e=>({kind:`fullThreadDiff`,input:e}))):q(J)({threadId:e.threadId,fromTurnCount:e.fromTurnCount,toTurnCount:e.toTurnCount}).pipe(n(e=>({kind:`turnDiff`,input:e})))}function ve(e){return e instanceof Error?e.message:typeof e==`string`?e:``}function ye(e){let t=ve(e).trim();if(t.length===0)return`Failed to load checkpoint diff.`;let n=t.toLowerCase();if(n.includes(`not a git repository`))return`Turn diffs are unavailable because this project is not a git repository.`;if(n.includes(`checkpoint unavailable for thread`)||n.includes(`checkpoint invariant violation`)){let e=t.indexOf(`:`);if(e>=0){let n=t.slice(e+1).trim();if(n.length>0)return n}}return t}function be(e){let t=ve(e).toLowerCase();return t.includes(`exceeds current turn count`)||t.includes(`checkpoint is unavailable for turn`)||t.includes(`filesystem checkpoint is unavailable`)}function xe(e){let t=_e(e);return O({queryKey:M.checkpointDiff(e),queryFn:async({signal:n})=>{if(!e.environmentId||!e.threadId||t._tag===`None`)throw Error(`Checkpoint diff is unavailable.`);let r=c(e.environmentId);try{return t.value.kind===`fullThreadDiff`?await r.orchestration.getFullThreadDiff(t.value.input,{signal:n}):await r.orchestration.getTurnDiff(t.value.input,{signal:n})}catch(e){throw Error(ye(e),{cause:e})}},enabled:(e.enabled??!0)&&!!e.environmentId&&!!e.threadId&&t._tag===`Some`,staleTime:1/0,retry:(e,t)=>be(t)?e<12:e<3,retryDelay:(e,t)=>be(t)?Math.min(5e3,250*2**(e-1)):Math.min(1e3,100*2**(e-1))})}function Se(e){if(!(!e.canScrollLeft&&!e.canScrollRight))return{maskImage:`linear-gradient(to right, ${e.canScrollLeft?`transparent 24px, black 72px`:`black`}, ${e.canScrollRight?`black calc(100% - 72px), transparent calc(100% - 24px)`:`black`})`}}function Ce(e){return e instanceof Error?e.message:e?`Failed to load checkpoint diff.`:null}function we(e){return{environmentId:e.environmentId??null,threadId:e.threadId,fromTurnCount:e.range?.fromTurnCount??null,toTurnCount:e.range?.toTurnCount??null,cacheScope:e.selectedTurnId?`turn:${e.selectedTurnId}`:e.conversationCacheScope,enabled:e.enabled}}function Te(){let e=(0,$.c)(8),t=(0,X.useRef)(null),[n,r]=(0,X.useState)(!1),[i,a]=(0,X.useState)(!1),o;e[0]===Symbol.for(`react.memo_cache_sentinel`)?(o=()=>{let e=t.current;if(!e){r(!1),a(!1);return}let n=de({scrollLeft:e.scrollLeft,scrollWidth:e.scrollWidth,clientWidth:e.clientWidth});r(n.canScrollLeft),a(n.canScrollRight)},e[0]=o):o=e[0];let s=o,c;e[1]===Symbol.for(`react.memo_cache_sentinel`)?(c=e=>{t.current?.scrollBy({left:e,behavior:`smooth`})},e[1]=c):c=e[1];let l=c,u;e[2]===Symbol.for(`react.memo_cache_sentinel`)?(u=e=>{let n=t.current;n&&ne({scrollWidth:n.scrollWidth,clientWidth:n.clientWidth,deltaX:e.deltaX,deltaY:e.deltaY})&&(e.preventDefault(),n.scrollBy({left:e.deltaY,behavior:`auto`}))},e[2]=u):u=e[2];let d=u,f,p;e[3]===Symbol.for(`react.memo_cache_sentinel`)?(f=()=>{let e=t.current;if(!e)return;let n=window.requestAnimationFrame(s),r=()=>s();e.addEventListener(`scroll`,r,{passive:!0});let i=new ResizeObserver(()=>s());return i.observe(e),()=>{window.cancelAnimationFrame(n),e.removeEventListener(`scroll`,r),i.disconnect()}},p=[s],e[3]=f,e[4]=p):(f=e[3],p=e[4]),(0,X.useEffect)(f,p);let m;return e[5]!==n||e[6]!==i?(m={ref:t,canScrollLeft:n,canScrollRight:i,scrollBy:l,onWheel:d,remeasure:s},e[5]=n,e[6]=i,e[7]=m):m=e[7],m}function Ee(e,t){let n=(0,$.c)(5),r;n[0]===e.current?r=n[1]:(r=()=>{(e.current?.querySelector(`[data-turn-chip-selected='true']`))?.scrollIntoView({block:`nearest`,inline:`nearest`,behavior:`smooth`})},n[0]=e.current,n[1]=r);let i;n[2]!==e||n[3]!==t?(i=[e,t],n[2]=e,n[3]=t,n[4]=i):i=n[4],(0,X.useEffect)(r,i)}var De=`
|
|
2
2
|
[data-diffs-header],
|
|
3
3
|
[data-diff],
|
|
4
4
|
[data-file],
|
|
@@ -61,4 +61,4 @@ import{r as e}from"./rolldown-runtime-hePW80VL.js";import{Ha as t,Mi as n,Ua as
|
|
|
61
61
|
text-decoration-color: currentColor;
|
|
62
62
|
}
|
|
63
63
|
`;function Oe(e){let t=(0,$.c)(44),{mode:n}=e,r=n===void 0?`inline`:n,{resolvedTheme:i}=b(),a=g(),[c,l]=(0,X.useState)(`stacked`),[u,d]=(0,X.useState)(a.diffWordWrap),h=(0,X.useRef)(null),_=(0,X.useRef)(!1),v=Te(),{activeCwd:y,activeThread:x,bodyState:S,diffOpen:C,inferredCheckpointTurnCountByTurnId:w,orderedTurnDiffSummaries:T,renderableFiles:E,renderablePatch:D,selectedFilePath:O,selectedTurn:k,selectedTurnId:j}=Le(i),M,P;t[0]!==C||t[1]!==a.diffWordWrap?(M=()=>{C&&!_.current&&d(a.diffWordWrap),_.current=C},P=[C,a.diffWordWrap],t[0]=C,t[1]=a.diffWordWrap,t[2]=M,t[3]=P):(M=t[2],P=t[3]),(0,X.useEffect)(M,P);let F;t[4]===O?F=t[5]:(F=()=>{!O||!h.current||Array.from(h.current.querySelectorAll(`[data-diff-file-path]`)).find(e=>e.dataset.diffFilePath===O)?.scrollIntoView({block:`nearest`})},t[4]=O,t[5]=F);let I;t[6]!==E||t[7]!==O?(I=[O,E],t[6]=E,t[7]=O,t[8]=I):I=t[8],(0,X.useEffect)(F,I);let L;t[9]===y?L=t[10]:(L=e=>{let t=f();if(!t)return;let n=y?p(e,y):e;N(t,n).catch(ke)},t[9]=y,t[10]=L);let R=L,z;t[11]===x?z=t[12]:(z=e=>{x&&o.getState().openDiff(s(m(x.environmentId,x.id)),e)},t[11]=x,t[12]=z);let B=z,V;t[13]===x?V=t[14]:(V=()=>{x&&o.getState().openDiff(s(m(x.environmentId,x.id)))},t[13]=x,t[14]=V);let H=V,U;t[15]===v.remeasure?U=t[16]:(U=()=>{let e=window.requestAnimationFrame(v.remeasure);return()=>window.cancelAnimationFrame(e)},t[15]=v.remeasure,t[16]=U);let W;t[17]!==T||t[18]!==j||t[19]!==v.remeasure?(W=[T,j,v.remeasure],t[17]=T,t[18]=j,t[19]=v.remeasure,t[20]=W):W=t[20],(0,X.useEffect)(U,W),Ee(v.ref,k?.turnId??j);let G;t[21]!==c||t[22]!==u||t[23]!==w||t[24]!==T||t[25]!==B||t[26]!==H||t[27]!==k||t[28]!==j||t[29]!==a||t[30]!==v?(G=(0,Q.jsx)(Ae,{diffRenderMode:c,diffWordWrap:u,inferredCheckpointTurnCountByTurnId:w,orderedTurnDiffSummaries:T,selectTurn:B,selectWholeConversation:H,selectedTurn:k,selectedTurnId:j,setDiffRenderMode:l,setDiffWordWrap:d,settings:a,turnStrip:v}),t[21]=c,t[22]=u,t[23]=w,t[24]=T,t[25]=B,t[26]=H,t[27]=k,t[28]=j,t[29]=a,t[30]=v,t[31]=G):G=t[31];let K=G,q;t[32]!==S||t[33]!==c||t[34]!==u||t[35]!==R||t[36]!==E||t[37]!==D||t[38]!==i?(q=(0,Q.jsx)(je,{bodyState:S,diffRenderMode:c,diffWordWrap:u,openDiffFileInEditor:R,patchViewportRef:h,renderableFiles:E,renderablePatch:D,resolvedTheme:i}),t[32]=S,t[33]=c,t[34]=u,t[35]=R,t[36]=E,t[37]=D,t[38]=i,t[39]=q):q=t[39];let J;return t[40]!==K||t[41]!==r||t[42]!==q?(J=(0,Q.jsx)(A,{mode:r,header:K,children:q}),t[40]=K,t[41]=r,t[42]=q,t[43]=J):J=t[43],J}function ke(e){console.warn(`Failed to open diff file in editor.`,e)}function Ae(e){let t=(0,$.c)(59),{diffRenderMode:n,diffWordWrap:r,inferredCheckpointTurnCountByTurnId:i,orderedTurnDiffSummaries:a,selectTurn:o,selectWholeConversation:s,selectedTurn:c,selectedTurnId:l,setDiffRenderMode:u,setDiffWordWrap:d,settings:f,turnStrip:p}=e,m,h;t[0]===p?(m=t[1],h=t[2]):(m=(0,Q.jsx)(Be,{direction:`left`,strip:p}),h=(0,Q.jsx)(Be,{direction:`right`,strip:p}),t[0]=p,t[1]=m,t[2]=h);let g=p.ref,_;t[3]!==p.canScrollLeft||t[4]!==p.canScrollRight?(_=Se({canScrollLeft:p.canScrollLeft,canScrollRight:p.canScrollRight}),t[3]=p.canScrollLeft,t[4]=p.canScrollRight,t[5]=_):_=t[5];let v=p.onWheel,y=l===null,b=l===null?`border-border bg-accent text-accent-foreground`:`border-border/70 bg-background/70 text-muted-foreground/80 hover:border-border hover:text-foreground/80`,x;t[6]===b?x=t[7]:(x=P(`rounded-md border px-2 py-1 text-left transition-colors`,b),t[6]=b,t[7]=x);let S;t[8]===Symbol.for(`react.memo_cache_sentinel`)?(S=(0,Q.jsx)(`div`,{className:`text-[10px] leading-tight font-medium`,children:`All turns`}),t[8]=S):S=t[8];let C;t[9]===x?C=t[10]:(C=(0,Q.jsx)(`div`,{className:x,children:S}),t[9]=x,t[10]=C);let w;t[11]!==s||t[12]!==C||t[13]!==y?(w=(0,Q.jsx)(`button`,{type:`button`,className:`shrink-0 rounded-md`,onClick:s,"data-turn-chip-selected":y,children:C}),t[11]=s,t[12]=C,t[13]=y,t[14]=w):w=t[14];let T;if(t[15]!==i||t[16]!==a||t[17]!==o||t[18]!==c?.turnId||t[19]!==f){let e;t[21]!==i||t[22]!==o||t[23]!==c?.turnId||t[24]!==f?(e=e=>(0,Q.jsx)(Ve,{summary:e,selected:e.turnId===c?.turnId,turnCount:e.checkpointTurnCount??i[e.turnId],timestampFormat:f.timestampFormat,onSelect:o},e.turnId),t[21]=i,t[22]=o,t[23]=c?.turnId,t[24]=f,t[25]=e):e=t[25],T=a.map(e),t[15]=i,t[16]=a,t[17]=o,t[18]=c?.turnId,t[19]=f,t[20]=T}else T=t[20];let E;t[26]!==w||t[27]!==T||t[28]!==_||t[29]!==p.onWheel||t[30]!==p.ref?(E=(0,Q.jsxs)(`div`,{ref:g,className:`turn-chip-strip flex gap-1 overflow-x-auto px-8 py-0.5`,style:_,onWheel:v,children:[w,T]}),t[26]=w,t[27]=T,t[28]=_,t[29]=p.onWheel,t[30]=p.ref,t[31]=E):E=t[31];let D;t[32]!==m||t[33]!==E||t[34]!==h?(D=(0,Q.jsxs)(`div`,{className:`relative min-w-0 flex-1 [-webkit-app-region:no-drag]`,children:[m,h,E]}),t[32]=m,t[33]=E,t[34]=h,t[35]=D):D=t[35];let O;t[36]===n?O=t[37]:(O=[n],t[36]=n,t[37]=O);let k;t[38]===u?k=t[39]:(k=e=>{let t=e[0];(t===`stacked`||t===`split`)&&u(t)},t[38]=u,t[39]=k);let A;t[40]===Symbol.for(`react.memo_cache_sentinel`)?(A=(0,Q.jsx)(R,{"aria-label":`Stacked diff view`,value:`stacked`,children:(0,Q.jsx)(se,{className:`size-3`})}),t[40]=A):A=t[40];let j;t[41]===Symbol.for(`react.memo_cache_sentinel`)?(j=(0,Q.jsx)(R,{"aria-label":`Split diff view`,value:`split`,children:(0,Q.jsx)(ie,{className:`size-3`})}),t[41]=j):j=t[41];let M;t[42]!==O||t[43]!==k?(M=(0,Q.jsxs)(F,{className:`shrink-0`,variant:`outline`,size:`xs`,value:O,onValueChange:k,children:[A,j]}),t[42]=O,t[43]=k,t[44]=M):M=t[44];let N=r?`Disable diff line wrapping`:`Enable diff line wrapping`,I=r?`Disable line wrapping`:`Enable line wrapping`,L;t[45]===d?L=t[46]:(L=e=>{d(!!e)},t[45]=d,t[46]=L);let z;t[47]===Symbol.for(`react.memo_cache_sentinel`)?(z=(0,Q.jsx)(le,{className:`size-3`}),t[47]=z):z=t[47];let B;t[48]!==r||t[49]!==N||t[50]!==I||t[51]!==L?(B=(0,Q.jsx)(R,{"aria-label":N,title:I,variant:`outline`,size:`xs`,pressed:r,onPressedChange:L,children:z}),t[48]=r,t[49]=N,t[50]=I,t[51]=L,t[52]=B):B=t[52];let V;t[53]!==M||t[54]!==B?(V=(0,Q.jsxs)(`div`,{className:`flex shrink-0 items-center gap-1 [-webkit-app-region:no-drag]`,children:[M,B]}),t[53]=M,t[54]=B,t[55]=V):V=t[55];let H;return t[56]!==D||t[57]!==V?(H=(0,Q.jsxs)(Q.Fragment,{children:[D,V]}),t[56]=D,t[57]=V,t[58]=H):H=t[58],H}function je(e){let t=(0,$.c)(18),{bodyState:n,diffRenderMode:r,diffWordWrap:i,openDiffFileInEditor:a,patchViewportRef:o,renderableFiles:s,renderablePatch:c,resolvedTheme:l}=e;if(n.kind===`no-thread`){let e;return t[0]===Symbol.for(`react.memo_cache_sentinel`)?(e=(0,Q.jsx)(Ne,{children:`Select a thread to inspect turn diffs.`}),t[0]=e):e=t[0],e}if(n.kind===`not-git-repo`){let e;return t[1]===Symbol.for(`react.memo_cache_sentinel`)?(e=(0,Q.jsx)(Ne,{children:`Turn diffs are unavailable because this project is not a git repository.`}),t[1]=e):e=t[1],e}if(n.kind===`no-completed-turns`){let e;return t[2]===Symbol.for(`react.memo_cache_sentinel`)?(e=(0,Q.jsx)(Ne,{children:`No completed turns yet.`}),t[2]=e):e=t[2],e}let u;t[3]!==n.error||t[4]!==c?(u=n.error&&!c&&(0,Q.jsx)(`div`,{className:`px-3`,children:(0,Q.jsx)(`p`,{className:`mb-2 text-[11px] text-destructive-foreground`,children:n.error})}),t[3]=n.error,t[4]=c,t[5]=u):u=t[5];let d;t[6]!==n||t[7]!==r||t[8]!==i||t[9]!==a||t[10]!==s||t[11]!==c||t[12]!==l?(d=c?c.kind===`files`?(0,Q.jsx)(K,{className:`diff-render-surface h-full min-h-0 overflow-auto px-2 pb-2`,config:{overscrollSize:600,intersectionObserverMargin:1200},children:s.map(e=>{let t=Y(e),n=`${fe(e)}:${l}`;return(0,Q.jsx)(`div`,{"data-diff-file-path":t,className:`diff-render-file mb-2 rounded-md first:mt-2 last:mb-0`,onClickCapture:e=>{(e.nativeEvent.composedPath?.()??[]).some(Me)&&a(t)},children:(0,Q.jsx)(ge,{fileDiff:e,options:{diffStyle:r===`split`?`split`:`unified`,lineDiffType:`none`,overflow:i?`wrap`:`scroll`,theme:T(l),themeType:l,unsafeCSS:De}})},n)})}):(0,Q.jsx)(`div`,{className:`h-full overflow-auto p-2`,children:(0,Q.jsxs)(`div`,{className:`space-y-2`,children:[(0,Q.jsx)(`p`,{className:`text-[11px] text-muted-foreground/75`,children:c.reason}),(0,Q.jsx)(`pre`,{className:P(`max-h-[72vh] rounded-md border border-border/70 bg-background/70 p-3 font-mono text-[11px] leading-relaxed text-muted-foreground/90`,i?`overflow-auto whitespace-pre-wrap wrap-break-word`:`overflow-auto`),children:c.text})]})}):(0,Q.jsx)(Re,{bodyState:n}),t[6]=n,t[7]=r,t[8]=i,t[9]=a,t[10]=s,t[11]=c,t[12]=l,t[13]=d):d=t[13];let f;return t[14]!==o||t[15]!==u||t[16]!==d?(f=(0,Q.jsx)(Q.Fragment,{children:(0,Q.jsxs)(`div`,{ref:o,className:`diff-panel-viewport min-h-0 min-w-0 flex-1 overflow-hidden`,children:[u,d]})}),t[14]=o,t[15]=u,t[16]=d,t[17]=f):f=t[17],f}function Me(e){return e instanceof Element&&e.hasAttribute(`data-title`)}function Ne(e){let t=(0,$.c)(2),{children:n}=e,r;return t[0]===n?r=t[1]:(r=(0,Q.jsx)(`div`,{className:`flex flex-1 items-center justify-center px-5 text-center text-xs text-muted-foreground/70`,children:n}),t[0]=n,t[1]=r),r}function Pe(){let e=(0,$.c)(19),t;e[0]===Symbol.for(`react.memo_cache_sentinel`)?(t={strict:!1,select:Ie},e[0]=t):t=e[0];let n=a(t),r;e[1]===Symbol.for(`react.memo_cache_sentinel`)?(r={strict:!1,select:Fe},e[1]=r):r=e[1];let i=B(r),o=i.rightPanel===`diff`,s=n?.threadId??null,c,l;e[2]===n?l=e[3]:(l=C(n),e[2]=n,e[3]=l),c=l;let u=E(c),f=He(u),p=u?.worktreePath,m=f?.cwd,h;e[4]!==p||e[5]!==m?(h=I({threadWorktreePath:p,projectCwd:m}),e[4]=p,e[5]=m,e[6]=h):h=e[6];let g=h,_=u?.environmentId??null,v=g??null,y;e[7]!==_||e[8]!==v?(y={environmentId:_,cwd:v},e[7]=_,e[8]=v,e[9]=y):y=e[9];let b=d(y),x;e[10]===b.data?x=e[11]:(x=z(b.data),e[10]=b.data,e[11]=x);let S=x,w;return e[12]!==g||e[13]!==u||e[14]!==s||e[15]!==o||e[16]!==i||e[17]!==S?(w={activeThread:u,activeThreadId:s,activeCwd:g,isGitRepo:S,diffSearch:i,diffOpen:o},e[12]=g,e[13]=u,e[14]=s,e[15]=o,e[16]=i,e[17]=S,e[18]=w):w=e[18],w}function Fe(e){return G(e)}function Ie(e){return h(e)}function Le(e){let{activeThread:t,activeThreadId:n,activeCwd:r,isGitRepo:i,diffSearch:a,diffOpen:o}=Pe(),{turnDiffSummaries:s,inferredCheckpointTurnCountByTurnId:c}=H(t),u=a.diffTurnId??null,d=u===null?null:a.diffFilePath??null,{activeCheckpointRange:f,conversationCacheScope:p,orderedTurnDiffSummaries:m,selectedTurn:h}=(0,X.useMemo)(()=>te({summaries:s,inferredCheckpointTurnCountByTurnId:c,selectedTurnId:u}),[c,u,s]),g=l(xe(we({environmentId:t?.environmentId,threadId:n,range:f,selectedTurnId:h?.turnId,conversationCacheScope:p,enabled:i}))),_=g.isLoading,v=Ce(g.error),y=g.data?.diff,b=typeof y==`string`&&y.trim().length===0,{emptyDiffMessage:x}=(0,X.useMemo)(()=>ae(h),[h]),S=(0,X.useMemo)(()=>ce(y,`diff-panel:${e}`),[e,y]),C=(0,X.useMemo)(()=>Ue(S),[S]);return{activeCwd:r,activeThread:t,bodyState:ue({hasActiveThread:!!t,isGitRepo:i,turnSummaryCount:m.length,checkpointDiffError:v,hasRenderablePatch:!!S,isLoadingCheckpointDiff:_,hasNoNetChanges:b,emptyDiffMessage:x}),diffOpen:o,inferredCheckpointTurnCountByTurnId:c,orderedTurnDiffSummaries:m,renderableFiles:C,renderablePatch:S,selectedFilePath:d,selectedTurn:h,selectedTurnId:u}}function Re(e){let t=(0,$.c)(4),{bodyState:n}=e;if(n.kind===`loading`){let e;return t[0]===Symbol.for(`react.memo_cache_sentinel`)?(e=(0,Q.jsx)(D,{label:`Loading checkpoint diff...`}),t[0]=e):e=t[0],e}let r;t[1]===Symbol.for(`react.memo_cache_sentinel`)?(r=(0,Q.jsx)(L,{className:`size-7 text-muted-foreground/25`}),t[1]=r):r=t[1];let i=n.kind===`empty`?n.message:`No diff is available for this range.`,a;return t[2]===i?a=t[3]:(a=(0,Q.jsxs)(`div`,{className:`flex h-full flex-col items-center justify-center gap-2 px-5 py-2 text-center`,children:[r,(0,Q.jsx)(`p`,{className:`text-xs text-muted-foreground/70`,children:i})]}),t[2]=i,t[3]=a),a}var ze=180;function Be(e){let t=(0,$.c)(14),{direction:n,strip:r}=e,i=n===`left`,a=i?r.canScrollLeft:r.canScrollRight,o=i?U:u,s=i?`left-0`:`right-0`,c=a?`border-border/70 hover:border-border hover:text-foreground`:`cursor-not-allowed border-border/40 text-muted-foreground/40`,l;t[0]!==s||t[1]!==c?(l=P(`absolute top-1/2 z-20 inline-flex size-6 -translate-y-1/2 items-center justify-center rounded-md border bg-background/90 text-muted-foreground transition-colors`,s,c),t[0]=s,t[1]=c,t[2]=l):l=t[2];let d;t[3]!==i||t[4]!==r?(d=()=>r.scrollBy(i?-180:ze),t[3]=i,t[4]=r,t[5]=d):d=t[5];let f=!a,p=`Scroll turn list ${n}`,m;t[6]===o?m=t[7]:(m=(0,Q.jsx)(o,{className:`size-3.5`}),t[6]=o,t[7]=m);let h;return t[8]!==l||t[9]!==d||t[10]!==f||t[11]!==p||t[12]!==m?(h=(0,Q.jsx)(`button`,{type:`button`,className:l,onClick:d,disabled:f,"aria-label":p,children:m}),t[8]=l,t[9]=d,t[10]=f,t[11]=p,t[12]=m,t[13]=h):h=t[13],h}function Ve(e){let t=(0,$.c)(23),{summary:n,selected:r,turnCount:i,timestampFormat:a,onSelect:o}=e,s;t[0]!==o||t[1]!==n.turnId?(s=()=>o(n.turnId),t[0]=o,t[1]=n.turnId,t[2]=s):s=t[2];let c=n.turnId,l=r?`border-border bg-accent text-accent-foreground`:`border-border/70 bg-background/70 text-muted-foreground/80 hover:border-border hover:text-foreground/80`,u;t[3]===l?u=t[4]:(u=P(`rounded-md border px-2 py-1 text-left transition-colors`,l),t[3]=l,t[4]=u);let d=i??`?`,f;t[5]===d?f=t[6]:(f=(0,Q.jsxs)(`span`,{className:`text-[10px] leading-tight font-medium`,children:[`Turn `,d]}),t[5]=d,t[6]=f);let p;t[7]!==n.completedAt||t[8]!==a?(p=y(n.completedAt,a),t[7]=n.completedAt,t[8]=a,t[9]=p):p=t[9];let m;t[10]===p?m=t[11]:(m=(0,Q.jsx)(`span`,{className:`text-[9px] leading-tight opacity-70`,children:p}),t[10]=p,t[11]=m);let h;t[12]!==f||t[13]!==m?(h=(0,Q.jsxs)(`div`,{className:`flex items-center gap-1`,children:[f,m]}),t[12]=f,t[13]=m,t[14]=h):h=t[14];let g;t[15]!==u||t[16]!==h?(g=(0,Q.jsx)(`div`,{className:u,children:h}),t[15]=u,t[16]=h,t[17]=g):g=t[17];let _;return t[18]!==r||t[19]!==n.turnId||t[20]!==s||t[21]!==g?(_=(0,Q.jsx)(`button`,{type:`button`,className:`shrink-0 rounded-md`,onClick:s,title:c,"data-turn-chip-selected":r,children:g}),t[18]=r,t[19]=n.turnId,t[20]=s,t[21]=g,t[22]=_):_=t[22],_}function He(e){let t=(0,$.c)(3),n=e?.environmentId,r=e?.projectId,i;return t[0]!==n||t[1]!==r?(i=e=>n&&r?S(e,{environmentId:n,projectId:r}):void 0,t[0]=n,t[1]=r,t[2]=i):i=t[2],E(i)}function Ue(e){return!e||e.kind!==`files`?[]:e.files.toSorted((e,t)=>Y(e).localeCompare(Y(t),void 0,{numeric:!0,sensitivity:`base`}))}export{Oe as default};
|
|
64
|
-
//# sourceMappingURL=DiffPanel-
|
|
64
|
+
//# sourceMappingURL=DiffPanel-7EXLivet.js.map
|