@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.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
|
|
25
|
+
import os, { homedir, networkInterfaces, tmpdir } from "node:os";
|
|
26
26
|
import * as Path$1 from "node:path";
|
|
27
27
|
import path, { basename, delimiter, dirname, extname, isAbsolute, join, posix, relative, resolve, sep } from "node:path";
|
|
28
28
|
import { pathToFileURL } from "node:url";
|
|
@@ -22164,6 +22164,40 @@ const ServerProviderAuth = Struct({
|
|
|
22164
22164
|
accountEmail: optional$4(TrimmedNonEmptyString),
|
|
22165
22165
|
provenance: optional$4(ProviderAuthProvenance)
|
|
22166
22166
|
});
|
|
22167
|
+
/**
|
|
22168
|
+
* Whether this Environment will let a person select the provider.
|
|
22169
|
+
*
|
|
22170
|
+
* Published for every provider the build knows about. A provider is never
|
|
22171
|
+
* dropped from the list: absence is not a state, and four different reasons
|
|
22172
|
+
* once collapsed into one "Coming soon." string because the Shell had to infer
|
|
22173
|
+
* meaning from what was missing. See
|
|
22174
|
+
* `docs/adr/withheld-providers-carry-their-reason.md`.
|
|
22175
|
+
*/
|
|
22176
|
+
const ProviderAvailability = Literals(["available", "unavailable"]);
|
|
22177
|
+
/**
|
|
22178
|
+
* Why a Withheld Provider is withheld. The four members are the glossary's,
|
|
22179
|
+
* in `CONTEXT.md` section "Provider availability": Unbuilt, Not Offered, Not
|
|
22180
|
+
* Usable, Not on this Environment. Only Unbuilt may read "Coming soon."
|
|
22181
|
+
*
|
|
22182
|
+
* An Environment can only ever report Unbuilt, Not Usable, or Not on this
|
|
22183
|
+
* Environment. Not Offered is a CMMD fact about the organization's Provider
|
|
22184
|
+
* Offer, which the Shell adds when it joins.
|
|
22185
|
+
*/
|
|
22186
|
+
const ProviderUnavailableReason = Literals([
|
|
22187
|
+
"unbuilt",
|
|
22188
|
+
"not-offered",
|
|
22189
|
+
"not-usable",
|
|
22190
|
+
"not-on-this-environment"
|
|
22191
|
+
]);
|
|
22192
|
+
/**
|
|
22193
|
+
* The Not Usable sub-case. It picks a second line of copy, not a fifth state,
|
|
22194
|
+
* which is why it is a separate field rather than three more reasons.
|
|
22195
|
+
*/
|
|
22196
|
+
const ProviderUnavailableDetail = Literals([
|
|
22197
|
+
"no-connection",
|
|
22198
|
+
"delivery-pending",
|
|
22199
|
+
"reconnect-required"
|
|
22200
|
+
]);
|
|
22167
22201
|
const ServerProviderModel = Struct({
|
|
22168
22202
|
slug: TrimmedNonEmptyString,
|
|
22169
22203
|
name: TrimmedNonEmptyString,
|
|
@@ -22195,6 +22229,9 @@ const ServerProvider = Struct({
|
|
|
22195
22229
|
checkedAt: IsoDateTime,
|
|
22196
22230
|
message: optional$4(TrimmedNonEmptyString),
|
|
22197
22231
|
models: ArraySchema(ServerProviderModel),
|
|
22232
|
+
availability: optional$4(ProviderAvailability),
|
|
22233
|
+
unavailableReason: optional$4(ProviderUnavailableReason),
|
|
22234
|
+
unavailableDetail: optional$4(ProviderUnavailableDetail),
|
|
22198
22235
|
slashCommands: ArraySchema(ServerProviderSlashCommand).pipe(withDecodingDefault(succeed([]))),
|
|
22199
22236
|
skills: ArraySchema(ServerProviderSkill).pipe(withDecodingDefault(succeed([])))
|
|
22200
22237
|
});
|
|
@@ -49482,7 +49519,7 @@ function _supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) {
|
|
|
49482
49519
|
const min = forceColor || 0;
|
|
49483
49520
|
if (env.TERM === "dumb") return min;
|
|
49484
49521
|
if (process$1.platform === "win32") {
|
|
49485
|
-
const osRelease =
|
|
49522
|
+
const osRelease = os.release().split(".");
|
|
49486
49523
|
if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) return Number(osRelease[2]) >= 14931 ? 3 : 2;
|
|
49487
49524
|
return 1;
|
|
49488
49525
|
}
|
|
@@ -51925,7 +51962,7 @@ var require_ProcessDetector = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
51925
51962
|
exports.processDetector = void 0;
|
|
51926
51963
|
const api_1 = (init_esm$1(), __toCommonJS(esm_exports$1));
|
|
51927
51964
|
const semconv_1 = require_semconv$1();
|
|
51928
|
-
const os$
|
|
51965
|
+
const os$4 = __require("os");
|
|
51929
51966
|
/**
|
|
51930
51967
|
* ProcessDetector will be used to detect the resources related current process running
|
|
51931
51968
|
* and being instrumented from the NodeJS Process module.
|
|
@@ -51947,7 +51984,7 @@ var require_ProcessDetector = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
51947
51984
|
};
|
|
51948
51985
|
if (process.argv.length > 1) attributes[semconv_1.ATTR_PROCESS_COMMAND] = process.argv[1];
|
|
51949
51986
|
try {
|
|
51950
|
-
const userInfo = os$
|
|
51987
|
+
const userInfo = os$4.userInfo();
|
|
51951
51988
|
attributes[semconv_1.ATTR_PROCESS_OWNER] = userInfo.username;
|
|
51952
51989
|
} catch (e) {
|
|
51953
51990
|
api_1.diag.debug(`error obtaining process owner: ${e}`);
|
|
@@ -64870,7 +64907,7 @@ function normalizeNumberish(value) {
|
|
|
64870
64907
|
}
|
|
64871
64908
|
//#endregion
|
|
64872
64909
|
//#region package.json
|
|
64873
|
-
var version$1 = "0.9.
|
|
64910
|
+
var version$1 = "0.9.17";
|
|
64874
64911
|
//#endregion
|
|
64875
64912
|
//#region src/sentry.ts
|
|
64876
64913
|
const SERVER_APP_NAME = "forge-server";
|
|
@@ -73500,7 +73537,7 @@ var require_node_gyp_build = /* @__PURE__ */ __commonJSMin(((exports, module) =>
|
|
|
73500
73537
|
var fs$6 = __require("fs");
|
|
73501
73538
|
var path$5 = __require("path");
|
|
73502
73539
|
var url$1 = __require("url");
|
|
73503
|
-
var os$
|
|
73540
|
+
var os$3 = __require("os");
|
|
73504
73541
|
var runtimeRequire = typeof __webpack_require__ === "function" ? __non_webpack_require__ : __require;
|
|
73505
73542
|
var vars = process.config && process.config.variables || {};
|
|
73506
73543
|
var prebuildsOnly = !!process.env.PREBUILDS_ONLY;
|
|
@@ -73508,8 +73545,8 @@ var require_node_gyp_build = /* @__PURE__ */ __commonJSMin(((exports, module) =>
|
|
|
73508
73545
|
var abi = versions.modules;
|
|
73509
73546
|
if (versions.deno || process.isBun) abi = "unsupported";
|
|
73510
73547
|
var runtime = isElectron() ? "electron" : isNwjs() ? "node-webkit" : "node";
|
|
73511
|
-
var arch = process.env.npm_config_arch || os$
|
|
73512
|
-
var platform = process.env.npm_config_platform || os$
|
|
73548
|
+
var arch = process.env.npm_config_arch || os$3.arch();
|
|
73549
|
+
var platform = process.env.npm_config_platform || os$3.platform();
|
|
73513
73550
|
var libc = process.env.LIBC || (isMusl(platform) ? "musl" : "glibc");
|
|
73514
73551
|
var armv = process.env.ARM_VERSION || (arch === "arm64" ? "8" : vars.arm_version) || "";
|
|
73515
73552
|
var uv = (versions.uv || "").split(".")[0];
|
|
@@ -91167,7 +91204,7 @@ const launchDetached = (launch) => gen(function* () {
|
|
|
91167
91204
|
});
|
|
91168
91205
|
const OpenLive = effect(Open, gen(function* () {
|
|
91169
91206
|
const open = yield* tryPromise({
|
|
91170
|
-
try: () => import("./open-
|
|
91207
|
+
try: () => import("./open-FIgTDosf.mjs"),
|
|
91171
91208
|
catch: (cause) => new OpenError$1({
|
|
91172
91209
|
message: "failed to load browser opener",
|
|
91173
91210
|
cause
|
|
@@ -91964,8 +92001,8 @@ const ProjectionThread = Struct({
|
|
|
91964
92001
|
createdAt: IsoDateTime,
|
|
91965
92002
|
updatedAt: IsoDateTime,
|
|
91966
92003
|
archivedAt: NullOr(IsoDateTime),
|
|
91967
|
-
settledAt:
|
|
91968
|
-
settledOverride:
|
|
92004
|
+
settledAt: NullOr(IsoDateTime),
|
|
92005
|
+
settledOverride: NullOr(Literals(["settled", "active"])),
|
|
91969
92006
|
latestUserMessageAt: NullOr(IsoDateTime),
|
|
91970
92007
|
pendingApprovalCount: NonNegativeInt,
|
|
91971
92008
|
pendingUserInputCount: NonNegativeInt,
|
|
@@ -92150,16 +92187,23 @@ const FORGE_GIT_COMMIT_PATTERN = /^[0-9a-f]{7,40}$/i;
|
|
|
92150
92187
|
/**
|
|
92151
92188
|
* Resolve the commit this forge checkout actually booted from, so the
|
|
92152
92189
|
* 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
|
-
*
|
|
92190
|
+
* one. The commit cannot change for the life of the process, so resolve it
|
|
92191
|
+
* once and cache it.
|
|
92192
|
+
*
|
|
92193
|
+
* This comment used to say Sprites clone forge at bootstrap and read their
|
|
92194
|
+
* commit from that checkout. They do not. A cloud runtime installs the
|
|
92195
|
+
* published `@cmmd-center/forge` tarball and has no `.git` anywhere near it,
|
|
92196
|
+
* which is why its descriptor reported no commit at all until the server
|
|
92197
|
+
* started baking one into its bundle. See `../environment/buildIdentity.ts`,
|
|
92198
|
+
* which calls this only as its last rung.
|
|
92199
|
+
*
|
|
92200
|
+
* `git rev-parse HEAD` run with the process's own cwd walks up to find `.git`
|
|
92201
|
+
* on its own, so no `-C` path is needed. That also means a cwd inside some
|
|
92202
|
+
* unrelated repository answers with that repository's HEAD, which is one more
|
|
92203
|
+
* reason this is the last rung rather than the first. Returns `undefined`
|
|
92204
|
+
* (never throws) when there is no checkout to read, so the heartbeat body
|
|
92205
|
+
* omits the field exactly like an older Forge build that does not know about
|
|
92206
|
+
* it yet.
|
|
92163
92207
|
*/
|
|
92164
92208
|
let cached$1 = null;
|
|
92165
92209
|
async function resolveForgeGitCommit(execImpl = (command, args) => execFileAsync$6(command, [...args], { timeout: 5e3 })) {
|
|
@@ -184373,7 +184417,7 @@ var Vce = {
|
|
|
184373
184417
|
function Ve() {
|
|
184374
184418
|
return Vce;
|
|
184375
184419
|
}
|
|
184376
|
-
var Us =
|
|
184420
|
+
var Us = os.homedir(), lk = os.tmpdir(), { env: Vc } = process$1, Yce = (e) => {
|
|
184377
184421
|
let t = path.join(Us, "Library");
|
|
184378
184422
|
return {
|
|
184379
184423
|
data: path.join(t, "Application Support", e),
|
|
@@ -194573,12 +194617,12 @@ function qZ(e, t) {
|
|
|
194573
194617
|
}
|
|
194574
194618
|
return n;
|
|
194575
194619
|
}
|
|
194576
|
-
var os$
|
|
194620
|
+
var os$2 = "https://code.claude.com/docs/en", kKe = [
|
|
194577
194621
|
{
|
|
194578
194622
|
matches: (e) => e.path === "permissions.defaultMode" && e.code === "invalid_value",
|
|
194579
194623
|
tip: {
|
|
194580
194624
|
suggestion: "Valid modes: \"acceptEdits\" (ask before file changes), \"plan\" (analysis only), \"bypassPermissions\" (auto-accept all), or \"default\" (standard behavior)",
|
|
194581
|
-
docLink: `${os$
|
|
194625
|
+
docLink: `${os$2}/iam#permission-modes`
|
|
194582
194626
|
}
|
|
194583
194627
|
},
|
|
194584
194628
|
{
|
|
@@ -194593,7 +194637,7 @@ var os$1 = "https://code.claude.com/docs/en", kKe = [
|
|
|
194593
194637
|
matches: (e) => e.path.startsWith("env.") && e.code === "invalid_type",
|
|
194594
194638
|
tip: {
|
|
194595
194639
|
suggestion: "Environment variables must be strings. Wrap numbers and booleans in quotes. Example: \"DEBUG\": \"true\", \"PORT\": \"3000\"",
|
|
194596
|
-
docLink: `${os$
|
|
194640
|
+
docLink: `${os$2}/settings#environment-variables`
|
|
194597
194641
|
}
|
|
194598
194642
|
},
|
|
194599
194643
|
{
|
|
@@ -194604,14 +194648,14 @@ var os$1 = "https://code.claude.com/docs/en", kKe = [
|
|
|
194604
194648
|
matches: (e) => e.path.startsWith("hooks.") && e.code === "invalid_key",
|
|
194605
194649
|
tip: {
|
|
194606
194650
|
suggestion: "Not a recognized hook event. Common events: PreToolUse, PostToolUse, UserPromptSubmit, SessionStart, SessionEnd, Stop. Check spelling and capitalization.",
|
|
194607
|
-
docLink: `${os$
|
|
194651
|
+
docLink: `${os$2}/hooks`
|
|
194608
194652
|
}
|
|
194609
194653
|
},
|
|
194610
194654
|
{
|
|
194611
194655
|
matches: (e) => /\.hooks\.\d+\.command$/.test(e.path) && e.code === "invalid_type" && e.received === "undefined",
|
|
194612
194656
|
tip: {
|
|
194613
194657
|
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\"}.",
|
|
194614
|
-
docLink: `${os$
|
|
194658
|
+
docLink: `${os$2}/hooks#exec-form-and-shell-form`
|
|
194615
194659
|
}
|
|
194616
194660
|
},
|
|
194617
194661
|
{
|
|
@@ -194626,7 +194670,7 @@ var os$1 = "https://code.claude.com/docs/en", kKe = [
|
|
|
194626
194670
|
matches: (e) => e.code === "unrecognized_keys",
|
|
194627
194671
|
tip: {
|
|
194628
194672
|
suggestion: "Check for typos or refer to the documentation for valid fields",
|
|
194629
|
-
docLink: `${os$
|
|
194673
|
+
docLink: `${os$2}/settings`
|
|
194630
194674
|
}
|
|
194631
194675
|
},
|
|
194632
194676
|
{
|
|
@@ -194641,13 +194685,13 @@ var os$1 = "https://code.claude.com/docs/en", kKe = [
|
|
|
194641
194685
|
matches: (e) => e.path === "permissions.additionalDirectories" && e.code === "invalid_type",
|
|
194642
194686
|
tip: {
|
|
194643
194687
|
suggestion: "Must be an array of directory paths. Example: [\"~/projects\", \"/tmp/workspace\"]. You can also use --add-dir flag or /add-dir command",
|
|
194644
|
-
docLink: `${os$
|
|
194688
|
+
docLink: `${os$2}/iam#working-directories`
|
|
194645
194689
|
}
|
|
194646
194690
|
}
|
|
194647
194691
|
], AKe = {
|
|
194648
|
-
permissions: `${os$
|
|
194649
|
-
env: `${os$
|
|
194650
|
-
hooks: `${os$
|
|
194692
|
+
permissions: `${os$2}/iam#configuring-permissions`,
|
|
194693
|
+
env: `${os$2}/settings#environment-variables`,
|
|
194694
|
+
hooks: `${os$2}/hooks`
|
|
194651
194695
|
};
|
|
194652
194696
|
function GZ(e) {
|
|
194653
194697
|
let t = kKe.find((r) => r.matches(e));
|
|
@@ -206252,7 +206296,7 @@ function createDevServerMcpServer(deps, threadId) {
|
|
|
206252
206296
|
* per provider — this module only adapts descriptor + result shapes.
|
|
206253
206297
|
*
|
|
206254
206298
|
* Without it a Claude agent has no brokered push path at all and falls back to
|
|
206255
|
-
* raw `git`, which
|
|
206299
|
+
* raw `git`, which has no authenticated remote credentials in the agent runtime.
|
|
206256
206300
|
*
|
|
206257
206301
|
* The catalog is filtered through `applyPluginSkillPermissionsToDynamicTools`,
|
|
206258
206302
|
* exactly as the Codex surface filters its dynamic tools. `settings` is required
|
|
@@ -258900,7 +258944,7 @@ var import_multicast_dns = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMi
|
|
|
258900
258944
|
var dgram = __require("dgram");
|
|
258901
258945
|
var thunky = require_thunky();
|
|
258902
258946
|
var events = __require("events");
|
|
258903
|
-
var os = __require("os");
|
|
258947
|
+
var os$1 = __require("os");
|
|
258904
258948
|
var noop = function() {};
|
|
258905
258949
|
module.exports = function(opts) {
|
|
258906
258950
|
if (!opts) opts = {};
|
|
@@ -259031,14 +259075,14 @@ var import_multicast_dns = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMi
|
|
|
259031
259075
|
return that;
|
|
259032
259076
|
};
|
|
259033
259077
|
function defaultInterface() {
|
|
259034
|
-
var networks = os.networkInterfaces();
|
|
259078
|
+
var networks = os$1.networkInterfaces();
|
|
259035
259079
|
var names = Object.keys(networks);
|
|
259036
259080
|
for (var i = 0; i < names.length; i++) {
|
|
259037
259081
|
var net = networks[names[i]];
|
|
259038
259082
|
for (var j = 0; j < net.length; j++) {
|
|
259039
259083
|
var iface = net[j];
|
|
259040
259084
|
if (isIPv4(iface.family) && !iface.internal) {
|
|
259041
|
-
if (os.platform() === "darwin" && names[i] === "en0") return iface.address;
|
|
259085
|
+
if (os$1.platform() === "darwin" && names[i] === "en0") return iface.address;
|
|
259042
259086
|
return "0.0.0.0";
|
|
259043
259087
|
}
|
|
259044
259088
|
}
|
|
@@ -259046,7 +259090,7 @@ var import_multicast_dns = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMi
|
|
|
259046
259090
|
return "127.0.0.1";
|
|
259047
259091
|
}
|
|
259048
259092
|
function allInterfaces() {
|
|
259049
|
-
var networks = os.networkInterfaces();
|
|
259093
|
+
var networks = os$1.networkInterfaces();
|
|
259050
259094
|
var names = Object.keys(networks);
|
|
259051
259095
|
var res = [];
|
|
259052
259096
|
for (var i = 0; i < names.length; i++) {
|
|
@@ -284570,14 +284614,87 @@ const writeProviderStatusCache = (input) => {
|
|
|
284570
284614
|
})));
|
|
284571
284615
|
};
|
|
284572
284616
|
//#endregion
|
|
284573
|
-
//#region
|
|
284617
|
+
//#region ../../packages/shared/src/providerExecution.ts
|
|
284574
284618
|
/**
|
|
284575
|
-
*
|
|
284576
|
-
*
|
|
284577
|
-
*
|
|
284578
|
-
*
|
|
284619
|
+
* Canonical provider-readiness gate for starting work.
|
|
284620
|
+
*
|
|
284621
|
+
* Cloud user VMs can execute through Forge's scoped provider proxy without a
|
|
284622
|
+
* provider-local login. In that case the provider reports unauthenticated but
|
|
284623
|
+
* its credential provenance is explicitly `proxied`.
|
|
284579
284624
|
*/
|
|
284580
|
-
|
|
284625
|
+
function isServerProviderExecutable(provider) {
|
|
284626
|
+
const hasExecutionAuthority = provider.auth.status !== "unauthenticated" || provider.auth.provenance?.credentialScope === "proxied";
|
|
284627
|
+
return provider.enabled && provider.installed && provider.status === "ready" && hasExecutionAuthority && provider.models.length > 0;
|
|
284628
|
+
}
|
|
284629
|
+
//#endregion
|
|
284630
|
+
//#region src/provider/providerAvailability.ts
|
|
284631
|
+
/**
|
|
284632
|
+
* Derive what this Environment knows about a provider's availability.
|
|
284633
|
+
*
|
|
284634
|
+
* WHY THIS EXISTS
|
|
284635
|
+
*
|
|
284636
|
+
* Four different states (Unbuilt, Not Offered, Not Usable, Not on this
|
|
284637
|
+
* Environment) used to render as one "Coming soon." string, because consumers
|
|
284638
|
+
* were handed a filtered list and had to infer meaning from what was missing.
|
|
284639
|
+
* Every provider is now published with an explicit availability and, when
|
|
284640
|
+
* withheld, its reason. See `docs/adr/withheld-providers-carry-their-reason.md`
|
|
284641
|
+
* and the glossary in `CONTEXT.md` section "Provider availability".
|
|
284642
|
+
*
|
|
284643
|
+
* WHAT AN ENVIRONMENT MAY SAY
|
|
284644
|
+
*
|
|
284645
|
+
* Three of the four reasons only. Not Offered is a fact about the CMMD
|
|
284646
|
+
* organization's Provider Offer, and delivery-pending is a fact about a
|
|
284647
|
+
* credential in flight; neither is visible from inside an Environment, so the
|
|
284648
|
+
* Shell adds them when it joins. Nothing here may invent them.
|
|
284649
|
+
*
|
|
284650
|
+
* This reads the snapshot a provider Layer already produced. It runs no probe
|
|
284651
|
+
* and changes no probe: `isServerProviderExecutable` stays the single gate on
|
|
284652
|
+
* whether a turn can be dispatched, and this restates its answer in the
|
|
284653
|
+
* vocabulary a person sees.
|
|
284654
|
+
*/
|
|
284655
|
+
/**
|
|
284656
|
+
* Providers Forge ships in the UI but cannot execute, published as Unbuilt.
|
|
284657
|
+
*
|
|
284658
|
+
* Empty in this build: codex, claudeAgent, ollama, cmmd, apex, cursor and
|
|
284659
|
+
* opencode all have a real detection Layer and a registered adapter. The list
|
|
284660
|
+
* stays as the one place a provider that ships ahead of its server-side
|
|
284661
|
+
* implementation is named, so "Coming soon." is a deliberate entry rather than
|
|
284662
|
+
* a fallback something fell into.
|
|
284663
|
+
*/
|
|
284664
|
+
const UNBUILT_PROVIDERS = [];
|
|
284665
|
+
function deriveProviderAvailability(provider, unbuiltProviders = UNBUILT_PROVIDERS) {
|
|
284666
|
+
if (unbuiltProviders.includes(provider.provider)) return {
|
|
284667
|
+
availability: "unavailable",
|
|
284668
|
+
unavailableReason: "unbuilt"
|
|
284669
|
+
};
|
|
284670
|
+
if (isServerProviderExecutable(provider)) return { availability: "available" };
|
|
284671
|
+
const hasProxiedCredential = provider.auth.provenance?.credentialScope === "proxied";
|
|
284672
|
+
if (provider.installed && provider.auth.status === "unauthenticated" && !hasProxiedCredential) return {
|
|
284673
|
+
availability: "unavailable",
|
|
284674
|
+
unavailableReason: "not-usable",
|
|
284675
|
+
unavailableDetail: "no-connection"
|
|
284676
|
+
};
|
|
284677
|
+
return {
|
|
284678
|
+
availability: "unavailable",
|
|
284679
|
+
unavailableReason: "not-on-this-environment"
|
|
284680
|
+
};
|
|
284681
|
+
}
|
|
284682
|
+
/**
|
|
284683
|
+
* Stamp a snapshot with its derived availability.
|
|
284684
|
+
*
|
|
284685
|
+
* The previous fields are dropped before the new ones are applied. Merging
|
|
284686
|
+
* would leave a provider that just connected reporting `available` while still
|
|
284687
|
+
* carrying the reason it was withheld for.
|
|
284688
|
+
*/
|
|
284689
|
+
function withProviderAvailability(provider, unbuiltProviders = UNBUILT_PROVIDERS) {
|
|
284690
|
+
const { availability: _availability, unavailableReason: _unavailableReason, unavailableDetail: _unavailableDetail, ...rest } = provider;
|
|
284691
|
+
return {
|
|
284692
|
+
...rest,
|
|
284693
|
+
...deriveProviderAvailability(provider, unbuiltProviders)
|
|
284694
|
+
};
|
|
284695
|
+
}
|
|
284696
|
+
//#endregion
|
|
284697
|
+
//#region src/provider/Layers/ProviderRegistry.ts
|
|
284581
284698
|
const loadProviders = (codexProvider, claudeProvider, ollamaProvider, cmmdProvider, apexProvider, cursorProvider, opencodeProvider) => all([
|
|
284582
284699
|
codexProvider.getSnapshot,
|
|
284583
284700
|
claudeProvider.getSnapshot,
|
|
@@ -284614,9 +284731,9 @@ const ProviderRegistryLive = effect(ProviderRegistry, gen(function* () {
|
|
|
284614
284731
|
cachedProvider,
|
|
284615
284732
|
fallbackProvider
|
|
284616
284733
|
});
|
|
284617
|
-
return
|
|
284734
|
+
return UNBUILT_PROVIDERS.includes(provider) ? fallbackProvider : void 0;
|
|
284618
284735
|
}));
|
|
284619
|
-
}, { concurrency: "unbounded" }).pipe(map$3((providers) => orderProviderSnapshots(providers.filter((provider) => provider !== void 0)))));
|
|
284736
|
+
}, { concurrency: "unbounded" }).pipe(map$3((providers) => orderProviderSnapshots(providers.filter((provider) => provider !== void 0).map((provider) => withProviderAvailability(provider))))));
|
|
284620
284737
|
const persistProvider = (provider) => writeProviderStatusCache({
|
|
284621
284738
|
filePath: cachePathByProvider.get(provider.provider),
|
|
284622
284739
|
provider
|
|
@@ -284624,7 +284741,8 @@ const ProviderRegistryLive = effect(ProviderRegistry, gen(function* () {
|
|
|
284624
284741
|
provider: provider.provider,
|
|
284625
284742
|
error
|
|
284626
284743
|
})), ignore);
|
|
284627
|
-
const upsertProviders = fn$1("upsertProviders")(function* (
|
|
284744
|
+
const upsertProviders = fn$1("upsertProviders")(function* (incomingProviders, options) {
|
|
284745
|
+
const nextProviders = incomingProviders.map((provider) => withProviderAvailability(provider));
|
|
284628
284746
|
const [previousProviders, providers] = yield* modify$3(providersRef, (previousProviders) => {
|
|
284629
284747
|
const mergedProviders = new Map(previousProviders.map((provider) => [provider.provider, provider]));
|
|
284630
284748
|
for (const provider of nextProviders) mergedProviders.set(provider.provider, provider);
|
|
@@ -287328,13 +287446,32 @@ const ObservabilityLive = unwrap(gen(function* () {
|
|
|
287328
287446
|
//#region ../../packages/shared/src/buildCommit.ts
|
|
287329
287447
|
const COMMIT_SHA_PATTERN$1 = /^[0-9a-f]{40}$/i;
|
|
287330
287448
|
/**
|
|
287449
|
+
* Whether a value is a full commit sha.
|
|
287450
|
+
*
|
|
287451
|
+
* Exported so each surface can keep its own list of env names while sharing one
|
|
287452
|
+
* definition of "valid". Surfaces genuinely differ: Railway injects
|
|
287453
|
+
* RAILWAY_GIT_COMMIT_SHA into the cloud shell build, while the desktop release
|
|
287454
|
+
* workflows pass VITE_SENTRY_COMMIT_SHA. What must not differ is the check.
|
|
287455
|
+
*/
|
|
287456
|
+
function isFullCommitSha(value) {
|
|
287457
|
+
const candidate = value?.trim();
|
|
287458
|
+
return candidate !== void 0 && candidate !== "" && COMMIT_SHA_PATTERN$1.test(candidate);
|
|
287459
|
+
}
|
|
287460
|
+
/**
|
|
287331
287461
|
* Build identity from the environment.
|
|
287332
287462
|
*
|
|
287333
287463
|
* `.dockerignore` excludes `.git`, so `git rev-parse HEAD` cannot resolve inside
|
|
287334
287464
|
* the Railway container — the surface where "which commit is live?" is asked
|
|
287335
287465
|
* most. The commit therefore has to arrive as env there, with git left as the
|
|
287336
|
-
* fallback for surfaces that genuinely are checkouts (
|
|
287337
|
-
*
|
|
287466
|
+
* fallback for surfaces that genuinely are checkouts (desktop and dev run from
|
|
287467
|
+
* a repo).
|
|
287468
|
+
*
|
|
287469
|
+
* A cloud runtime is neither: it installs the published `@cmmd-center/forge`
|
|
287470
|
+
* tarball, so it has no build variables and no `.git`, and both rungs below
|
|
287471
|
+
* return nothing there. That is why the server bakes the commit into its bundle
|
|
287472
|
+
* at build time and reads that first — see `apps/server/src/environment/
|
|
287473
|
+
* buildIdentity.ts`. This comment previously claimed a sprite clones forge at
|
|
287474
|
+
* bootstrap; it does not, and believing it cost a diagnosis.
|
|
287338
287475
|
*
|
|
287339
287476
|
* `RAILWAY_GIT_COMMIT_SHA` is injected by the platform, which is what lets this
|
|
287340
287477
|
* work on the cloud shell with no Dockerfile plumbing. `FORGE_BUILD_COMMIT`
|
|
@@ -287352,6 +287489,30 @@ function resolveBuildCommitFromEnv(env) {
|
|
|
287352
287489
|
}
|
|
287353
287490
|
}
|
|
287354
287491
|
//#endregion
|
|
287492
|
+
//#region src/environment/buildIdentity.ts
|
|
287493
|
+
/**
|
|
287494
|
+
* Which commit this server was built from.
|
|
287495
|
+
*
|
|
287496
|
+
* @module
|
|
287497
|
+
*/
|
|
287498
|
+
/**
|
|
287499
|
+
* The commit baked into this bundle, if it is a bundle at all.
|
|
287500
|
+
*
|
|
287501
|
+
* `typeof` rather than a bare reference: running from source there is no such
|
|
287502
|
+
* binding, and a bare reference is a ReferenceError that would take the whole
|
|
287503
|
+
* environment descriptor down instead of reporting an honest "unknown".
|
|
287504
|
+
*/
|
|
287505
|
+
function readBakedBuildCommit() {
|
|
287506
|
+
return "86c71b55bb7a5dfbbad063d6e4c082570759f006";
|
|
287507
|
+
}
|
|
287508
|
+
async function resolveServerBuildCommit(input) {
|
|
287509
|
+
if (isFullCommitSha(input.baked)) return input.baked;
|
|
287510
|
+
const fromEnv = resolveBuildCommitFromEnv(input.env);
|
|
287511
|
+
if (fromEnv !== void 0) return fromEnv;
|
|
287512
|
+
const fromGit = await input.readGitCommit();
|
|
287513
|
+
return isFullCommitSha(fromGit) ? fromGit : void 0;
|
|
287514
|
+
}
|
|
287515
|
+
//#endregion
|
|
287355
287516
|
//#region src/environment/Layers/ServerEnvironmentLabel.ts
|
|
287356
287517
|
function normalizeLabel(value) {
|
|
287357
287518
|
const trimmed = value?.trim();
|
|
@@ -287499,7 +287660,11 @@ const ServerEnvironmentLive = effect(ServerEnvironment, fn$1("makeServerEnvironm
|
|
|
287499
287660
|
const writeMetadata = (metadata) => fileSystem.writeFileString(serverConfig.environmentMetadataPath, `${JSON.stringify(metadata, null, 2)}\n`);
|
|
287500
287661
|
const displayNameOverrideRef = yield* make$47(normalizeDisplayName((yield* readMetadata).displayName ?? null));
|
|
287501
287662
|
const posthog = yield* PosthogPublicConfig.asEffect();
|
|
287502
|
-
const buildCommit =
|
|
287663
|
+
const buildCommit = yield* promise(() => resolveServerBuildCommit({
|
|
287664
|
+
baked: readBakedBuildCommit(),
|
|
287665
|
+
env: process.env,
|
|
287666
|
+
readGitCommit: resolveForgeGitCommit
|
|
287667
|
+
}));
|
|
287503
287668
|
const buildDescriptor = gen(function* () {
|
|
287504
287669
|
const displayNameOverride = yield* get$5(displayNameOverrideRef);
|
|
287505
287670
|
const os = platformOs();
|
|
@@ -296243,6 +296408,71 @@ function selectProviderUsageEnvironment(rawCatalog, ownerUserId) {
|
|
|
296243
296408
|
};
|
|
296244
296409
|
}
|
|
296245
296410
|
//#endregion
|
|
296411
|
+
//#region src/command/claudeUsageRefreshFailure.ts
|
|
296412
|
+
/**
|
|
296413
|
+
* Why a Claude usage refresh failed, in words the Usage page can show.
|
|
296414
|
+
*
|
|
296415
|
+
* CMMD answers a failed refresh with a machine reason (`runtime_not_active`,
|
|
296416
|
+
* `credential_changed`, …). The Shell used to collapse every one of them into
|
|
296417
|
+
* "Claude usage refresh is unavailable.", so the card could only say the
|
|
296418
|
+
* refresh failed, never what to do about it.
|
|
296419
|
+
*
|
|
296420
|
+
* The mapping is a closed allowlist in both directions: a reason CMMD may add
|
|
296421
|
+
* later falls back to the generic code, and the text a client sees is written
|
|
296422
|
+
* here, never forwarded from upstream. That keeps a credential or runtime
|
|
296423
|
+
* detail in a CMMD error body from reaching the browser.
|
|
296424
|
+
*/
|
|
296425
|
+
const FAILURE_TEXT = {
|
|
296426
|
+
CLAUDE_USAGE_ENVIRONMENT_UNAVAILABLE: "Refresh needs your Environment running.",
|
|
296427
|
+
CLAUDE_USAGE_CREDENTIAL_NOT_READY: "The Claude credential is not applied to your Environment yet.",
|
|
296428
|
+
CLAUDE_USAGE_REFRESH_IN_PROGRESS: "A refresh for this account is already running.",
|
|
296429
|
+
CLAUDE_USAGE_PROBE_FAILED: "Your Environment could not read Claude usage.",
|
|
296430
|
+
CLAUDE_USAGE_RECONNECT_REQUIRED: "Reconnect this Claude account to refresh usage.",
|
|
296431
|
+
CLAUDE_USAGE_ACCOUNT_NOT_FOUND: "This Claude account is no longer connected.",
|
|
296432
|
+
CLAUDE_USAGE_REFRESH_UNAVAILABLE: "Claude usage refresh is unavailable."
|
|
296433
|
+
};
|
|
296434
|
+
/** Verbatim from CMMD `personal-claude-usage.ts` and `provider-usage-service.ts`. */
|
|
296435
|
+
const CODE_BY_UPSTREAM_REASON = {
|
|
296436
|
+
runtime_not_active: "CLAUDE_USAGE_ENVIRONMENT_UNAVAILABLE",
|
|
296437
|
+
claude_native_delivery_disabled: "CLAUDE_USAGE_ENVIRONMENT_UNAVAILABLE",
|
|
296438
|
+
runtime_identity_mismatch: "CLAUDE_USAGE_ENVIRONMENT_UNAVAILABLE",
|
|
296439
|
+
credential_generation_not_applied: "CLAUDE_USAGE_CREDENTIAL_NOT_READY",
|
|
296440
|
+
credential_generation_unavailable: "CLAUDE_USAGE_CREDENTIAL_NOT_READY",
|
|
296441
|
+
credential_changed: "CLAUDE_USAGE_CREDENTIAL_NOT_READY",
|
|
296442
|
+
refresh_in_progress: "CLAUDE_USAGE_REFRESH_IN_PROGRESS",
|
|
296443
|
+
usage_probe_failed: "CLAUDE_USAGE_PROBE_FAILED",
|
|
296444
|
+
usage_receipt_invalid: "CLAUDE_USAGE_PROBE_FAILED",
|
|
296445
|
+
usage_output_invalid: "CLAUDE_USAGE_PROBE_FAILED"
|
|
296446
|
+
};
|
|
296447
|
+
function failure(code, status) {
|
|
296448
|
+
return {
|
|
296449
|
+
status,
|
|
296450
|
+
body: {
|
|
296451
|
+
error: FAILURE_TEXT[code],
|
|
296452
|
+
code
|
|
296453
|
+
}
|
|
296454
|
+
};
|
|
296455
|
+
}
|
|
296456
|
+
/**
|
|
296457
|
+
* CMMD sends the reason as `{ "error": "<reason>" }`. Anything else, including
|
|
296458
|
+
* a longer sentence that happens to start with an allowlisted word, is treated
|
|
296459
|
+
* as unknown.
|
|
296460
|
+
*/
|
|
296461
|
+
function readAllowlistedReasonCode(body) {
|
|
296462
|
+
if (body === null || typeof body !== "object") return null;
|
|
296463
|
+
const reason = body.error;
|
|
296464
|
+
if (typeof reason !== "string") return null;
|
|
296465
|
+
return Object.hasOwn(CODE_BY_UPSTREAM_REASON, reason) ? CODE_BY_UPSTREAM_REASON[reason] ?? null : null;
|
|
296466
|
+
}
|
|
296467
|
+
function describeClaudeUsageRefreshFailure(error) {
|
|
296468
|
+
if (!(error instanceof CmmdProxyRequestError)) return failure("CLAUDE_USAGE_REFRESH_UNAVAILABLE", 502);
|
|
296469
|
+
if (error.status === 409) return failure("CLAUDE_USAGE_RECONNECT_REQUIRED", 409);
|
|
296470
|
+
if (error.status === 404) return failure("CLAUDE_USAGE_ACCOUNT_NOT_FOUND", 404);
|
|
296471
|
+
const reasonCode = readAllowlistedReasonCode(error.body);
|
|
296472
|
+
if (reasonCode !== null) return failure(reasonCode, 503);
|
|
296473
|
+
return failure("CLAUDE_USAGE_REFRESH_UNAVAILABLE", error.status === 503 ? 503 : 502);
|
|
296474
|
+
}
|
|
296475
|
+
//#endregion
|
|
296246
296476
|
//#region src/command/wsToken.ts
|
|
296247
296477
|
/**
|
|
296248
296478
|
* Build the WS token response given an optional PAT and the configured CMMD
|
|
@@ -297979,10 +298209,8 @@ function resolveProviderUsageOwnerUserId(session) {
|
|
|
297979
298209
|
return Number.isSafeInteger(userId) && userId > 0 ? userId : null;
|
|
297980
298210
|
}
|
|
297981
298211
|
function buildClaudeUsageRefreshErrorResponse(error) {
|
|
297982
|
-
|
|
297983
|
-
|
|
297984
|
-
code: "CLAUDE_USAGE_REFRESH_UNAVAILABLE"
|
|
297985
|
-
}, { status: error instanceof CmmdProxyRequestError && error.status === 503 ? 503 : 502 });
|
|
298212
|
+
const failure = describeClaudeUsageRefreshFailure(error);
|
|
298213
|
+
return jsonUnsafe(failure.body, { status: failure.status });
|
|
297986
298214
|
}
|
|
297987
298215
|
function makeRefreshProviderUsageHandler(dependencies = {}) {
|
|
297988
298216
|
return gen(function* () {
|
|
@@ -299937,21 +300165,24 @@ function processRuntimeReplicationOutboxItem(item, dependencies) {
|
|
|
299937
300165
|
//#endregion
|
|
299938
300166
|
//#region src/runtimeReplication/Services/ActiveHourMeter.ts
|
|
299939
300167
|
var ActiveHourMeter = class extends Service$1()("forge/runtimeReplication/Services/ActiveHourMeter") {};
|
|
299940
|
-
|
|
299941
|
-
|
|
300168
|
+
/** `slug` and `name` are `.min(1).max(512)` on the receiving schema. */
|
|
300169
|
+
const FIELD_MAX_LENGTH = 512;
|
|
300170
|
+
/** `version` is `.max(128)` on the receiving schema, with no length floor. */
|
|
300171
|
+
const VERSION_MAX_LENGTH = 128;
|
|
300172
|
+
/** `binaryPath` is `.max(2048)` on the receiving schema, with no length floor. */
|
|
300173
|
+
const BINARY_PATH_MAX_LENGTH = 2048;
|
|
299942
300174
|
/**
|
|
299943
|
-
*
|
|
300175
|
+
* Trim to the receiving schema's maximum, or null.
|
|
299944
300176
|
*
|
|
299945
|
-
*
|
|
299946
|
-
*
|
|
299947
|
-
*
|
|
300177
|
+
* Neither field has a length floor, so an empty string would be legal — but it
|
|
300178
|
+
* is not an answer. A version nobody resolved and a binary nobody found are
|
|
300179
|
+
* both null, which is what the control plane stores and what a reader can tell
|
|
300180
|
+
* apart from a real value.
|
|
299948
300181
|
*/
|
|
299949
|
-
function
|
|
299950
|
-
const
|
|
299951
|
-
return
|
|
300182
|
+
function cappedText(value, max) {
|
|
300183
|
+
const normalized = value?.trim().slice(0, max);
|
|
300184
|
+
return normalized ? normalized : null;
|
|
299952
300185
|
}
|
|
299953
|
-
/** `slug` and `name` are `.min(1).max(512)` on the receiving schema. */
|
|
299954
|
-
const FIELD_MAX_LENGTH = 512;
|
|
299955
300186
|
function serializeModel(model) {
|
|
299956
300187
|
const slug = model.slug.trim().slice(0, FIELD_MAX_LENGTH);
|
|
299957
300188
|
const name = model.name.trim().slice(0, FIELD_MAX_LENGTH);
|
|
@@ -299965,20 +300196,48 @@ function serializeModel(model) {
|
|
|
299965
300196
|
name
|
|
299966
300197
|
};
|
|
299967
300198
|
}
|
|
300199
|
+
/**
|
|
300200
|
+
* The availability to publish for one provider.
|
|
300201
|
+
*
|
|
300202
|
+
* The registry stamps every snapshot before it leaves, so this normally copies.
|
|
300203
|
+
* Deriving over a stamped snapshot would lose the one fact only the registry
|
|
300204
|
+
* holds — which providers this build cannot run at all — and republish every
|
|
300205
|
+
* Unbuilt provider as Not on this Environment. The fallback covers a snapshot
|
|
300206
|
+
* that reached the outbox from some other path.
|
|
300207
|
+
*
|
|
300208
|
+
* Built by spread so a key with no value is ABSENT rather than `undefined`: the
|
|
300209
|
+
* receiving object is `.strict()`, `undefined` is not a legal enum member, and
|
|
300210
|
+
* the outbox retries the resulting 400 forever.
|
|
300211
|
+
*/
|
|
300212
|
+
function publishedAvailability(provider) {
|
|
300213
|
+
if (provider.availability === void 0) return deriveProviderAvailability(provider);
|
|
300214
|
+
const reason = provider.unavailableReason;
|
|
300215
|
+
const detail = provider.unavailableDetail;
|
|
300216
|
+
return {
|
|
300217
|
+
availability: provider.availability,
|
|
300218
|
+
...reason === void 0 ? {} : { unavailableReason: reason },
|
|
300219
|
+
...detail === void 0 ? {} : { unavailableDetail: detail }
|
|
300220
|
+
};
|
|
300221
|
+
}
|
|
300222
|
+
function serializeProvider(provider) {
|
|
300223
|
+
const models = [];
|
|
300224
|
+
for (const model of provider.models) {
|
|
300225
|
+
if (models.length >= 256) break;
|
|
300226
|
+
const serialized = serializeModel(model);
|
|
300227
|
+
if (serialized) models.push(serialized);
|
|
300228
|
+
}
|
|
300229
|
+
return {
|
|
300230
|
+
provider: provider.provider,
|
|
300231
|
+
models,
|
|
300232
|
+
...publishedAvailability(provider),
|
|
300233
|
+
version: cappedText(provider.version, VERSION_MAX_LENGTH),
|
|
300234
|
+
binaryPath: cappedText(provider.auth.provenance?.binaryPath, BINARY_PATH_MAX_LENGTH)
|
|
300235
|
+
};
|
|
300236
|
+
}
|
|
299968
300237
|
function serializeProviderCatalog(providers) {
|
|
299969
300238
|
const catalog = [];
|
|
299970
300239
|
for (const provider of providers) {
|
|
299971
|
-
|
|
299972
|
-
const models = [];
|
|
299973
|
-
for (const model of provider.models) {
|
|
299974
|
-
if (models.length >= 256) break;
|
|
299975
|
-
const serialized = serializeModel(model);
|
|
299976
|
-
if (serialized) models.push(serialized);
|
|
299977
|
-
}
|
|
299978
|
-
catalog.push({
|
|
299979
|
-
provider: provider.provider,
|
|
299980
|
-
models
|
|
299981
|
-
});
|
|
300240
|
+
catalog.push(serializeProvider(provider));
|
|
299982
300241
|
if (catalog.length >= 16) break;
|
|
299983
300242
|
}
|
|
299984
300243
|
return catalog;
|
|
@@ -310426,7 +310685,7 @@ const connectLoginCommand = make$32("login", {
|
|
|
310426
310685
|
})).json();
|
|
310427
310686
|
},
|
|
310428
310687
|
openBrowser: async (url) => {
|
|
310429
|
-
const open = (await import("./open-
|
|
310688
|
+
const open = (await import("./open-FIgTDosf.mjs")).default;
|
|
310430
310689
|
await open(url);
|
|
310431
310690
|
return true;
|
|
310432
310691
|
},
|