@cmmd-center/forge 0.9.15 → 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.cjs +138 -22
- package/dist/bin.mjs +138 -21
- package/dist/client/assets/{index-Bublmabf.js → index-BvXPD-Rk.js} +56 -56
- package/dist/client/index.html +1 -1
- package/package.json +1 -1
package/dist/bin.cjs
CHANGED
|
@@ -65123,7 +65123,7 @@ function normalizeNumberish(value) {
|
|
|
65123
65123
|
}
|
|
65124
65124
|
//#endregion
|
|
65125
65125
|
//#region package.json
|
|
65126
|
-
var version$1 = "0.9.
|
|
65126
|
+
var version$1 = "0.9.16";
|
|
65127
65127
|
//#endregion
|
|
65128
65128
|
//#region src/sentry.ts
|
|
65129
65129
|
const SERVER_APP_NAME = "forge-server";
|
|
@@ -92279,8 +92279,8 @@ const ProjectionThread = require_Schema$1.Struct({
|
|
|
92279
92279
|
createdAt: IsoDateTime,
|
|
92280
92280
|
updatedAt: IsoDateTime,
|
|
92281
92281
|
archivedAt: require_Schema$1.NullOr(IsoDateTime),
|
|
92282
|
-
settledAt: require_Schema$1.
|
|
92283
|
-
settledOverride: require_Schema$1.
|
|
92282
|
+
settledAt: require_Schema$1.NullOr(IsoDateTime),
|
|
92283
|
+
settledOverride: require_Schema$1.NullOr(require_Schema$1.Literals(["settled", "active"])),
|
|
92284
92284
|
latestUserMessageAt: require_Schema$1.NullOr(IsoDateTime),
|
|
92285
92285
|
pendingApprovalCount: NonNegativeInt,
|
|
92286
92286
|
pendingUserInputCount: NonNegativeInt,
|
|
@@ -92466,16 +92466,23 @@ const FORGE_GIT_COMMIT_PATTERN = /^[0-9a-f]{7,40}$/i;
|
|
|
92466
92466
|
/**
|
|
92467
92467
|
* Resolve the commit this forge checkout actually booted from, so the
|
|
92468
92468
|
* heartbeat can report it and CMMD can tell a stale Sprite from a current
|
|
92469
|
-
* one.
|
|
92470
|
-
*
|
|
92471
|
-
*
|
|
92472
|
-
*
|
|
92473
|
-
*
|
|
92474
|
-
*
|
|
92475
|
-
*
|
|
92476
|
-
*
|
|
92477
|
-
*
|
|
92478
|
-
*
|
|
92469
|
+
* one. The commit cannot change for the life of the process, so resolve it
|
|
92470
|
+
* once and cache it.
|
|
92471
|
+
*
|
|
92472
|
+
* This comment used to say Sprites clone forge at bootstrap and read their
|
|
92473
|
+
* commit from that checkout. They do not. A cloud runtime installs the
|
|
92474
|
+
* published `@cmmd-center/forge` tarball and has no `.git` anywhere near it,
|
|
92475
|
+
* which is why its descriptor reported no commit at all until the server
|
|
92476
|
+
* started baking one into its bundle. See `../environment/buildIdentity.ts`,
|
|
92477
|
+
* which calls this only as its last rung.
|
|
92478
|
+
*
|
|
92479
|
+
* `git rev-parse HEAD` run with the process's own cwd walks up to find `.git`
|
|
92480
|
+
* on its own, so no `-C` path is needed. That also means a cwd inside some
|
|
92481
|
+
* unrelated repository answers with that repository's HEAD, which is one more
|
|
92482
|
+
* reason this is the last rung rather than the first. Returns `undefined`
|
|
92483
|
+
* (never throws) when there is no checkout to read, so the heartbeat body
|
|
92484
|
+
* omits the field exactly like an older Forge build that does not know about
|
|
92485
|
+
* it yet.
|
|
92479
92486
|
*/
|
|
92480
92487
|
let cached$1 = null;
|
|
92481
92488
|
async function resolveForgeGitCommit(execImpl = (command, args) => execFileAsync$6(command, [...args], { timeout: 5e3 })) {
|
|
@@ -206610,7 +206617,7 @@ function createDevServerMcpServer(deps, threadId) {
|
|
|
206610
206617
|
* per provider — this module only adapts descriptor + result shapes.
|
|
206611
206618
|
*
|
|
206612
206619
|
* Without it a Claude agent has no brokered push path at all and falls back to
|
|
206613
|
-
* raw `git`, which
|
|
206620
|
+
* raw `git`, which has no authenticated remote credentials in the agent runtime.
|
|
206614
206621
|
*
|
|
206615
206622
|
* The catalog is filtered through `applyPluginSkillPermissionsToDynamicTools`,
|
|
206616
206623
|
* exactly as the Codex surface filters its dynamic tools. `settings` is required
|
|
@@ -287823,13 +287830,32 @@ const ObservabilityLive = require_Schema$1.unwrap(require_Schema$1.gen(function*
|
|
|
287823
287830
|
//#region ../../packages/shared/src/buildCommit.ts
|
|
287824
287831
|
const COMMIT_SHA_PATTERN$1 = /^[0-9a-f]{40}$/i;
|
|
287825
287832
|
/**
|
|
287833
|
+
* Whether a value is a full commit sha.
|
|
287834
|
+
*
|
|
287835
|
+
* Exported so each surface can keep its own list of env names while sharing one
|
|
287836
|
+
* definition of "valid". Surfaces genuinely differ: Railway injects
|
|
287837
|
+
* RAILWAY_GIT_COMMIT_SHA into the cloud shell build, while the desktop release
|
|
287838
|
+
* workflows pass VITE_SENTRY_COMMIT_SHA. What must not differ is the check.
|
|
287839
|
+
*/
|
|
287840
|
+
function isFullCommitSha(value) {
|
|
287841
|
+
const candidate = value?.trim();
|
|
287842
|
+
return candidate !== void 0 && candidate !== "" && COMMIT_SHA_PATTERN$1.test(candidate);
|
|
287843
|
+
}
|
|
287844
|
+
/**
|
|
287826
287845
|
* Build identity from the environment.
|
|
287827
287846
|
*
|
|
287828
287847
|
* `.dockerignore` excludes `.git`, so `git rev-parse HEAD` cannot resolve inside
|
|
287829
287848
|
* the Railway container — the surface where "which commit is live?" is asked
|
|
287830
287849
|
* most. The commit therefore has to arrive as env there, with git left as the
|
|
287831
|
-
* fallback for surfaces that genuinely are checkouts (
|
|
287832
|
-
*
|
|
287850
|
+
* fallback for surfaces that genuinely are checkouts (desktop and dev run from
|
|
287851
|
+
* a repo).
|
|
287852
|
+
*
|
|
287853
|
+
* A cloud runtime is neither: it installs the published `@cmmd-center/forge`
|
|
287854
|
+
* tarball, so it has no build variables and no `.git`, and both rungs below
|
|
287855
|
+
* return nothing there. That is why the server bakes the commit into its bundle
|
|
287856
|
+
* at build time and reads that first — see `apps/server/src/environment/
|
|
287857
|
+
* buildIdentity.ts`. This comment previously claimed a sprite clones forge at
|
|
287858
|
+
* bootstrap; it does not, and believing it cost a diagnosis.
|
|
287833
287859
|
*
|
|
287834
287860
|
* `RAILWAY_GIT_COMMIT_SHA` is injected by the platform, which is what lets this
|
|
287835
287861
|
* work on the cloud shell with no Dockerfile plumbing. `FORGE_BUILD_COMMIT`
|
|
@@ -287847,6 +287873,30 @@ function resolveBuildCommitFromEnv(env) {
|
|
|
287847
287873
|
}
|
|
287848
287874
|
}
|
|
287849
287875
|
//#endregion
|
|
287876
|
+
//#region src/environment/buildIdentity.ts
|
|
287877
|
+
/**
|
|
287878
|
+
* Which commit this server was built from.
|
|
287879
|
+
*
|
|
287880
|
+
* @module
|
|
287881
|
+
*/
|
|
287882
|
+
/**
|
|
287883
|
+
* The commit baked into this bundle, if it is a bundle at all.
|
|
287884
|
+
*
|
|
287885
|
+
* `typeof` rather than a bare reference: running from source there is no such
|
|
287886
|
+
* binding, and a bare reference is a ReferenceError that would take the whole
|
|
287887
|
+
* environment descriptor down instead of reporting an honest "unknown".
|
|
287888
|
+
*/
|
|
287889
|
+
function readBakedBuildCommit() {
|
|
287890
|
+
return "e65edd4d1b8a361863a52b171b5e73295d691e11";
|
|
287891
|
+
}
|
|
287892
|
+
async function resolveServerBuildCommit(input) {
|
|
287893
|
+
if (isFullCommitSha(input.baked)) return input.baked;
|
|
287894
|
+
const fromEnv = resolveBuildCommitFromEnv(input.env);
|
|
287895
|
+
if (fromEnv !== void 0) return fromEnv;
|
|
287896
|
+
const fromGit = await input.readGitCommit();
|
|
287897
|
+
return isFullCommitSha(fromGit) ? fromGit : void 0;
|
|
287898
|
+
}
|
|
287899
|
+
//#endregion
|
|
287850
287900
|
//#region src/environment/Layers/ServerEnvironmentLabel.ts
|
|
287851
287901
|
function normalizeLabel(value) {
|
|
287852
287902
|
const trimmed = value?.trim();
|
|
@@ -287995,7 +288045,11 @@ const makeServerEnvironment = require_Schema$1.fn("makeServerEnvironment")(funct
|
|
|
287995
288045
|
const initialMetadata = yield* readMetadata;
|
|
287996
288046
|
const displayNameOverrideRef = yield* require_HttpServer.make$3(normalizeDisplayName(initialMetadata.displayName ?? null));
|
|
287997
288047
|
const posthog = yield* PosthogPublicConfig.asEffect();
|
|
287998
|
-
const buildCommit =
|
|
288048
|
+
const buildCommit = yield* require_Schema$1.promise(() => resolveServerBuildCommit({
|
|
288049
|
+
baked: readBakedBuildCommit(),
|
|
288050
|
+
env: process.env,
|
|
288051
|
+
readGitCommit: resolveForgeGitCommit
|
|
288052
|
+
}));
|
|
287999
288053
|
const buildDescriptor = require_Schema$1.gen(function* () {
|
|
288000
288054
|
const displayNameOverride = yield* require_HttpServer.get$1(displayNameOverrideRef);
|
|
288001
288055
|
const os = platformOs();
|
|
@@ -296810,6 +296864,71 @@ function selectProviderUsageEnvironment(rawCatalog, ownerUserId) {
|
|
|
296810
296864
|
};
|
|
296811
296865
|
}
|
|
296812
296866
|
//#endregion
|
|
296867
|
+
//#region src/command/claudeUsageRefreshFailure.ts
|
|
296868
|
+
/**
|
|
296869
|
+
* Why a Claude usage refresh failed, in words the Usage page can show.
|
|
296870
|
+
*
|
|
296871
|
+
* CMMD answers a failed refresh with a machine reason (`runtime_not_active`,
|
|
296872
|
+
* `credential_changed`, …). The Shell used to collapse every one of them into
|
|
296873
|
+
* "Claude usage refresh is unavailable.", so the card could only say the
|
|
296874
|
+
* refresh failed, never what to do about it.
|
|
296875
|
+
*
|
|
296876
|
+
* The mapping is a closed allowlist in both directions: a reason CMMD may add
|
|
296877
|
+
* later falls back to the generic code, and the text a client sees is written
|
|
296878
|
+
* here, never forwarded from upstream. That keeps a credential or runtime
|
|
296879
|
+
* detail in a CMMD error body from reaching the browser.
|
|
296880
|
+
*/
|
|
296881
|
+
const FAILURE_TEXT = {
|
|
296882
|
+
CLAUDE_USAGE_ENVIRONMENT_UNAVAILABLE: "Refresh needs your Environment running.",
|
|
296883
|
+
CLAUDE_USAGE_CREDENTIAL_NOT_READY: "The Claude credential is not applied to your Environment yet.",
|
|
296884
|
+
CLAUDE_USAGE_REFRESH_IN_PROGRESS: "A refresh for this account is already running.",
|
|
296885
|
+
CLAUDE_USAGE_PROBE_FAILED: "Your Environment could not read Claude usage.",
|
|
296886
|
+
CLAUDE_USAGE_RECONNECT_REQUIRED: "Reconnect this Claude account to refresh usage.",
|
|
296887
|
+
CLAUDE_USAGE_ACCOUNT_NOT_FOUND: "This Claude account is no longer connected.",
|
|
296888
|
+
CLAUDE_USAGE_REFRESH_UNAVAILABLE: "Claude usage refresh is unavailable."
|
|
296889
|
+
};
|
|
296890
|
+
/** Verbatim from CMMD `personal-claude-usage.ts` and `provider-usage-service.ts`. */
|
|
296891
|
+
const CODE_BY_UPSTREAM_REASON = {
|
|
296892
|
+
runtime_not_active: "CLAUDE_USAGE_ENVIRONMENT_UNAVAILABLE",
|
|
296893
|
+
claude_native_delivery_disabled: "CLAUDE_USAGE_ENVIRONMENT_UNAVAILABLE",
|
|
296894
|
+
runtime_identity_mismatch: "CLAUDE_USAGE_ENVIRONMENT_UNAVAILABLE",
|
|
296895
|
+
credential_generation_not_applied: "CLAUDE_USAGE_CREDENTIAL_NOT_READY",
|
|
296896
|
+
credential_generation_unavailable: "CLAUDE_USAGE_CREDENTIAL_NOT_READY",
|
|
296897
|
+
credential_changed: "CLAUDE_USAGE_CREDENTIAL_NOT_READY",
|
|
296898
|
+
refresh_in_progress: "CLAUDE_USAGE_REFRESH_IN_PROGRESS",
|
|
296899
|
+
usage_probe_failed: "CLAUDE_USAGE_PROBE_FAILED",
|
|
296900
|
+
usage_receipt_invalid: "CLAUDE_USAGE_PROBE_FAILED",
|
|
296901
|
+
usage_output_invalid: "CLAUDE_USAGE_PROBE_FAILED"
|
|
296902
|
+
};
|
|
296903
|
+
function failure(code, status) {
|
|
296904
|
+
return {
|
|
296905
|
+
status,
|
|
296906
|
+
body: {
|
|
296907
|
+
error: FAILURE_TEXT[code],
|
|
296908
|
+
code
|
|
296909
|
+
}
|
|
296910
|
+
};
|
|
296911
|
+
}
|
|
296912
|
+
/**
|
|
296913
|
+
* CMMD sends the reason as `{ "error": "<reason>" }`. Anything else, including
|
|
296914
|
+
* a longer sentence that happens to start with an allowlisted word, is treated
|
|
296915
|
+
* as unknown.
|
|
296916
|
+
*/
|
|
296917
|
+
function readAllowlistedReasonCode(body) {
|
|
296918
|
+
if (body === null || typeof body !== "object") return null;
|
|
296919
|
+
const reason = body.error;
|
|
296920
|
+
if (typeof reason !== "string") return null;
|
|
296921
|
+
return Object.hasOwn(CODE_BY_UPSTREAM_REASON, reason) ? CODE_BY_UPSTREAM_REASON[reason] ?? null : null;
|
|
296922
|
+
}
|
|
296923
|
+
function describeClaudeUsageRefreshFailure(error) {
|
|
296924
|
+
if (!(error instanceof CmmdProxyRequestError)) return failure("CLAUDE_USAGE_REFRESH_UNAVAILABLE", 502);
|
|
296925
|
+
if (error.status === 409) return failure("CLAUDE_USAGE_RECONNECT_REQUIRED", 409);
|
|
296926
|
+
if (error.status === 404) return failure("CLAUDE_USAGE_ACCOUNT_NOT_FOUND", 404);
|
|
296927
|
+
const reasonCode = readAllowlistedReasonCode(error.body);
|
|
296928
|
+
if (reasonCode !== null) return failure(reasonCode, 503);
|
|
296929
|
+
return failure("CLAUDE_USAGE_REFRESH_UNAVAILABLE", error.status === 503 ? 503 : 502);
|
|
296930
|
+
}
|
|
296931
|
+
//#endregion
|
|
296813
296932
|
//#region src/command/wsToken.ts
|
|
296814
296933
|
/**
|
|
296815
296934
|
* Build the WS token response given an optional PAT and the configured CMMD
|
|
@@ -298549,11 +298668,8 @@ function resolveProviderUsageOwnerUserId(session) {
|
|
|
298549
298668
|
return Number.isSafeInteger(userId) && userId > 0 ? userId : null;
|
|
298550
298669
|
}
|
|
298551
298670
|
function buildClaudeUsageRefreshErrorResponse(error) {
|
|
298552
|
-
const
|
|
298553
|
-
return require_HttpServer.jsonUnsafe({
|
|
298554
|
-
error: "Claude usage refresh is unavailable.",
|
|
298555
|
-
code: "CLAUDE_USAGE_REFRESH_UNAVAILABLE"
|
|
298556
|
-
}, { status });
|
|
298671
|
+
const failure = describeClaudeUsageRefreshFailure(error);
|
|
298672
|
+
return require_HttpServer.jsonUnsafe(failure.body, { status: failure.status });
|
|
298557
298673
|
}
|
|
298558
298674
|
function makeRefreshProviderUsageHandler(dependencies = {}) {
|
|
298559
298675
|
return require_Schema$1.gen(function* () {
|
package/dist/bin.mjs
CHANGED
|
@@ -64870,7 +64870,7 @@ function normalizeNumberish(value) {
|
|
|
64870
64870
|
}
|
|
64871
64871
|
//#endregion
|
|
64872
64872
|
//#region package.json
|
|
64873
|
-
var version$1 = "0.9.
|
|
64873
|
+
var version$1 = "0.9.16";
|
|
64874
64874
|
//#endregion
|
|
64875
64875
|
//#region src/sentry.ts
|
|
64876
64876
|
const SERVER_APP_NAME = "forge-server";
|
|
@@ -91964,8 +91964,8 @@ const ProjectionThread = Struct({
|
|
|
91964
91964
|
createdAt: IsoDateTime,
|
|
91965
91965
|
updatedAt: IsoDateTime,
|
|
91966
91966
|
archivedAt: NullOr(IsoDateTime),
|
|
91967
|
-
settledAt:
|
|
91968
|
-
settledOverride:
|
|
91967
|
+
settledAt: NullOr(IsoDateTime),
|
|
91968
|
+
settledOverride: NullOr(Literals(["settled", "active"])),
|
|
91969
91969
|
latestUserMessageAt: NullOr(IsoDateTime),
|
|
91970
91970
|
pendingApprovalCount: NonNegativeInt,
|
|
91971
91971
|
pendingUserInputCount: NonNegativeInt,
|
|
@@ -92150,16 +92150,23 @@ const FORGE_GIT_COMMIT_PATTERN = /^[0-9a-f]{7,40}$/i;
|
|
|
92150
92150
|
/**
|
|
92151
92151
|
* Resolve the commit this forge checkout actually booted from, so the
|
|
92152
92152
|
* heartbeat can report it and CMMD can tell a stale Sprite from a current
|
|
92153
|
-
* one.
|
|
92154
|
-
*
|
|
92155
|
-
*
|
|
92156
|
-
*
|
|
92157
|
-
*
|
|
92158
|
-
*
|
|
92159
|
-
*
|
|
92160
|
-
*
|
|
92161
|
-
*
|
|
92162
|
-
*
|
|
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.
|
|
92163
92170
|
*/
|
|
92164
92171
|
let cached$1 = null;
|
|
92165
92172
|
async function resolveForgeGitCommit(execImpl = (command, args) => execFileAsync$6(command, [...args], { timeout: 5e3 })) {
|
|
@@ -206252,7 +206259,7 @@ function createDevServerMcpServer(deps, threadId) {
|
|
|
206252
206259
|
* per provider — this module only adapts descriptor + result shapes.
|
|
206253
206260
|
*
|
|
206254
206261
|
* Without it a Claude agent has no brokered push path at all and falls back to
|
|
206255
|
-
* raw `git`, which
|
|
206262
|
+
* raw `git`, which has no authenticated remote credentials in the agent runtime.
|
|
206256
206263
|
*
|
|
206257
206264
|
* The catalog is filtered through `applyPluginSkillPermissionsToDynamicTools`,
|
|
206258
206265
|
* exactly as the Codex surface filters its dynamic tools. `settings` is required
|
|
@@ -287328,13 +287335,32 @@ const ObservabilityLive = unwrap(gen(function* () {
|
|
|
287328
287335
|
//#region ../../packages/shared/src/buildCommit.ts
|
|
287329
287336
|
const COMMIT_SHA_PATTERN$1 = /^[0-9a-f]{40}$/i;
|
|
287330
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
|
+
/**
|
|
287331
287350
|
* Build identity from the environment.
|
|
287332
287351
|
*
|
|
287333
287352
|
* `.dockerignore` excludes `.git`, so `git rev-parse HEAD` cannot resolve inside
|
|
287334
287353
|
* the Railway container — the surface where "which commit is live?" is asked
|
|
287335
287354
|
* most. The commit therefore has to arrive as env there, with git left as the
|
|
287336
|
-
* fallback for surfaces that genuinely are checkouts (
|
|
287337
|
-
*
|
|
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.
|
|
287338
287364
|
*
|
|
287339
287365
|
* `RAILWAY_GIT_COMMIT_SHA` is injected by the platform, which is what lets this
|
|
287340
287366
|
* work on the cloud shell with no Dockerfile plumbing. `FORGE_BUILD_COMMIT`
|
|
@@ -287352,6 +287378,30 @@ function resolveBuildCommitFromEnv(env) {
|
|
|
287352
287378
|
}
|
|
287353
287379
|
}
|
|
287354
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
|
|
287355
287405
|
//#region src/environment/Layers/ServerEnvironmentLabel.ts
|
|
287356
287406
|
function normalizeLabel(value) {
|
|
287357
287407
|
const trimmed = value?.trim();
|
|
@@ -287499,7 +287549,11 @@ const ServerEnvironmentLive = effect(ServerEnvironment, fn$1("makeServerEnvironm
|
|
|
287499
287549
|
const writeMetadata = (metadata) => fileSystem.writeFileString(serverConfig.environmentMetadataPath, `${JSON.stringify(metadata, null, 2)}\n`);
|
|
287500
287550
|
const displayNameOverrideRef = yield* make$47(normalizeDisplayName((yield* readMetadata).displayName ?? null));
|
|
287501
287551
|
const posthog = yield* PosthogPublicConfig.asEffect();
|
|
287502
|
-
const buildCommit =
|
|
287552
|
+
const buildCommit = yield* promise(() => resolveServerBuildCommit({
|
|
287553
|
+
baked: readBakedBuildCommit(),
|
|
287554
|
+
env: process.env,
|
|
287555
|
+
readGitCommit: resolveForgeGitCommit
|
|
287556
|
+
}));
|
|
287503
287557
|
const buildDescriptor = gen(function* () {
|
|
287504
287558
|
const displayNameOverride = yield* get$5(displayNameOverrideRef);
|
|
287505
287559
|
const os = platformOs();
|
|
@@ -296243,6 +296297,71 @@ function selectProviderUsageEnvironment(rawCatalog, ownerUserId) {
|
|
|
296243
296297
|
};
|
|
296244
296298
|
}
|
|
296245
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
|
|
296246
296365
|
//#region src/command/wsToken.ts
|
|
296247
296366
|
/**
|
|
296248
296367
|
* Build the WS token response given an optional PAT and the configured CMMD
|
|
@@ -297979,10 +298098,8 @@ function resolveProviderUsageOwnerUserId(session) {
|
|
|
297979
298098
|
return Number.isSafeInteger(userId) && userId > 0 ? userId : null;
|
|
297980
298099
|
}
|
|
297981
298100
|
function buildClaudeUsageRefreshErrorResponse(error) {
|
|
297982
|
-
|
|
297983
|
-
|
|
297984
|
-
code: "CLAUDE_USAGE_REFRESH_UNAVAILABLE"
|
|
297985
|
-
}, { status: error instanceof CmmdProxyRequestError && error.status === 503 ? 503 : 502 });
|
|
298101
|
+
const failure = describeClaudeUsageRefreshFailure(error);
|
|
298102
|
+
return jsonUnsafe(failure.body, { status: failure.status });
|
|
297986
298103
|
}
|
|
297987
298104
|
function makeRefreshProviderUsageHandler(dependencies = {}) {
|
|
297988
298105
|
return gen(function* () {
|