@cmmd-center/forge 0.9.14 → 0.9.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.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, writeFileSync } from "node:fs";
24
24
  import * as NodeOS from "node:os";
25
- import os, { homedir, networkInterfaces, tmpdir } from "node:os";
25
+ import $U, { 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";
@@ -49482,7 +49482,7 @@ function _supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) {
49482
49482
  const min = forceColor || 0;
49483
49483
  if (env.TERM === "dumb") return min;
49484
49484
  if (process$1.platform === "win32") {
49485
- const osRelease = os.release().split(".");
49485
+ const osRelease = $U.release().split(".");
49486
49486
  if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) return Number(osRelease[2]) >= 14931 ? 3 : 2;
49487
49487
  return 1;
49488
49488
  }
@@ -51925,7 +51925,7 @@ var require_ProcessDetector = /* @__PURE__ */ __commonJSMin(((exports) => {
51925
51925
  exports.processDetector = void 0;
51926
51926
  const api_1 = (init_esm$1(), __toCommonJS(esm_exports$1));
51927
51927
  const semconv_1 = require_semconv$1();
51928
- const os$4 = __require("os");
51928
+ const os$3 = __require("os");
51929
51929
  /**
51930
51930
  * ProcessDetector will be used to detect the resources related current process running
51931
51931
  * and being instrumented from the NodeJS Process module.
@@ -51947,7 +51947,7 @@ var require_ProcessDetector = /* @__PURE__ */ __commonJSMin(((exports) => {
51947
51947
  };
51948
51948
  if (process.argv.length > 1) attributes[semconv_1.ATTR_PROCESS_COMMAND] = process.argv[1];
51949
51949
  try {
51950
- const userInfo = os$4.userInfo();
51950
+ const userInfo = os$3.userInfo();
51951
51951
  attributes[semconv_1.ATTR_PROCESS_OWNER] = userInfo.username;
51952
51952
  } catch (e) {
51953
51953
  api_1.diag.debug(`error obtaining process owner: ${e}`);
@@ -64870,7 +64870,7 @@ function normalizeNumberish(value) {
64870
64870
  }
64871
64871
  //#endregion
64872
64872
  //#region package.json
64873
- var version$1 = "0.9.14";
64873
+ var version$1 = "0.9.16";
64874
64874
  //#endregion
64875
64875
  //#region src/sentry.ts
64876
64876
  const SERVER_APP_NAME = "forge-server";
@@ -66692,13 +66692,13 @@ function validateCommandBootstrapPayload(payload) {
66692
66692
  });
66693
66693
  const payloadIdentity = normalizeCmmdUserIdentity(payload.user);
66694
66694
  if (payloadIdentity) {
66695
- const cmmdIdentity = payloadIdentity.avatarUrl ? payloadIdentity : mergeCmmdUserIdentity(payloadIdentity, yield* fetchOptionalCmmdIdentityForAccessToken(payload.accessToken));
66695
+ const cmmdIdentity = payloadIdentity.avatarUrl ? payloadIdentity : mergeCmmdUserIdentity(payloadIdentity, yield* fetchOptionalCmmdIdentityForAccessToken(cmmdUserCredential(payload)));
66696
66696
  return {
66697
66697
  subjectUserId: cmmdIdentity.id,
66698
66698
  cmmdIdentity
66699
66699
  };
66700
66700
  }
66701
- const tokenIdentity = yield* fetchCmmdIdentityForAccessToken(payload.accessToken);
66701
+ const tokenIdentity = yield* fetchCmmdIdentityForAccessToken(cmmdUserCredential(payload));
66702
66702
  if (tokenIdentity) return {
66703
66703
  subjectUserId: tokenIdentity.id,
66704
66704
  cmmdIdentity: tokenIdentity
@@ -66791,6 +66791,23 @@ function isCmmdJwtToken(token) {
66791
66791
  return token?.startsWith("eyJ") ?? false;
66792
66792
  }
66793
66793
  /**
66794
+ * The credential CMMD's userinfo accepts.
66795
+ *
66796
+ * Since CMMD's OAuth update, `access_token` is a JWT and the personal access
66797
+ * token arrives separately as `mcp_token`. userinfo authenticates the PAT and
66798
+ * rejects the JWT with `INVALID_OR_NON_USER_TOKEN`, so presenting the JWT
66799
+ * resolves no identity: the bootstrap then finds no subject and answers 401,
66800
+ * even though the sign-in itself succeeded and the caller did nothing wrong.
66801
+ *
66802
+ * `resolveCmmdTokenBundleCredentials` already draws this distinction for the
66803
+ * owner lanes. This is the same rule for the bootstrap lane, which had only ever
66804
+ * seen the pre-update shape where `access_token` was itself the PAT.
66805
+ */
66806
+ function cmmdUserCredential(payload) {
66807
+ const pat = payload.mcpToken?.trim();
66808
+ return isCmmdJwtToken(payload.accessToken) && pat ? pat : payload.accessToken;
66809
+ }
66810
+ /**
66794
66811
  * Read one CMMD token-exchange response into the credential set Forge stores.
66795
66812
  *
66796
66813
  * Both owner lanes redeem the same `cmmd-forge-web` authorization code — the
@@ -73483,7 +73500,7 @@ var require_node_gyp_build = /* @__PURE__ */ __commonJSMin(((exports, module) =>
73483
73500
  var fs$6 = __require("fs");
73484
73501
  var path$5 = __require("path");
73485
73502
  var url$1 = __require("url");
73486
- var os$3 = __require("os");
73503
+ var os$2 = __require("os");
73487
73504
  var runtimeRequire = typeof __webpack_require__ === "function" ? __non_webpack_require__ : __require;
73488
73505
  var vars = process.config && process.config.variables || {};
73489
73506
  var prebuildsOnly = !!process.env.PREBUILDS_ONLY;
@@ -73491,8 +73508,8 @@ var require_node_gyp_build = /* @__PURE__ */ __commonJSMin(((exports, module) =>
73491
73508
  var abi = versions.modules;
73492
73509
  if (versions.deno || process.isBun) abi = "unsupported";
73493
73510
  var runtime = isElectron() ? "electron" : isNwjs() ? "node-webkit" : "node";
73494
- var arch = process.env.npm_config_arch || os$3.arch();
73495
- var platform = process.env.npm_config_platform || os$3.platform();
73511
+ var arch = process.env.npm_config_arch || os$2.arch();
73512
+ var platform = process.env.npm_config_platform || os$2.platform();
73496
73513
  var libc = process.env.LIBC || (isMusl(platform) ? "musl" : "glibc");
73497
73514
  var armv = process.env.ARM_VERSION || (arch === "arm64" ? "8" : vars.arm_version) || "";
73498
73515
  var uv = (versions.uv || "").split(".")[0];
@@ -91150,7 +91167,7 @@ const launchDetached = (launch) => gen(function* () {
91150
91167
  });
91151
91168
  const OpenLive = effect(Open, gen(function* () {
91152
91169
  const open = yield* tryPromise({
91153
- try: () => import("./open-FIgTDosf.mjs"),
91170
+ try: () => import("./open-D8mGyWmt.mjs"),
91154
91171
  catch: (cause) => new OpenError$1({
91155
91172
  message: "failed to load browser opener",
91156
91173
  cause
@@ -91947,8 +91964,8 @@ const ProjectionThread = Struct({
91947
91964
  createdAt: IsoDateTime,
91948
91965
  updatedAt: IsoDateTime,
91949
91966
  archivedAt: NullOr(IsoDateTime),
91950
- settledAt: optional$4(NullOr(IsoDateTime)),
91951
- settledOverride: optional$4(NullOr(Literals(["settled", "active"]))),
91967
+ settledAt: NullOr(IsoDateTime),
91968
+ settledOverride: NullOr(Literals(["settled", "active"])),
91952
91969
  latestUserMessageAt: NullOr(IsoDateTime),
91953
91970
  pendingApprovalCount: NonNegativeInt,
91954
91971
  pendingUserInputCount: NonNegativeInt,
@@ -92133,16 +92150,23 @@ const FORGE_GIT_COMMIT_PATTERN = /^[0-9a-f]{7,40}$/i;
92133
92150
  /**
92134
92151
  * Resolve the commit this forge checkout actually booted from, so the
92135
92152
  * heartbeat can report it and CMMD can tell a stale Sprite from a current
92136
- * one. Sprites clone forge ONLY at bootstrap (see the CMMD-side bootstrap
92137
- * script) and nothing else refreshes them, so this value never changes for
92138
- * the life of the process — resolve it once and cache it.
92139
- *
92140
- * `git rev-parse HEAD` run with the process's own cwd (`apps/server`, a
92141
- * subdirectory of the cloned repo) walks up to find `.git` on its own; no
92142
- * `-C` path is needed. Returns `undefined` (never throws) when there is no
92143
- * git checkout to read local dev run from a tarball, or any environment
92144
- * this simply does not apply to so the heartbeat body omits the field
92145
- * exactly like an older Forge build that doesn't know about it yet.
92153
+ * one. The commit cannot change for the life of the process, so resolve it
92154
+ * once and cache it.
92155
+ *
92156
+ * This comment used to say Sprites clone forge at bootstrap and read their
92157
+ * commit from that checkout. They do not. A cloud runtime installs the
92158
+ * published `@cmmd-center/forge` tarball and has no `.git` anywhere near it,
92159
+ * which is why its descriptor reported no commit at all until the server
92160
+ * started baking one into its bundle. See `../environment/buildIdentity.ts`,
92161
+ * which calls this only as its last rung.
92162
+ *
92163
+ * `git rev-parse HEAD` run with the process's own cwd walks up to find `.git`
92164
+ * on its own, so no `-C` path is needed. That also means a cwd inside some
92165
+ * unrelated repository answers with that repository's HEAD, which is one more
92166
+ * reason this is the last rung rather than the first. Returns `undefined`
92167
+ * (never throws) when there is no checkout to read, so the heartbeat body
92168
+ * omits the field exactly like an older Forge build that does not know about
92169
+ * it yet.
92146
92170
  */
92147
92171
  let cached$1 = null;
92148
92172
  async function resolveForgeGitCommit(execImpl = (command, args) => execFileAsync$6(command, [...args], { timeout: 5e3 })) {
@@ -92509,7 +92533,7 @@ function hasValidRuntimeIdentity(value) {
92509
92533
  }
92510
92534
  function decodeRuntime$1(value) {
92511
92535
  if (!isRecord$16(value) || !hasValidRuntimeIdentity(value)) return null;
92512
- if (!nullableString(value.endpointUrl) || value.endpointUrl !== null && !URL.canParse(value.endpointUrl) || !nullableInteger(value.deviceId) || !nullableInteger(value.projectId) || !nullableString(value.projectName) || !nullableString(value.workspaceRoot) || !nullableString(value.githubRepoFullName) || !(value.lastSeenAt === null || isIsoDateTime(value.lastSeenAt)) || !(value.metadata === null || isRecord$16(value.metadata))) return null;
92536
+ if (!nullableString(value.endpointUrl) || value.endpointUrl !== null && !URL.canParse(value.endpointUrl) || !nullableInteger(value.deviceId) || !nullableString(value.workspaceRoot) || !nullableString(value.githubRepoFullName) || !(value.lastSeenAt === null || isIsoDateTime(value.lastSeenAt)) || !(value.metadata === null || isRecord$16(value.metadata))) return null;
92513
92537
  return value;
92514
92538
  }
92515
92539
  function decodeProject(value) {
@@ -92820,8 +92844,6 @@ function replicaRuntimeMatches(left, right) {
92820
92844
  [left.status, right.status],
92821
92845
  [left.endpointUrl, right.endpointUrl],
92822
92846
  [left.deviceId, right.deviceId],
92823
- [left.projectId, right.projectId],
92824
- [left.projectName, right.projectName],
92825
92847
  [left.workspaceRoot, right.workspaceRoot],
92826
92848
  [left.githubRepoFullName, right.githubRepoFullName],
92827
92849
  [left.lastSeenAt, right.lastSeenAt]
@@ -184358,7 +184380,7 @@ var Vce = {
184358
184380
  function Ve() {
184359
184381
  return Vce;
184360
184382
  }
184361
- var Us = os.homedir(), lk = os.tmpdir(), { env: Vc } = process$1, Yce = (e) => {
184383
+ var Us = $U.homedir(), lk = $U.tmpdir(), { env: Vc } = process$1, Yce = (e) => {
184362
184384
  let t = path.join(Us, "Library");
184363
184385
  return {
184364
184386
  data: path.join(t, "Application Support", e),
@@ -194558,12 +194580,12 @@ function qZ(e, t) {
194558
194580
  }
194559
194581
  return n;
194560
194582
  }
194561
- var os$2 = "https://code.claude.com/docs/en", kKe = [
194583
+ var os$1 = "https://code.claude.com/docs/en", kKe = [
194562
194584
  {
194563
194585
  matches: (e) => e.path === "permissions.defaultMode" && e.code === "invalid_value",
194564
194586
  tip: {
194565
194587
  suggestion: "Valid modes: \"acceptEdits\" (ask before file changes), \"plan\" (analysis only), \"bypassPermissions\" (auto-accept all), or \"default\" (standard behavior)",
194566
- docLink: `${os$2}/iam#permission-modes`
194588
+ docLink: `${os$1}/iam#permission-modes`
194567
194589
  }
194568
194590
  },
194569
194591
  {
@@ -194578,7 +194600,7 @@ var os$2 = "https://code.claude.com/docs/en", kKe = [
194578
194600
  matches: (e) => e.path.startsWith("env.") && e.code === "invalid_type",
194579
194601
  tip: {
194580
194602
  suggestion: "Environment variables must be strings. Wrap numbers and booleans in quotes. Example: \"DEBUG\": \"true\", \"PORT\": \"3000\"",
194581
- docLink: `${os$2}/settings#environment-variables`
194603
+ docLink: `${os$1}/settings#environment-variables`
194582
194604
  }
194583
194605
  },
194584
194606
  {
@@ -194589,14 +194611,14 @@ var os$2 = "https://code.claude.com/docs/en", kKe = [
194589
194611
  matches: (e) => e.path.startsWith("hooks.") && e.code === "invalid_key",
194590
194612
  tip: {
194591
194613
  suggestion: "Not a recognized hook event. Common events: PreToolUse, PostToolUse, UserPromptSubmit, SessionStart, SessionEnd, Stop. Check spelling and capitalization.",
194592
- docLink: `${os$2}/hooks`
194614
+ docLink: `${os$1}/hooks`
194593
194615
  }
194594
194616
  },
194595
194617
  {
194596
194618
  matches: (e) => /\.hooks\.\d+\.command$/.test(e.path) && e.code === "invalid_type" && e.received === "undefined",
194597
194619
  tip: {
194598
194620
  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\"}.",
194599
- docLink: `${os$2}/hooks#exec-form-and-shell-form`
194621
+ docLink: `${os$1}/hooks#exec-form-and-shell-form`
194600
194622
  }
194601
194623
  },
194602
194624
  {
@@ -194611,7 +194633,7 @@ var os$2 = "https://code.claude.com/docs/en", kKe = [
194611
194633
  matches: (e) => e.code === "unrecognized_keys",
194612
194634
  tip: {
194613
194635
  suggestion: "Check for typos or refer to the documentation for valid fields",
194614
- docLink: `${os$2}/settings`
194636
+ docLink: `${os$1}/settings`
194615
194637
  }
194616
194638
  },
194617
194639
  {
@@ -194626,13 +194648,13 @@ var os$2 = "https://code.claude.com/docs/en", kKe = [
194626
194648
  matches: (e) => e.path === "permissions.additionalDirectories" && e.code === "invalid_type",
194627
194649
  tip: {
194628
194650
  suggestion: "Must be an array of directory paths. Example: [\"~/projects\", \"/tmp/workspace\"]. You can also use --add-dir flag or /add-dir command",
194629
- docLink: `${os$2}/iam#working-directories`
194651
+ docLink: `${os$1}/iam#working-directories`
194630
194652
  }
194631
194653
  }
194632
194654
  ], AKe = {
194633
- permissions: `${os$2}/iam#configuring-permissions`,
194634
- env: `${os$2}/settings#environment-variables`,
194635
- hooks: `${os$2}/hooks`
194655
+ permissions: `${os$1}/iam#configuring-permissions`,
194656
+ env: `${os$1}/settings#environment-variables`,
194657
+ hooks: `${os$1}/hooks`
194636
194658
  };
194637
194659
  function GZ(e) {
194638
194660
  let t = kKe.find((r) => r.matches(e));
@@ -206237,7 +206259,7 @@ function createDevServerMcpServer(deps, threadId) {
206237
206259
  * per provider — this module only adapts descriptor + result shapes.
206238
206260
  *
206239
206261
  * Without it a Claude agent has no brokered push path at all and falls back to
206240
- * raw `git`, which the runtime deliberately blocks via `GIT_ASKPASS=/bin/false`.
206262
+ * raw `git`, which has no authenticated remote credentials in the agent runtime.
206241
206263
  *
206242
206264
  * The catalog is filtered through `applyPluginSkillPermissionsToDynamicTools`,
206243
206265
  * exactly as the Codex surface filters its dynamic tools. `settings` is required
@@ -258885,7 +258907,7 @@ var import_multicast_dns = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMi
258885
258907
  var dgram = __require("dgram");
258886
258908
  var thunky = require_thunky();
258887
258909
  var events = __require("events");
258888
- var os$1 = __require("os");
258910
+ var os = __require("os");
258889
258911
  var noop = function() {};
258890
258912
  module.exports = function(opts) {
258891
258913
  if (!opts) opts = {};
@@ -259016,14 +259038,14 @@ var import_multicast_dns = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMi
259016
259038
  return that;
259017
259039
  };
259018
259040
  function defaultInterface() {
259019
- var networks = os$1.networkInterfaces();
259041
+ var networks = os.networkInterfaces();
259020
259042
  var names = Object.keys(networks);
259021
259043
  for (var i = 0; i < names.length; i++) {
259022
259044
  var net = networks[names[i]];
259023
259045
  for (var j = 0; j < net.length; j++) {
259024
259046
  var iface = net[j];
259025
259047
  if (isIPv4(iface.family) && !iface.internal) {
259026
- if (os$1.platform() === "darwin" && names[i] === "en0") return iface.address;
259048
+ if (os.platform() === "darwin" && names[i] === "en0") return iface.address;
259027
259049
  return "0.0.0.0";
259028
259050
  }
259029
259051
  }
@@ -259031,7 +259053,7 @@ var import_multicast_dns = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMi
259031
259053
  return "127.0.0.1";
259032
259054
  }
259033
259055
  function allInterfaces() {
259034
- var networks = os$1.networkInterfaces();
259056
+ var networks = os.networkInterfaces();
259035
259057
  var names = Object.keys(networks);
259036
259058
  var res = [];
259037
259059
  for (var i = 0; i < names.length; i++) {
@@ -287313,13 +287335,32 @@ const ObservabilityLive = unwrap(gen(function* () {
287313
287335
  //#region ../../packages/shared/src/buildCommit.ts
287314
287336
  const COMMIT_SHA_PATTERN$1 = /^[0-9a-f]{40}$/i;
287315
287337
  /**
287338
+ * Whether a value is a full commit sha.
287339
+ *
287340
+ * Exported so each surface can keep its own list of env names while sharing one
287341
+ * definition of "valid". Surfaces genuinely differ: Railway injects
287342
+ * RAILWAY_GIT_COMMIT_SHA into the cloud shell build, while the desktop release
287343
+ * workflows pass VITE_SENTRY_COMMIT_SHA. What must not differ is the check.
287344
+ */
287345
+ function isFullCommitSha(value) {
287346
+ const candidate = value?.trim();
287347
+ return candidate !== void 0 && candidate !== "" && COMMIT_SHA_PATTERN$1.test(candidate);
287348
+ }
287349
+ /**
287316
287350
  * Build identity from the environment.
287317
287351
  *
287318
287352
  * `.dockerignore` excludes `.git`, so `git rev-parse HEAD` cannot resolve inside
287319
287353
  * the Railway container — the surface where "which commit is live?" is asked
287320
287354
  * most. The commit therefore has to arrive as env there, with git left as the
287321
- * fallback for surfaces that genuinely are checkouts (a sprite clones forge at
287322
- * bootstrap; desktop and dev run from a repo).
287355
+ * fallback for surfaces that genuinely are checkouts (desktop and dev run from
287356
+ * a repo).
287357
+ *
287358
+ * A cloud runtime is neither: it installs the published `@cmmd-center/forge`
287359
+ * tarball, so it has no build variables and no `.git`, and both rungs below
287360
+ * return nothing there. That is why the server bakes the commit into its bundle
287361
+ * at build time and reads that first — see `apps/server/src/environment/
287362
+ * buildIdentity.ts`. This comment previously claimed a sprite clones forge at
287363
+ * bootstrap; it does not, and believing it cost a diagnosis.
287323
287364
  *
287324
287365
  * `RAILWAY_GIT_COMMIT_SHA` is injected by the platform, which is what lets this
287325
287366
  * work on the cloud shell with no Dockerfile plumbing. `FORGE_BUILD_COMMIT`
@@ -287337,6 +287378,30 @@ function resolveBuildCommitFromEnv(env) {
287337
287378
  }
287338
287379
  }
287339
287380
  //#endregion
287381
+ //#region src/environment/buildIdentity.ts
287382
+ /**
287383
+ * Which commit this server was built from.
287384
+ *
287385
+ * @module
287386
+ */
287387
+ /**
287388
+ * The commit baked into this bundle, if it is a bundle at all.
287389
+ *
287390
+ * `typeof` rather than a bare reference: running from source there is no such
287391
+ * binding, and a bare reference is a ReferenceError that would take the whole
287392
+ * environment descriptor down instead of reporting an honest "unknown".
287393
+ */
287394
+ function readBakedBuildCommit() {
287395
+ return "e65edd4d1b8a361863a52b171b5e73295d691e11";
287396
+ }
287397
+ async function resolveServerBuildCommit(input) {
287398
+ if (isFullCommitSha(input.baked)) return input.baked;
287399
+ const fromEnv = resolveBuildCommitFromEnv(input.env);
287400
+ if (fromEnv !== void 0) return fromEnv;
287401
+ const fromGit = await input.readGitCommit();
287402
+ return isFullCommitSha(fromGit) ? fromGit : void 0;
287403
+ }
287404
+ //#endregion
287340
287405
  //#region src/environment/Layers/ServerEnvironmentLabel.ts
287341
287406
  function normalizeLabel(value) {
287342
287407
  const trimmed = value?.trim();
@@ -287484,7 +287549,11 @@ const ServerEnvironmentLive = effect(ServerEnvironment, fn$1("makeServerEnvironm
287484
287549
  const writeMetadata = (metadata) => fileSystem.writeFileString(serverConfig.environmentMetadataPath, `${JSON.stringify(metadata, null, 2)}\n`);
287485
287550
  const displayNameOverrideRef = yield* make$47(normalizeDisplayName((yield* readMetadata).displayName ?? null));
287486
287551
  const posthog = yield* PosthogPublicConfig.asEffect();
287487
- const buildCommit = resolveBuildCommitFromEnv(process.env) ?? (yield* promise(() => resolveForgeGitCommit()));
287552
+ const buildCommit = yield* promise(() => resolveServerBuildCommit({
287553
+ baked: readBakedBuildCommit(),
287554
+ env: process.env,
287555
+ readGitCommit: resolveForgeGitCommit
287556
+ }));
287488
287557
  const buildDescriptor = gen(function* () {
287489
287558
  const displayNameOverride = yield* get$5(displayNameOverrideRef);
287490
287559
  const os = platformOs();
@@ -296228,6 +296297,71 @@ function selectProviderUsageEnvironment(rawCatalog, ownerUserId) {
296228
296297
  };
296229
296298
  }
296230
296299
  //#endregion
296300
+ //#region src/command/claudeUsageRefreshFailure.ts
296301
+ /**
296302
+ * Why a Claude usage refresh failed, in words the Usage page can show.
296303
+ *
296304
+ * CMMD answers a failed refresh with a machine reason (`runtime_not_active`,
296305
+ * `credential_changed`, …). The Shell used to collapse every one of them into
296306
+ * "Claude usage refresh is unavailable.", so the card could only say the
296307
+ * refresh failed, never what to do about it.
296308
+ *
296309
+ * The mapping is a closed allowlist in both directions: a reason CMMD may add
296310
+ * later falls back to the generic code, and the text a client sees is written
296311
+ * here, never forwarded from upstream. That keeps a credential or runtime
296312
+ * detail in a CMMD error body from reaching the browser.
296313
+ */
296314
+ const FAILURE_TEXT = {
296315
+ CLAUDE_USAGE_ENVIRONMENT_UNAVAILABLE: "Refresh needs your Environment running.",
296316
+ CLAUDE_USAGE_CREDENTIAL_NOT_READY: "The Claude credential is not applied to your Environment yet.",
296317
+ CLAUDE_USAGE_REFRESH_IN_PROGRESS: "A refresh for this account is already running.",
296318
+ CLAUDE_USAGE_PROBE_FAILED: "Your Environment could not read Claude usage.",
296319
+ CLAUDE_USAGE_RECONNECT_REQUIRED: "Reconnect this Claude account to refresh usage.",
296320
+ CLAUDE_USAGE_ACCOUNT_NOT_FOUND: "This Claude account is no longer connected.",
296321
+ CLAUDE_USAGE_REFRESH_UNAVAILABLE: "Claude usage refresh is unavailable."
296322
+ };
296323
+ /** Verbatim from CMMD `personal-claude-usage.ts` and `provider-usage-service.ts`. */
296324
+ const CODE_BY_UPSTREAM_REASON = {
296325
+ runtime_not_active: "CLAUDE_USAGE_ENVIRONMENT_UNAVAILABLE",
296326
+ claude_native_delivery_disabled: "CLAUDE_USAGE_ENVIRONMENT_UNAVAILABLE",
296327
+ runtime_identity_mismatch: "CLAUDE_USAGE_ENVIRONMENT_UNAVAILABLE",
296328
+ credential_generation_not_applied: "CLAUDE_USAGE_CREDENTIAL_NOT_READY",
296329
+ credential_generation_unavailable: "CLAUDE_USAGE_CREDENTIAL_NOT_READY",
296330
+ credential_changed: "CLAUDE_USAGE_CREDENTIAL_NOT_READY",
296331
+ refresh_in_progress: "CLAUDE_USAGE_REFRESH_IN_PROGRESS",
296332
+ usage_probe_failed: "CLAUDE_USAGE_PROBE_FAILED",
296333
+ usage_receipt_invalid: "CLAUDE_USAGE_PROBE_FAILED",
296334
+ usage_output_invalid: "CLAUDE_USAGE_PROBE_FAILED"
296335
+ };
296336
+ function failure(code, status) {
296337
+ return {
296338
+ status,
296339
+ body: {
296340
+ error: FAILURE_TEXT[code],
296341
+ code
296342
+ }
296343
+ };
296344
+ }
296345
+ /**
296346
+ * CMMD sends the reason as `{ "error": "<reason>" }`. Anything else, including
296347
+ * a longer sentence that happens to start with an allowlisted word, is treated
296348
+ * as unknown.
296349
+ */
296350
+ function readAllowlistedReasonCode(body) {
296351
+ if (body === null || typeof body !== "object") return null;
296352
+ const reason = body.error;
296353
+ if (typeof reason !== "string") return null;
296354
+ return Object.hasOwn(CODE_BY_UPSTREAM_REASON, reason) ? CODE_BY_UPSTREAM_REASON[reason] ?? null : null;
296355
+ }
296356
+ function describeClaudeUsageRefreshFailure(error) {
296357
+ if (!(error instanceof CmmdProxyRequestError)) return failure("CLAUDE_USAGE_REFRESH_UNAVAILABLE", 502);
296358
+ if (error.status === 409) return failure("CLAUDE_USAGE_RECONNECT_REQUIRED", 409);
296359
+ if (error.status === 404) return failure("CLAUDE_USAGE_ACCOUNT_NOT_FOUND", 404);
296360
+ const reasonCode = readAllowlistedReasonCode(error.body);
296361
+ if (reasonCode !== null) return failure(reasonCode, 503);
296362
+ return failure("CLAUDE_USAGE_REFRESH_UNAVAILABLE", error.status === 503 ? 503 : 502);
296363
+ }
296364
+ //#endregion
296231
296365
  //#region src/command/wsToken.ts
296232
296366
  /**
296233
296367
  * Build the WS token response given an optional PAT and the configured CMMD
@@ -297964,10 +298098,8 @@ function resolveProviderUsageOwnerUserId(session) {
297964
298098
  return Number.isSafeInteger(userId) && userId > 0 ? userId : null;
297965
298099
  }
297966
298100
  function buildClaudeUsageRefreshErrorResponse(error) {
297967
- return jsonUnsafe({
297968
- error: "Claude usage refresh is unavailable.",
297969
- code: "CLAUDE_USAGE_REFRESH_UNAVAILABLE"
297970
- }, { status: error instanceof CmmdProxyRequestError && error.status === 503 ? 503 : 502 });
298101
+ const failure = describeClaudeUsageRefreshFailure(error);
298102
+ return jsonUnsafe(failure.body, { status: failure.status });
297971
298103
  }
297972
298104
  function makeRefreshProviderUsageHandler(dependencies = {}) {
297973
298105
  return gen(function* () {
@@ -310411,7 +310543,7 @@ const connectLoginCommand = make$32("login", {
310411
310543
  })).json();
310412
310544
  },
310413
310545
  openBrowser: async (url) => {
310414
- const open = (await import("./open-FIgTDosf.mjs")).default;
310546
+ const open = (await import("./open-D8mGyWmt.mjs")).default;
310415
310547
  await open(url);
310416
310548
  return true;
310417
310549
  },