@cmmd-center/forge 0.13.20 → 0.13.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (21) hide show
  1. package/dist/bin.cjs +218 -25
  2. package/dist/bin.mjs +242 -49
  3. package/dist/client/assets/{CanvasPanel-uofZ5KUR.js → CanvasPanel-DeDjy-X2.js} +2 -2
  4. package/dist/client/assets/{CompositeItem-C5YSRi5B.js → CompositeItem-CQubmTuU.js} +2 -2
  5. package/dist/client/assets/{DiffPanel-vMgoOgmn.js → DiffPanel-4dMGnRQx.js} +2 -2
  6. package/dist/client/assets/{DiffPanel.logic-BL2ToTuV.js → DiffPanel.logic-SlNrN8IK.js} +2 -2
  7. package/dist/client/assets/{DiffPanelShell-Dq2v4pBo.js → DiffPanelShell-BEZZSyhr.js} +3 -3
  8. package/dist/client/assets/{DiffWorkerPoolProvider-B2Da35Tj.js → DiffWorkerPoolProvider-DE_ppGS9.js} +2 -2
  9. package/dist/client/assets/{PullRequestCodePanel-ByFVxByx.js → PullRequestCodePanel-CsYCbixY.js} +2 -2
  10. package/dist/client/assets/{Virtualizer-B3YnhtJt.js → Virtualizer-DzZ4roLU.js} +2 -2
  11. package/dist/client/assets/{canvasStateStore-IBP3QM_C.js → canvasStateStore-UwPGvihn.js} +2 -2
  12. package/dist/client/assets/{cmmdAuthRedirect-Ck1QfYaS.js → cmmdAuthRedirect-BUli2MC4.js} +2 -2
  13. package/dist/client/assets/{index-gEW4XdYw.js → index-DW9nsKTK.js} +6 -6
  14. package/dist/client/assets/{popover-BQaoSlk7.js → popover-BpdZi4Uc.js} +2 -2
  15. package/dist/client/assets/src-DVtrA7UW.js +3 -0
  16. package/dist/client/assets/{toggle-CE2NHkQL.js → toggle-BUkm7Wlt.js} +2 -2
  17. package/dist/client/index.html +10 -10
  18. package/dist/forge-build.json +1 -1
  19. package/dist/{open-D8mGyWmt.mjs → open-FIgTDosf.mjs} +3 -3
  20. package/package.json +1 -1
  21. package/dist/client/assets/src-Bj8vTcnP.js +0 -3
package/dist/bin.mjs CHANGED
@@ -22,7 +22,7 @@ import { createCipheriv, createDecipheriv, createHash, createHmac, createPublicK
22
22
  import * as FS$1 from "node:fs";
23
23
  import fs, { accessSync, chmodSync, constants, createReadStream, existsSync, mkdirSync, mkdtempSync, promises, readFile, readFileSync, readdir, readdirSync, realpathSync, rmSync, statSync, unlinkSync, writeFileSync } from "node:fs";
24
24
  import * as NodeOS from "node:os";
25
- import $U, { homedir, networkInterfaces, tmpdir } from "node:os";
25
+ import os, { homedir, networkInterfaces, tmpdir } from "node:os";
26
26
  import * as Path$1 from "node:path";
27
27
  import path, { basename, delimiter, dirname, extname, isAbsolute, join, posix, relative, resolve, sep } from "node:path";
28
28
  import { pathToFileURL } from "node:url";
@@ -17203,7 +17203,8 @@ const ExecutionEnvironmentAuthCapabilities = Struct({
17203
17203
  ownerBootstrapPromotion: Boolean$1,
17204
17204
  bearerBootstrap: Boolean$1,
17205
17205
  desktopBootstrap: Boolean$1,
17206
- providerCredentialBroker: Boolean$1
17206
+ providerCredentialBroker: Boolean$1,
17207
+ cloudOwnerAssertion: optionalKey(Boolean$1)
17207
17208
  });
17208
17209
  const ExecutionEnvironmentToolCapabilities = Struct({
17209
17210
  agents: Boolean$1,
@@ -50548,7 +50549,7 @@ function _supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) {
50548
50549
  const min = forceColor || 0;
50549
50550
  if (env.TERM === "dumb") return min;
50550
50551
  if (process$1.platform === "win32") {
50551
- const osRelease = $U.release().split(".");
50552
+ const osRelease = os.release().split(".");
50552
50553
  if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) return Number(osRelease[2]) >= 14931 ? 3 : 2;
50553
50554
  return 1;
50554
50555
  }
@@ -52991,7 +52992,7 @@ var require_ProcessDetector = /* @__PURE__ */ __commonJSMin(((exports) => {
52991
52992
  exports.processDetector = void 0;
52992
52993
  const api_1 = (init_esm$1(), __toCommonJS(esm_exports$1));
52993
52994
  const semconv_1 = require_semconv$1();
52994
- const os$3 = __require("os");
52995
+ const os$4 = __require("os");
52995
52996
  /**
52996
52997
  * ProcessDetector will be used to detect the resources related current process running
52997
52998
  * and being instrumented from the NodeJS Process module.
@@ -53013,7 +53014,7 @@ var require_ProcessDetector = /* @__PURE__ */ __commonJSMin(((exports) => {
53013
53014
  };
53014
53015
  if (process.argv.length > 1) attributes[semconv_1.ATTR_PROCESS_COMMAND] = process.argv[1];
53015
53016
  try {
53016
- const userInfo = os$3.userInfo();
53017
+ const userInfo = os$4.userInfo();
53017
53018
  attributes[semconv_1.ATTR_PROCESS_OWNER] = userInfo.username;
53018
53019
  } catch (e) {
53019
53020
  api_1.diag.debug(`error obtaining process owner: ${e}`);
@@ -65936,7 +65937,7 @@ function normalizeNumberish(value) {
65936
65937
  }
65937
65938
  //#endregion
65938
65939
  //#region package.json
65939
- var version$1 = "0.13.20";
65940
+ var version$1 = "0.13.22";
65940
65941
  //#endregion
65941
65942
  //#region src/sentry.ts
65942
65943
  const SERVER_APP_NAME = "forge-server";
@@ -68060,6 +68061,7 @@ function extractCookieValue(setCookieHeaders, name) {
68060
68061
  }
68061
68062
  return null;
68062
68063
  }
68064
+ /** @internal exported for the other owner lanes and their tests. */
68063
68065
  function readCmmdJsonResponse(response, fallbackMessage) {
68064
68066
  return gen(function* () {
68065
68067
  const payload = yield* tryPromise({
@@ -68321,11 +68323,14 @@ function cmmdUserCredential(payload) {
68321
68323
  /**
68322
68324
  * Read one CMMD token-exchange response into the credential set Forge stores.
68323
68325
  *
68324
- * Both owner lanes redeem the same `cmmd-forge-web` authorization code — the
68325
- * browser OAuth callback and Cloud's password login — so they see the same
68326
- * bundle shape. Reading it in one place is what keeps them from drifting on
68327
- * which field carries the PAT; callers still own their own error reporting,
68328
- * which is the only part that legitimately differs between the two.
68326
+ * Every owner lane that redeems a CMMD grant — the browser OAuth callback,
68327
+ * Cloud's password login, the cookie-shaped session renew, and the bearer
68328
+ * device-session redeem in cloudOwnerDeviceSession.ts — sees the same bundle
68329
+ * shape. Reading it in one place is what keeps them from drifting on which
68330
+ * field carries the PAT; callers still own their own error reporting, which
68331
+ * is the only part that legitimately differs between them.
68332
+ *
68333
+ * @internal exported for the other owner lanes and their tests.
68329
68334
  */
68330
68335
  function resolveCmmdTokenBundleCredentials(tokenResult) {
68331
68336
  return gen(function* () {
@@ -69277,22 +69282,52 @@ const deriveServerPaths = fn$1(function* (baseDir, _devUrl, _mode) {
69277
69282
  secretsDir: join(stateDir, "secrets")
69278
69283
  };
69279
69284
  });
69280
- const ensureServerDirectories = fn$1(function* (derivedPaths) {
69285
+ /**
69286
+ * Create `dir`, healing the one case a recursive mkdir cannot: a symlink whose
69287
+ * target no longer exists.
69288
+ *
69289
+ * A recursive mkdir is a no-op when the path is already a directory, but it
69290
+ * fails when the path is a DANGLING symlink, because nothing on the way down
69291
+ * resolves. bun reports that as EEXIST and node as ENOENT; either way the call
69292
+ * raises instead of recreating what the link points at.
69293
+ *
69294
+ * That shape is normal on a CMMD cloud runtime. The regenerable cache
69295
+ * directories there are symlinks onto the sprite's machine-local /tmp, which is
69296
+ * a separate device that is NOT part of the checkpointed overlay, so a wipe
69297
+ * leaves the links behind and takes their targets. Only provisioning recreated
69298
+ * them, and provisioning does not re-run on a restart, so a wiped store meant
69299
+ * the server raised at boot, exited 1, and did so again on every supervisor
69300
+ * restart for as long as the runtime existed.
69301
+ *
69302
+ * Observed live on cloud runtime 33dff4ee: /forge/caches pointed at
69303
+ * /tmp/forge-store/caches, the target was gone, and the server failed to boot
69304
+ * 974 times between 2026-09-15 and 2026-09-16. Recreating the target directory
69305
+ * by hand was the whole repair, which is what this does automatically.
69306
+ *
69307
+ * Healing is deliberately narrow. A path that is not a symlink, or a symlink
69308
+ * whose target cannot be created, re-raises the original error rather than
69309
+ * inventing a directory somewhere the caller did not ask for.
69310
+ */
69311
+ const ensureDirectory = fn$1(function* (dir) {
69281
69312
  const fs = yield* FileSystem;
69313
+ const path = yield* Path$2;
69314
+ yield* fs.makeDirectory(dir, { recursive: true }).pipe(catch_((error) => fs.readLink(dir).pipe(flatMap((target) => fs.makeDirectory(path.resolve(path.dirname(dir), target), { recursive: true })), mapError(() => error))));
69315
+ });
69316
+ const ensureServerDirectories = fn$1(function* (derivedPaths) {
69282
69317
  const path = yield* Path$2;
69283
69318
  yield* all([
69284
- fs.makeDirectory(derivedPaths.stateDir, { recursive: true }),
69285
- fs.makeDirectory(derivedPaths.logsDir, { recursive: true }),
69286
- fs.makeDirectory(derivedPaths.providerLogsDir, { recursive: true }),
69287
- fs.makeDirectory(derivedPaths.terminalLogsDir, { recursive: true }),
69288
- fs.makeDirectory(derivedPaths.attachmentsDir, { recursive: true }),
69289
- fs.makeDirectory(derivedPaths.worktreesDir, { recursive: true }),
69290
- fs.makeDirectory(path.dirname(derivedPaths.keybindingsConfigPath), { recursive: true }),
69291
- fs.makeDirectory(path.dirname(derivedPaths.settingsPath), { recursive: true }),
69292
- fs.makeDirectory(derivedPaths.providerStatusCacheDir, { recursive: true }),
69293
- fs.makeDirectory(path.dirname(derivedPaths.anonymousIdPath), { recursive: true }),
69294
- fs.makeDirectory(path.dirname(derivedPaths.environmentMetadataPath), { recursive: true }),
69295
- fs.makeDirectory(path.dirname(derivedPaths.serverRuntimeStatePath), { recursive: true })
69319
+ ensureDirectory(derivedPaths.stateDir),
69320
+ ensureDirectory(derivedPaths.logsDir),
69321
+ ensureDirectory(derivedPaths.providerLogsDir),
69322
+ ensureDirectory(derivedPaths.terminalLogsDir),
69323
+ ensureDirectory(derivedPaths.attachmentsDir),
69324
+ ensureDirectory(derivedPaths.worktreesDir),
69325
+ ensureDirectory(path.dirname(derivedPaths.keybindingsConfigPath)),
69326
+ ensureDirectory(path.dirname(derivedPaths.settingsPath)),
69327
+ ensureDirectory(derivedPaths.providerStatusCacheDir),
69328
+ ensureDirectory(path.dirname(derivedPaths.anonymousIdPath)),
69329
+ ensureDirectory(path.dirname(derivedPaths.environmentMetadataPath)),
69330
+ ensureDirectory(path.dirname(derivedPaths.serverRuntimeStatePath))
69296
69331
  ], { concurrency: "unbounded" });
69297
69332
  });
69298
69333
  /**
@@ -75784,7 +75819,7 @@ var require_node_gyp_build = /* @__PURE__ */ __commonJSMin(((exports, module) =>
75784
75819
  var fs$6 = __require("fs");
75785
75820
  var path$5 = __require("path");
75786
75821
  var url$1 = __require("url");
75787
- var os$2 = __require("os");
75822
+ var os$3 = __require("os");
75788
75823
  var runtimeRequire = typeof __webpack_require__ === "function" ? __non_webpack_require__ : __require;
75789
75824
  var vars = process.config && process.config.variables || {};
75790
75825
  var prebuildsOnly = !!process.env.PREBUILDS_ONLY;
@@ -75792,8 +75827,8 @@ var require_node_gyp_build = /* @__PURE__ */ __commonJSMin(((exports, module) =>
75792
75827
  var abi = versions.modules;
75793
75828
  if (versions.deno || process.isBun) abi = "unsupported";
75794
75829
  var runtime = isElectron() ? "electron" : isNwjs() ? "node-webkit" : "node";
75795
- var arch = process.env.npm_config_arch || os$2.arch();
75796
- var platform = process.env.npm_config_platform || os$2.platform();
75830
+ var arch = process.env.npm_config_arch || os$3.arch();
75831
+ var platform = process.env.npm_config_platform || os$3.platform();
75797
75832
  var libc = process.env.LIBC || (isMusl(platform) ? "musl" : "glibc");
75798
75833
  var armv = process.env.ARM_VERSION || (arch === "arm64" ? "8" : vars.arm_version) || "";
75799
75834
  var uv = (versions.uv || "").split(".")[0];
@@ -93481,7 +93516,7 @@ const launchDetached = (launch) => gen(function* () {
93481
93516
  });
93482
93517
  const OpenLive = effect(Open, gen(function* () {
93483
93518
  const open = yield* tryPromise({
93484
- try: () => import("./open-D8mGyWmt.mjs"),
93519
+ try: () => import("./open-FIgTDosf.mjs"),
93485
93520
  catch: (cause) => new OpenError$1({
93486
93521
  message: "failed to load browser opener",
93487
93522
  cause
@@ -190957,7 +190992,7 @@ var Vce = {
190957
190992
  function Ve() {
190958
190993
  return Vce;
190959
190994
  }
190960
- var Us = $U.homedir(), lk = $U.tmpdir(), { env: Vc } = process$1, Yce = (e) => {
190995
+ var Us = os.homedir(), lk = os.tmpdir(), { env: Vc } = process$1, Yce = (e) => {
190961
190996
  let t = path.join(Us, "Library");
190962
190997
  return {
190963
190998
  data: path.join(t, "Application Support", e),
@@ -201157,12 +201192,12 @@ function qZ(e, t) {
201157
201192
  }
201158
201193
  return n;
201159
201194
  }
201160
- var os$1 = "https://code.claude.com/docs/en", kKe = [
201195
+ var os$2 = "https://code.claude.com/docs/en", kKe = [
201161
201196
  {
201162
201197
  matches: (e) => e.path === "permissions.defaultMode" && e.code === "invalid_value",
201163
201198
  tip: {
201164
201199
  suggestion: "Valid modes: \"acceptEdits\" (ask before file changes), \"plan\" (analysis only), \"bypassPermissions\" (auto-accept all), or \"default\" (standard behavior)",
201165
- docLink: `${os$1}/iam#permission-modes`
201200
+ docLink: `${os$2}/iam#permission-modes`
201166
201201
  }
201167
201202
  },
201168
201203
  {
@@ -201177,7 +201212,7 @@ var os$1 = "https://code.claude.com/docs/en", kKe = [
201177
201212
  matches: (e) => e.path.startsWith("env.") && e.code === "invalid_type",
201178
201213
  tip: {
201179
201214
  suggestion: "Environment variables must be strings. Wrap numbers and booleans in quotes. Example: \"DEBUG\": \"true\", \"PORT\": \"3000\"",
201180
- docLink: `${os$1}/settings#environment-variables`
201215
+ docLink: `${os$2}/settings#environment-variables`
201181
201216
  }
201182
201217
  },
201183
201218
  {
@@ -201188,14 +201223,14 @@ var os$1 = "https://code.claude.com/docs/en", kKe = [
201188
201223
  matches: (e) => e.path.startsWith("hooks.") && e.code === "invalid_key",
201189
201224
  tip: {
201190
201225
  suggestion: "Not a recognized hook event. Common events: PreToolUse, PostToolUse, UserPromptSubmit, SessionStart, SessionEnd, Stop. Check spelling and capitalization.",
201191
- docLink: `${os$1}/hooks`
201226
+ docLink: `${os$2}/hooks`
201192
201227
  }
201193
201228
  },
201194
201229
  {
201195
201230
  matches: (e) => /\.hooks\.\d+\.command$/.test(e.path) && e.code === "invalid_type" && e.received === "undefined",
201196
201231
  tip: {
201197
201232
  suggestion: "Command hooks require `command`. For exec form (no shell), set `command` to the executable and `args` to its arguments: {\"type\": \"command\", \"command\": \"echo\", \"args\": [\"hi\"]}. For shell form, set `command` to the full shell string: {\"type\": \"command\", \"command\": \"echo hi\"}.",
201198
- docLink: `${os$1}/hooks#exec-form-and-shell-form`
201233
+ docLink: `${os$2}/hooks#exec-form-and-shell-form`
201199
201234
  }
201200
201235
  },
201201
201236
  {
@@ -201210,7 +201245,7 @@ var os$1 = "https://code.claude.com/docs/en", kKe = [
201210
201245
  matches: (e) => e.code === "unrecognized_keys",
201211
201246
  tip: {
201212
201247
  suggestion: "Check for typos or refer to the documentation for valid fields",
201213
- docLink: `${os$1}/settings`
201248
+ docLink: `${os$2}/settings`
201214
201249
  }
201215
201250
  },
201216
201251
  {
@@ -201225,13 +201260,13 @@ var os$1 = "https://code.claude.com/docs/en", kKe = [
201225
201260
  matches: (e) => e.path === "permissions.additionalDirectories" && e.code === "invalid_type",
201226
201261
  tip: {
201227
201262
  suggestion: "Must be an array of directory paths. Example: [\"~/projects\", \"/tmp/workspace\"]. You can also use --add-dir flag or /add-dir command",
201228
- docLink: `${os$1}/iam#working-directories`
201263
+ docLink: `${os$2}/iam#working-directories`
201229
201264
  }
201230
201265
  }
201231
201266
  ], AKe = {
201232
- permissions: `${os$1}/iam#configuring-permissions`,
201233
- env: `${os$1}/settings#environment-variables`,
201234
- hooks: `${os$1}/hooks`
201267
+ permissions: `${os$2}/iam#configuring-permissions`,
201268
+ env: `${os$2}/settings#environment-variables`,
201269
+ hooks: `${os$2}/hooks`
201235
201270
  };
201236
201271
  function GZ(e) {
201237
201272
  let t = kKe.find((r) => r.matches(e));
@@ -268541,7 +268576,7 @@ var import_multicast_dns = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMi
268541
268576
  var dgram = __require("dgram");
268542
268577
  var thunky = require_thunky();
268543
268578
  var events = __require("events");
268544
- var os = __require("os");
268579
+ var os$1 = __require("os");
268545
268580
  var noop = function() {};
268546
268581
  module.exports = function(opts) {
268547
268582
  if (!opts) opts = {};
@@ -268672,14 +268707,14 @@ var import_multicast_dns = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMi
268672
268707
  return that;
268673
268708
  };
268674
268709
  function defaultInterface() {
268675
- var networks = os.networkInterfaces();
268710
+ var networks = os$1.networkInterfaces();
268676
268711
  var names = Object.keys(networks);
268677
268712
  for (var i = 0; i < names.length; i++) {
268678
268713
  var net = networks[names[i]];
268679
268714
  for (var j = 0; j < net.length; j++) {
268680
268715
  var iface = net[j];
268681
268716
  if (isIPv4(iface.family) && !iface.internal) {
268682
- if (os.platform() === "darwin" && names[i] === "en0") return iface.address;
268717
+ if (os$1.platform() === "darwin" && names[i] === "en0") return iface.address;
268683
268718
  return "0.0.0.0";
268684
268719
  }
268685
268720
  }
@@ -268687,7 +268722,7 @@ var import_multicast_dns = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMi
268687
268722
  return "127.0.0.1";
268688
268723
  }
268689
268724
  function allInterfaces() {
268690
- var networks = os.networkInterfaces();
268725
+ var networks = os$1.networkInterfaces();
268691
268726
  var names = Object.keys(networks);
268692
268727
  var res = [];
268693
268728
  for (var i = 0; i < names.length; i++) {
@@ -297643,7 +297678,7 @@ function resolveBuildCommitFromEnv(env) {
297643
297678
  * environment descriptor down instead of reporting an honest "unknown".
297644
297679
  */
297645
297680
  function readBakedBuildCommit() {
297646
- return "6acacb9afbe43fd2b2053436daaddaec3459a517";
297681
+ return "c9bc6514eb0d5f3c0eff3b12d396a11f7a96abef";
297647
297682
  }
297648
297683
  async function resolveServerBuildCommit(input) {
297649
297684
  if (isFullCommitSha(input.baked)) return input.baked;
@@ -297771,9 +297806,10 @@ function executionEnvironmentCapabilities(serverConfig) {
297771
297806
  auth: {
297772
297807
  cmmd: true,
297773
297808
  ownerBootstrapPromotion: !isRuntime && !isCloudShell,
297774
- bearerBootstrap: true,
297809
+ bearerBootstrap: !isCloudShell,
297775
297810
  desktopBootstrap: serverConfig.mode === "desktop" || serverConfig.desktopBootstrapToken !== void 0,
297776
- providerCredentialBroker: false
297811
+ providerCredentialBroker: false,
297812
+ cloudOwnerAssertion: isCloudShell
297777
297813
  },
297778
297814
  tools: {
297779
297815
  agents: isExecutionPlane,
@@ -297863,6 +297899,145 @@ const ServerEnvironmentLive = effect(ServerEnvironment, fn$1("makeServerEnvironm
297863
297899
  };
297864
297900
  })());
297865
297901
  //#endregion
297902
+ //#region src/auth/http/cloudOwnerDeviceSession.ts
297903
+ /**
297904
+ * Native OAuth clients allowed to redeem a Forge Cloud session grant through
297905
+ * this bearer lane. Starts with only the mobile client from FOR-36841's
297906
+ * companion CMMD change; widen deliberately, not by accident.
297907
+ */
297908
+ const CLOUD_NATIVE_OAUTH_CLIENT_IDS = new Set(["cmmd-forge-mobile"]);
297909
+ const CloudOwnerSessionRedeemInput = Struct({
297910
+ sessionGrant: String$1,
297911
+ clientId: String$1
297912
+ });
297913
+ function outsideCloudShell() {
297914
+ return fail(new AuthError({
297915
+ message: "Forge Cloud session redemption is unavailable.",
297916
+ status: 403
297917
+ }));
297918
+ }
297919
+ function redeemSessionGrantWithCmmd(input) {
297920
+ return tryPromise({
297921
+ try: () => fetch(resolveCommandAppEndpoint("/api/mcp-server/oauth/token"), {
297922
+ method: "POST",
297923
+ headers: { "content-type": "application/json" },
297924
+ body: JSON.stringify({
297925
+ grant_type: "forge_session_grant",
297926
+ grant_token: input.sessionGrant,
297927
+ client_id: input.clientId
297928
+ })
297929
+ }),
297930
+ catch: (cause) => new AuthError({
297931
+ message: "Failed to redeem the Forge Cloud session grant with CMMD.",
297932
+ status: 500,
297933
+ cause
297934
+ })
297935
+ }).pipe(flatMap((response) => readCmmdJsonResponse(response, "Failed to redeem the Forge Cloud session grant with CMMD.")));
297936
+ }
297937
+ const cloudOwnerSessionRedeemRouteLayer = add$1("POST", "/api/auth/cloud/session", gen(function* () {
297938
+ const serverAuth = yield* ServerAuth;
297939
+ if (serverAuth.isForgeCloudShell?.() !== true) return yield* outsideCloudShell();
297940
+ const request = yield* HttpServerRequest;
297941
+ const limited = rateLimitOrRespond({
297942
+ routeClass: "auth-bootstrap",
297943
+ clientKey: clientKeyFromRequest(request)
297944
+ });
297945
+ if (limited) return limited;
297946
+ yield* serverAuth.authenticateHttpRequest(request);
297947
+ const payload = yield* schemaBodyJson(CloudOwnerSessionRedeemInput).pipe(mapError((cause) => new AuthError({
297948
+ message: "Invalid Forge Cloud session redeem payload.",
297949
+ status: 400,
297950
+ cause
297951
+ })));
297952
+ if (!CLOUD_NATIVE_OAUTH_CLIENT_IDS.has(payload.clientId)) return yield* new AuthError({
297953
+ message: "Unknown Forge Cloud native client.",
297954
+ status: 403
297955
+ });
297956
+ const tokenResult = yield* redeemSessionGrantWithCmmd(payload);
297957
+ const { tokenObj, cmmdIdentity, userId, accessToken, mcpToken, refreshToken, forgeOwnerAssertion, sessionGrantExpiresAt, isJwt, patToStore, jwtToStore } = yield* resolveCmmdTokenBundleCredentials(tokenResult);
297958
+ if (!cmmdIdentity || !userId || !patToStore) {
297959
+ yield* logError("Forge Cloud session redeem: CMMD token response incomplete.", {
297960
+ clientId: payload.clientId,
297961
+ tokenResponse: describeCmmdTokenResponseForLog({
297962
+ tokenObj,
297963
+ tokenResult,
297964
+ accessToken,
297965
+ mcpToken,
297966
+ refreshToken,
297967
+ isJwt,
297968
+ userId,
297969
+ patToStore,
297970
+ jwtToStore
297971
+ })
297972
+ });
297973
+ return yield* new AuthError({
297974
+ message: "CMMD did not return a complete redeemed Forge Cloud session.",
297975
+ status: 500
297976
+ });
297977
+ }
297978
+ const assertion = yield* validateCloudOwnerCallbackAssertion({
297979
+ serverAuth,
297980
+ assertionToken: forgeOwnerAssertion,
297981
+ authenticatedUserId: userId
297982
+ });
297983
+ if (!assertion) return yield* die$1(/* @__PURE__ */ new Error("Expected a redeemed Forge Cloud owner assertion for a native client."));
297984
+ yield* persistCloudOwnerArtifacts({
297985
+ assertion,
297986
+ accessToken: patToStore,
297987
+ identity: cmmdIdentity,
297988
+ jwtToken: jwtToStore,
297989
+ refreshToken,
297990
+ sessionGrant: payload.sessionGrant,
297991
+ sessionGrantExpiresAt
297992
+ });
297993
+ yield* logInfo("Forge Cloud owner session redeemed.", {
297994
+ sessionId: assertion.sessionId,
297995
+ clientId: payload.clientId,
297996
+ userId
297997
+ });
297998
+ return jsonUnsafe({
297999
+ assertion: assertion.token,
298000
+ assertionExpiresAt: toUtc(assertion.expiresAt),
298001
+ sessionGrantExpiresAt,
298002
+ user: {
298003
+ id: assertion.userId,
298004
+ email: cmmdIdentity.email
298005
+ }
298006
+ }, { status: 200 });
298007
+ }).pipe(catchTag("AuthError", (error) => respondToAuthError(error))));
298008
+ /**
298009
+ * Seam for CMMD-side session-grant revocation.
298010
+ *
298011
+ * As of FOR-36841, CMMD does not expose an endpoint to revoke a
298012
+ * `forge_session_grant` token server-side (confirm with the CMMD change
298013
+ * before wiring this in). When it does, call it here, best-effort: a failure
298014
+ * revoking at CMMD must never block the local row deletion below, which is
298015
+ * this endpoint's actual guarantee. Left as a documented no-op rather than a
298016
+ * guess at CMMD's eventual route shape.
298017
+ */
298018
+ function revokeCmmdSessionGrantBestEffort(_sessionId) {
298019
+ return void_$1;
298020
+ }
298021
+ const cloudOwnerSessionRevokeRouteLayer = add$1("POST", "/api/auth/cloud/session/revoke", gen(function* () {
298022
+ const serverAuth = yield* ServerAuth;
298023
+ if (serverAuth.isForgeCloudShell?.() !== true) return yield* outsideCloudShell();
298024
+ const request = yield* HttpServerRequest;
298025
+ const limited = rateLimitOrRespond({
298026
+ routeClass: "auth-bootstrap",
298027
+ clientKey: clientKeyFromRequest(request)
298028
+ });
298029
+ if (limited) return limited;
298030
+ const session = yield* serverAuth.authenticateHttpRequest(request);
298031
+ yield* clearCommandAccessTokenForSession(session.sessionId).pipe(mapError((cause) => new AuthError({
298032
+ message: "Failed to revoke the Forge Cloud owner session.",
298033
+ status: 500,
298034
+ cause
298035
+ })));
298036
+ yield* revokeCmmdSessionGrantBestEffort(session.sessionId);
298037
+ yield* logInfo("Forge Cloud owner session revoked.", { sessionId: session.sessionId });
298038
+ return empty$6({ status: 204 });
298039
+ }).pipe(catchTag("AuthError", (error) => respondToAuthError(error))));
298040
+ //#endregion
297866
298041
  //#region src/docs/http.ts
297867
298042
  const SCALAR_API_REFERENCE_URL = "https://cdn.jsdelivr.net/npm/@scalar/api-reference@1.64.1";
297868
298043
  const SCALAR_API_REFERENCE_INTEGRITY = "sha384-SmFRDuBBmEoCbbBCTcn8/+cIQONyH0xgOKpsp6OttBlI8uG9uc6iAO8SonYQ3e1W";
@@ -299983,6 +300158,24 @@ function readWebSocketUpgradeToken(params) {
299983
300158
  }
299984
300159
  //#endregion
299985
300160
  //#region src/auth/Layers/ForgeCloudOwnerAuthentication.ts
300161
+ const BEARER_PREFIX = "Bearer ";
300162
+ /**
300163
+ * Reads a Forge Cloud owner assertion presented as `Authorization: Bearer
300164
+ * <assertion>`, alongside the cookie read below.
300165
+ *
300166
+ * Deliberately its own parser rather than a shared one: `ServerAuth.ts`
300167
+ * carries a bearer reader for the local bootstrap/pairing/desktop lanes, and
300168
+ * those must stay untouched by this change. This function is only ever
300169
+ * called from `authenticateForgeCloudOwnerRequest`, which itself only runs
300170
+ * when `serverConfig.forgeCloudShellMode === true` — so a non-cloud server's
300171
+ * bearer lanes never pass through here.
300172
+ */
300173
+ function readForgeCloudOwnerBearerToken(request) {
300174
+ const header = request.headers["authorization"];
300175
+ if (typeof header !== "string" || !header.startsWith(BEARER_PREFIX)) return null;
300176
+ const token = header.slice(7).trim();
300177
+ return token.length > 0 ? token : null;
300178
+ }
299986
300179
  function assertionVerifierConfig(config) {
299987
300180
  const publicKey = config.forgeCloudOwnerAssertionPublicKey;
299988
300181
  const keyId = config.forgeCloudOwnerAssertionKeyId;
@@ -300010,7 +300203,7 @@ function authenticateForgeCloudOwnerRequest(request, config) {
300010
300203
  message: "Forge Cloud owner assertion verification is misconfigured.",
300011
300204
  status: 500
300012
300205
  });
300013
- const token = request.cookies[FORGE_CLOUD_OWNER_ASSERTION_COOKIE];
300206
+ const token = request.cookies["forge_cloud_owner_assertion"] ?? readForgeCloudOwnerBearerToken(request);
300014
300207
  if (!token) return yield* new AuthError({
300015
300208
  message: "Authentication required.",
300016
300209
  status: 401
@@ -321069,7 +321262,7 @@ const MiscRuntimeLayerLive = mergeAll(AnalyticsServiceLayerLive, OpenLive, Serve
321069
321262
  const RuntimeDependenciesWithoutPullRequestsLive = ReactorLayerLive.pipe(provideMerge(CheckpointingLayerLive), provideMerge(GitLayerLive), provideMerge(OrchestrationLayerLive), provideMerge(ProviderLayerLive), provideMerge(TerminalLayerLive), provideMerge(AgentBrowserManagerLive), provideMerge(mergeAll(ThreadDevRuntimeLive, DevTunnelLive.pipe(provide$1(ThreadDevRuntimeLive)))), provideMerge(PersistenceLayerLive), provideMerge(KeybindingsLive), provideMerge(ProviderRegistryLive), provideMerge(mergeAll(ServerSettingsLive, UsageServiceLive.pipe(provide$1(ServerSettingsLive), provide$1(UsageSessionLookupLive), provide$1(UsageRecordedSourceLive.pipe(provide$1(OrchestrationLayerLive), provide$1(RuntimeReplicationOutboxLive), provide$1(ProviderSessionRuntimeRepositoryLive), provide$1(TextGenerationSessionRepositoryLive)))))), provideMerge(WorkspaceLayerLive), provideMerge(WorkspaceReadinessStateLive), provideMerge(ProjectFaviconResolverLive), provideMerge(RepositoryIdentityResolverLive), provideMerge(ServerEnvironmentLive), provideMerge(AuthLayerLive), provideMerge(ServerSecretStoreLayerLive), provideMerge(mergeAll(CloudRedisCoordinationLive, CloudStateSyncLive.pipe(provide$1(PersistenceLayerLive)))), provideMerge(MiscRuntimeLayerLive));
321070
321263
  const RuntimeDependenciesLive = pullRequestServiceFactoryLayer.pipe(provideMerge(RuntimeDependenciesWithoutPullRequestsLive), provideMerge(cmmdProjectIdentityResolverLive.pipe(provide$1(RuntimeDependenciesWithoutPullRequestsLive))), provideMerge(pullRequestProviderRuntimeLive));
321071
321264
  const RuntimeServicesLive = ServerRuntimeStartupLive.pipe(provideMerge(RuntimeDependenciesLive));
321072
- const makeRoutesLayer = mergeAll(attachmentUploadRouteLayer, authBearerBootstrapRouteLayer, authBootstrapRouteLayer, authCommandBearerBootstrapRouteLayer, authCommandBootstrapRouteLayer, authOAuthTokenExchangeRouteLayer, authWebSocketTicketRouteLayer, authCmmdOAuthDeviceRouteLayer, authCmmdOAuthTokenRouteLayer, authCmmdPasswordTokenRouteLayer, authClientsRevokeOthersRouteLayer, authClientsRevokeRouteLayer, authClientsRouteLayer, authLogoutRouteLayer, authPairingLinksRevokeRouteLayer, authPairingLinksRouteLayer, authPairingCredentialRouteLayer, authSessionRenewRouteLayer, authSessionRouteLayer, authWebSocketTokenRouteLayer, authWebLoginRouteLayer, authWebCallbackRouteLayer, desktopLocalWebBridgePublishRouteLayer, desktopLocalWebBridgeBootstrapRouteLayer, desktopRelayControlPlaneRouteLayer, apiDocsHtmlRouteLayer, apiDocsOpenApiRouteLayer, pulseSignalStateListRouteLayer, pulseSignalStateUpsertRouteLayer, cmmdProfileRouteLayer, ...cmmdProxyRouteLayers, cmmdWorkIntakeRouteLayer, cmmdWsTokenRouteLayer, cmmdGetProjectCommentsRouteLayer, cmmdAddProjectCommentRouteLayer, cmmdUpdateProjectRouteLayer, cmmdUploadTaskAttachmentRouteLayer, cmmdUpdateTaskCommentRouteLayer, cmmdListWorkspaceMembersRouteLayer, cmmdAddProjectMemberRouteLayer, cmmdUpdateProjectAgentMemberAvatarRouteLayer, runtimeReplicationStatusRouteLayer, cmmdRefreshProviderUsageRouteLayer, cmmdForgeCloudProjectsListRouteLayer, cmmdForgeCloudProjectsEventsRouteLayer, 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(provide$1(browserApiCorsLayer));
321265
+ const makeRoutesLayer = mergeAll(attachmentUploadRouteLayer, authBearerBootstrapRouteLayer, authBootstrapRouteLayer, authCommandBearerBootstrapRouteLayer, authCommandBootstrapRouteLayer, 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, 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(provide$1(browserApiCorsLayer));
321073
321266
  const runServer = launch$1(unwrap(gen(function* () {
321074
321267
  const config = yield* ServerConfig;
321075
321268
  fixPath({ preferProcessPath: config.forgeCmmdRuntimeMode === true });
@@ -321999,7 +322192,7 @@ const connectLoginCommand = make$34("login", {
321999
322192
  })).json();
322000
322193
  },
322001
322194
  openBrowser: async (url) => {
322002
- const open = (await import("./open-D8mGyWmt.mjs")).default;
322195
+ const open = (await import("./open-FIgTDosf.mjs")).default;
322003
322196
  await open(url);
322004
322197
  return true;
322005
322198
  },