@bike4mind/cli 0.15.3 → 0.17.0
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/README.md +49 -0
- package/bin/bike4mind-cli.mjs +74 -0
- package/dist/{tools-GgAy5rmD.mjs → BackgroundAgentManager-CcetLt8q.mjs} +8637 -8283
- package/dist/{ConfigStore-D4gALtkZ.mjs → ConfigStore-CNveAjEv.mjs} +451 -27
- package/dist/commands/apiCommand.mjs +1 -1
- package/dist/commands/doctorCommand.mjs +1 -1
- package/dist/commands/envCommand.mjs +1 -1
- package/dist/commands/headlessCommand.mjs +6 -2
- package/dist/commands/mcpCommand.mjs +1 -1
- package/dist/commands/updateCommand.mjs +1 -1
- package/dist/index.mjs +2273 -2061
- package/dist/{package-CfGv2cC5.mjs → package-BJU2qiwQ.mjs} +1 -1
- package/package.json +13 -9
|
@@ -291,9 +291,6 @@ let ImageModels = /* @__PURE__ */ function(ImageModels) {
|
|
|
291
291
|
ImageModels["FLUX_PRO_1_1"] = "flux-pro-1.1";
|
|
292
292
|
ImageModels["FLUX_PRO_ULTRA"] = "flux-pro-1.1-ultra";
|
|
293
293
|
ImageModels["FLUX_PRO_FILL"] = "flux-pro-1.0-fill";
|
|
294
|
-
ImageModels["FLUX_PRO_CANNY"] = "flux-pro-1.0-canny";
|
|
295
|
-
ImageModels["FLUX_PRO_DEPTH"] = "flux-pro-1.0-depth";
|
|
296
|
-
ImageModels["FLUX_DEV"] = "flux-dev";
|
|
297
294
|
ImageModels["FLUX_KONTEXT_PRO"] = "flux-kontext-pro";
|
|
298
295
|
ImageModels["FLUX_KONTEXT_MAX"] = "flux-kontext-max";
|
|
299
296
|
ImageModels["GROK_IMAGINE_IMAGE_QUALITY"] = "grok-imagine-image-quality";
|
|
@@ -593,7 +590,8 @@ const b4mLLMTools = z.enum([
|
|
|
593
590
|
"navigate_view",
|
|
594
591
|
"generate_jupyter_notebook",
|
|
595
592
|
"excel_generation",
|
|
596
|
-
"fmp_financial_data"
|
|
593
|
+
"fmp_financial_data",
|
|
594
|
+
"skill"
|
|
597
595
|
]);
|
|
598
596
|
b4mLLMTools.options.map((tool) => tool);
|
|
599
597
|
/**
|
|
@@ -905,6 +903,45 @@ const ClaudeArtifactMimeTypes = {
|
|
|
905
903
|
PYTHON: "application/vnd.ant.python",
|
|
906
904
|
BLOG_DRAFT: "application/vnd.b4m.blog-draft"
|
|
907
905
|
};
|
|
906
|
+
/**
|
|
907
|
+
* Map a MIME type (or AI-provider artifact-type string) to an internal {@link ArtifactType}.
|
|
908
|
+
*
|
|
909
|
+
* Single source of truth — consumed by the artifact parsers (b4m-core/utils + client) and the
|
|
910
|
+
* tool_result dedup in ChatCompletionProcess. Exact blessed-type matches first (case-insensitive,
|
|
911
|
+
* since MIME types are), then language/format inference; returns null if unrecognized.
|
|
912
|
+
*
|
|
913
|
+
* #8907: this previously lived as three hand-maintained copies that drifted — e.g. the lattice
|
|
914
|
+
* tool emits `application/vnd.b4m.lattice` but a copy matched `application/vnd.ant.lattice`,
|
|
915
|
+
* letting lattice tool_result artifacts dodge the dedup set (#8905).
|
|
916
|
+
*/
|
|
917
|
+
function mapMimeTypeToArtifactType(mimeType) {
|
|
918
|
+
if (!mimeType) return null;
|
|
919
|
+
const normalized = mimeType.toLowerCase().trim();
|
|
920
|
+
switch (normalized) {
|
|
921
|
+
case ClaudeArtifactMimeTypes.REACT.toLowerCase(): return "react";
|
|
922
|
+
case ClaudeArtifactMimeTypes.HTML.toLowerCase(): return "html";
|
|
923
|
+
case ClaudeArtifactMimeTypes.SVG.toLowerCase(): return "svg";
|
|
924
|
+
case ClaudeArtifactMimeTypes.MERMAID.toLowerCase(): return "mermaid";
|
|
925
|
+
case ClaudeArtifactMimeTypes.RECHARTS.toLowerCase(): return "recharts";
|
|
926
|
+
case ClaudeArtifactMimeTypes.CHESS.toLowerCase(): return "chess";
|
|
927
|
+
case ClaudeArtifactMimeTypes.CODE.toLowerCase(): return "code";
|
|
928
|
+
case ClaudeArtifactMimeTypes.MARKDOWN.toLowerCase(): return "code";
|
|
929
|
+
case ClaudeArtifactMimeTypes.LATTICE.toLowerCase(): return "lattice";
|
|
930
|
+
case ClaudeArtifactMimeTypes.PYTHON.toLowerCase(): return "python";
|
|
931
|
+
case ClaudeArtifactMimeTypes.BLOG_DRAFT.toLowerCase(): return "blog-draft";
|
|
932
|
+
}
|
|
933
|
+
if (normalized.includes("jsx") || normalized.includes("react")) return "react";
|
|
934
|
+
if (normalized.includes("javascript") || normalized.includes("typescript")) return "code";
|
|
935
|
+
if (normalized.includes("python") || normalized === "text/x-python") return "python";
|
|
936
|
+
if (normalized.includes("java") || normalized.includes("c++") || normalized.includes("rust") || normalized.includes("go") || normalized.includes("ruby") || normalized.includes("php") || normalized.includes("swift") || normalized.includes("kotlin") || normalized.includes("csharp") || normalized.includes("c#")) return "code";
|
|
937
|
+
if (normalized.includes("html") || normalized.includes("xhtml")) return "html";
|
|
938
|
+
if (normalized.includes("svg")) return "svg";
|
|
939
|
+
if (normalized.includes("markdown") || normalized.includes("md")) return "code";
|
|
940
|
+
if (normalized.includes("mermaid")) return "mermaid";
|
|
941
|
+
if (normalized.includes("recharts") || normalized.includes("chart")) return "recharts";
|
|
942
|
+
if (normalized.includes("chess")) return "chess";
|
|
943
|
+
return null;
|
|
944
|
+
}
|
|
908
945
|
let KnowledgeType = /* @__PURE__ */ function(KnowledgeType) {
|
|
909
946
|
/**
|
|
910
947
|
* A knowledge that is from a URL.
|
|
@@ -1278,7 +1315,7 @@ const SreRepoConfigSchema = z.object({
|
|
|
1278
1315
|
/** Whether this repo's SRE pipeline is active */
|
|
1279
1316
|
enabled: z.boolean().default(false),
|
|
1280
1317
|
/** LLM model ID for Diagnostician */
|
|
1281
|
-
modelId: z.string().default("claude-sonnet-4-
|
|
1318
|
+
modelId: z.string().default("claude-sonnet-4-6"),
|
|
1282
1319
|
/** Max diff lines per fix */
|
|
1283
1320
|
maxDiffLines: z.number().min(1).default(50),
|
|
1284
1321
|
/** Max fixes dispatched per day for this repo */
|
|
@@ -2663,7 +2700,8 @@ const AgentCompletedAction = z.object({
|
|
|
2663
2700
|
executionId: z.string(),
|
|
2664
2701
|
answer: z.string().optional(),
|
|
2665
2702
|
totalIterations: z.number(),
|
|
2666
|
-
totalCreditsUsed: z.number()
|
|
2703
|
+
totalCreditsUsed: z.number(),
|
|
2704
|
+
mementoIds: z.array(z.string()).optional()
|
|
2667
2705
|
});
|
|
2668
2706
|
const AgentFailedAction = z.object({
|
|
2669
2707
|
action: z.literal("failed"),
|
|
@@ -3615,10 +3653,37 @@ const OpenAIImageStyleSchema = z.enum(["vivid", "natural"]);
|
|
|
3615
3653
|
/**
|
|
3616
3654
|
* Maps legacy/removed image model IDs to their current replacements.
|
|
3617
3655
|
* Prevents Zod validation failures when clients send stale persisted model names.
|
|
3656
|
+
*
|
|
3657
|
+
* Values are constrained to `ALL_IMAGE_MODELS` so a remap target can never point at
|
|
3658
|
+
* a model that the schema would itself reject. If a target model is later retired,
|
|
3659
|
+
* re-point its entry to the current replacement rather than deleting it — clients may
|
|
3660
|
+
* still hold the legacy key in persisted state. When adding an entry, bump the
|
|
3661
|
+
* `llm-settings` persist `version` in LLMContext so existing clients re-run the remap.
|
|
3662
|
+
*
|
|
3663
|
+
* Only `flux-dev` is aliased among the recently removed Flux ids. It was a general
|
|
3664
|
+
* text-to-image model with a confirmed stale client (#8853), so remapping to the
|
|
3665
|
+
* current BFL standard is safe and faithful. `flux-pro-1.0-canny` and
|
|
3666
|
+
* `flux-pro-1.0-depth` are intentionally left as hard validation errors: they were
|
|
3667
|
+
* never UI-selectable (nothing persists them, and no alerts show callers sending them)
|
|
3668
|
+
* and they are structural-control models — silently remapping them to a plain
|
|
3669
|
+
* text-to-image model would drop the control image and return the wrong kind of result,
|
|
3670
|
+
* so a clear "unsupported model" error is the more honest response.
|
|
3671
|
+
*
|
|
3672
|
+
* The three `grok-2-image*` ids are the prior xAI image-model ids, all superseded
|
|
3673
|
+
* in the enum by `grok-imagine-image-quality`. The id went through an un-aliased
|
|
3674
|
+
* rename chain — `grok-2-image-1212` (original) → `grok-2-image` → `grok-2-image-gen`
|
|
3675
|
+
* → `grok-imagine-image-quality` — so any of the three could survive in stale
|
|
3676
|
+
* persisted client state (`grok-2-image-1212` was the one observed in #9211). All are
|
|
3677
|
+
* xAI text-to-image models, so remapping to the current id is a faithful
|
|
3678
|
+
* same-provider, same-modality replacement.
|
|
3618
3679
|
*/
|
|
3619
3680
|
const LEGACY_IMAGE_MODEL_MAP = {
|
|
3620
3681
|
"dall-e-3": "gpt-image-1",
|
|
3621
|
-
"dall-e-2": "gpt-image-1"
|
|
3682
|
+
"dall-e-2": "gpt-image-1",
|
|
3683
|
+
"flux-dev": "flux-pro-1.1",
|
|
3684
|
+
"grok-2-image-1212": "grok-imagine-image-quality",
|
|
3685
|
+
"grok-2-image": "grok-imagine-image-quality",
|
|
3686
|
+
"grok-2-image-gen": "grok-imagine-image-quality"
|
|
3622
3687
|
};
|
|
3623
3688
|
const OpenAIImageGenerationInput = z.object({
|
|
3624
3689
|
prompt: z.string(),
|
|
@@ -3829,6 +3894,8 @@ z.enum([
|
|
|
3829
3894
|
"logoSettings",
|
|
3830
3895
|
"enforceCredits",
|
|
3831
3896
|
"enableTeamPlan",
|
|
3897
|
+
"allowOpenRegistration",
|
|
3898
|
+
"defaultFreeCredits",
|
|
3832
3899
|
"enableGoogleCalendar",
|
|
3833
3900
|
"googleCalendarServiceAccountEmail",
|
|
3834
3901
|
"googleCalendarServiceAccountSecret",
|
|
@@ -3913,8 +3980,36 @@ const OrchestrationDefaultsSchema = z.object({
|
|
|
3913
3980
|
"code_execute",
|
|
3914
3981
|
"coordinate_task"
|
|
3915
3982
|
]),
|
|
3916
|
-
/**
|
|
3917
|
-
|
|
3983
|
+
/**
|
|
3984
|
+
* Tool names explicitly forbidden. Enforced as a final subtraction in
|
|
3985
|
+
* `pickEffectiveEnabledTools` — wins even over payload-pinned tools — so this
|
|
3986
|
+
* is the defense-in-depth backstop for the case where an admin broadens
|
|
3987
|
+
* `allowedTools` without realizing a parallel denylist is also needed.
|
|
3988
|
+
*
|
|
3989
|
+
* Seeded with every tool that mutates user data (#8945, the spec's
|
|
3990
|
+
* "anything tagged `mutates_user_data`"): destructive/overwriting filesystem
|
|
3991
|
+
* writes, arbitrary shell execution, blog authoring/publishing, persisted
|
|
3992
|
+
* lattice-model writes, and storage-backed artifact generation. New mutating
|
|
3993
|
+
* tools should be added here until the tag-derived denylist (Option B, #8923/
|
|
3994
|
+
* #8924) replaces this hand-curated list.
|
|
3995
|
+
*/
|
|
3996
|
+
deniedTools: z.array(z.string()).default([
|
|
3997
|
+
"create_file",
|
|
3998
|
+
"edit_file",
|
|
3999
|
+
"edit_local_file",
|
|
4000
|
+
"delete_file",
|
|
4001
|
+
"bash_execute",
|
|
4002
|
+
"blog_draft",
|
|
4003
|
+
"blog_edit",
|
|
4004
|
+
"blog_publish",
|
|
4005
|
+
"lattice_create_model",
|
|
4006
|
+
"lattice_add_entity",
|
|
4007
|
+
"lattice_set_value",
|
|
4008
|
+
"lattice_create_rule",
|
|
4009
|
+
"image_generation",
|
|
4010
|
+
"edit_image",
|
|
4011
|
+
"excel_generation"
|
|
4012
|
+
]),
|
|
3918
4013
|
/** Per-thoroughness iteration ceiling. Matches `IAgent.maxIterations`. */
|
|
3919
4014
|
maxIterations: z.object({
|
|
3920
4015
|
quick: z.number().int().positive(),
|
|
@@ -5765,6 +5860,22 @@ const settingsMap = {
|
|
|
5765
5860
|
group: API_SERVICE_GROUPS.CREDITS.id,
|
|
5766
5861
|
category: "Users"
|
|
5767
5862
|
}),
|
|
5863
|
+
allowOpenRegistration: makeBooleanSetting({
|
|
5864
|
+
key: "allowOpenRegistration",
|
|
5865
|
+
name: "Allow Open Registration",
|
|
5866
|
+
defaultValue: false,
|
|
5867
|
+
description: "Master switch for self-serve signup. When OFF (default), a valid invite code is required to register. When ON, users may register without an invite code; the Default Free Credits grant is then applied after they verify their email (anti-spam — see Default Free Credits). Safe to enable: the pre-request credit reservation caps every free user at the credits they are granted.",
|
|
5868
|
+
group: API_SERVICE_GROUPS.CREDITS.id,
|
|
5869
|
+
category: "Users"
|
|
5870
|
+
}),
|
|
5871
|
+
defaultFreeCredits: makeNumberSetting({
|
|
5872
|
+
key: "defaultFreeCredits",
|
|
5873
|
+
name: "Default Free Credits",
|
|
5874
|
+
defaultValue: 0,
|
|
5875
|
+
description: "Credits granted to a user who registers WITHOUT an invite code (only applies when Allow Open Registration is ON). Granted after the user verifies their email, NOT at signup — an unverified throwaway account gets 0 credits (anti-spam). A free user can never spend more than this — their hard ceiling of real model cost is roughly credits ÷ 1500 USD.",
|
|
5876
|
+
group: API_SERVICE_GROUPS.CREDITS.id,
|
|
5877
|
+
category: "Users"
|
|
5878
|
+
}),
|
|
5768
5879
|
FacebookLink: makeStringSetting({
|
|
5769
5880
|
key: "FacebookLink",
|
|
5770
5881
|
name: "Facebook Link",
|
|
@@ -7229,6 +7340,13 @@ const PromptMetaZodSchema = z.object({
|
|
|
7229
7340
|
generatedImageReferences: z.array(z.string()).optional(),
|
|
7230
7341
|
promptErrors: z.array(z.string()).optional(),
|
|
7231
7342
|
warnings: z.array(z.string()).optional(),
|
|
7343
|
+
/**
|
|
7344
|
+
* The provider's reason for ending generation (Anthropic vocabulary:
|
|
7345
|
+
* 'end_turn' | 'max_tokens' | 'tool_use' | 'stop_sequence' | 'pause_turn').
|
|
7346
|
+
* 'max_tokens' signals the response was truncated against the output ceiling,
|
|
7347
|
+
* letting the client render a truncated-artifact recovery affordance (#9259).
|
|
7348
|
+
*/
|
|
7349
|
+
finishReason: z.string().optional(),
|
|
7232
7350
|
statusLog: z.array(z.object({
|
|
7233
7351
|
status: z.string(),
|
|
7234
7352
|
timestamp: z.date().or(z.string())
|
|
@@ -7950,12 +8068,14 @@ z$1.object({
|
|
|
7950
8068
|
slug: z$1.string().min(2).max(60).regex(slugRegex, "Slug must be lowercase alphanumeric with hyphens (e.g. \"my-data-lake\")"),
|
|
7951
8069
|
description: z$1.string().max(2e3).optional(),
|
|
7952
8070
|
fileTagPrefix: z$1.string().min(2).max(30).refine((s) => s.endsWith(":"), "Tag prefix must end with \":\" (e.g. \"acme:\")"),
|
|
7953
|
-
requiredUserTag: z$1.string().min(1).max(100).optional()
|
|
8071
|
+
requiredUserTag: z$1.string().min(1).max(100).optional(),
|
|
8072
|
+
requiredEntitlement: z$1.string().min(3).max(100).refine((s) => s.includes(":") && s.split(":").every((part) => part.length > 0), "Entitlement key must be namespaced with non-empty parts (e.g. \"product:pro\")").optional()
|
|
7954
8073
|
});
|
|
7955
8074
|
z$1.object({
|
|
7956
8075
|
name: z$1.string().min(1).max(200).optional(),
|
|
7957
8076
|
description: z$1.string().max(2e3).optional(),
|
|
7958
|
-
requiredUserTag: z$1.string().min(1).max(100).optional()
|
|
8077
|
+
requiredUserTag: z$1.string().min(1).max(100).optional(),
|
|
8078
|
+
requiredEntitlement: z$1.string().min(3).max(100).refine((s) => s.includes(":") && s.split(":").every((part) => part.length > 0), "Entitlement key must be namespaced with non-empty parts (e.g. \"product:pro\")").optional()
|
|
7959
8079
|
});
|
|
7960
8080
|
z$1.object({
|
|
7961
8081
|
organizationId: z$1.string().optional(),
|
|
@@ -8101,37 +8221,68 @@ const DATA_LAKES = [{
|
|
|
8101
8221
|
id: "ionq-sales",
|
|
8102
8222
|
name: "IonQ Sales Intelligence",
|
|
8103
8223
|
requiredUserTag: "Opti",
|
|
8224
|
+
requiredEntitlement: "optihashi:pro",
|
|
8104
8225
|
fileTagPrefix: "ionq:",
|
|
8105
8226
|
datalakeTag: "datalake:ionq-sales"
|
|
8106
8227
|
}, {
|
|
8107
8228
|
id: "opti-knowledge",
|
|
8108
8229
|
name: "Optimization Knowledge Base",
|
|
8109
8230
|
requiredUserTag: "Opti",
|
|
8231
|
+
requiredEntitlement: "optihashi:pro",
|
|
8110
8232
|
fileTagPrefix: "opti:",
|
|
8111
8233
|
datalakeTag: "datalake:opti-knowledge"
|
|
8112
8234
|
}];
|
|
8113
8235
|
/**
|
|
8114
|
-
*
|
|
8115
|
-
*
|
|
8116
|
-
*
|
|
8117
|
-
*
|
|
8236
|
+
* Canonical normalization for entitlement keys + `requiredEntitlement` values — the ONE
|
|
8237
|
+
* rule, applied at write time (create/update/stamp) and at match time. Mirrors the
|
|
8238
|
+
* entitlement registry's `normalizeTag` (trim + lowercase) so a value authored in any
|
|
8239
|
+
* casing matches the lowercase keys the resolver produces.
|
|
8240
|
+
*/
|
|
8241
|
+
const normalizeEntitlementKey = (key) => key.trim().toLowerCase();
|
|
8242
|
+
/**
|
|
8243
|
+
* Single projection from a persisted lake document to the lightweight DataLakeConfig
|
|
8244
|
+
* the access filters operate on. Centralized so the `requiredEntitlement` field (and any
|
|
8245
|
+
* future field) cannot be silently dropped at one of the many former inline projections.
|
|
8246
|
+
*/
|
|
8247
|
+
function toDataLakeConfig(dl) {
|
|
8248
|
+
return {
|
|
8249
|
+
id: dl.id,
|
|
8250
|
+
name: dl.name,
|
|
8251
|
+
requiredUserTag: dl.requiredUserTag,
|
|
8252
|
+
requiredEntitlement: dl.requiredEntitlement,
|
|
8253
|
+
fileTagPrefix: dl.fileTagPrefix,
|
|
8254
|
+
datalakeTag: dl.datalakeTag
|
|
8255
|
+
};
|
|
8256
|
+
}
|
|
8257
|
+
/**
|
|
8258
|
+
* Returns data lakes accessible to a user.
|
|
8259
|
+
*
|
|
8260
|
+
* Access rule (generic, any-of declared requirements): a lake is accessible iff it
|
|
8261
|
+
* declares NO access requirement, OR the user satisfies ANY declared requirement —
|
|
8262
|
+
* either `requiredUserTag` (matched against the user's tags) or `requiredEntitlement`
|
|
8263
|
+
* (matched against the caller's resolved `entitlementKeys`). A lake declaring an
|
|
8264
|
+
* entitlement but no tag is therefore NOT public (it is gated by the key).
|
|
8265
|
+
*
|
|
8266
|
+
* Data lakes without any requirement are accessible to all authenticated users.
|
|
8267
|
+
* When dynamicDataLakes is provided (fetched from DB), those take precedence over
|
|
8268
|
+
* hardcoded DATA_LAKES entries with the same id. `entitlementKeys` is optional — callers
|
|
8269
|
+
* that don't resolve entitlements (tag-only surfaces) omit it and get tag-only matching.
|
|
8118
8270
|
*/
|
|
8119
|
-
function getAccessibleDataLakes(userTags, dynamicDataLakes) {
|
|
8271
|
+
function getAccessibleDataLakes(userTags, dynamicDataLakes, entitlementKeys) {
|
|
8120
8272
|
const normalizedUserTags = userTags.map((tag) => tag.toLowerCase());
|
|
8273
|
+
const normalizedKeys = (entitlementKeys ?? []).map(normalizeEntitlementKey);
|
|
8121
8274
|
let allLakes;
|
|
8122
8275
|
if (dynamicDataLakes && dynamicDataLakes.length > 0) {
|
|
8123
8276
|
const dynamicIds = new Set(dynamicDataLakes.map((d) => d.id));
|
|
8124
8277
|
const fallbacks = DATA_LAKES.filter((dl) => !dynamicIds.has(dl.id));
|
|
8125
8278
|
allLakes = [...dynamicDataLakes, ...fallbacks];
|
|
8126
8279
|
} else allLakes = DATA_LAKES;
|
|
8127
|
-
return allLakes.filter((dl) =>
|
|
8128
|
-
|
|
8129
|
-
|
|
8130
|
-
|
|
8131
|
-
|
|
8132
|
-
|
|
8133
|
-
function getDataLakeTags(userTags, dynamicDataLakes) {
|
|
8134
|
-
return getAccessibleDataLakes(userTags, dynamicDataLakes).map((dl) => dl.datalakeTag);
|
|
8280
|
+
return allLakes.filter((dl) => {
|
|
8281
|
+
if (!(!!dl.requiredUserTag || !!dl.requiredEntitlement)) return true;
|
|
8282
|
+
const tagMatch = !!dl.requiredUserTag && normalizedUserTags.includes(dl.requiredUserTag.toLowerCase());
|
|
8283
|
+
const entMatch = !!dl.requiredEntitlement && normalizedKeys.includes(normalizeEntitlementKey(dl.requiredEntitlement));
|
|
8284
|
+
return tagMatch || entMatch;
|
|
8285
|
+
});
|
|
8135
8286
|
}
|
|
8136
8287
|
/**
|
|
8137
8288
|
* Jupyter Kernel Constants and Validation
|
|
@@ -8284,6 +8435,8 @@ z.object({
|
|
|
8284
8435
|
/** Notebook session ID */
|
|
8285
8436
|
sessionId: z.string(),
|
|
8286
8437
|
historyCount: z.number(),
|
|
8438
|
+
/** Epoch ms when the client submitted the prompt (for the request-lifecycle status log). */
|
|
8439
|
+
clientSubmittedAt: z.number().optional(),
|
|
8287
8440
|
imageConfig: GenerateImageToolCallSchema.optional(),
|
|
8288
8441
|
deepResearchConfig: z.object({
|
|
8289
8442
|
maxDepth: z.number().optional(),
|
|
@@ -8564,6 +8717,114 @@ BaseArtifactSchema.extend({
|
|
|
8564
8717
|
})
|
|
8565
8718
|
});
|
|
8566
8719
|
/**
|
|
8720
|
+
* Annotation schemas — the collaboration layer that sits on top of a
|
|
8721
|
+
* PublishedArtifact. An annotation is a structured input keyed to an artifact
|
|
8722
|
+
* by its `publicId`. The `kind` discriminator is deliberately generic: v1 ships
|
|
8723
|
+
* only `comment`, but `approval`/`vote`/`signature` are reserved so the layer
|
|
8724
|
+
* can grow into approval workflows and e-signature without a model migration.
|
|
8725
|
+
*
|
|
8726
|
+
* Anchoring is PINS-FIRST: an annotation may carry normalized viewport
|
|
8727
|
+
* coordinates (0..1) and a best-effort CSS selector. Robust element-range
|
|
8728
|
+
* anchoring is deferred. An annotation with no anchor is document-level.
|
|
8729
|
+
*
|
|
8730
|
+
* Every annotation records the artifact version it was made against
|
|
8731
|
+
* (`artifactVersionSha`, the artifact's `sha256Index` at the time) so feedback
|
|
8732
|
+
* stays pinned to the version it critiqued — this is also the input the AI
|
|
8733
|
+
* revision loop (Phase C) consumes.
|
|
8734
|
+
*/
|
|
8735
|
+
const AnnotationKindSchema = z.enum([
|
|
8736
|
+
"comment",
|
|
8737
|
+
"approval",
|
|
8738
|
+
"vote",
|
|
8739
|
+
"signature"
|
|
8740
|
+
]);
|
|
8741
|
+
/** Owner-controlled gate on WHO may annotate, orthogonal to artifact visibility
|
|
8742
|
+
* (which controls who may VIEW). `none` = read-only (today's behavior, default).
|
|
8743
|
+
* `open` = any authenticated viewer who passes the visibility gate. `restricted`
|
|
8744
|
+
* = owner + explicit allowlist (allowlist UI deferred). */
|
|
8745
|
+
const CommentPolicySchema = z.enum([
|
|
8746
|
+
"none",
|
|
8747
|
+
"open",
|
|
8748
|
+
"restricted"
|
|
8749
|
+
]);
|
|
8750
|
+
const AnnotationAnchorSchema = z.object({
|
|
8751
|
+
/** Normalized horizontal position within the artifact viewport, 0..1. */
|
|
8752
|
+
x: z.number().min(0).max(1).optional(),
|
|
8753
|
+
/** Normalized vertical position within the artifact document, 0..1. */
|
|
8754
|
+
y: z.number().min(0).max(1).optional(),
|
|
8755
|
+
/** Best-effort CSS selector path captured by the widget (may be stale). */
|
|
8756
|
+
selector: z.string().max(1024).optional(),
|
|
8757
|
+
/** Optional scroll-section label / fragment the pin was dropped in. */
|
|
8758
|
+
scrollSection: z.string().max(256).optional()
|
|
8759
|
+
});
|
|
8760
|
+
const AnnotationPayloadSchema = z.object({
|
|
8761
|
+
/** kind === 'approval' */
|
|
8762
|
+
decision: z.enum(["approve", "reject"]).optional(),
|
|
8763
|
+
/** kind === 'vote' */
|
|
8764
|
+
choice: z.string().max(256).optional(),
|
|
8765
|
+
/** kind === 'signature' */
|
|
8766
|
+
signedName: z.string().max(200).optional(),
|
|
8767
|
+
signatureHash: z.string().max(128).optional()
|
|
8768
|
+
}).optional();
|
|
8769
|
+
const ANNOTATION_BODY_MAX = 5e3;
|
|
8770
|
+
z.object({
|
|
8771
|
+
id: z.string(),
|
|
8772
|
+
/** FK → PublishedArtifact.publicId. */
|
|
8773
|
+
publicId: z.string(),
|
|
8774
|
+
/** PublishedArtifact.sha256Index at the time the annotation was made. */
|
|
8775
|
+
artifactVersionSha: z.string().optional(),
|
|
8776
|
+
kind: AnnotationKindSchema.prefault("comment"),
|
|
8777
|
+
authorId: z.string(),
|
|
8778
|
+
/** Denormalized for render — annotation authorship is immutable. */
|
|
8779
|
+
authorDisplayName: z.string().max(200),
|
|
8780
|
+
body: z.string().max(ANNOTATION_BODY_MAX),
|
|
8781
|
+
anchor: AnnotationAnchorSchema.optional(),
|
|
8782
|
+
/** null/absent = top-level; else this is a reply to another annotation. */
|
|
8783
|
+
threadRootId: z.string().nullish(),
|
|
8784
|
+
payload: AnnotationPayloadSchema,
|
|
8785
|
+
resolvedAt: z.date().nullish(),
|
|
8786
|
+
resolvedBy: z.string().nullish(),
|
|
8787
|
+
createdAt: z.date(),
|
|
8788
|
+
updatedAt: z.date(),
|
|
8789
|
+
deletedAt: z.date().nullish(),
|
|
8790
|
+
deletedBy: z.string().nullish()
|
|
8791
|
+
});
|
|
8792
|
+
z.object({
|
|
8793
|
+
body: z.string().min(1).max(ANNOTATION_BODY_MAX),
|
|
8794
|
+
/** v1 accepts only 'comment'; defaulted server-side. */
|
|
8795
|
+
kind: AnnotationKindSchema.optional(),
|
|
8796
|
+
anchor: AnnotationAnchorSchema.optional(),
|
|
8797
|
+
threadRootId: z.string().optional(),
|
|
8798
|
+
artifactVersionSha: z.string().optional(),
|
|
8799
|
+
payload: AnnotationPayloadSchema
|
|
8800
|
+
});
|
|
8801
|
+
z.object({
|
|
8802
|
+
body: z.string().min(1).max(ANNOTATION_BODY_MAX).optional(),
|
|
8803
|
+
/** Toggle resolution. true → resolve, false → reopen. */
|
|
8804
|
+
resolved: z.boolean().optional()
|
|
8805
|
+
}).refine((v) => v.body !== void 0 || v.resolved !== void 0, { message: "Nothing to update" });
|
|
8806
|
+
const AnnotationDtoSchema = z.object({
|
|
8807
|
+
id: z.string(),
|
|
8808
|
+
publicId: z.string(),
|
|
8809
|
+
kind: AnnotationKindSchema,
|
|
8810
|
+
authorId: z.string(),
|
|
8811
|
+
authorDisplayName: z.string(),
|
|
8812
|
+
body: z.string(),
|
|
8813
|
+
anchor: AnnotationAnchorSchema.optional(),
|
|
8814
|
+
threadRootId: z.string().nullish(),
|
|
8815
|
+
payload: AnnotationPayloadSchema,
|
|
8816
|
+
artifactVersionSha: z.string().optional(),
|
|
8817
|
+
resolvedAt: z.string().nullish(),
|
|
8818
|
+
createdAt: z.string()
|
|
8819
|
+
});
|
|
8820
|
+
z.object({
|
|
8821
|
+
annotations: z.array(AnnotationDtoSchema),
|
|
8822
|
+
/** Echoed so the widget can render the right affordance without a 2nd call. */
|
|
8823
|
+
commentPolicy: CommentPolicySchema,
|
|
8824
|
+
/** Whether the CURRENT caller may create an annotation right now. */
|
|
8825
|
+
canComment: z.boolean()
|
|
8826
|
+
});
|
|
8827
|
+
/**
|
|
8567
8828
|
* Published-artifact schemas — the B4M instantiation of the `artifact-publishing`
|
|
8568
8829
|
* blueprint (MillionOnMars/blueprints, extracted from Polaris Publish v1).
|
|
8569
8830
|
*
|
|
@@ -8645,6 +8906,9 @@ z.object({
|
|
|
8645
8906
|
visibility: VisibilitySchema.prefault("private"),
|
|
8646
8907
|
/** Group id a viewer must belong to when gated cross-scope. */
|
|
8647
8908
|
gatedToGroupId: z.string().optional(),
|
|
8909
|
+
/** Collaboration gate: who (among viewers) may annotate. Orthogonal to
|
|
8910
|
+
* `visibility`. Defaults to `none` (read-only) until the owner opts in. */
|
|
8911
|
+
commentPolicy: CommentPolicySchema.prefault("none"),
|
|
8648
8912
|
ownerId: z.string(),
|
|
8649
8913
|
lastPublishedBy: z.string().optional(),
|
|
8650
8914
|
source: PublishSourceSchema,
|
|
@@ -8662,6 +8926,8 @@ z.object({
|
|
|
8662
8926
|
publishedAt: z.date(),
|
|
8663
8927
|
previousVersionMeta: ArtifactVersionMetaSchema.optional(),
|
|
8664
8928
|
viewCount: z.int().nonnegative().prefault(0),
|
|
8929
|
+
/** Concurrency lock for AI revise (set while a revision is in flight). */
|
|
8930
|
+
revisingAt: z.date().nullish(),
|
|
8665
8931
|
createdAt: z.date(),
|
|
8666
8932
|
updatedAt: z.date(),
|
|
8667
8933
|
deletedAt: z.date().nullish(),
|
|
@@ -8696,6 +8962,8 @@ z.object({
|
|
|
8696
8962
|
description: z.string().max(1e3).optional(),
|
|
8697
8963
|
visibility: VisibilitySchema.optional(),
|
|
8698
8964
|
gatedToGroupId: z.string().optional(),
|
|
8965
|
+
/** Who may annotate the published artifact. Defaults to `none` (read-only). */
|
|
8966
|
+
commentPolicy: CommentPolicySchema.optional(),
|
|
8699
8967
|
source: PublishSourceSchema.optional(),
|
|
8700
8968
|
files: z.array(FileDescriptorSchema).min(1).max(PUBLISH_LIMITS.maxFiles)
|
|
8701
8969
|
});
|
|
@@ -8851,6 +9119,33 @@ function isGPTImage2Model(model) {
|
|
|
8851
9119
|
return model === "gpt-image-2" || model.startsWith("gpt-image-2");
|
|
8852
9120
|
}
|
|
8853
9121
|
/**
|
|
9122
|
+
* Whether a user can access a model. Access is any-of (mirrors the Q3b data-lake
|
|
9123
|
+
* rule, `getAccessibleDataLakes`): a non-admin reaches the model via
|
|
9124
|
+
* `allowedUserTags ∩ userTags` OR `allowedEntitlements ∩ entitlementKeys`.
|
|
9125
|
+
*
|
|
9126
|
+
* `entitlementKeys` is optional — when omitted/empty the entitlement branch is
|
|
9127
|
+
* inert, so a model with no `allowedEntitlements` behaves exactly as before
|
|
9128
|
+
* (tag-only). This lets a tag-less subscriber reach an entitlement-gated model
|
|
9129
|
+
* while leaving every existing tag-gated model unchanged (zero regression).
|
|
9130
|
+
*
|
|
9131
|
+
* Pure + zero-dependency (only types + the shared key normalizer), so it lives
|
|
9132
|
+
* in `@bike4mind/common` as the SINGLE source of truth — imported by the core
|
|
9133
|
+
* `@bike4mind/utils` re-export (server/services) AND the client
|
|
9134
|
+
* `useAccessibleModels` hook, which previously kept a hand-rolled twin "to avoid
|
|
9135
|
+
* AWS SDK imports". Common is browser-safe, so there is no longer any reason to
|
|
9136
|
+
* duplicate the logic.
|
|
9137
|
+
*/
|
|
9138
|
+
function isModelAccessible(model, userTags, isAdmin = false, entitlementKeys = []) {
|
|
9139
|
+
if (!model.enabled) return false;
|
|
9140
|
+
if (isAdmin) return true;
|
|
9141
|
+
const normalizedUserTags = userTags.map((tag) => tag.toLowerCase());
|
|
9142
|
+
const normalizedAllowedTags = (model.allowedUserTags ?? []).map((tag) => tag.toLowerCase());
|
|
9143
|
+
if (normalizedUserTags.some((tag) => normalizedAllowedTags.includes(tag))) return true;
|
|
9144
|
+
const normalizedKeys = entitlementKeys.map(normalizeEntitlementKey);
|
|
9145
|
+
const normalizedAllowedEntitlements = (model.allowedEntitlements ?? []).map(normalizeEntitlementKey);
|
|
9146
|
+
return normalizedKeys.some((key) => normalizedAllowedEntitlements.includes(key));
|
|
9147
|
+
}
|
|
9148
|
+
/**
|
|
8854
9149
|
* Telemetry Error Sanitizer
|
|
8855
9150
|
*
|
|
8856
9151
|
* Removes PII (Personally Identifiable Information) from error messages
|
|
@@ -8976,6 +9271,67 @@ z.array(triggerWordSchema).max(20, "Up to 20 trigger words allowed.").transform(
|
|
|
8976
9271
|
}
|
|
8977
9272
|
return out;
|
|
8978
9273
|
});
|
|
9274
|
+
/**
|
|
9275
|
+
* Skill body argument substitution. Shared by the server-side SkillsFeature,
|
|
9276
|
+
* the LLM `skill` tool, and the CLI's skill executor so the same patterns
|
|
9277
|
+
* resolve identically across surfaces.
|
|
9278
|
+
*
|
|
9279
|
+
* Supports Claude Code's substitution syntax:
|
|
9280
|
+
* - `$ARGUMENTS` — all positional args joined by spaces
|
|
9281
|
+
* - `$1`, `$2`, ... — individual positional args
|
|
9282
|
+
*
|
|
9283
|
+
* Why a shared helper: the CLI already implemented this in
|
|
9284
|
+
* `apps/cli/src/utils/argumentSubstitution.ts`. Centralizing here avoids a
|
|
9285
|
+
* second drift surface — a skill body authored on the web and run in the CLI
|
|
9286
|
+
* (or vice versa) must expand identically.
|
|
9287
|
+
*/
|
|
9288
|
+
function substituteArguments(template, args) {
|
|
9289
|
+
let result = template;
|
|
9290
|
+
for (let i = args.length; i >= 1; i--) {
|
|
9291
|
+
const pattern = new RegExp(`\\$${i}`, "g");
|
|
9292
|
+
result = result.replace(pattern, args[i - 1] ?? "");
|
|
9293
|
+
}
|
|
9294
|
+
return result.replace(/\$ARGUMENTS/g, args.join(" "));
|
|
9295
|
+
}
|
|
9296
|
+
/**
|
|
9297
|
+
* Shell-style argument splitter — supports double / single quotes for
|
|
9298
|
+
* grouping. Identical semantics to the CLI's `parseArguments`.
|
|
9299
|
+
*
|
|
9300
|
+
* parseSkillArguments('hello world') => ['hello', 'world']
|
|
9301
|
+
* parseSkillArguments('"hello world" test') => ['hello world', 'test']
|
|
9302
|
+
* parseSkillArguments("'one two' three") => ['one two', 'three']
|
|
9303
|
+
* parseSkillArguments('') => []
|
|
9304
|
+
*
|
|
9305
|
+
* Unclosed quotes are forgiven, not flagged: `parseSkillArguments('"unclosed')`
|
|
9306
|
+
* returns `['unclosed']`. This matches the CLI's pre-existing behavior and is
|
|
9307
|
+
* deliberate — skills are user-authored prose, not a shell, and surfacing a
|
|
9308
|
+
* parse error for a stray `"` would punish a typo more than the cost of a
|
|
9309
|
+
* slightly-wrong split warrants.
|
|
9310
|
+
*/
|
|
9311
|
+
function parseSkillArguments(input) {
|
|
9312
|
+
if (!input) return [];
|
|
9313
|
+
const args = [];
|
|
9314
|
+
let current = "";
|
|
9315
|
+
let inQuotes = false;
|
|
9316
|
+
let quoteChar = "";
|
|
9317
|
+
for (let i = 0; i < input.length; i++) {
|
|
9318
|
+
const char = input[i];
|
|
9319
|
+
if (!inQuotes && (char === "\"" || char === "'")) {
|
|
9320
|
+
inQuotes = true;
|
|
9321
|
+
quoteChar = char;
|
|
9322
|
+
} else if (inQuotes && char === quoteChar) {
|
|
9323
|
+
inQuotes = false;
|
|
9324
|
+
quoteChar = "";
|
|
9325
|
+
} else if (!inQuotes && /\s/.test(char)) {
|
|
9326
|
+
if (current.length > 0) {
|
|
9327
|
+
args.push(current);
|
|
9328
|
+
current = "";
|
|
9329
|
+
}
|
|
9330
|
+
} else current += char;
|
|
9331
|
+
}
|
|
9332
|
+
if (current.length > 0) args.push(current);
|
|
9333
|
+
return args;
|
|
9334
|
+
}
|
|
8979
9335
|
const VIEW_REGISTRY = [
|
|
8980
9336
|
{
|
|
8981
9337
|
id: "opti.root",
|
|
@@ -10238,11 +10594,32 @@ const ApiConfigSchema = z.object({ customUrl: z.url().optional() });
|
|
|
10238
10594
|
*/
|
|
10239
10595
|
const McpServerSchema = z.object({
|
|
10240
10596
|
name: z.string(),
|
|
10597
|
+
type: z.enum(["stdio", "http"]).optional(),
|
|
10241
10598
|
command: z.string().optional(),
|
|
10242
10599
|
args: z.array(z.string()).optional(),
|
|
10600
|
+
url: z.string().optional(),
|
|
10601
|
+
headers: z.record(z.string(), z.string()).optional(),
|
|
10243
10602
|
env: z.record(z.string(), z.string()).prefault({}),
|
|
10244
10603
|
enabled: z.boolean().prefault(true)
|
|
10245
|
-
});
|
|
10604
|
+
}).superRefine(mcpServerTransportRefine);
|
|
10605
|
+
/**
|
|
10606
|
+
* A valid MCP server is exactly one of:
|
|
10607
|
+
* - stdio: has `command` (spawned child process)
|
|
10608
|
+
* - http: has `url` (streamable-HTTP endpoint)
|
|
10609
|
+
* Rejecting malformed configs at parse time (not connect time) surfaces errors early.
|
|
10610
|
+
*/
|
|
10611
|
+
function mcpServerTransportRefine(server, ctx) {
|
|
10612
|
+
const hasCommand = typeof server.command === "string" && server.command.trim() !== "";
|
|
10613
|
+
const hasUrl = typeof server.url === "string" && server.url.trim() !== "";
|
|
10614
|
+
if (hasCommand && hasUrl) ctx.addIssue({
|
|
10615
|
+
code: "custom",
|
|
10616
|
+
message: "MCP server cannot set both \"command\" (stdio) and \"url\" (http)"
|
|
10617
|
+
});
|
|
10618
|
+
else if (!hasCommand && !hasUrl) ctx.addIssue({
|
|
10619
|
+
code: "custom",
|
|
10620
|
+
message: "MCP server must set either \"command\" (stdio) or \"url\" (http)"
|
|
10621
|
+
});
|
|
10622
|
+
}
|
|
10246
10623
|
/**
|
|
10247
10624
|
* MCP Servers can be specified in two formats:
|
|
10248
10625
|
* 1. Array format (B4M native): [{ "name": "...", "command": "...", ... }]
|
|
@@ -10251,11 +10628,14 @@ const McpServerSchema = z.object({
|
|
|
10251
10628
|
* Both formats are supported for maximum flexibility and compatibility.
|
|
10252
10629
|
*/
|
|
10253
10630
|
const McpServersSchema = z.union([z.array(McpServerSchema), z.record(z.string(), z.object({
|
|
10631
|
+
type: z.enum(["stdio", "http"]).optional(),
|
|
10254
10632
|
command: z.string().optional(),
|
|
10255
10633
|
args: z.array(z.string()).optional(),
|
|
10634
|
+
url: z.string().optional(),
|
|
10635
|
+
headers: z.record(z.string(), z.string()).optional(),
|
|
10256
10636
|
env: z.record(z.string(), z.string()).optional(),
|
|
10257
10637
|
enabled: z.boolean().optional()
|
|
10258
|
-
}))]);
|
|
10638
|
+
}).superRefine(mcpServerTransportRefine))]);
|
|
10259
10639
|
/**
|
|
10260
10640
|
* Normalize MCP servers to internal array format
|
|
10261
10641
|
* Accepts both array and object formats
|
|
@@ -10263,15 +10643,21 @@ const McpServersSchema = z.union([z.array(McpServerSchema), z.record(z.string(),
|
|
|
10263
10643
|
function normalizeMcpServers(servers) {
|
|
10264
10644
|
if (Array.isArray(servers)) return servers.map((server) => ({
|
|
10265
10645
|
name: server.name,
|
|
10646
|
+
type: server.type,
|
|
10266
10647
|
command: server.command,
|
|
10267
10648
|
args: server.args,
|
|
10649
|
+
url: server.url,
|
|
10650
|
+
headers: server.headers,
|
|
10268
10651
|
env: server.env || {},
|
|
10269
10652
|
enabled: server.enabled ?? true
|
|
10270
10653
|
}));
|
|
10271
10654
|
else return Object.entries(servers).map(([name, config]) => ({
|
|
10272
10655
|
name,
|
|
10656
|
+
type: config.type,
|
|
10273
10657
|
command: config.command,
|
|
10274
10658
|
args: config.args,
|
|
10659
|
+
url: config.url,
|
|
10660
|
+
headers: config.headers,
|
|
10275
10661
|
env: config.env || {},
|
|
10276
10662
|
enabled: config.enabled ?? true
|
|
10277
10663
|
}));
|
|
@@ -10302,6 +10688,14 @@ const CliConfigSchema = z.object({
|
|
|
10302
10688
|
exportFormat: z.enum(["markdown", "json"]),
|
|
10303
10689
|
maxIterations: z.number().nullable().prefault(10),
|
|
10304
10690
|
enableSkillTool: z.boolean().optional().prefault(true),
|
|
10691
|
+
/**
|
|
10692
|
+
* When false (or set via the `--no-remote-skills` CLI flag / the
|
|
10693
|
+
* `B4M_NO_REMOTE_SKILLS=1` env var), the CLI skips fetching skills from
|
|
10694
|
+
* the B4M backend's `/api/skills` endpoint and runs with local files only.
|
|
10695
|
+
* Defaults to true so authenticated users get cross-machine skill sync
|
|
10696
|
+
* out of the box.
|
|
10697
|
+
*/
|
|
10698
|
+
enableRemoteSkills: z.boolean().optional().prefault(true),
|
|
10305
10699
|
enableDynamicAgentCreation: z.boolean().optional().prefault(false),
|
|
10306
10700
|
enableCoordinatorMode: z.boolean().optional().prefault(false),
|
|
10307
10701
|
/**
|
|
@@ -10393,6 +10787,7 @@ const DEFAULT_CONFIG = {
|
|
|
10393
10787
|
exportFormat: "markdown",
|
|
10394
10788
|
maxIterations: 10,
|
|
10395
10789
|
enableSkillTool: true,
|
|
10790
|
+
enableRemoteSkills: true,
|
|
10396
10791
|
enableDynamicAgentCreation: false,
|
|
10397
10792
|
enableCoordinatorMode: false,
|
|
10398
10793
|
promptVariant: "current"
|
|
@@ -10503,6 +10898,29 @@ async function loadMcpJsonConfig(projectDir) {
|
|
|
10503
10898
|
}
|
|
10504
10899
|
}
|
|
10505
10900
|
/**
|
|
10901
|
+
* Load an explicit `--mcp-config <file>` (claude shape: `{ "mcpServers": {...} }`).
|
|
10902
|
+
* Unlike `.mcp.json` this is an absolute path passed at launch, not project-relative.
|
|
10903
|
+
* Returns null on missing/malformed file (a bad config must not brick the launch).
|
|
10904
|
+
*/
|
|
10905
|
+
async function loadMcpConfigFile(filePath) {
|
|
10906
|
+
try {
|
|
10907
|
+
const data = await promises.readFile(filePath, "utf-8");
|
|
10908
|
+
const rawConfig = JSON.parse(data);
|
|
10909
|
+
return normalizeMcpServers(McpJsonConfigSchema.parse(rawConfig).mcpServers);
|
|
10910
|
+
} catch (error) {
|
|
10911
|
+
if (error.code === "ENOENT") {
|
|
10912
|
+
console.error(`--mcp-config file not found: ${filePath}`);
|
|
10913
|
+
return null;
|
|
10914
|
+
}
|
|
10915
|
+
if (error instanceof z.ZodError) {
|
|
10916
|
+
console.error("--mcp-config validation error:", error.issues);
|
|
10917
|
+
return null;
|
|
10918
|
+
}
|
|
10919
|
+
console.error("Failed to load --mcp-config file:", error);
|
|
10920
|
+
return null;
|
|
10921
|
+
}
|
|
10922
|
+
}
|
|
10923
|
+
/**
|
|
10506
10924
|
* Merge MCP servers from multiple configs
|
|
10507
10925
|
* Later configs can override earlier ones by name
|
|
10508
10926
|
*/
|
|
@@ -10684,6 +11102,12 @@ var ConfigStore = class {
|
|
|
10684
11102
|
} else this.projectConfigDir = null;
|
|
10685
11103
|
const mergedConfig = mergeConfigs(globalConfig, projectConfig, projectLocalConfig);
|
|
10686
11104
|
if (mcpJsonServers && mcpJsonServers.length > 0) mergedConfig.mcpServers = mergeMcpServers(mcpJsonServers, mergedConfig.mcpServers);
|
|
11105
|
+
const mcpConfigFile = process.env.B4M_MCP_CONFIG_FILE;
|
|
11106
|
+
if (mcpConfigFile) {
|
|
11107
|
+
const injected = await loadMcpConfigFile(mcpConfigFile);
|
|
11108
|
+
if (process.env.B4M_STRICT_MCP_CONFIG === "1") mergedConfig.mcpServers = injected ?? [];
|
|
11109
|
+
else if (injected) mergedConfig.mcpServers = mergeMcpServers(mergedConfig.mcpServers, injected);
|
|
11110
|
+
}
|
|
10687
11111
|
this.config = mergedConfig;
|
|
10688
11112
|
return this.config;
|
|
10689
11113
|
} catch (error) {
|
|
@@ -11030,4 +11454,4 @@ var ConfigStore = class {
|
|
|
11030
11454
|
}
|
|
11031
11455
|
};
|
|
11032
11456
|
//#endregion
|
|
11033
|
-
export { ProjectEvents as $, GenerateImageToolCallSchema as A, dayjsConfig_default as At, InviteEvents as B,
|
|
11457
|
+
export { ProjectEvents as $, parseRateLimitHeaders as $t, GenerateImageToolCallSchema as A, dayjsConfig_default as At, InviteEvents as B, obfuscateApiKey as Bt, ElabsEvents as C, UnauthorizedError as Ct, ForbiddenError as D, VideoModels as Dt, FileEvents as E, VideoGenerationUsageTransaction as Et, ImageEditUsageTransaction as F, isGPTImageModel as Ft, ModalEvents as G, settingsMap as Gt, KnowledgeType as H, resolveNavigationIntents as Ht, ImageGenerationUsageTransaction as I, isModelAccessible as It, OpenAIEmbeddingModel as J, validateJupyterKernelName as Jt, ModelBackend as K, substituteArguments as Kt, ImageModels as L, isSupportedEmbeddingModel as Lt, GenericCreditDeductTransaction as M, getMcpProviderMetadata as Mt, HTTPError as N, getViewById as Nt, FriendshipEvents as O, XAI_IMAGE_MODELS as Ot, HttpStatus as P, isGPTImage2Model as Pt, ProfileEvents as Q, isNearLimit as Qt, InboxEvents as R, isZodError as Rt, DashboardParamsSchema as S, UiNavigationEvents as St, FeedbackEvents as T, VIDEO_SIZE_CONSTRAINTS as Tt, LLMEvents as U, sanitizeTelemetryError as Ut, InviteType as V, parseSkillArguments as Vt, MiscEvents as W, secureParameters as Wt, Permission as X, buildRateLimitLogEntry as Xt, OpenAIImageGenerationInput as Y, validateNotebookPath as Yt, PermissionDeniedError as Z, extractSnippetMeta as Zt, ChatCompletionCreateInputSchema as _, TaskScheduleHandler as _t, ALERT_THRESHOLDS as a, ReceivedCreditTransaction as at, CompletionApiUsageTransaction as b, ToolUsageTransaction as bt, ApiKeyScope as c, ResearchModeParamsSchema as ct, ArtifactTypeSchema as d, ResearchTaskType as dt, CollectionType as en, PromptIntentSchema as et, AuthEvents as f, SessionEvents as ft, CREDIT_DEDUCT_TRANSACTION_TYPES as g, TagType as gt, BadRequestError as h, SupportedFabFileMimeTypes as ht, getEnvironmentName as i, RealtimeVoiceUsageTransaction as it, GenericCreditAddTransaction as j, getAccessibleDataLakes as jt, GEMINI_IMAGE_MODELS as k, b4mLLMTools as kt, ApiKeyType as l, ResearchTaskExecutionType as lt, BFL_SAFETY_TOLERANCE as m, SubscriptionCreditTransaction as mt, logger as n, PurchaseTransaction as nt, AiEvents as o, RechartsChartTypeList as ot, BFL_IMAGE_MODELS as p, SpeechToTextUsageTransaction as pt, NotFoundError as q, toDataLakeConfig as qt, getApiUrl as r, QuestMasterParamsSchema as rt, ApiKeyEvents as s, RegInviteEvents as st, ConfigStore as t, PromptMetaZodSchema as tt, AppFileEvents as u, ResearchTaskPeriodicFrequencyType as ut, ChatModels as v, TextGenerationUsageTransaction as vt, FavoriteDocumentType as w, UnprocessableEntityError as wt, CorruptedFileError as x, TransferCreditTransaction as xt, ClaudeArtifactMimeTypes as y, TooManyRequestsError as yt, InternalServerError as z, mapMimeTypeToArtifactType as zt };
|