@cmmd-center/forge 0.14.1 → 0.14.2

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 (71) hide show
  1. package/dist/bin.cjs +487 -41
  2. package/dist/bin.mjs +511 -65
  3. package/dist/client/assets/{CanvasPanel-BeSvnc85.js → CanvasPanel-Bk1rL0LG.js} +3 -3
  4. package/dist/client/assets/CompositeItem-D1GrQhpu.js +2 -0
  5. package/dist/client/assets/CompositeList-CEBk8sf_.js +2 -0
  6. package/dist/client/assets/DiffPanel-jC25MVg8.js +64 -0
  7. package/dist/client/assets/{DiffPanel.logic-CAb-hngG.js → DiffPanel.logic-DHo4jHZM.js} +6 -6
  8. package/dist/client/assets/{DiffWorkerPoolProvider-BbyQb-ye.js → DiffWorkerPoolProvider-r1DUfA1d.js} +10 -10
  9. package/dist/client/assets/Effect-CyyMOw4X.js +15 -0
  10. package/dist/client/assets/PullRequestCodePanel-BWuWxI6X.js +5 -0
  11. package/dist/client/assets/Schema-8EGdYSms.js +3 -0
  12. package/dist/client/assets/Stream-Bvl2Z6qI.js +2 -0
  13. package/dist/client/assets/ThemeEditorPanel-BOkxNR3t.js +2 -0
  14. package/dist/client/assets/Virtualizer-DOL71mrP.js +2 -0
  15. package/dist/client/assets/WorkerPoolContext-gI2-7Koc.js +2538 -0
  16. package/dist/client/assets/activeOrganizationId-CySrJzMX.js +2 -0
  17. package/dist/client/assets/atomRegistry-CzI2zp7d.js +3 -0
  18. package/dist/client/assets/{canvasStateStore-DYt9MgFu.js → canvasStateStore-dUmp5NFj.js} +2 -2
  19. package/dist/client/assets/chevron-left-BXpfeqsT.js +2 -0
  20. package/dist/client/assets/chevron-right-B103tjaJ.js +2 -0
  21. package/dist/client/assets/cmmdAuthRedirect-CHlAAdAc.js +3 -0
  22. package/dist/client/assets/cmmdEmbedMode-DhVvltxi.js +2 -0
  23. package/dist/client/assets/currentForgeWorkspaceStore-eKCBxKgv.js +2 -0
  24. package/dist/client/assets/dialog-BfhVD7Kq.js +2 -0
  25. package/dist/client/assets/getPseudoElementBounds-DBf0dPO9.js +2 -0
  26. package/dist/client/assets/index-BS4FABiE.js +301 -0
  27. package/dist/client/assets/minus-CbEeUDFT.js +2 -0
  28. package/dist/client/assets/popover-D8qvi5AD.js +2 -0
  29. package/dist/client/assets/{react-D4GEJ3ir.js → react-A0DLYjwn.js} +2 -2
  30. package/dist/client/assets/scroll-area-BDuz0PnB.js +2 -0
  31. package/dist/client/assets/sidebarV2CustomSnoozeDialog-pxOuwuY2.js +2 -0
  32. package/dist/client/assets/src-7C4X1Oy2.js +2 -0
  33. package/dist/client/assets/switch-BeaQwtm7.js +2 -0
  34. package/dist/client/assets/{themePalette-BWvBK22X.js → themePalette-D4rx0k29.js} +2 -2
  35. package/dist/client/assets/toggle--uJ9LmDV.js +2 -0
  36. package/dist/client/assets/toggle-group-6UcL78FY.js +2 -0
  37. package/dist/client/assets/tooltip-C_eHCky0.js +2 -0
  38. package/dist/client/assets/useOpenInteractionType-BFjAYgzR.js +2 -0
  39. package/dist/client/assets/{workspaceReadinessAtom-D6bgTvKb.js → workspaceReadinessAtom-BIm13kLJ.js} +2 -2
  40. package/dist/client/index.html +33 -31
  41. package/dist/forge-build.json +1 -1
  42. package/dist/{open-yHTUkd2Y.mjs → open-2-reBrpv.mjs} +3 -3
  43. package/package.json +1 -1
  44. package/dist/client/assets/CompositeItem-DTyKsv13.js +0 -2
  45. package/dist/client/assets/CompositeList-Bc8TYCyy.js +0 -2
  46. package/dist/client/assets/DiffPanel-3EEekk0W.js +0 -64
  47. package/dist/client/assets/PullRequestCodePanel--fricLiS.js +0 -5
  48. package/dist/client/assets/Stream-mUTFnSY9.js +0 -15
  49. package/dist/client/assets/ThemeEditorPanel-BCJuZ6ai.js +0 -2
  50. package/dist/client/assets/Virtualizer-DX75f7Mw.js +0 -2
  51. package/dist/client/assets/WorkerPoolContext-D_2lN-9Y.js +0 -1533
  52. package/dist/client/assets/atomRegistry-C1yW8tyM.js +0 -3
  53. package/dist/client/assets/chevron-left-1XbEOuXj.js +0 -2
  54. package/dist/client/assets/chevron-right-DAF000Jz.js +0 -2
  55. package/dist/client/assets/cmmdAuthRedirect-DRkKjx0G.js +0 -3
  56. package/dist/client/assets/cmmdForgeRuntimeListResource-Df9wlF4U.js +0 -1008
  57. package/dist/client/assets/currentForgeWorkspaceStore-CiCPSq18.js +0 -2
  58. package/dist/client/assets/dialog-C0bK23D8.js +0 -2
  59. package/dist/client/assets/getPseudoElementBounds-DJTX-eyW.js +0 -2
  60. package/dist/client/assets/index-BHReJBiJ.js +0 -301
  61. package/dist/client/assets/minus-_j91i8bf.js +0 -2
  62. package/dist/client/assets/popover-ClRgm7hB.js +0 -2
  63. package/dist/client/assets/scroll-area-BHqISEYq.js +0 -2
  64. package/dist/client/assets/sidebarV2CustomSnoozeDialog-lbAJoff_.js +0 -2
  65. package/dist/client/assets/src-B_wrKHuq.js +0 -3
  66. package/dist/client/assets/switch-BWQHMJkU.js +0 -2
  67. package/dist/client/assets/toggle-CORC1vE9.js +0 -2
  68. package/dist/client/assets/toggle-group-C9nAr6VB.js +0 -2
  69. package/dist/client/assets/tooltip-CrS-TEbN.js +0 -2
  70. package/dist/client/assets/useOpenInteractionType-KsRrk6uH.js +0 -2
  71. package/dist/client/assets/utils-xQJ3ak-X.js +0 -2
package/dist/bin.mjs CHANGED
@@ -23,7 +23,7 @@ import { createCipheriv, createDecipheriv, createHash, createHmac, createPublicK
23
23
  import * as FS$1 from "node:fs";
24
24
  import fs, { accessSync, chmodSync, constants, createReadStream, existsSync, mkdirSync, mkdtempSync, promises, readFile, readFileSync, readdir, readdirSync, realpathSync, rmSync, statSync, unlinkSync, writeFileSync } from "node:fs";
25
25
  import * as NodeOS from "node:os";
26
- import os, { homedir, networkInterfaces, tmpdir } from "node:os";
26
+ import $U, { homedir, networkInterfaces, tmpdir } from "node:os";
27
27
  import * as Path$1 from "node:path";
28
28
  import path, { basename, delimiter, dirname, extname, isAbsolute, join, posix, relative, resolve, sep } from "node:path";
29
29
  import { pathToFileURL } from "node:url";
@@ -26852,14 +26852,15 @@ function nonEmptyString$3(value) {
26852
26852
  }
26853
26853
  /**
26854
26854
  * Resolve the operational Sprite name for provision / connect / readiness gates.
26855
+ * Only the authoritative `spriteName` counts: missing, empty, and explicit-null
26856
+ * values all resolve to null.
26855
26857
  *
26856
- * @see CmmdRuntimeSpriteIdentitySource for the dual-contract rules.
26858
+ * @see CmmdRuntimeSpriteIdentitySource for the identity rules.
26857
26859
  */
26858
26860
  function resolveCmmdRuntimeSpriteName(runtime) {
26859
- if (Object.hasOwn(runtime, "spriteName")) return nonEmptyString$3(runtime.spriteName);
26860
- return nonEmptyString$3(runtime.flyMachineId);
26861
+ return nonEmptyString$3(runtime.spriteName);
26861
26862
  }
26862
- /** True when the payload carries an operational Sprite name after dual-read. */
26863
+ /** True when the payload carries an authoritative Sprite name. */
26863
26864
  function hasCmmdRuntimeSpriteIdentity(runtime) {
26864
26865
  return resolveCmmdRuntimeSpriteName(runtime) !== null;
26865
26866
  }
@@ -26934,7 +26935,8 @@ const WORKTREE_LAUNCH_ACTIVITY_KINDS = new Set([
26934
26935
  "setup-script.skipped",
26935
26936
  "setup-script.failed",
26936
26937
  "launch.agent-starting",
26937
- "launch.cancelled"
26938
+ "launch.cancelled",
26939
+ "checkout-launch"
26938
26940
  ]);
26939
26941
  function encodeCmmdRuntimeWorktreeLaunchActivityPayload(kind, payload) {
26940
26942
  return {
@@ -26988,6 +26990,258 @@ function decodeCmmdRuntimePlanActivity(payload) {
26988
26990
  };
26989
26991
  }
26990
26992
  //#endregion
26993
+ //#region ../../packages/contracts/src/checkoutLaunch.ts
26994
+ /**
26995
+ * One Checkout launch snapshot for a thread.
26996
+ *
26997
+ * Upstream (`pingdotgg/t3code`, WorktreeSetupTracker) owns this shape: one
26998
+ * snapshot, fixed activity id, handoff when the agent stage is done and the
26999
+ * turn has started. Forge keeps its own stage names. The admission wait is
27000
+ * the first stage of this same snapshot, because a shared Environment can be
27001
+ * busy before any Checkout activity exists.
27002
+ */
27003
+ const CHECKOUT_LAUNCH_ACTIVITY_KIND = "checkout-launch";
27004
+ const PREPARATION_STAGES = [
27005
+ {
27006
+ id: "resolve",
27007
+ label: "Resolve base branch"
27008
+ },
27009
+ {
27010
+ id: "checkout",
27011
+ label: "Check out files"
27012
+ },
27013
+ {
27014
+ id: "dependencies",
27015
+ label: "Prepare dependencies"
27016
+ },
27017
+ {
27018
+ id: "setup",
27019
+ label: "Start setup script"
27020
+ },
27021
+ {
27022
+ id: "agent",
27023
+ label: "Start agent"
27024
+ }
27025
+ ];
27026
+ function checkoutLaunchActivityId(threadId) {
27027
+ return `checkout-launch:${threadId}`;
27028
+ }
27029
+ function preparationStages(statusFor, detailFor = () => null) {
27030
+ return PREPARATION_STAGES.map((stage) => ({
27031
+ id: stage.id,
27032
+ label: stage.label,
27033
+ status: statusFor(stage.id),
27034
+ detail: detailFor(stage.id)
27035
+ }));
27036
+ }
27037
+ function beginPreparationCheckoutLaunch(input) {
27038
+ return {
27039
+ phase: "running",
27040
+ stages: preparationStages((id) => id === "resolve" ? "running" : "pending"),
27041
+ startedAt: input.startedAt,
27042
+ completedAt: null,
27043
+ error: null,
27044
+ cancellable: true,
27045
+ worktreePath: input.worktreePath ?? null,
27046
+ turnStartedAt: null
27047
+ };
27048
+ }
27049
+ function replaceStage(snapshot, id, patch) {
27050
+ return {
27051
+ ...snapshot,
27052
+ stages: snapshot.stages.map((stage) => stage.id === id ? {
27053
+ ...stage,
27054
+ ...patch
27055
+ } : stage)
27056
+ };
27057
+ }
27058
+ function ensurePreparation(snapshot, startedAt) {
27059
+ if (snapshot && snapshot.stages.some((stage) => stage.id === "resolve")) return snapshot;
27060
+ return beginPreparationCheckoutLaunch({
27061
+ startedAt: snapshot?.startedAt ?? startedAt,
27062
+ worktreePath: snapshot?.worktreePath ?? null
27063
+ });
27064
+ }
27065
+ function settlePhase(snapshot) {
27066
+ if (snapshot.phase === "failed" || snapshot.phase === "cancelled") return snapshot;
27067
+ const agent = snapshot.stages.find((stage) => stage.id === "agent");
27068
+ const setup = snapshot.stages.find((stage) => stage.id === "setup");
27069
+ const agentDone = agent?.status === "done" && snapshot.turnStartedAt !== null;
27070
+ const setupStillRunning = setup?.status === "running";
27071
+ if (!agentDone || setupStillRunning) return snapshot;
27072
+ return {
27073
+ ...snapshot,
27074
+ phase: "done",
27075
+ completedAt: snapshot.completedAt ?? snapshot.turnStartedAt,
27076
+ cancellable: false
27077
+ };
27078
+ }
27079
+ function payloadString(payload, key) {
27080
+ const value = payload?.[key];
27081
+ return typeof value === "string" && value.length > 0 ? value : null;
27082
+ }
27083
+ function payloadNumber(payload, key) {
27084
+ const value = payload?.[key];
27085
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
27086
+ }
27087
+ const STAGE_IDS = new Set([
27088
+ "resolve",
27089
+ "checkout",
27090
+ "dependencies",
27091
+ "setup",
27092
+ "agent"
27093
+ ]);
27094
+ const STAGE_STATUSES = new Set([
27095
+ "done",
27096
+ "failed",
27097
+ "skipped",
27098
+ "running"
27099
+ ]);
27100
+ const LAUNCH_FAILING_STAGES = new Set([
27101
+ "resolve",
27102
+ "checkout",
27103
+ "dependencies"
27104
+ ]);
27105
+ function stageIdFromPayload(payload) {
27106
+ const id = payloadString(payload, "id") ?? payloadString(payload, "stage");
27107
+ return id !== null && STAGE_IDS.has(id) ? id : null;
27108
+ }
27109
+ function stageStatus(status) {
27110
+ return status !== null && STAGE_STATUSES.has(status) ? status : "running";
27111
+ }
27112
+ function applyStage(snapshot, event) {
27113
+ const id = stageIdFromPayload(event.payload);
27114
+ if (!id) return snapshot;
27115
+ snapshot = ensurePreparation(snapshot, event.createdAt);
27116
+ const nextStatus = stageStatus(payloadString(event.payload, "status"));
27117
+ const percent = payloadNumber(event.payload, "percent");
27118
+ const detail = percent !== null ? `${Math.round(percent)}%` : payloadString(event.payload, "detail");
27119
+ const next = replaceStage(snapshot, id, {
27120
+ status: nextStatus,
27121
+ detail
27122
+ });
27123
+ if (nextStatus !== "failed" || !LAUNCH_FAILING_STAGES.has(id)) return next;
27124
+ return {
27125
+ ...next,
27126
+ phase: "failed",
27127
+ error: event.summary ?? next.error,
27128
+ completedAt: event.createdAt,
27129
+ cancellable: false
27130
+ };
27131
+ }
27132
+ /**
27133
+ * Fold one launch activity into the snapshot. A recorded `checkout-launch`
27134
+ * payload replaces the snapshot. Older per-step activities still fold so a
27135
+ * thread that never received the snapshot does not go blank.
27136
+ */
27137
+ function reduceCheckoutLaunch(snapshot, event) {
27138
+ if (event.kind === "checkout-launch") return readCheckoutLaunchSnapshot(event) ?? snapshot;
27139
+ if (event.kind === "worktree.requested") return beginPreparationCheckoutLaunch({
27140
+ startedAt: snapshot?.startedAt ?? event.createdAt,
27141
+ worktreePath: payloadString(event.payload, "worktreePath") ?? snapshot?.worktreePath ?? null
27142
+ });
27143
+ if (!snapshot && event.kind !== "worktree.stage" && event.kind !== "worktree.failed") snapshot = beginPreparationCheckoutLaunch({ startedAt: event.createdAt });
27144
+ if (!snapshot) return snapshot;
27145
+ return CHECKOUT_LAUNCH_EVENT_HANDLERS[event.kind]?.(snapshot, event) ?? snapshot;
27146
+ }
27147
+ function setSetup(snapshot, event, status, detail) {
27148
+ return replaceStage(ensurePreparation(snapshot, event.createdAt), "setup", {
27149
+ status,
27150
+ detail
27151
+ });
27152
+ }
27153
+ function applyCreated(snapshot, event) {
27154
+ return {
27155
+ ...snapshot,
27156
+ worktreePath: payloadString(event.payload, "worktreePath") ?? snapshot.worktreePath
27157
+ };
27158
+ }
27159
+ function applyOutput(snapshot, event) {
27160
+ const detail = payloadString(event.payload, "detail") ?? event.summary ?? null;
27161
+ const match = detail ? /Updating files:\s+(\d+)%/.exec(detail) : null;
27162
+ if (!match) return snapshot;
27163
+ return replaceStage(ensurePreparation(snapshot, event.createdAt), "checkout", {
27164
+ status: "running",
27165
+ detail: `${match[1]}%`
27166
+ });
27167
+ }
27168
+ function applyAgentStarting(snapshot, event) {
27169
+ const prepared = ensurePreparation(snapshot, event.createdAt);
27170
+ return {
27171
+ ...replaceStage(event.payload?.setupRequested === false ? setSetup(prepared, event, "skipped", "Not requested") : prepared, "agent", {
27172
+ status: "running",
27173
+ detail: event.summary ?? "Starting agent"
27174
+ }),
27175
+ cancellable: false
27176
+ };
27177
+ }
27178
+ function applyFailed(snapshot, event) {
27179
+ snapshot = ensurePreparation(snapshot, event.createdAt);
27180
+ const detail = payloadString(event.payload, "detail") ?? event.summary ?? snapshot.error;
27181
+ const running = snapshot.stages.find((stage) => stage.status === "running");
27182
+ return {
27183
+ ...running ? replaceStage(snapshot, running.id, {
27184
+ status: "failed",
27185
+ detail
27186
+ }) : snapshot,
27187
+ phase: "failed",
27188
+ error: detail ?? "Checkout launch failed",
27189
+ completedAt: event.createdAt,
27190
+ cancellable: false
27191
+ };
27192
+ }
27193
+ function applyCancelled(snapshot, event) {
27194
+ return {
27195
+ ...snapshot,
27196
+ phase: "cancelled",
27197
+ error: event.summary ?? "Launch cancelled",
27198
+ completedAt: event.createdAt,
27199
+ cancellable: false
27200
+ };
27201
+ }
27202
+ const CHECKOUT_LAUNCH_EVENT_HANDLERS = {
27203
+ "worktree.stage": applyStage,
27204
+ "worktree.created": applyCreated,
27205
+ "worktree.output": applyOutput,
27206
+ "setup-script.skipped": (snapshot, event) => setSetup(snapshot, event, "skipped", event.summary ?? "No setup script configured"),
27207
+ "setup-script.running": (snapshot, event) => setSetup(snapshot, event, "running", event.summary ?? null),
27208
+ "setup-script.started": (snapshot, event) => setSetup(snapshot, event, "done", event.summary ?? "Running in terminal"),
27209
+ "setup-script.completed": (snapshot, event) => setSetup(snapshot, event, "done", event.summary ?? null),
27210
+ "setup-script.failed": (snapshot, event) => setSetup(snapshot, event, "failed", event.summary ?? "Setup script failed"),
27211
+ "launch.agent-starting": applyAgentStarting,
27212
+ "worktree.failed": applyFailed,
27213
+ "launch.cancelled": applyCancelled
27214
+ };
27215
+ function withCheckoutLaunchTurnStarted(snapshot, turnStartedAt) {
27216
+ if (!turnStartedAt) return snapshot;
27217
+ const agent = snapshot.stages.find((stage) => stage.id === "agent");
27218
+ return settlePhase({
27219
+ ...agent && (agent.status === "running" || agent.status === "done") ? replaceStage(snapshot, "agent", {
27220
+ status: "done",
27221
+ detail: agent.detail
27222
+ }) : snapshot,
27223
+ turnStartedAt
27224
+ });
27225
+ }
27226
+ function readCheckoutLaunchSnapshot(activity) {
27227
+ if (activity.kind !== "checkout-launch") return null;
27228
+ const payload = activity.payload;
27229
+ if (!payload || typeof payload !== "object") return null;
27230
+ const record = payload;
27231
+ if (record.phase !== "running" && record.phase !== "done" && record.phase !== "failed" && record.phase !== "cancelled") return null;
27232
+ if (!Array.isArray(record.stages) || typeof record.startedAt !== "string") return null;
27233
+ return {
27234
+ phase: record.phase,
27235
+ stages: record.stages,
27236
+ startedAt: record.startedAt,
27237
+ completedAt: record.completedAt ?? null,
27238
+ error: record.error ?? null,
27239
+ cancellable: record.cancellable === true,
27240
+ worktreePath: record.worktreePath ?? null,
27241
+ turnStartedAt: record.turnStartedAt ?? null
27242
+ };
27243
+ }
27244
+ //#endregion
26991
27245
  //#region ../../packages/contracts/src/cmmdProjectPatch.ts
26992
27246
  const CMMD_PROJECT_PATCH_FIELDS = {
26993
27247
  title: "requiredText",
@@ -50753,7 +51007,7 @@ function _supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) {
50753
51007
  const min = forceColor || 0;
50754
51008
  if (env.TERM === "dumb") return min;
50755
51009
  if (process$1.platform === "win32") {
50756
- const osRelease = os.release().split(".");
51010
+ const osRelease = $U.release().split(".");
50757
51011
  if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) return Number(osRelease[2]) >= 14931 ? 3 : 2;
50758
51012
  return 1;
50759
51013
  }
@@ -53196,7 +53450,7 @@ var require_ProcessDetector = /* @__PURE__ */ __commonJSMin(((exports) => {
53196
53450
  exports.processDetector = void 0;
53197
53451
  const api_1 = (init_esm$1(), __toCommonJS(esm_exports$1));
53198
53452
  const semconv_1 = require_semconv$1();
53199
- const os$4 = __require("os");
53453
+ const os$3 = __require("os");
53200
53454
  /**
53201
53455
  * ProcessDetector will be used to detect the resources related current process running
53202
53456
  * and being instrumented from the NodeJS Process module.
@@ -53218,7 +53472,7 @@ var require_ProcessDetector = /* @__PURE__ */ __commonJSMin(((exports) => {
53218
53472
  };
53219
53473
  if (process.argv.length > 1) attributes[semconv_1.ATTR_PROCESS_COMMAND] = process.argv[1];
53220
53474
  try {
53221
- const userInfo = os$4.userInfo();
53475
+ const userInfo = os$3.userInfo();
53222
53476
  attributes[semconv_1.ATTR_PROCESS_OWNER] = userInfo.username;
53223
53477
  } catch (e) {
53224
53478
  api_1.diag.debug(`error obtaining process owner: ${e}`);
@@ -66141,7 +66395,7 @@ function normalizeNumberish(value) {
66141
66395
  }
66142
66396
  //#endregion
66143
66397
  //#region package.json
66144
- var version$1 = "0.14.1";
66398
+ var version$1 = "0.14.2";
66145
66399
  //#endregion
66146
66400
  //#region src/sentry.ts
66147
66401
  const SERVER_APP_NAME = "forge-server";
@@ -66427,6 +66681,12 @@ function deriveAuthClientMetadata(input) {
66427
66681
  * Hosts a PaaS assigns automatically. They are reachable, but they are not the
66428
66682
  * domain the product is registered under, so they must never end up in an OAuth
66429
66683
  * `redirect_uri`.
66684
+ *
66685
+ * The hosted web channels (forge#3557) are the deliberate exception: their
66686
+ * `.up.railway.app` hosts ARE product surfaces, the operator enumerates them in
66687
+ * `FORGE_WEB_CHANNEL_ORIGINS`, and CMMD's `cmmd-forge-web` OAuth client lists
66688
+ * their callback URIs as registered redirect URIs. Every other platform host
66689
+ * stays excluded.
66430
66690
  */
66431
66691
  const PLATFORM_DEFAULT_HOST_SUFFIXES = [".up.railway.app"];
66432
66692
  function isPlatformDefaultHost(hostname) {
@@ -66465,6 +66725,41 @@ function resolveForgePublicUrl(env = process.env) {
66465
66725
  if (canonicalDomain.toLowerCase() === configuredHost.toLowerCase()) return configured;
66466
66726
  return `https://${canonicalDomain}`.replace(/\/$/, "");
66467
66727
  }
66728
+ /**
66729
+ * Hosted web channel origins (forge#3557) allowed to complete OAuth against
66730
+ * this server, from `FORGE_WEB_CHANNEL_ORIGINS`.
66731
+ *
66732
+ * The channels serve the web bundle from their own domains and forward `/api`
66733
+ * here with `x-forwarded-host` set to the channel host. Login must build its
66734
+ * `redirect_uri` from that host, or CMMD's authorize round-trip returns the
66735
+ * browser to `FORGE_PUBLIC_URL` — an origin where the login's state and PKCE
66736
+ * cookies do not exist. Because anything on the internet can also send this
66737
+ * server an `x-forwarded-host` header, the accepted hosts are never inferred
66738
+ * from the request: the operator enumerates the exact channel origins, and
66739
+ * only those count.
66740
+ *
66741
+ * Entries are comma-separated origins (`https://host[:port]`); a bare hostname
66742
+ * assumes https. Every entry normalizes to its URL origin, so stray path,
66743
+ * query, or userinfo components never reach a redirect URI. Whitespace,
66744
+ * trailing slashes, and unparseable entries are dropped.
66745
+ */
66746
+ function resolveForgeWebChannelOrigins(env = process.env) {
66747
+ return (env.FORGE_WEB_CHANNEL_ORIGINS ?? "").split(",").map((entry) => entry.trim().replace(/\/+$/, "")).filter((entry) => entry.length > 0).map((entry) => entry.includes("://") ? entry : `https://${entry}`).filter((entry) => URL.canParse(entry)).map((entry) => new URL(entry).origin).filter((origin) => origin !== "null");
66748
+ }
66749
+ /**
66750
+ * The allowlisted channel origin for a forwarded host, or null when the host
66751
+ * is not one. Matching is on the URL host — hostname plus explicit port — so
66752
+ * an entry with a port only matches that exact host:port, and a lookalike or
66753
+ * suffix host never matches. The returned origin is the operator's registered
66754
+ * form, scheme included, so it is exactly the redirect URI CMMD's client
66755
+ * registry holds.
66756
+ */
66757
+ function matchForgeWebChannelOrigin(forwardedHost, env = process.env) {
66758
+ const candidate = forwardedHost.trim().toLowerCase();
66759
+ if (!candidate) return null;
66760
+ for (const origin of resolveForgeWebChannelOrigins(env)) if (new URL(origin).host.toLowerCase() === candidate) return origin;
66761
+ return null;
66762
+ }
66468
66763
  function forgeConfiguredPublicUrl() {
66469
66764
  return resolveForgePublicUrl();
66470
66765
  }
@@ -66506,7 +66801,11 @@ function forgePublicUrlForRequest(request) {
66506
66801
  const forwardedHost = normalizeForwardedHeaderValue(request.headers["x-forwarded-host"]);
66507
66802
  const forwardedProto = normalizeForwardedHeaderValue(request.headers["x-forwarded-proto"]) ?? "https";
66508
66803
  if (forwardedHost && (isLoopbackOAuthHost(forwardedHost) || isPrivateDevOAuthHost(forwardedHost))) return `${forwardedProto === "https" ? "https" : "http"}://${forwardedHost}`.replace(/\/$/, "");
66509
- if (forwardedHost) return forgeConfiguredPublicUrl();
66804
+ if (forwardedHost) {
66805
+ const channelOrigin = matchForgeWebChannelOrigin(forwardedHost);
66806
+ if (channelOrigin) return channelOrigin;
66807
+ return forgeConfiguredPublicUrl();
66808
+ }
66510
66809
  const directHost = normalizeForwardedHeaderValue(request.headers["host"]);
66511
66810
  if (directHost && (isLoopbackOAuthHost(directHost) || isPrivateDevOAuthHost(directHost))) return `http://${directHost}`.replace(/\/$/, "");
66512
66811
  return forgeConfiguredPublicUrl();
@@ -70439,6 +70738,19 @@ function createCloudflareTurnIceServerResolver(options) {
70439
70738
  }
70440
70739
  //#endregion
70441
70740
  //#region src/http.ts
70741
+ /**
70742
+ * What the catch-all does with a non-API GET. A managed cloud surface is
70743
+ * runtime-only no matter what files exist on disk — the web client's single
70744
+ * home is the static channels (forge#3567) — while a local server either
70745
+ * serves its bundle or reports itself unconfigured.
70746
+ */
70747
+ function resolveWebServingOutcome(input) {
70748
+ if (input.managedCloudSurface) return { kind: "runtime-only" };
70749
+ return input.staticDir ? {
70750
+ kind: "serve",
70751
+ staticDir: input.staticDir
70752
+ } : { kind: "unconfigured" };
70753
+ }
70442
70754
  const PROJECT_FAVICON_CACHE_CONTROL = "public, max-age=3600";
70443
70755
  const PROJECT_FAVICON_MISS_CACHE_CONTROL = "public, max-age=300";
70444
70756
  /**
@@ -70697,8 +71009,16 @@ const staticAndDevRouteLayer = add$1("GET", "*", gen(function* () {
70697
71009
  }, { status: 404 });
70698
71010
  const config = yield* ServerConfig;
70699
71011
  if (config.devUrl && isLoopbackHostname(url.value.hostname)) return redirect(resolveDevRedirectUrl(config.devUrl, url.value), { status: 302 });
70700
- const staticDir = config.staticDir ?? (config.devUrl ? yield* resolveStaticDir() : void 0);
70701
- if (!staticDir) return text$2("No static directory configured and no dev URL set.", { status: 503 });
71012
+ const outcome = resolveWebServingOutcome({
71013
+ staticDir: config.staticDir ?? (config.devUrl ? yield* resolveStaticDir() : void 0),
71014
+ managedCloudSurface: (config.forgeCloudShellMode ?? false) || (config.forgeCmmdRuntimeMode ?? false)
71015
+ });
71016
+ if (outcome.kind === "runtime-only") return jsonUnsafe({
71017
+ error: "not_found",
71018
+ message: "This server does not serve the web client."
71019
+ }, { status: 404 });
71020
+ if (outcome.kind === "unconfigured") return text$2("No static directory configured and no dev URL set.", { status: 503 });
71021
+ const staticDir = outcome.staticDir;
70702
71022
  const fileSystem = yield* FileSystem;
70703
71023
  const path = yield* Path$2;
70704
71024
  const staticRoot = path.resolve(staticDir);
@@ -75908,7 +76228,7 @@ var require_node_gyp_build = /* @__PURE__ */ __commonJSMin(((exports, module) =>
75908
76228
  var fs$6 = __require("fs");
75909
76229
  var path$5 = __require("path");
75910
76230
  var url$1 = __require("url");
75911
- var os$3 = __require("os");
76231
+ var os$2 = __require("os");
75912
76232
  var runtimeRequire = typeof __webpack_require__ === "function" ? __non_webpack_require__ : __require;
75913
76233
  var vars = process.config && process.config.variables || {};
75914
76234
  var prebuildsOnly = !!process.env.PREBUILDS_ONLY;
@@ -75916,8 +76236,8 @@ var require_node_gyp_build = /* @__PURE__ */ __commonJSMin(((exports, module) =>
75916
76236
  var abi = versions.modules;
75917
76237
  if (versions.deno || process.isBun) abi = "unsupported";
75918
76238
  var runtime = isElectron() ? "electron" : isNwjs() ? "node-webkit" : "node";
75919
- var arch = process.env.npm_config_arch || os$3.arch();
75920
- var platform = process.env.npm_config_platform || os$3.platform();
76239
+ var arch = process.env.npm_config_arch || os$2.arch();
76240
+ var platform = process.env.npm_config_platform || os$2.platform();
75921
76241
  var libc = process.env.LIBC || (isMusl(platform) ? "musl" : "glibc");
75922
76242
  var armv = process.env.ARM_VERSION || (arch === "arm64" ? "8" : vars.arm_version) || "";
75923
76243
  var uv = (versions.uv || "").split(".")[0];
@@ -88494,7 +88814,7 @@ function refreshForgeManagedGitHubRemoteCredentials(input, dependencies = {
88494
88814
  }));
88495
88815
  isCacheManaged = helper.code === 0 && helper.stdout.includes("cache");
88496
88816
  }
88497
- if (!isEmbedded && !isCacheManaged) return {
88817
+ if (!isEmbedded && !isCacheManaged && !input.allowUnmanagedRemote) return {
88498
88818
  status: 200,
88499
88819
  body: {
88500
88820
  refreshed: false,
@@ -93761,7 +94081,7 @@ const launchDetached = (launch) => gen(function* () {
93761
94081
  });
93762
94082
  const OpenLive = effect(Open, gen(function* () {
93763
94083
  const open = yield* tryPromise({
93764
- try: () => import("./open-yHTUkd2Y.mjs"),
94084
+ try: () => import("./open-2-reBrpv.mjs"),
93765
94085
  catch: (cause) => new OpenError$1({
93766
94086
  message: "failed to load browser opener",
93767
94087
  cause
@@ -94188,6 +94508,37 @@ const toBootstrapDispatchCommandCauseError = (cause) => {
94188
94508
  });
94189
94509
  };
94190
94510
  const bootstrapCommandId = (command, stage) => CommandId.make(`server:${stage}:${command.commandId}`);
94511
+ const publishCheckoutLaunchSnapshot = (input) => input.orchestrationEngine.dispatch({
94512
+ type: "thread.activity.append",
94513
+ commandId: CommandId.make(`server:checkout-launch:${crypto.randomUUID()}`),
94514
+ threadId: input.threadId,
94515
+ activity: {
94516
+ id: EventId.make(checkoutLaunchActivityId(input.threadId)),
94517
+ tone: input.snapshot.phase === "failed" ? "error" : "info",
94518
+ kind: CHECKOUT_LAUNCH_ACTIVITY_KIND,
94519
+ summary: "Checkout launch",
94520
+ payload: input.snapshot,
94521
+ turnId: null,
94522
+ createdAt: input.createdAt
94523
+ },
94524
+ createdAt: input.createdAt
94525
+ });
94526
+ const recordCheckoutLaunchEvent = (input) => {
94527
+ const next = reduceCheckoutLaunch(input.launch.snapshot, {
94528
+ kind: input.kind,
94529
+ createdAt: input.createdAt,
94530
+ summary: input.summary,
94531
+ payload: input.payload
94532
+ });
94533
+ input.launch.snapshot = next;
94534
+ if (!next) return void_$1;
94535
+ return publishCheckoutLaunchSnapshot({
94536
+ orchestrationEngine: input.orchestrationEngine,
94537
+ threadId: input.threadId,
94538
+ snapshot: next,
94539
+ createdAt: input.createdAt
94540
+ });
94541
+ };
94191
94542
  const appendSetupScriptActivity = (input) => input.orchestrationEngine.dispatch({
94192
94543
  type: "thread.activity.append",
94193
94544
  commandId: input.commandId,
@@ -94202,7 +94553,15 @@ const appendSetupScriptActivity = (input) => input.orchestrationEngine.dispatch(
94202
94553
  createdAt: input.createdAt
94203
94554
  },
94204
94555
  createdAt: input.createdAt
94205
- });
94556
+ }).pipe(andThen(() => recordCheckoutLaunchEvent({
94557
+ orchestrationEngine: input.orchestrationEngine,
94558
+ threadId: input.threadId,
94559
+ launch: input.launch,
94560
+ kind: input.kind,
94561
+ summary: input.summary,
94562
+ createdAt: input.createdAt,
94563
+ payload: input.payload
94564
+ })));
94206
94565
  const appendWorktreeActivity = (input) => input.orchestrationEngine.dispatch({
94207
94566
  type: "thread.activity.append",
94208
94567
  commandId: input.commandId,
@@ -94217,7 +94576,15 @@ const appendWorktreeActivity = (input) => input.orchestrationEngine.dispatch({
94217
94576
  createdAt: input.createdAt
94218
94577
  },
94219
94578
  createdAt: input.createdAt
94220
- });
94579
+ }).pipe(andThen(() => recordCheckoutLaunchEvent({
94580
+ orchestrationEngine: input.orchestrationEngine,
94581
+ threadId: input.threadId,
94582
+ launch: input.launch,
94583
+ kind: input.kind,
94584
+ summary: input.summary,
94585
+ createdAt: input.createdAt,
94586
+ payload: input.payload
94587
+ })));
94221
94588
  const truncateBootstrapOutputLine = (line) => {
94222
94589
  const trimmed = line.trim();
94223
94590
  if (trimmed.length <= MAX_BOOTSTRAP_OUTPUT_LINE_LENGTH) return trimmed;
@@ -94327,9 +94694,11 @@ const prepareBootstrapWorktree = (input) => gen(function* () {
94327
94694
  summary: "Creating worktree",
94328
94695
  createdAt: requestedAt,
94329
94696
  payload: requestedPayload,
94330
- tone: "info"
94697
+ tone: "info",
94698
+ launch: input.launch
94331
94699
  });
94332
94700
  if (bootstrapWorktree.startFromOrigin) {
94701
+ if (input.refreshSourceControlCredentials) yield* input.refreshSourceControlCredentials(projectCwd);
94333
94702
  if (!input.git.fetch) return yield* fail(/* @__PURE__ */ new Error("Git fetch is unavailable for origin worktree preparation."));
94334
94703
  yield* input.git.fetch({
94335
94704
  cwd: projectCwd,
@@ -94351,7 +94720,8 @@ const prepareBootstrapWorktree = (input) => gen(function* () {
94351
94720
  detail,
94352
94721
  stage: "fetch"
94353
94722
  },
94354
- tone: "error"
94723
+ tone: "error",
94724
+ launch: input.launch
94355
94725
  }).pipe(andThen(failCause$1(cause)));
94356
94726
  }));
94357
94727
  }
@@ -94369,7 +94739,8 @@ const prepareBootstrapWorktree = (input) => gen(function* () {
94369
94739
  summary: "Preparing worktree",
94370
94740
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
94371
94741
  payload: update,
94372
- tone: "info"
94742
+ tone: "info",
94743
+ launch: input.launch
94373
94744
  }).pipe(asVoid, ignore),
94374
94745
  progress: {
94375
94746
  onStdoutLine: (line) => {
@@ -94387,7 +94758,8 @@ const prepareBootstrapWorktree = (input) => gen(function* () {
94387
94758
  stream: "stdout",
94388
94759
  detail
94389
94760
  },
94390
- tone: "info"
94761
+ tone: "info",
94762
+ launch: input.launch
94391
94763
  }).pipe(asVoid, ignore);
94392
94764
  },
94393
94765
  onStderrLine: (line) => {
@@ -94405,7 +94777,8 @@ const prepareBootstrapWorktree = (input) => gen(function* () {
94405
94777
  stream: "stderr",
94406
94778
  detail
94407
94779
  },
94408
- tone: "info"
94780
+ tone: "info",
94781
+ launch: input.launch
94409
94782
  }).pipe(asVoid, ignore);
94410
94783
  }
94411
94784
  }
@@ -94424,7 +94797,8 @@ const prepareBootstrapWorktree = (input) => gen(function* () {
94424
94797
  ...requestedPayload,
94425
94798
  detail
94426
94799
  },
94427
- tone: "error"
94800
+ tone: "error",
94801
+ launch: input.launch
94428
94802
  }).pipe(andThen(failCause$1(cause)));
94429
94803
  }));
94430
94804
  const worktreePath = worktree.worktree.path;
@@ -94448,7 +94822,8 @@ const prepareBootstrapWorktree = (input) => gen(function* () {
94448
94822
  worktreePath,
94449
94823
  checkedOutBranch: worktree.worktree.branch
94450
94824
  },
94451
- tone: "info"
94825
+ tone: "info",
94826
+ launch: input.launch
94452
94827
  });
94453
94828
  return {
94454
94829
  worktreePath,
@@ -94464,6 +94839,7 @@ const dispatchBootstrapTurnStart$1 = (input, command) => gen(function* () {
94464
94839
  let targetProjectCwd = bootstrap?.prepareWorktree?.projectCwd;
94465
94840
  let targetWorktreePath = bootstrap?.createThread?.worktreePath ?? null;
94466
94841
  let worktreeFailureReported = false;
94842
+ const launch = { snapshot: null };
94467
94843
  const cleanupCreatedThread = () => createdThread && !worktreeFailureReported ? orchestrationEngine.dispatch({
94468
94844
  type: "thread.delete",
94469
94845
  commandId: bootstrapCommandId(command, "bootstrap-thread-delete"),
@@ -94482,7 +94858,8 @@ const dispatchBootstrapTurnStart$1 = (input, command) => gen(function* () {
94482
94858
  detail,
94483
94859
  worktreePath: record.worktreePath
94484
94860
  },
94485
- tone: "error"
94861
+ tone: "error",
94862
+ launch
94486
94863
  }).pipe(ignoreCause({ log: false }), flatMap(() => logWarning$1("bootstrap turn start failed to launch setup script", {
94487
94864
  threadId: command.threadId,
94488
94865
  worktreePath: record.worktreePath,
@@ -94504,7 +94881,8 @@ const dispatchBootstrapTurnStart$1 = (input, command) => gen(function* () {
94504
94881
  summary: "Starting setup script",
94505
94882
  createdAt: record.requestedAt,
94506
94883
  payload,
94507
- tone: "info"
94884
+ tone: "info",
94885
+ launch
94508
94886
  }), appendSetupScriptActivity({
94509
94887
  orchestrationEngine,
94510
94888
  threadId: command.threadId,
@@ -94513,7 +94891,8 @@ const dispatchBootstrapTurnStart$1 = (input, command) => gen(function* () {
94513
94891
  summary: "Setup script started",
94514
94892
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
94515
94893
  payload,
94516
- tone: "info"
94894
+ tone: "info",
94895
+ launch
94517
94896
  })]).pipe(asVoid, catch_((error) => logWarning$1("bootstrap turn start launched setup script but failed to record setup activity", {
94518
94897
  threadId: command.threadId,
94519
94898
  worktreePath: record.worktreePath,
@@ -94545,7 +94924,8 @@ const dispatchBootstrapTurnStart$1 = (input, command) => gen(function* () {
94545
94924
  summary: "No setup script configured",
94546
94925
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
94547
94926
  payload: {},
94548
- tone: "info"
94927
+ tone: "info",
94928
+ launch
94549
94929
  }).pipe(asVoid, ignore);
94550
94930
  return recordSetupScriptStarted({
94551
94931
  requestedAt,
@@ -94566,7 +94946,9 @@ const dispatchBootstrapTurnStart$1 = (input, command) => gen(function* () {
94566
94946
  orchestrationEngine,
94567
94947
  git,
94568
94948
  gitStatusBroadcaster,
94569
- command
94949
+ ...input.refreshSourceControlCredentials ? { refreshSourceControlCredentials: input.refreshSourceControlCredentials } : {},
94950
+ command,
94951
+ launch
94570
94952
  }).pipe(catchCause((cause) => {
94571
94953
  if (!hasInterruptsOnly(cause)) worktreeFailureReported = true;
94572
94954
  return failCause$1(cause);
@@ -94584,10 +94966,22 @@ const dispatchBootstrapTurnStart$1 = (input, command) => gen(function* () {
94584
94966
  summary: "Starting agent",
94585
94967
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
94586
94968
  payload: { setupRequested: bootstrap.runSetupScript === true },
94587
- tone: "info"
94969
+ tone: "info",
94970
+ launch
94588
94971
  }).pipe(ignore);
94589
94972
  sealBootstrapCommand(orchestrationEngine, command.commandId);
94590
- return yield* orchestrationEngine.dispatch(finalTurnStartCommand);
94973
+ const started = yield* orchestrationEngine.dispatch(finalTurnStartCommand);
94974
+ if (launch.snapshot && bootstrap?.prepareWorktree) {
94975
+ const settledAt = (/* @__PURE__ */ new Date()).toISOString();
94976
+ launch.snapshot = withCheckoutLaunchTurnStarted(launch.snapshot, settledAt);
94977
+ yield* publishCheckoutLaunchSnapshot({
94978
+ orchestrationEngine,
94979
+ threadId: command.threadId,
94980
+ snapshot: launch.snapshot,
94981
+ createdAt: settledAt
94982
+ }).pipe(ignore);
94983
+ }
94984
+ return started;
94591
94985
  });
94592
94986
  return yield* runBootstrapCommand(orchestrationEngine, command.commandId, bootstrapProgram.pipe(onInterrupt(() => appendWorktreeActivity({
94593
94987
  orchestrationEngine,
@@ -94597,7 +94991,8 @@ const dispatchBootstrapTurnStart$1 = (input, command) => gen(function* () {
94597
94991
  summary: "Launch cancelled",
94598
94992
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
94599
94993
  payload: {},
94600
- tone: "info"
94994
+ tone: "info",
94995
+ launch
94601
94996
  }).pipe(ignore)), catchCause((cause) => {
94602
94997
  const dispatchError = toBootstrapDispatchCommandCauseError(cause);
94603
94998
  if (hasInterruptsOnly(cause)) return fail(dispatchError);
@@ -192236,7 +192631,7 @@ var Vce = {
192236
192631
  function Ve() {
192237
192632
  return Vce;
192238
192633
  }
192239
- var Us = os.homedir(), lk = os.tmpdir(), { env: Vc } = process$1, Yce = (e) => {
192634
+ var Us = $U.homedir(), lk = $U.tmpdir(), { env: Vc } = process$1, Yce = (e) => {
192240
192635
  let t = path.join(Us, "Library");
192241
192636
  return {
192242
192637
  data: path.join(t, "Application Support", e),
@@ -202436,12 +202831,12 @@ function qZ(e, t) {
202436
202831
  }
202437
202832
  return n;
202438
202833
  }
202439
- var os$2 = "https://code.claude.com/docs/en", kKe = [
202834
+ var os$1 = "https://code.claude.com/docs/en", kKe = [
202440
202835
  {
202441
202836
  matches: (e) => e.path === "permissions.defaultMode" && e.code === "invalid_value",
202442
202837
  tip: {
202443
202838
  suggestion: "Valid modes: \"acceptEdits\" (ask before file changes), \"plan\" (analysis only), \"bypassPermissions\" (auto-accept all), or \"default\" (standard behavior)",
202444
- docLink: `${os$2}/iam#permission-modes`
202839
+ docLink: `${os$1}/iam#permission-modes`
202445
202840
  }
202446
202841
  },
202447
202842
  {
@@ -202456,7 +202851,7 @@ var os$2 = "https://code.claude.com/docs/en", kKe = [
202456
202851
  matches: (e) => e.path.startsWith("env.") && e.code === "invalid_type",
202457
202852
  tip: {
202458
202853
  suggestion: "Environment variables must be strings. Wrap numbers and booleans in quotes. Example: \"DEBUG\": \"true\", \"PORT\": \"3000\"",
202459
- docLink: `${os$2}/settings#environment-variables`
202854
+ docLink: `${os$1}/settings#environment-variables`
202460
202855
  }
202461
202856
  },
202462
202857
  {
@@ -202467,14 +202862,14 @@ var os$2 = "https://code.claude.com/docs/en", kKe = [
202467
202862
  matches: (e) => e.path.startsWith("hooks.") && e.code === "invalid_key",
202468
202863
  tip: {
202469
202864
  suggestion: "Not a recognized hook event. Common events: PreToolUse, PostToolUse, UserPromptSubmit, SessionStart, SessionEnd, Stop. Check spelling and capitalization.",
202470
- docLink: `${os$2}/hooks`
202865
+ docLink: `${os$1}/hooks`
202471
202866
  }
202472
202867
  },
202473
202868
  {
202474
202869
  matches: (e) => /\.hooks\.\d+\.command$/.test(e.path) && e.code === "invalid_type" && e.received === "undefined",
202475
202870
  tip: {
202476
202871
  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\"}.",
202477
- docLink: `${os$2}/hooks#exec-form-and-shell-form`
202872
+ docLink: `${os$1}/hooks#exec-form-and-shell-form`
202478
202873
  }
202479
202874
  },
202480
202875
  {
@@ -202489,7 +202884,7 @@ var os$2 = "https://code.claude.com/docs/en", kKe = [
202489
202884
  matches: (e) => e.code === "unrecognized_keys",
202490
202885
  tip: {
202491
202886
  suggestion: "Check for typos or refer to the documentation for valid fields",
202492
- docLink: `${os$2}/settings`
202887
+ docLink: `${os$1}/settings`
202493
202888
  }
202494
202889
  },
202495
202890
  {
@@ -202504,13 +202899,13 @@ var os$2 = "https://code.claude.com/docs/en", kKe = [
202504
202899
  matches: (e) => e.path === "permissions.additionalDirectories" && e.code === "invalid_type",
202505
202900
  tip: {
202506
202901
  suggestion: "Must be an array of directory paths. Example: [\"~/projects\", \"/tmp/workspace\"]. You can also use --add-dir flag or /add-dir command",
202507
- docLink: `${os$2}/iam#working-directories`
202902
+ docLink: `${os$1}/iam#working-directories`
202508
202903
  }
202509
202904
  }
202510
202905
  ], AKe = {
202511
- permissions: `${os$2}/iam#configuring-permissions`,
202512
- env: `${os$2}/settings#environment-variables`,
202513
- hooks: `${os$2}/hooks`
202906
+ permissions: `${os$1}/iam#configuring-permissions`,
202907
+ env: `${os$1}/settings#environment-variables`,
202908
+ hooks: `${os$1}/hooks`
202514
202909
  };
202515
202910
  function GZ(e) {
202516
202911
  let t = kKe.find((r) => r.matches(e));
@@ -271648,7 +272043,7 @@ var import_multicast_dns = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMi
271648
272043
  var dgram = __require("dgram");
271649
272044
  var thunky = require_thunky();
271650
272045
  var events = __require("events");
271651
- var os$1 = __require("os");
272046
+ var os = __require("os");
271652
272047
  var noop = function() {};
271653
272048
  module.exports = function(opts) {
271654
272049
  if (!opts) opts = {};
@@ -271779,14 +272174,14 @@ var import_multicast_dns = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMi
271779
272174
  return that;
271780
272175
  };
271781
272176
  function defaultInterface() {
271782
- var networks = os$1.networkInterfaces();
272177
+ var networks = os.networkInterfaces();
271783
272178
  var names = Object.keys(networks);
271784
272179
  for (var i = 0; i < names.length; i++) {
271785
272180
  var net = networks[names[i]];
271786
272181
  for (var j = 0; j < net.length; j++) {
271787
272182
  var iface = net[j];
271788
272183
  if (isIPv4(iface.family) && !iface.internal) {
271789
- if (os$1.platform() === "darwin" && names[i] === "en0") return iface.address;
272184
+ if (os.platform() === "darwin" && names[i] === "en0") return iface.address;
271790
272185
  return "0.0.0.0";
271791
272186
  }
271792
272187
  }
@@ -271794,7 +272189,7 @@ var import_multicast_dns = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMi
271794
272189
  return "127.0.0.1";
271795
272190
  }
271796
272191
  function allInterfaces() {
271797
- var networks = os$1.networkInterfaces();
272192
+ var networks = os.networkInterfaces();
271798
272193
  var names = Object.keys(networks);
271799
272194
  var res = [];
271800
272195
  for (var i = 0; i < names.length; i++) {
@@ -291118,6 +291513,21 @@ function readProviderAccountId(selection) {
291118
291513
  return "providerAccountId" in selection ? selection.providerAccountId : void 0;
291119
291514
  }
291120
291515
  //#endregion
291516
+ //#region src/orchestration/expiredProviderEgressToken.ts
291517
+ const EXPIRED_PROVIDER_EGRESS_TOKEN = "Provider egress token is invalid or expired";
291518
+ /**
291519
+ * The CMMD egress authorizer uses this sentence for a token it will not
291520
+ * accept. A workspace reconnect 401 is a different failure and must not
291521
+ * take this path.
291522
+ *
291523
+ * The token lives in the provider process environment. Stopping that process
291524
+ * lets the next session start mint a new one. A later send of this same turn
291525
+ * delivers the user message a second time, so the recovery stops at the session.
291526
+ */
291527
+ function isExpiredProviderEgressTokenError(message) {
291528
+ return typeof message === "string" && message.includes(EXPIRED_PROVIDER_EGRESS_TOKEN);
291529
+ }
291530
+ //#endregion
291121
291531
  //#region src/persistence/Layers/ProjectionTurns.ts
291122
291532
  const ProjectionTurnDbRowSchema = ProjectionTurn.mapFields(assign({
291123
291533
  checkpointFiles: fromJsonString(ArraySchema(OrchestrationCheckpointFile)),
@@ -293010,6 +293420,7 @@ const ProviderRuntimeIngestionLive = effect(ProviderRuntimeIngestionService, fn$
293010
293420
  const handleRuntimeError = fn$1("handleRuntimeError")(function* (event, thread, eventTurnId, activeTurnId) {
293011
293421
  if (event.type !== "runtime.error") return;
293012
293422
  if (!shouldApplyRuntimeErrorEvent(activeTurnId, eventTurnId)) return;
293423
+ if (isExpiredProviderEgressTokenError(event.payload.message)) yield* providerService.stopSession({ threadId: thread.id }).pipe(catchCause(() => void_$1));
293013
293424
  yield* orchestrationEngine.dispatch({
293014
293425
  type: "thread.session.set",
293015
293426
  commandId: providerCommandId(event, "runtime-error-session-set"),
@@ -300659,7 +301070,7 @@ function resolveBuildCommitFromEnv(env) {
300659
301070
  * environment descriptor down instead of reporting an honest "unknown".
300660
301071
  */
300661
301072
  function readBakedBuildCommit() {
300662
- return "c541d41e992712a7ab25a9ae28ee5e3815e77426";
301073
+ return "a00cf0b653c5ad41933b286a3f108c74a8b61780";
300663
301074
  }
300664
301075
  async function resolveServerBuildCommit(input) {
300665
301076
  if (isFullCommitSha(input.baked)) return input.baked;
@@ -309835,6 +310246,26 @@ function readNonEmptyString(value) {
309835
310246
  //#region src/orchestration/http.ts
309836
310247
  const DISPATCH_CAUSE_DETAIL_LIMIT = 500;
309837
310248
  const DISPATCH_CAUSE_MAX_DEPTH = 8;
310249
+ const resolveBootstrapGithubAuth = (input) => {
310250
+ const runtimeGrantAuth = resolveRuntimeGitHubGrantAuth(input.config, input.headers);
310251
+ return runtimeGrantAuth ? succeed(runtimeGrantAuth) : resolveCmmdJwtForSession({
310252
+ sessionId: input.session.sessionId,
310253
+ subject: input.session.subject,
310254
+ logPrefix: "bootstrap-github"
310255
+ });
310256
+ };
310257
+ const refreshBootstrapSourceControlCredentials = (input) => gen(function* () {
310258
+ if (!input.auth) return yield* fail(/* @__PURE__ */ new Error("CMMD GitHub authorization is unavailable for Work Session bootstrap."));
310259
+ const result = yield* refreshForgeManagedGitHubRemoteCredentials({
310260
+ cwd: input.cwd,
310261
+ auth: typeof input.auth === "object" ? input.auth : {
310262
+ kind: "cmmd-jwt",
310263
+ jwt: input.auth
310264
+ },
310265
+ allowUnmanagedRemote: true
310266
+ }, void 0, input.forgeGitCredentialCache ? "cache" : "embed");
310267
+ if (result.status >= 400) return yield* fail(new Error(typeof result.body.error === "string" ? result.body.error : "CMMD GitHub authorization failed for Work Session bootstrap."));
310268
+ });
309838
310269
  /**
309839
310270
  * Render the underlying failure of a dispatch refusal as a single line.
309840
310271
  *
@@ -310273,7 +310704,9 @@ const orchestrationSnapshotRouteLayer = add$1("GET", "/api/orchestration/snapsho
310273
310704
  }))), { status: 200 });
310274
310705
  }).pipe(catchTag("AuthError", respondToOrchestrationAuthError), catchTag("OrchestrationGetSnapshotError", respondToOrchestrationHttpError)));
310275
310706
  const orchestrationDispatchRouteLayer = add$1("POST", "/api/orchestration/dispatch", gen(function* () {
310707
+ const request = yield* HttpServerRequest;
310276
310708
  const session = yield* authenticateOwnerSession$1;
310709
+ const config = yield* ServerConfig;
310277
310710
  const orchestrationEngine = yield* OrchestrationEngineService;
310278
310711
  const startup = yield* ServerRuntimeStartup;
310279
310712
  const { result } = yield* admitNormalizedDispatchCommand(yield* schemaBodyJson(ClientOrchestrationCommand).pipe(mapError((cause) => new OrchestrationDispatchCommandError({
@@ -310291,6 +310724,11 @@ const orchestrationDispatchRouteLayer = add$1("POST", "/api/orchestration/dispat
310291
310724
  });
310292
310725
  if (admissionRefusal !== null) return yield* new OrchestrationDispatchCommandError({ message: admissionRefusal });
310293
310726
  if (normalizedCommand.type === "thread.turn.start") recordThreadSession(normalizedCommand.threadId, session.sessionId);
310727
+ const bootstrapGithubAuth = normalizedCommand.type === "thread.turn.start" && normalizedCommand.bootstrap ? yield* resolveBootstrapGithubAuth({
310728
+ config,
310729
+ headers: request.headers,
310730
+ session
310731
+ }) : null;
310294
310732
  const dispatchEffect = normalizedCommand.type === "thread.turn.start" && normalizedCommand.bootstrap ? gen(function* () {
310295
310733
  const git = yield* serviceOption(GitCore);
310296
310734
  const gitStatusBroadcaster = yield* serviceOption(GitStatusBroadcaster);
@@ -310300,7 +310738,12 @@ const orchestrationDispatchRouteLayer = add$1("POST", "/api/orchestration/dispat
310300
310738
  orchestrationEngine,
310301
310739
  git: git.value,
310302
310740
  gitStatusBroadcaster: gitStatusBroadcaster.value,
310303
- projectSetupScriptRunner: projectSetupScriptRunner.value
310741
+ projectSetupScriptRunner: projectSetupScriptRunner.value,
310742
+ refreshSourceControlCredentials: (cwd) => refreshBootstrapSourceControlCredentials({
310743
+ forgeGitCredentialCache: config.forgeGitCredentialCache,
310744
+ auth: bootstrapGithubAuth,
310745
+ cwd
310746
+ })
310304
310747
  }, normalizedCommand);
310305
310748
  }).pipe(mapError((cause) => new OrchestrationDispatchCommandError({
310306
310749
  message: "Failed to dispatch orchestration command.",
@@ -313463,12 +313906,15 @@ registerProxyRoute({
313463
313906
  orgIdSource: "none"
313464
313907
  });
313465
313908
  const cmmdForgeCloudProjectsListRouteLayer = add$1("GET", "/api/cmmd/forge-cloud-projects", gen(function* () {
313909
+ const request = yield* HttpServerRequest;
313910
+ const organizationId = normalizeOrgIdQueryParam(new URL(request.url, "http://localhost").searchParams.get("orgId"));
313466
313911
  const { session, jwt: accessToken } = yield* makeCmmdProxyAccessTokenEffect("forge-cloud-projects");
313467
313912
  if (!accessToken) return yield* respondCmmdConnectionRequired(session.sessionId);
313468
313913
  const upstreamUrl = `${resolveCommandAppUrl()}${CMMD_CLOUD_PROJECTS_PATH}`;
313469
313914
  const upstreamOrError = yield* fetchCmmdUpstreamWithRetry(() => fetch(upstreamUrl, { headers: {
313470
313915
  Authorization: `Bearer ${accessToken}`,
313471
- Accept: "application/json"
313916
+ Accept: "application/json",
313917
+ ...organizationId !== void 0 ? { "X-Organization-ID": organizationId } : {}
313472
313918
  } }), "idempotent");
313473
313919
  if (!upstreamOrError.ok) return jsonUnsafe({
313474
313920
  error: "CMMD is unreachable. Check your connection.",
@@ -313572,10 +314018,11 @@ const FORGE_RUNTIMES_KICKOFF_FRAME = new TextEncoder().encode(": ok\n\n");
313572
314018
  * is the designed failure mode. Retrying here would stack a second backoff
313573
314019
  * inside the first and delay the frame the client is waiting on.
313574
314020
  */
313575
- function makeFetchForgeCatalogFrameEffect(proxy, upstreamUrl, accessToken) {
314021
+ function makeFetchForgeCatalogFrameEffect(proxy, upstreamUrl, accessToken, organizationId) {
313576
314022
  return tryPromise(() => fetch(upstreamUrl, { headers: {
313577
314023
  Authorization: `Bearer ${accessToken}`,
313578
- Accept: "application/json"
314024
+ Accept: "application/json",
314025
+ ...organizationId !== void 0 ? { "X-Organization-ID": organizationId } : {}
313579
314026
  } }).then(async (res) => {
313580
314027
  if (res.status === 401 || res.status === 403) {
313581
314028
  console.warn(`[${proxy.logTag}] upstream auth failure: ${res.status}`);
@@ -313600,9 +314047,11 @@ function makeFetchForgeCatalogFrameEffect(proxy, upstreamUrl, accessToken) {
313600
314047
  */
313601
314048
  function makeForgeCatalogEventsHandler(proxy, upstreamUrl) {
313602
314049
  return gen(function* () {
314050
+ const request = yield* HttpServerRequest;
314051
+ const organizationId = normalizeOrgIdQueryParam(new URL(request.url, "http://localhost").searchParams.get("orgId"));
313603
314052
  const { session, jwt: accessToken } = yield* makeCmmdProxyAccessTokenEffect(proxy.logTag);
313604
314053
  if (!accessToken) return yield* respondCmmdConnectionRequired(session.sessionId);
313605
- const fetchFrame = makeFetchForgeCatalogFrameEffect(proxy, upstreamUrl, accessToken);
314054
+ const fetchFrame = makeFetchForgeCatalogFrameEffect(proxy, upstreamUrl, accessToken, organizationId);
313606
314055
  const dataStream = concat$2(fromEffect(fetchFrame), tick(FORGE_RUNTIMES_POLL_INTERVAL).pipe(mapEffect$1(() => fetchFrame))).pipe(filter$2((frame) => frame !== null));
313607
314056
  const keepaliveStream = tick(FORGE_RUNTIMES_KEEPALIVE_INTERVAL).pipe(map$7(() => FORGE_RUNTIMES_KEEPALIVE_FRAME));
313608
314057
  return stream(concat$2(succeed$4(FORGE_RUNTIMES_KICKOFF_FRAME), merge$4(dataStream, keepaliveStream)).pipe(ensuring$1(sync$1(() => {
@@ -314092,10 +314541,7 @@ function isLegacyRuntimeConnection(value, expectedRuntimeId) {
314092
314541
  return [
314093
314542
  runtime.runtimeId === expectedRuntimeId,
314094
314543
  runtime.runtimeCapabilityMode === "legacy_env",
314095
- hasCmmdRuntimeSpriteIdentity({
314096
- ...Object.hasOwn(runtime, "spriteName") ? { spriteName: typeof runtime.spriteName === "string" ? runtime.spriteName : null } : {},
314097
- ...Object.hasOwn(runtime, "flyMachineId") ? { flyMachineId: typeof runtime.flyMachineId === "string" ? runtime.flyMachineId : null } : {}
314098
- }),
314544
+ hasCmmdRuntimeSpriteIdentity({ spriteName: typeof runtime.spriteName === "string" ? runtime.spriteName : null }),
314099
314545
  !Object.hasOwn(runtime, "brokerAuthorityAttestedForgeGitCommit"),
314100
314546
  runtime.brokerAuthorityAttestedImageRef === null,
314101
314547
  runtime.brokerAuthorityAttestedImageVersion === null,
@@ -326582,7 +327028,7 @@ const resolveServerConfig = (flags, cliLogLevel, options) => gen(function* () {
326582
327028
  const isManagedCloudSurface = (env.forgeCmmdRuntimeMode ?? false) || (env.forgeCloudShellMode ?? false);
326583
327029
  const autoBootstrapProjectFromCwd = getOrElse(resolveOptionPrecedence(fromUndefinedOr(options?.forceAutoBootstrapProjectFromCwd), isHeadlessStartup ? some(false) : none$4(), normalizedFlags.autoBootstrapProjectFromCwd, fromUndefinedOr(env.autoBootstrapProjectFromCwd), fromUndefinedOr(bootstrap?.autoBootstrapProjectFromCwd)), () => mode === "web" && !isManagedCloudSurface);
326584
327030
  const logWebSocketEvents = getOrElse(resolveOptionPrecedence(normalizedFlags.logWebSocketEvents, fromUndefinedOr(env.logWebSocketEvents), fromUndefinedOr(bootstrap?.logWebSocketEvents)), () => Boolean(devUrl));
326585
- const staticDir = devUrl ? void 0 : yield* resolveStaticDir();
327031
+ const staticDir = devUrl || isManagedCloudSurface ? void 0 : yield* resolveStaticDir();
326586
327032
  const host = getOrElse(resolveOptionPrecedence(normalizedFlags.host, fromUndefinedOr(env.host), fromUndefinedOr(bootstrap?.host)), () => mode === "desktop" ? "127.0.0.1" : void 0);
326587
327033
  const logLevel = getOrElse(cliLogLevel, () => env.logLevel);
326588
327034
  const browserWebRtcIceServers = parseBrowserWebRtcIceServers(env.browserWebRtcIceServers);
@@ -326766,16 +327212,16 @@ const findActiveProjectTarget = fn$1("findActiveProjectTarget")(function* (input
326766
327212
  workspaceRoot: resolved.workspaceRoot
326767
327213
  };
326768
327214
  });
326769
- const fetchLiveOrchestrationSnapshot = (origin, bearerToken$2) => runLiveServerRequest(get$7(`${origin}/api/orchestration/snapshot`).pipe(acceptJson, bearerToken(bearerToken$2)), matchStatus({
327215
+ const fetchLiveOrchestrationSnapshot = (origin, bearerToken$3) => runLiveServerRequest(get$7(`${origin}/api/orchestration/snapshot`).pipe(acceptJson, bearerToken(bearerToken$3)), matchStatus({
326770
327216
  "2xx": decodeOrchestrationReadModelResponse,
326771
327217
  orElse: (response) => readErrorMessageFromResponse(response).pipe(flatMap((message) => fail(new Error(message))))
326772
327218
  }));
326773
327219
  const ProjectLeadExplanationEnvelope = Struct({ data: ProjectLeadExplanationSchema });
326774
- const fetchLiveProjectLeadExplanation = (origin, bearerToken$1, projectId) => runLiveServerRequest(get$7(`${origin}/api/cmmd/projects/${projectId}/project-lead/explain`).pipe(acceptJson, bearerToken(bearerToken$1)), matchStatus({
327220
+ const fetchLiveProjectLeadExplanation = (origin, bearerToken$2, projectId) => runLiveServerRequest(get$7(`${origin}/api/cmmd/projects/${projectId}/project-lead/explain`).pipe(acceptJson, bearerToken(bearerToken$2)), matchStatus({
326775
327221
  "2xx": (response) => schemaBodyJson$1(ProjectLeadExplanationEnvelope)(response).pipe(map$3((body) => body.data)),
326776
327222
  orElse: (response) => readErrorMessageFromResponse(response).pipe(flatMap((message) => fail(new Error(message))))
326777
327223
  }));
326778
- const dispatchLiveOrchestrationCommand = (origin, bearerToken$3, command) => post(`${origin}/api/orchestration/dispatch`).pipe(acceptJson, bearerToken(bearerToken$3), bodyJson(command), flatMap((request) => runLiveServerRequest(request, matchStatus({
327224
+ const dispatchLiveOrchestrationCommand = (origin, bearerToken$1, command) => post(`${origin}/api/orchestration/dispatch`).pipe(acceptJson, bearerToken(bearerToken$1), bodyJson(command), flatMap((request) => runLiveServerRequest(request, matchStatus({
326779
327225
  "2xx": () => void_$1,
326780
327226
  orElse: (response) => readErrorMessageFromResponse(response).pipe(flatMap((message) => fail(new Error(message))))
326781
327227
  }))));
@@ -327076,7 +327522,7 @@ const connectLoginCommand = make$33("login", {
327076
327522
  })).json();
327077
327523
  },
327078
327524
  openBrowser: async (url) => {
327079
- const open = (await import("./open-yHTUkd2Y.mjs")).default;
327525
+ const open = (await import("./open-2-reBrpv.mjs")).default;
327080
327526
  await open(url);
327081
327527
  return true;
327082
327528
  },