@cmmd-center/forge 0.9.15 → 0.9.17
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 +311 -53
- package/dist/bin.mjs +335 -76
- package/dist/client/assets/{CanvasPanel-Dc8j2ouo.js → CanvasPanel-D9sKevpO.js} +2 -2
- package/dist/client/assets/{CompositeItem-Cn5DdObI.js → CompositeItem-Bsi2e5qV.js} +2 -2
- package/dist/client/assets/{DiffPanel-CRdkVPEz.js → DiffPanel-ClMWe6-c.js} +2 -2
- package/dist/client/assets/{DiffPanel.logic-BIPcNz_o.js → DiffPanel.logic-o7tDdTzf.js} +2 -2
- package/dist/client/assets/{DiffPanelShell-tIdXu7mn.js → DiffPanelShell-R1Q66jTc.js} +2 -2
- package/dist/client/assets/{DiffWorkerPoolProvider-tFSGuVfC.js → DiffWorkerPoolProvider-lEFZi8QR.js} +2 -2
- package/dist/client/assets/{PullRequestCodePanel-inwJYfZC.js → PullRequestCodePanel-BpbniIb5.js} +2 -2
- package/dist/client/assets/{Virtualizer-DeKxnAeU.js → Virtualizer-B5NXbN5f.js} +2 -2
- package/dist/client/assets/{canvasStateStore-Dlm4tanv.js → canvasStateStore-qioLqOoT.js} +2 -2
- package/dist/client/assets/{cmmdAuthRedirect-C1FjO86g.js → cmmdAuthRedirect-lmPzseq5.js} +2 -2
- package/dist/client/assets/{index-Bublmabf.js → index-D0769c3n.js} +57 -57
- package/dist/client/assets/{src-DljvAdHI.js → src-BL6J64vk.js} +3 -3
- package/dist/client/assets/{toggle-It2CvaLA.js → toggle-CW3yNjj1.js} +2 -2
- package/dist/client/assets/{useTriggerFocusGuards-DfUh9uvu.js → useTriggerFocusGuards-mvO6ygs-.js} +2 -2
- package/dist/client/index.html +10 -10
- package/dist/{open-D8mGyWmt.mjs → open-FIgTDosf.mjs} +3 -3
- package/package.json +1 -1
package/dist/bin.cjs
CHANGED
|
@@ -22345,6 +22345,40 @@ const ServerProviderAuth = require_Schema$1.Struct({
|
|
|
22345
22345
|
accountEmail: require_Schema$1.optional(TrimmedNonEmptyString),
|
|
22346
22346
|
provenance: require_Schema$1.optional(ProviderAuthProvenance)
|
|
22347
22347
|
});
|
|
22348
|
+
/**
|
|
22349
|
+
* Whether this Environment will let a person select the provider.
|
|
22350
|
+
*
|
|
22351
|
+
* Published for every provider the build knows about. A provider is never
|
|
22352
|
+
* dropped from the list: absence is not a state, and four different reasons
|
|
22353
|
+
* once collapsed into one "Coming soon." string because the Shell had to infer
|
|
22354
|
+
* meaning from what was missing. See
|
|
22355
|
+
* `docs/adr/withheld-providers-carry-their-reason.md`.
|
|
22356
|
+
*/
|
|
22357
|
+
const ProviderAvailability = require_Schema$1.Literals(["available", "unavailable"]);
|
|
22358
|
+
/**
|
|
22359
|
+
* Why a Withheld Provider is withheld. The four members are the glossary's,
|
|
22360
|
+
* in `CONTEXT.md` section "Provider availability": Unbuilt, Not Offered, Not
|
|
22361
|
+
* Usable, Not on this Environment. Only Unbuilt may read "Coming soon."
|
|
22362
|
+
*
|
|
22363
|
+
* An Environment can only ever report Unbuilt, Not Usable, or Not on this
|
|
22364
|
+
* Environment. Not Offered is a CMMD fact about the organization's Provider
|
|
22365
|
+
* Offer, which the Shell adds when it joins.
|
|
22366
|
+
*/
|
|
22367
|
+
const ProviderUnavailableReason = require_Schema$1.Literals([
|
|
22368
|
+
"unbuilt",
|
|
22369
|
+
"not-offered",
|
|
22370
|
+
"not-usable",
|
|
22371
|
+
"not-on-this-environment"
|
|
22372
|
+
]);
|
|
22373
|
+
/**
|
|
22374
|
+
* The Not Usable sub-case. It picks a second line of copy, not a fifth state,
|
|
22375
|
+
* which is why it is a separate field rather than three more reasons.
|
|
22376
|
+
*/
|
|
22377
|
+
const ProviderUnavailableDetail = require_Schema$1.Literals([
|
|
22378
|
+
"no-connection",
|
|
22379
|
+
"delivery-pending",
|
|
22380
|
+
"reconnect-required"
|
|
22381
|
+
]);
|
|
22348
22382
|
const ServerProviderModel = require_Schema$1.Struct({
|
|
22349
22383
|
slug: TrimmedNonEmptyString,
|
|
22350
22384
|
name: TrimmedNonEmptyString,
|
|
@@ -22376,6 +22410,9 @@ const ServerProvider = require_Schema$1.Struct({
|
|
|
22376
22410
|
checkedAt: IsoDateTime,
|
|
22377
22411
|
message: require_Schema$1.optional(TrimmedNonEmptyString),
|
|
22378
22412
|
models: require_Schema$1.ArraySchema(ServerProviderModel),
|
|
22413
|
+
availability: require_Schema$1.optional(ProviderAvailability),
|
|
22414
|
+
unavailableReason: require_Schema$1.optional(ProviderUnavailableReason),
|
|
22415
|
+
unavailableDetail: require_Schema$1.optional(ProviderUnavailableDetail),
|
|
22379
22416
|
slashCommands: require_Schema$1.ArraySchema(ServerProviderSlashCommand).pipe(require_Schema$1.withDecodingDefault(require_Schema$1.succeed([]))),
|
|
22380
22417
|
skills: require_Schema$1.ArraySchema(ServerProviderSkill).pipe(require_Schema$1.withDecodingDefault(require_Schema$1.succeed([])))
|
|
22381
22418
|
});
|
|
@@ -65123,7 +65160,7 @@ function normalizeNumberish(value) {
|
|
|
65123
65160
|
}
|
|
65124
65161
|
//#endregion
|
|
65125
65162
|
//#region package.json
|
|
65126
|
-
var version$1 = "0.9.
|
|
65163
|
+
var version$1 = "0.9.17";
|
|
65127
65164
|
//#endregion
|
|
65128
65165
|
//#region src/sentry.ts
|
|
65129
65166
|
const SERVER_APP_NAME = "forge-server";
|
|
@@ -92279,8 +92316,8 @@ const ProjectionThread = require_Schema$1.Struct({
|
|
|
92279
92316
|
createdAt: IsoDateTime,
|
|
92280
92317
|
updatedAt: IsoDateTime,
|
|
92281
92318
|
archivedAt: require_Schema$1.NullOr(IsoDateTime),
|
|
92282
|
-
settledAt: require_Schema$1.
|
|
92283
|
-
settledOverride: require_Schema$1.
|
|
92319
|
+
settledAt: require_Schema$1.NullOr(IsoDateTime),
|
|
92320
|
+
settledOverride: require_Schema$1.NullOr(require_Schema$1.Literals(["settled", "active"])),
|
|
92284
92321
|
latestUserMessageAt: require_Schema$1.NullOr(IsoDateTime),
|
|
92285
92322
|
pendingApprovalCount: NonNegativeInt,
|
|
92286
92323
|
pendingUserInputCount: NonNegativeInt,
|
|
@@ -92466,16 +92503,23 @@ const FORGE_GIT_COMMIT_PATTERN = /^[0-9a-f]{7,40}$/i;
|
|
|
92466
92503
|
/**
|
|
92467
92504
|
* Resolve the commit this forge checkout actually booted from, so the
|
|
92468
92505
|
* 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
|
-
*
|
|
92506
|
+
* one. The commit cannot change for the life of the process, so resolve it
|
|
92507
|
+
* once and cache it.
|
|
92508
|
+
*
|
|
92509
|
+
* This comment used to say Sprites clone forge at bootstrap and read their
|
|
92510
|
+
* commit from that checkout. They do not. A cloud runtime installs the
|
|
92511
|
+
* published `@cmmd-center/forge` tarball and has no `.git` anywhere near it,
|
|
92512
|
+
* which is why its descriptor reported no commit at all until the server
|
|
92513
|
+
* started baking one into its bundle. See `../environment/buildIdentity.ts`,
|
|
92514
|
+
* which calls this only as its last rung.
|
|
92515
|
+
*
|
|
92516
|
+
* `git rev-parse HEAD` run with the process's own cwd walks up to find `.git`
|
|
92517
|
+
* on its own, so no `-C` path is needed. That also means a cwd inside some
|
|
92518
|
+
* unrelated repository answers with that repository's HEAD, which is one more
|
|
92519
|
+
* reason this is the last rung rather than the first. Returns `undefined`
|
|
92520
|
+
* (never throws) when there is no checkout to read, so the heartbeat body
|
|
92521
|
+
* omits the field exactly like an older Forge build that does not know about
|
|
92522
|
+
* it yet.
|
|
92479
92523
|
*/
|
|
92480
92524
|
let cached$1 = null;
|
|
92481
92525
|
async function resolveForgeGitCommit(execImpl = (command, args) => execFileAsync$6(command, [...args], { timeout: 5e3 })) {
|
|
@@ -206610,7 +206654,7 @@ function createDevServerMcpServer(deps, threadId) {
|
|
|
206610
206654
|
* per provider — this module only adapts descriptor + result shapes.
|
|
206611
206655
|
*
|
|
206612
206656
|
* Without it a Claude agent has no brokered push path at all and falls back to
|
|
206613
|
-
* raw `git`, which
|
|
206657
|
+
* raw `git`, which has no authenticated remote credentials in the agent runtime.
|
|
206614
206658
|
*
|
|
206615
206659
|
* The catalog is filtered through `applyPluginSkillPermissionsToDynamicTools`,
|
|
206616
206660
|
* exactly as the Codex surface filters its dynamic tools. `settings` is required
|
|
@@ -285036,14 +285080,87 @@ const writeProviderStatusCache = (input) => {
|
|
|
285036
285080
|
})));
|
|
285037
285081
|
};
|
|
285038
285082
|
//#endregion
|
|
285039
|
-
//#region
|
|
285083
|
+
//#region ../../packages/shared/src/providerExecution.ts
|
|
285084
|
+
/**
|
|
285085
|
+
* Canonical provider-readiness gate for starting work.
|
|
285086
|
+
*
|
|
285087
|
+
* Cloud user VMs can execute through Forge's scoped provider proxy without a
|
|
285088
|
+
* provider-local login. In that case the provider reports unauthenticated but
|
|
285089
|
+
* its credential provenance is explicitly `proxied`.
|
|
285090
|
+
*/
|
|
285091
|
+
function isServerProviderExecutable(provider) {
|
|
285092
|
+
const hasExecutionAuthority = provider.auth.status !== "unauthenticated" || provider.auth.provenance?.credentialScope === "proxied";
|
|
285093
|
+
return provider.enabled && provider.installed && provider.status === "ready" && hasExecutionAuthority && provider.models.length > 0;
|
|
285094
|
+
}
|
|
285095
|
+
//#endregion
|
|
285096
|
+
//#region src/provider/providerAvailability.ts
|
|
285097
|
+
/**
|
|
285098
|
+
* Derive what this Environment knows about a provider's availability.
|
|
285099
|
+
*
|
|
285100
|
+
* WHY THIS EXISTS
|
|
285101
|
+
*
|
|
285102
|
+
* Four different states (Unbuilt, Not Offered, Not Usable, Not on this
|
|
285103
|
+
* Environment) used to render as one "Coming soon." string, because consumers
|
|
285104
|
+
* were handed a filtered list and had to infer meaning from what was missing.
|
|
285105
|
+
* Every provider is now published with an explicit availability and, when
|
|
285106
|
+
* withheld, its reason. See `docs/adr/withheld-providers-carry-their-reason.md`
|
|
285107
|
+
* and the glossary in `CONTEXT.md` section "Provider availability".
|
|
285108
|
+
*
|
|
285109
|
+
* WHAT AN ENVIRONMENT MAY SAY
|
|
285110
|
+
*
|
|
285111
|
+
* Three of the four reasons only. Not Offered is a fact about the CMMD
|
|
285112
|
+
* organization's Provider Offer, and delivery-pending is a fact about a
|
|
285113
|
+
* credential in flight; neither is visible from inside an Environment, so the
|
|
285114
|
+
* Shell adds them when it joins. Nothing here may invent them.
|
|
285115
|
+
*
|
|
285116
|
+
* This reads the snapshot a provider Layer already produced. It runs no probe
|
|
285117
|
+
* and changes no probe: `isServerProviderExecutable` stays the single gate on
|
|
285118
|
+
* whether a turn can be dispatched, and this restates its answer in the
|
|
285119
|
+
* vocabulary a person sees.
|
|
285120
|
+
*/
|
|
285040
285121
|
/**
|
|
285041
|
-
* Providers
|
|
285042
|
-
*
|
|
285043
|
-
*
|
|
285044
|
-
*
|
|
285122
|
+
* Providers Forge ships in the UI but cannot execute, published as Unbuilt.
|
|
285123
|
+
*
|
|
285124
|
+
* Empty in this build: codex, claudeAgent, ollama, cmmd, apex, cursor and
|
|
285125
|
+
* opencode all have a real detection Layer and a registered adapter. The list
|
|
285126
|
+
* stays as the one place a provider that ships ahead of its server-side
|
|
285127
|
+
* implementation is named, so "Coming soon." is a deliberate entry rather than
|
|
285128
|
+
* a fallback something fell into.
|
|
285045
285129
|
*/
|
|
285046
|
-
const
|
|
285130
|
+
const UNBUILT_PROVIDERS = [];
|
|
285131
|
+
function deriveProviderAvailability(provider, unbuiltProviders = UNBUILT_PROVIDERS) {
|
|
285132
|
+
if (unbuiltProviders.includes(provider.provider)) return {
|
|
285133
|
+
availability: "unavailable",
|
|
285134
|
+
unavailableReason: "unbuilt"
|
|
285135
|
+
};
|
|
285136
|
+
if (isServerProviderExecutable(provider)) return { availability: "available" };
|
|
285137
|
+
const hasProxiedCredential = provider.auth.provenance?.credentialScope === "proxied";
|
|
285138
|
+
if (provider.installed && provider.auth.status === "unauthenticated" && !hasProxiedCredential) return {
|
|
285139
|
+
availability: "unavailable",
|
|
285140
|
+
unavailableReason: "not-usable",
|
|
285141
|
+
unavailableDetail: "no-connection"
|
|
285142
|
+
};
|
|
285143
|
+
return {
|
|
285144
|
+
availability: "unavailable",
|
|
285145
|
+
unavailableReason: "not-on-this-environment"
|
|
285146
|
+
};
|
|
285147
|
+
}
|
|
285148
|
+
/**
|
|
285149
|
+
* Stamp a snapshot with its derived availability.
|
|
285150
|
+
*
|
|
285151
|
+
* The previous fields are dropped before the new ones are applied. Merging
|
|
285152
|
+
* would leave a provider that just connected reporting `available` while still
|
|
285153
|
+
* carrying the reason it was withheld for.
|
|
285154
|
+
*/
|
|
285155
|
+
function withProviderAvailability(provider, unbuiltProviders = UNBUILT_PROVIDERS) {
|
|
285156
|
+
const { availability: _availability, unavailableReason: _unavailableReason, unavailableDetail: _unavailableDetail, ...rest } = provider;
|
|
285157
|
+
return {
|
|
285158
|
+
...rest,
|
|
285159
|
+
...deriveProviderAvailability(provider, unbuiltProviders)
|
|
285160
|
+
};
|
|
285161
|
+
}
|
|
285162
|
+
//#endregion
|
|
285163
|
+
//#region src/provider/Layers/ProviderRegistry.ts
|
|
285047
285164
|
const loadProviders = (codexProvider, claudeProvider, ollamaProvider, cmmdProvider, apexProvider, cursorProvider, opencodeProvider) => require_Schema$1.all([
|
|
285048
285165
|
codexProvider.getSnapshot,
|
|
285049
285166
|
claudeProvider.getSnapshot,
|
|
@@ -285080,9 +285197,9 @@ const ProviderRegistryLive = require_Schema$1.effect(ProviderRegistry, require_S
|
|
|
285080
285197
|
cachedProvider,
|
|
285081
285198
|
fallbackProvider
|
|
285082
285199
|
});
|
|
285083
|
-
return
|
|
285200
|
+
return UNBUILT_PROVIDERS.includes(provider) ? fallbackProvider : void 0;
|
|
285084
285201
|
}));
|
|
285085
|
-
}, { concurrency: "unbounded" }).pipe(require_Schema$1.map((providers) => orderProviderSnapshots(providers.filter((provider) => provider !== void 0))));
|
|
285202
|
+
}, { concurrency: "unbounded" }).pipe(require_Schema$1.map((providers) => orderProviderSnapshots(providers.filter((provider) => provider !== void 0).map((provider) => withProviderAvailability(provider)))));
|
|
285086
285203
|
const providersRef = yield* require_HttpServer.make$3(cachedProviders);
|
|
285087
285204
|
const persistProvider = (provider) => writeProviderStatusCache({
|
|
285088
285205
|
filePath: cachePathByProvider.get(provider.provider),
|
|
@@ -285091,7 +285208,8 @@ const ProviderRegistryLive = require_Schema$1.effect(ProviderRegistry, require_S
|
|
|
285091
285208
|
provider: provider.provider,
|
|
285092
285209
|
error
|
|
285093
285210
|
})), require_Schema$1.ignore);
|
|
285094
|
-
const upsertProviders = require_Schema$1.fn("upsertProviders")(function* (
|
|
285211
|
+
const upsertProviders = require_Schema$1.fn("upsertProviders")(function* (incomingProviders, options) {
|
|
285212
|
+
const nextProviders = incomingProviders.map((provider) => withProviderAvailability(provider));
|
|
285095
285213
|
const [previousProviders, providers] = yield* require_HttpServer.modify(providersRef, (previousProviders) => {
|
|
285096
285214
|
const mergedProviders = new Map(previousProviders.map((provider) => [provider.provider, provider]));
|
|
285097
285215
|
for (const provider of nextProviders) mergedProviders.set(provider.provider, provider);
|
|
@@ -287823,13 +287941,32 @@ const ObservabilityLive = require_Schema$1.unwrap(require_Schema$1.gen(function*
|
|
|
287823
287941
|
//#region ../../packages/shared/src/buildCommit.ts
|
|
287824
287942
|
const COMMIT_SHA_PATTERN$1 = /^[0-9a-f]{40}$/i;
|
|
287825
287943
|
/**
|
|
287944
|
+
* Whether a value is a full commit sha.
|
|
287945
|
+
*
|
|
287946
|
+
* Exported so each surface can keep its own list of env names while sharing one
|
|
287947
|
+
* definition of "valid". Surfaces genuinely differ: Railway injects
|
|
287948
|
+
* RAILWAY_GIT_COMMIT_SHA into the cloud shell build, while the desktop release
|
|
287949
|
+
* workflows pass VITE_SENTRY_COMMIT_SHA. What must not differ is the check.
|
|
287950
|
+
*/
|
|
287951
|
+
function isFullCommitSha(value) {
|
|
287952
|
+
const candidate = value?.trim();
|
|
287953
|
+
return candidate !== void 0 && candidate !== "" && COMMIT_SHA_PATTERN$1.test(candidate);
|
|
287954
|
+
}
|
|
287955
|
+
/**
|
|
287826
287956
|
* Build identity from the environment.
|
|
287827
287957
|
*
|
|
287828
287958
|
* `.dockerignore` excludes `.git`, so `git rev-parse HEAD` cannot resolve inside
|
|
287829
287959
|
* the Railway container — the surface where "which commit is live?" is asked
|
|
287830
287960
|
* most. The commit therefore has to arrive as env there, with git left as the
|
|
287831
|
-
* fallback for surfaces that genuinely are checkouts (
|
|
287832
|
-
*
|
|
287961
|
+
* fallback for surfaces that genuinely are checkouts (desktop and dev run from
|
|
287962
|
+
* a repo).
|
|
287963
|
+
*
|
|
287964
|
+
* A cloud runtime is neither: it installs the published `@cmmd-center/forge`
|
|
287965
|
+
* tarball, so it has no build variables and no `.git`, and both rungs below
|
|
287966
|
+
* return nothing there. That is why the server bakes the commit into its bundle
|
|
287967
|
+
* at build time and reads that first — see `apps/server/src/environment/
|
|
287968
|
+
* buildIdentity.ts`. This comment previously claimed a sprite clones forge at
|
|
287969
|
+
* bootstrap; it does not, and believing it cost a diagnosis.
|
|
287833
287970
|
*
|
|
287834
287971
|
* `RAILWAY_GIT_COMMIT_SHA` is injected by the platform, which is what lets this
|
|
287835
287972
|
* work on the cloud shell with no Dockerfile plumbing. `FORGE_BUILD_COMMIT`
|
|
@@ -287847,6 +287984,30 @@ function resolveBuildCommitFromEnv(env) {
|
|
|
287847
287984
|
}
|
|
287848
287985
|
}
|
|
287849
287986
|
//#endregion
|
|
287987
|
+
//#region src/environment/buildIdentity.ts
|
|
287988
|
+
/**
|
|
287989
|
+
* Which commit this server was built from.
|
|
287990
|
+
*
|
|
287991
|
+
* @module
|
|
287992
|
+
*/
|
|
287993
|
+
/**
|
|
287994
|
+
* The commit baked into this bundle, if it is a bundle at all.
|
|
287995
|
+
*
|
|
287996
|
+
* `typeof` rather than a bare reference: running from source there is no such
|
|
287997
|
+
* binding, and a bare reference is a ReferenceError that would take the whole
|
|
287998
|
+
* environment descriptor down instead of reporting an honest "unknown".
|
|
287999
|
+
*/
|
|
288000
|
+
function readBakedBuildCommit() {
|
|
288001
|
+
return "86c71b55bb7a5dfbbad063d6e4c082570759f006";
|
|
288002
|
+
}
|
|
288003
|
+
async function resolveServerBuildCommit(input) {
|
|
288004
|
+
if (isFullCommitSha(input.baked)) return input.baked;
|
|
288005
|
+
const fromEnv = resolveBuildCommitFromEnv(input.env);
|
|
288006
|
+
if (fromEnv !== void 0) return fromEnv;
|
|
288007
|
+
const fromGit = await input.readGitCommit();
|
|
288008
|
+
return isFullCommitSha(fromGit) ? fromGit : void 0;
|
|
288009
|
+
}
|
|
288010
|
+
//#endregion
|
|
287850
288011
|
//#region src/environment/Layers/ServerEnvironmentLabel.ts
|
|
287851
288012
|
function normalizeLabel(value) {
|
|
287852
288013
|
const trimmed = value?.trim();
|
|
@@ -287995,7 +288156,11 @@ const makeServerEnvironment = require_Schema$1.fn("makeServerEnvironment")(funct
|
|
|
287995
288156
|
const initialMetadata = yield* readMetadata;
|
|
287996
288157
|
const displayNameOverrideRef = yield* require_HttpServer.make$3(normalizeDisplayName(initialMetadata.displayName ?? null));
|
|
287997
288158
|
const posthog = yield* PosthogPublicConfig.asEffect();
|
|
287998
|
-
const buildCommit =
|
|
288159
|
+
const buildCommit = yield* require_Schema$1.promise(() => resolveServerBuildCommit({
|
|
288160
|
+
baked: readBakedBuildCommit(),
|
|
288161
|
+
env: process.env,
|
|
288162
|
+
readGitCommit: resolveForgeGitCommit
|
|
288163
|
+
}));
|
|
287999
288164
|
const buildDescriptor = require_Schema$1.gen(function* () {
|
|
288000
288165
|
const displayNameOverride = yield* require_HttpServer.get$1(displayNameOverrideRef);
|
|
288001
288166
|
const os = platformOs();
|
|
@@ -296810,6 +296975,71 @@ function selectProviderUsageEnvironment(rawCatalog, ownerUserId) {
|
|
|
296810
296975
|
};
|
|
296811
296976
|
}
|
|
296812
296977
|
//#endregion
|
|
296978
|
+
//#region src/command/claudeUsageRefreshFailure.ts
|
|
296979
|
+
/**
|
|
296980
|
+
* Why a Claude usage refresh failed, in words the Usage page can show.
|
|
296981
|
+
*
|
|
296982
|
+
* CMMD answers a failed refresh with a machine reason (`runtime_not_active`,
|
|
296983
|
+
* `credential_changed`, …). The Shell used to collapse every one of them into
|
|
296984
|
+
* "Claude usage refresh is unavailable.", so the card could only say the
|
|
296985
|
+
* refresh failed, never what to do about it.
|
|
296986
|
+
*
|
|
296987
|
+
* The mapping is a closed allowlist in both directions: a reason CMMD may add
|
|
296988
|
+
* later falls back to the generic code, and the text a client sees is written
|
|
296989
|
+
* here, never forwarded from upstream. That keeps a credential or runtime
|
|
296990
|
+
* detail in a CMMD error body from reaching the browser.
|
|
296991
|
+
*/
|
|
296992
|
+
const FAILURE_TEXT = {
|
|
296993
|
+
CLAUDE_USAGE_ENVIRONMENT_UNAVAILABLE: "Refresh needs your Environment running.",
|
|
296994
|
+
CLAUDE_USAGE_CREDENTIAL_NOT_READY: "The Claude credential is not applied to your Environment yet.",
|
|
296995
|
+
CLAUDE_USAGE_REFRESH_IN_PROGRESS: "A refresh for this account is already running.",
|
|
296996
|
+
CLAUDE_USAGE_PROBE_FAILED: "Your Environment could not read Claude usage.",
|
|
296997
|
+
CLAUDE_USAGE_RECONNECT_REQUIRED: "Reconnect this Claude account to refresh usage.",
|
|
296998
|
+
CLAUDE_USAGE_ACCOUNT_NOT_FOUND: "This Claude account is no longer connected.",
|
|
296999
|
+
CLAUDE_USAGE_REFRESH_UNAVAILABLE: "Claude usage refresh is unavailable."
|
|
297000
|
+
};
|
|
297001
|
+
/** Verbatim from CMMD `personal-claude-usage.ts` and `provider-usage-service.ts`. */
|
|
297002
|
+
const CODE_BY_UPSTREAM_REASON = {
|
|
297003
|
+
runtime_not_active: "CLAUDE_USAGE_ENVIRONMENT_UNAVAILABLE",
|
|
297004
|
+
claude_native_delivery_disabled: "CLAUDE_USAGE_ENVIRONMENT_UNAVAILABLE",
|
|
297005
|
+
runtime_identity_mismatch: "CLAUDE_USAGE_ENVIRONMENT_UNAVAILABLE",
|
|
297006
|
+
credential_generation_not_applied: "CLAUDE_USAGE_CREDENTIAL_NOT_READY",
|
|
297007
|
+
credential_generation_unavailable: "CLAUDE_USAGE_CREDENTIAL_NOT_READY",
|
|
297008
|
+
credential_changed: "CLAUDE_USAGE_CREDENTIAL_NOT_READY",
|
|
297009
|
+
refresh_in_progress: "CLAUDE_USAGE_REFRESH_IN_PROGRESS",
|
|
297010
|
+
usage_probe_failed: "CLAUDE_USAGE_PROBE_FAILED",
|
|
297011
|
+
usage_receipt_invalid: "CLAUDE_USAGE_PROBE_FAILED",
|
|
297012
|
+
usage_output_invalid: "CLAUDE_USAGE_PROBE_FAILED"
|
|
297013
|
+
};
|
|
297014
|
+
function failure(code, status) {
|
|
297015
|
+
return {
|
|
297016
|
+
status,
|
|
297017
|
+
body: {
|
|
297018
|
+
error: FAILURE_TEXT[code],
|
|
297019
|
+
code
|
|
297020
|
+
}
|
|
297021
|
+
};
|
|
297022
|
+
}
|
|
297023
|
+
/**
|
|
297024
|
+
* CMMD sends the reason as `{ "error": "<reason>" }`. Anything else, including
|
|
297025
|
+
* a longer sentence that happens to start with an allowlisted word, is treated
|
|
297026
|
+
* as unknown.
|
|
297027
|
+
*/
|
|
297028
|
+
function readAllowlistedReasonCode(body) {
|
|
297029
|
+
if (body === null || typeof body !== "object") return null;
|
|
297030
|
+
const reason = body.error;
|
|
297031
|
+
if (typeof reason !== "string") return null;
|
|
297032
|
+
return Object.hasOwn(CODE_BY_UPSTREAM_REASON, reason) ? CODE_BY_UPSTREAM_REASON[reason] ?? null : null;
|
|
297033
|
+
}
|
|
297034
|
+
function describeClaudeUsageRefreshFailure(error) {
|
|
297035
|
+
if (!(error instanceof CmmdProxyRequestError)) return failure("CLAUDE_USAGE_REFRESH_UNAVAILABLE", 502);
|
|
297036
|
+
if (error.status === 409) return failure("CLAUDE_USAGE_RECONNECT_REQUIRED", 409);
|
|
297037
|
+
if (error.status === 404) return failure("CLAUDE_USAGE_ACCOUNT_NOT_FOUND", 404);
|
|
297038
|
+
const reasonCode = readAllowlistedReasonCode(error.body);
|
|
297039
|
+
if (reasonCode !== null) return failure(reasonCode, 503);
|
|
297040
|
+
return failure("CLAUDE_USAGE_REFRESH_UNAVAILABLE", error.status === 503 ? 503 : 502);
|
|
297041
|
+
}
|
|
297042
|
+
//#endregion
|
|
296813
297043
|
//#region src/command/wsToken.ts
|
|
296814
297044
|
/**
|
|
296815
297045
|
* Build the WS token response given an optional PAT and the configured CMMD
|
|
@@ -298549,11 +298779,8 @@ function resolveProviderUsageOwnerUserId(session) {
|
|
|
298549
298779
|
return Number.isSafeInteger(userId) && userId > 0 ? userId : null;
|
|
298550
298780
|
}
|
|
298551
298781
|
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 });
|
|
298782
|
+
const failure = describeClaudeUsageRefreshFailure(error);
|
|
298783
|
+
return require_HttpServer.jsonUnsafe(failure.body, { status: failure.status });
|
|
298557
298784
|
}
|
|
298558
298785
|
function makeRefreshProviderUsageHandler(dependencies = {}) {
|
|
298559
298786
|
return require_Schema$1.gen(function* () {
|
|
@@ -300509,21 +300736,24 @@ function processRuntimeReplicationOutboxItem(item, dependencies) {
|
|
|
300509
300736
|
//#endregion
|
|
300510
300737
|
//#region src/runtimeReplication/Services/ActiveHourMeter.ts
|
|
300511
300738
|
var ActiveHourMeter = class extends require_Schema$1.Service()("forge/runtimeReplication/Services/ActiveHourMeter") {};
|
|
300512
|
-
|
|
300513
|
-
|
|
300739
|
+
/** `slug` and `name` are `.min(1).max(512)` on the receiving schema. */
|
|
300740
|
+
const FIELD_MAX_LENGTH = 512;
|
|
300741
|
+
/** `version` is `.max(128)` on the receiving schema, with no length floor. */
|
|
300742
|
+
const VERSION_MAX_LENGTH = 128;
|
|
300743
|
+
/** `binaryPath` is `.max(2048)` on the receiving schema, with no length floor. */
|
|
300744
|
+
const BINARY_PATH_MAX_LENGTH = 2048;
|
|
300514
300745
|
/**
|
|
300515
|
-
*
|
|
300746
|
+
* Trim to the receiving schema's maximum, or null.
|
|
300516
300747
|
*
|
|
300517
|
-
*
|
|
300518
|
-
*
|
|
300519
|
-
*
|
|
300748
|
+
* Neither field has a length floor, so an empty string would be legal — but it
|
|
300749
|
+
* is not an answer. A version nobody resolved and a binary nobody found are
|
|
300750
|
+
* both null, which is what the control plane stores and what a reader can tell
|
|
300751
|
+
* apart from a real value.
|
|
300520
300752
|
*/
|
|
300521
|
-
function
|
|
300522
|
-
const
|
|
300523
|
-
return
|
|
300753
|
+
function cappedText(value, max) {
|
|
300754
|
+
const normalized = value?.trim().slice(0, max);
|
|
300755
|
+
return normalized ? normalized : null;
|
|
300524
300756
|
}
|
|
300525
|
-
/** `slug` and `name` are `.min(1).max(512)` on the receiving schema. */
|
|
300526
|
-
const FIELD_MAX_LENGTH = 512;
|
|
300527
300757
|
function serializeModel(model) {
|
|
300528
300758
|
const slug = model.slug.trim().slice(0, FIELD_MAX_LENGTH);
|
|
300529
300759
|
const name = model.name.trim().slice(0, FIELD_MAX_LENGTH);
|
|
@@ -300537,20 +300767,48 @@ function serializeModel(model) {
|
|
|
300537
300767
|
name
|
|
300538
300768
|
};
|
|
300539
300769
|
}
|
|
300770
|
+
/**
|
|
300771
|
+
* The availability to publish for one provider.
|
|
300772
|
+
*
|
|
300773
|
+
* The registry stamps every snapshot before it leaves, so this normally copies.
|
|
300774
|
+
* Deriving over a stamped snapshot would lose the one fact only the registry
|
|
300775
|
+
* holds — which providers this build cannot run at all — and republish every
|
|
300776
|
+
* Unbuilt provider as Not on this Environment. The fallback covers a snapshot
|
|
300777
|
+
* that reached the outbox from some other path.
|
|
300778
|
+
*
|
|
300779
|
+
* Built by spread so a key with no value is ABSENT rather than `undefined`: the
|
|
300780
|
+
* receiving object is `.strict()`, `undefined` is not a legal enum member, and
|
|
300781
|
+
* the outbox retries the resulting 400 forever.
|
|
300782
|
+
*/
|
|
300783
|
+
function publishedAvailability(provider) {
|
|
300784
|
+
if (provider.availability === void 0) return deriveProviderAvailability(provider);
|
|
300785
|
+
const reason = provider.unavailableReason;
|
|
300786
|
+
const detail = provider.unavailableDetail;
|
|
300787
|
+
return {
|
|
300788
|
+
availability: provider.availability,
|
|
300789
|
+
...reason === void 0 ? {} : { unavailableReason: reason },
|
|
300790
|
+
...detail === void 0 ? {} : { unavailableDetail: detail }
|
|
300791
|
+
};
|
|
300792
|
+
}
|
|
300793
|
+
function serializeProvider(provider) {
|
|
300794
|
+
const models = [];
|
|
300795
|
+
for (const model of provider.models) {
|
|
300796
|
+
if (models.length >= 256) break;
|
|
300797
|
+
const serialized = serializeModel(model);
|
|
300798
|
+
if (serialized) models.push(serialized);
|
|
300799
|
+
}
|
|
300800
|
+
return {
|
|
300801
|
+
provider: provider.provider,
|
|
300802
|
+
models,
|
|
300803
|
+
...publishedAvailability(provider),
|
|
300804
|
+
version: cappedText(provider.version, VERSION_MAX_LENGTH),
|
|
300805
|
+
binaryPath: cappedText(provider.auth.provenance?.binaryPath, BINARY_PATH_MAX_LENGTH)
|
|
300806
|
+
};
|
|
300807
|
+
}
|
|
300540
300808
|
function serializeProviderCatalog(providers) {
|
|
300541
300809
|
const catalog = [];
|
|
300542
300810
|
for (const provider of providers) {
|
|
300543
|
-
|
|
300544
|
-
const models = [];
|
|
300545
|
-
for (const model of provider.models) {
|
|
300546
|
-
if (models.length >= 256) break;
|
|
300547
|
-
const serialized = serializeModel(model);
|
|
300548
|
-
if (serialized) models.push(serialized);
|
|
300549
|
-
}
|
|
300550
|
-
catalog.push({
|
|
300551
|
-
provider: provider.provider,
|
|
300552
|
-
models
|
|
300553
|
-
});
|
|
300811
|
+
catalog.push(serializeProvider(provider));
|
|
300554
300812
|
if (catalog.length >= 16) break;
|
|
300555
300813
|
}
|
|
300556
300814
|
return catalog;
|