agentic-mermaid 0.1.1 → 0.2.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/AGENT_NATIVE.md +155 -91
- package/CHANGELOG.md +163 -4
- package/Instructions_for_agents.md +10 -10
- package/LICENSES/Apache-2.0.txt +202 -0
- package/README.md +31 -38
- package/THIRD_PARTY_NOTICES.md +29 -0
- package/assets/hero.png +0 -0
- package/dist/agent-core.d.ts +463 -0
- package/dist/agent-core.js +397 -0
- package/dist/agent.d.ts +73 -1868
- package/dist/agent.js +336 -37325
- package/dist/agentic-mermaid-mcp.js +7 -38399
- package/dist/am.js +33626 -40446
- package/dist/chunk-2P5FKUQC.js +3485 -0
- package/dist/chunk-2S3VSS6I.js +149 -0
- package/dist/chunk-2SILJDW2.js +37 -0
- package/dist/chunk-BJYMBT4I.js +61963 -0
- package/dist/chunk-FDM43XU7.js +615 -0
- package/dist/chunk-MYPDVZCO.js +4702 -0
- package/dist/index-DWPa2MJs.d.ts +6780 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +310 -33014
- package/dist/mcp-cli-5J47WEY3.js +17 -0
- package/docs/README.md +39 -31
- package/docs/agent-api-cookbook.md +44 -35
- package/docs/agent-mutation-policy.md +1 -1
- package/docs/agent-workflow-examples.md +2 -2
- package/docs/api.md +284 -77
- package/docs/ascii.md +26 -16
- package/docs/code-mode-impact.md +214 -0
- package/docs/comparison.md +23 -11
- package/docs/computational-aesthetics-prototype-plan.md +252 -0
- package/docs/config.md +26 -10
- package/docs/custom-fonts.md +265 -0
- package/docs/custom-style-cookbook.md +130 -9
- package/docs/diagram-families.md +48 -23
- package/docs/features.md +59 -52
- package/docs/fork-differences.md +6 -3
- package/docs/getting-started.md +23 -16
- package/docs/issue-derived-test-cases.md +29 -22
- package/docs/mcp-code-mode-rationale.md +13 -6
- package/docs/mcp-http-transport.md +20 -13
- package/docs/mermaid-layout-complaints.md +66 -52
- package/docs/mutation-testing.md +145 -66
- package/docs/quality.md +23 -14
- package/docs/react.md +2 -2
- package/docs/schemas/style-spec.schema.json +1221 -30
- package/docs/style-authoring.md +241 -38
- package/docs/svg-semantic-contract.md +49 -0
- package/docs/testing-strategy.md +166 -58
- package/docs/theming.md +19 -32
- package/examples/styles/catalog.json +54 -0
- package/examples/styles/cloudflare-workers-inspired-prototype.mmd +9 -0
- package/examples/styles/cloudflare-workers-inspired-prototype.style.json +110 -0
- package/examples/styles/cupertino-prototype.mmd +10 -0
- package/examples/styles/cupertino-prototype.style.json +63 -0
- package/examples/styles/mid-century-report.style.json +2 -2
- package/examples/styles/star-chart-atlas.style.json +2 -1
- package/examples/styles/transit-route-map.style.json +2 -2
- package/examples/styles/vercel-inspired-prototype.mmd +6 -0
- package/examples/styles/vercel-inspired-prototype.style.json +87 -0
- package/package.json +85 -45
- package/server.json +2 -2
- package/skills/agentic-mermaid-diagram-workflow/SKILL.md +17 -36
- package/skills/agentic-mermaid-diagram-workflow/references/cli.md +4 -5
- package/skills/agentic-mermaid-diagram-workflow/references/code-mode.md +15 -31
- package/skills/agentic-mermaid-diagram-workflow/references/sequence.md +10 -14
- package/skills/agentic-mermaid-live-editor/SKILL.md +100 -206
- package/TODO.md +0 -684
- package/dist/index-wjRjbsP-.d.ts +0 -1102
- package/docs/assets/improvements/box-start-connector.png +0 -0
- package/docs/assets/improvements/comment-dropped-lint.png +0 -0
- package/docs/assets/improvements/er-content-loss.png +0 -0
- package/docs/assets/improvements/fan-in-trunks.png +0 -0
- package/docs/assets/improvements/gantt-family.png +0 -0
- package/docs/assets/improvements/gantt-milestone-clamp.png +0 -0
- package/docs/assets/improvements/gantt-verify-seam.png +0 -0
- package/docs/assets/improvements/subgraph-container-edges.png +0 -0
- package/docs/assets/improvements/wrapper-frontmatter.png +0 -0
- package/docs/assets/improvements/wrapper-init-directive.png +0 -0
- package/docs/assets/improvements/wrapper-leading-comment.png +0 -0
- package/docs/assets/style-cookbook/mid-century-report.png +0 -0
- package/docs/assets/style-cookbook/star-chart-atlas.png +0 -0
- package/docs/assets/style-cookbook/transit-route-map.png +0 -0
- package/docs/contributing/adding-diagram-types.md +0 -144
- package/docs/contributing/diagram-family-citizenship.matrix.json +0 -2127
- package/docs/contributing/diagram-family-citizenship.md +0 -82
- package/docs/contributing/harvesting-upstream-tests.md +0 -126
- package/docs/contributing/lessons-learned.md +0 -276
- package/docs/contributing/releasing.md +0 -82
- package/docs/contributing/visual-review-evidence.md +0 -53
- package/docs/design/families/README.md +0 -26
- package/docs/design/families/architecture-beta.md +0 -48
- package/docs/design/families/flowchart-parser-conformance.md +0 -22
- package/docs/design/families/gantt-research.md +0 -137
- package/docs/design/families/gantt.md +0 -329
- package/docs/design/families/journey-agentic-cards-before.png +0 -0
- package/docs/design/families/journey-experience-curve-implementation.png +0 -0
- package/docs/design/families/journey-mermaid-classic-implementation.png +0 -0
- package/docs/design/families/journey-mermaid-classic-prototype.png +0 -0
- package/docs/design/families/journey-mermaid-classic-prototype.svg +0 -163
- package/docs/design/families/journey-mermaid-classic-prototype.svg.png +0 -0
- package/docs/design/families/journey-migration-parity.md +0 -439
- package/docs/design/families/journey-section-overlap-after.png +0 -0
- package/docs/design/families/journey-section-overlap-before.png +0 -0
- package/docs/design/families/journey-usage-research.md +0 -423
- package/docs/design/families/journey.md +0 -153
- package/docs/design/families/xychart.md +0 -85
- package/docs/design/system/README.md +0 -45
- package/docs/design/system/abstraction-audit.md +0 -224
- package/docs/design/system/abstraction-recommendations.md +0 -655
- package/docs/design/system/architecture.mmd +0 -29
- package/docs/design/system/architecture.svg +0 -137
- package/docs/design/system/layout-guarantees-and-robustness.md +0 -351
- package/docs/design/system/layout-rubric.md +0 -138
- package/docs/design/system/route-contracts.md +0 -848
- package/docs/design/system/source-preservation-ladder.md +0 -52
- package/docs/design/system/styles-rollout.md +0 -216
- package/docs/design/system/ugly-layouts.md +0 -119
- package/docs/layout-characterization/README.md +0 -245
- package/docs/layout-characterization/contact-sheet-families.md +0 -498
- package/docs/layout-characterization/contact-sheet.md +0 -638
- package/docs/layout-characterization/properties.md +0 -281
- package/docs/layout-characterization/visual-quality.md +0 -160
- package/docs/layout-characterization/visual-snapshots/architecture.svg +0 -79
- package/docs/layout-characterization/visual-snapshots/class.svg +0 -60
- package/docs/layout-characterization/visual-snapshots/er.svg +0 -61
- package/docs/layout-characterization/visual-snapshots/flowchart.svg +0 -58
- package/docs/layout-characterization/visual-snapshots/gantt.svg +0 -52
- package/docs/layout-characterization/visual-snapshots/journey.svg +0 -98
- package/docs/layout-characterization/visual-snapshots/pie.svg +0 -36
- package/docs/layout-characterization/visual-snapshots/quadrant.svg +0 -47
- package/docs/layout-characterization/visual-snapshots/sequence.svg +0 -59
- package/docs/layout-characterization/visual-snapshots/state.svg +0 -52
- package/docs/layout-characterization/visual-snapshots/timeline.svg +0 -64
- package/docs/layout-characterization/visual-snapshots/xychart.svg +0 -97
- package/docs/project/divergences.md +0 -98
- package/docs/project/dogfooding-docs-strategy.md +0 -66
- package/docs/project/hosted-mcp-cloudflare-plan.md +0 -338
- package/docs/project/lessons-learned.md +0 -1019
- package/docs/project/remove-role-styling-plan.md +0 -176
- package/examples/agent-improve-auth-flow.ts +0 -206
- package/examples/agent-loop.ts +0 -53
- package/examples/mcp-vs-cli-complex-diagrams.ts +0 -133
- package/llms.txt +0 -104
- package/skill-evals/README.md +0 -77
- package/skill-evals/fixtures/architecture-cache/input.mmd +0 -5
- package/skill-evals/fixtures/class-payment/input.mmd +0 -4
- package/skill-evals/fixtures/er-orders/input.mmd +0 -10
- package/skill-evals/fixtures/flowchart-cache/input.mmd +0 -3
- package/skill-evals/fixtures/gantt-release/input.mmd +0 -7
- package/skill-evals/fixtures/journey-checkout/input.mmd +0 -6
- package/skill-evals/fixtures/pie-exports/input.mmd +0 -5
- package/skill-evals/fixtures/quadrant-priorities/input.mmd +0 -11
- package/skill-evals/fixtures/sequence-retry/input.mmd +0 -8
- package/skill-evals/fixtures/state-lifecycle/input.mmd +0 -5
- package/skill-evals/fixtures/timeline-release/input.mmd +0 -6
- package/skill-evals/fixtures/xychart-growth/input.mmd +0 -5
- package/skill-evals/shared-benchmark.json +0 -510
- package/src/agent/analyze.ts +0 -157
- package/src/agent/apply.ts +0 -146
- package/src/agent/architecture-body.ts +0 -458
- package/src/agent/certificates.ts +0 -110
- package/src/agent/class-body.ts +0 -319
- package/src/agent/core.ts +0 -207
- package/src/agent/create.ts +0 -106
- package/src/agent/describe.ts +0 -368
- package/src/agent/er-body.ts +0 -224
- package/src/agent/facts.ts +0 -360
- package/src/agent/families-builtin.ts +0 -920
- package/src/agent/families.ts +0 -232
- package/src/agent/family-layouts.ts +0 -832
- package/src/agent/flowchart-body.ts +0 -372
- package/src/agent/flowchart-unsupported.ts +0 -243
- package/src/agent/font-coverage.ts +0 -288
- package/src/agent/gantt-body.ts +0 -495
- package/src/agent/index.ts +0 -10
- package/src/agent/journey-body.ts +0 -374
- package/src/agent/label-metrics.ts +0 -28
- package/src/agent/layout-to-rendered.ts +0 -63
- package/src/agent/mutate.ts +0 -97
- package/src/agent/mutation-ops.ts +0 -51
- package/src/agent/op-schema.ts +0 -443
- package/src/agent/parse.ts +0 -223
- package/src/agent/pie-body.ts +0 -270
- package/src/agent/png.ts +0 -171
- package/src/agent/quadrant-body.ts +0 -336
- package/src/agent/quality.ts +0 -363
- package/src/agent/readability-audit.ts +0 -92
- package/src/agent/rendered-route-audit.ts +0 -61
- package/src/agent/sequence-body.ts +0 -345
- package/src/agent/serialize.ts +0 -214
- package/src/agent/state-body.ts +0 -589
- package/src/agent/structural-count.ts +0 -150
- package/src/agent/timeline-body.ts +0 -280
- package/src/agent/trace-log.ts +0 -43
- package/src/agent/types.ts +0 -995
- package/src/agent/verify.ts +0 -603
- package/src/agent/xychart-body.ts +0 -472
- package/src/architecture/config.ts +0 -282
- package/src/architecture/layout.ts +0 -496
- package/src/architecture/parser.ts +0 -375
- package/src/architecture/renderer.ts +0 -611
- package/src/architecture/types.ts +0 -113
- package/src/ascii/ansi.ts +0 -423
- package/src/ascii/architecture.ts +0 -210
- package/src/ascii/canvas.ts +0 -443
- package/src/ascii/class-diagram.ts +0 -697
- package/src/ascii/converter.ts +0 -475
- package/src/ascii/draw.ts +0 -1693
- package/src/ascii/edge-bundling.ts +0 -328
- package/src/ascii/edge-routing.ts +0 -330
- package/src/ascii/er-diagram.ts +0 -439
- package/src/ascii/gantt.ts +0 -276
- package/src/ascii/grid.ts +0 -747
- package/src/ascii/index.ts +0 -196
- package/src/ascii/journey.ts +0 -169
- package/src/ascii/meta.ts +0 -244
- package/src/ascii/multiline-utils.ts +0 -80
- package/src/ascii/pathfinder.ts +0 -301
- package/src/ascii/pie.ts +0 -67
- package/src/ascii/quadrant.ts +0 -158
- package/src/ascii/reverse.ts +0 -216
- package/src/ascii/sequence.ts +0 -499
- package/src/ascii/shapes/circle.ts +0 -27
- package/src/ascii/shapes/corners.ts +0 -143
- package/src/ascii/shapes/diamond.ts +0 -27
- package/src/ascii/shapes/hexagon.ts +0 -27
- package/src/ascii/shapes/index.ts +0 -106
- package/src/ascii/shapes/rectangle.ts +0 -168
- package/src/ascii/shapes/rounded.ts +0 -27
- package/src/ascii/shapes/special.ts +0 -332
- package/src/ascii/shapes/stadium.ts +0 -110
- package/src/ascii/shapes/state.ts +0 -192
- package/src/ascii/shapes/types.ts +0 -73
- package/src/ascii/timeline.ts +0 -141
- package/src/ascii/types.ts +0 -312
- package/src/ascii/validate.ts +0 -120
- package/src/ascii/width.ts +0 -81
- package/src/ascii/wrap.ts +0 -131
- package/src/ascii/xychart.ts +0 -919
- package/src/browser.ts +0 -29
- package/src/class/layout.ts +0 -240
- package/src/class/parser.ts +0 -332
- package/src/class/renderer.ts +0 -638
- package/src/class/types.ts +0 -129
- package/src/cli/agent-instructions.ts +0 -1
- package/src/cli/am-bin.ts +0 -6
- package/src/cli/exit-codes.ts +0 -18
- package/src/cli/index.ts +0 -1132
- package/src/cli/init-agent.ts +0 -147
- package/src/cli/run-entrypoint.ts +0 -10
- package/src/color-resolver.ts +0 -152
- package/src/elk-instance.ts +0 -114
- package/src/er/layout.ts +0 -187
- package/src/er/parser.ts +0 -226
- package/src/er/renderer.ts +0 -792
- package/src/er/types.ts +0 -99
- package/src/family-rubric.ts +0 -425
- package/src/font-manifest.ts +0 -31
- package/src/gantt/layout.ts +0 -365
- package/src/gantt/parser.ts +0 -282
- package/src/gantt/pipeline.ts +0 -27
- package/src/gantt/renderer.ts +0 -414
- package/src/gantt/schedule.ts +0 -513
- package/src/gantt/types.ts +0 -255
- package/src/index.ts +0 -368
- package/src/journey/layout.ts +0 -554
- package/src/journey/parse-core.ts +0 -251
- package/src/journey/parser.ts +0 -101
- package/src/journey/renderer.ts +0 -917
- package/src/journey/types.ts +0 -144
- package/src/layout/geometry.ts +0 -182
- package/src/layout/pass.ts +0 -142
- package/src/layout/passes/index.ts +0 -3916
- package/src/layout-engine.ts +0 -1565
- package/src/layout-rubric.ts +0 -659
- package/src/mcp/artifacts.ts +0 -137
- package/src/mcp/dynamic-harness.ts +0 -31
- package/src/mcp/dynamic-modules.d.ts +0 -7
- package/src/mcp/facade.ts +0 -608
- package/src/mcp/harness-runtime.ts +0 -203
- package/src/mcp/hosted-server.ts +0 -468
- package/src/mcp/mcp-bin.ts +0 -3
- package/src/mcp/mcp-cli.ts +0 -82
- package/src/mcp/protocol.ts +0 -13
- package/src/mcp/sandbox.ts +0 -195
- package/src/mcp/sdk-decl.ts +0 -391
- package/src/mcp/server.ts +0 -362
- package/src/mcp/tool-surface.ts +0 -146
- package/src/mermaid-source.ts +0 -655
- package/src/multiline-utils.ts +0 -228
- package/src/parser.ts +0 -1031
- package/src/pie/layout.ts +0 -168
- package/src/pie/parser.ts +0 -136
- package/src/pie/renderer.ts +0 -234
- package/src/pie/types.ts +0 -82
- package/src/quadrant/layout.ts +0 -191
- package/src/quadrant/parser.ts +0 -183
- package/src/quadrant/renderer.ts +0 -322
- package/src/quadrant/types.ts +0 -113
- package/src/render-family-hooks.ts +0 -244
- package/src/renderer.ts +0 -1099
- package/src/route-contracts.ts +0 -2723
- package/src/scene/backend.ts +0 -76
- package/src/scene/fidelity.ts +0 -183
- package/src/scene/hybrid-backend.ts +0 -174
- package/src/scene/ir.ts +0 -181
- package/src/scene/marks.ts +0 -127
- package/src/scene/rough-backend.ts +0 -378
- package/src/scene/seed.ts +0 -46
- package/src/scene/style-registry.ts +0 -556
- package/src/sequence/layout.ts +0 -456
- package/src/sequence/parser.ts +0 -255
- package/src/sequence/renderer.ts +0 -604
- package/src/sequence/types.ts +0 -154
- package/src/shape-clipping.ts +0 -351
- package/src/shared/accessibility-directives.ts +0 -22
- package/src/shared/batched.ts +0 -61
- package/src/shared/color-math.ts +0 -93
- package/src/shared/svg-a11y.ts +0 -29
- package/src/shared/syntax-error.ts +0 -17
- package/src/shared/unicode-ranges.ts +0 -135
- package/src/styles.ts +0 -318
- package/src/svg-paths.ts +0 -28
- package/src/text-metrics.ts +0 -248
- package/src/theme.ts +0 -710
- package/src/timeline/layout.ts +0 -381
- package/src/timeline/parser.ts +0 -215
- package/src/timeline/renderer.ts +0 -538
- package/src/timeline/types.ts +0 -105
- package/src/types.ts +0 -462
- package/src/website-worker-modules.d.ts +0 -18
- package/src/xychart/axis-utils.ts +0 -109
- package/src/xychart/colors.ts +0 -124
- package/src/xychart/config.ts +0 -88
- package/src/xychart/layout.ts +0 -656
- package/src/xychart/parser.ts +0 -504
- package/src/xychart/renderer.ts +0 -640
- package/src/xychart/types.ts +0 -282
|
@@ -0,0 +1,3485 @@
|
|
|
1
|
+
import {
|
|
2
|
+
projectKnownRenderErrorDiagnostic,
|
|
3
|
+
projectRenderErrorDiagnostic
|
|
4
|
+
} from "./chunk-2SILJDW2.js";
|
|
5
|
+
import {
|
|
6
|
+
renderMermaidPNG,
|
|
7
|
+
renderMermaidPNGWithReceipt
|
|
8
|
+
} from "./chunk-FDM43XU7.js";
|
|
9
|
+
import {
|
|
10
|
+
analyzeMermaid,
|
|
11
|
+
analyzeMermaidSource,
|
|
12
|
+
buildChecked,
|
|
13
|
+
buildMermaid,
|
|
14
|
+
checkMermaid,
|
|
15
|
+
checkMermaidSource,
|
|
16
|
+
configWarningsForMermaid,
|
|
17
|
+
createMermaid,
|
|
18
|
+
describeMermaid,
|
|
19
|
+
describeMermaidFacts,
|
|
20
|
+
describeMermaidFactsSource,
|
|
21
|
+
describeMermaidSource,
|
|
22
|
+
describeOps,
|
|
23
|
+
hasOpSchema,
|
|
24
|
+
layoutMermaidWithReceipt,
|
|
25
|
+
mutate,
|
|
26
|
+
mutateChecked,
|
|
27
|
+
opSignatures,
|
|
28
|
+
renderMermaidASCII,
|
|
29
|
+
renderMermaidASCIIWithReceipt,
|
|
30
|
+
renderMermaidSVG,
|
|
31
|
+
renderMermaidSVGWithReceipt,
|
|
32
|
+
verifyMermaid,
|
|
33
|
+
verifySummary
|
|
34
|
+
} from "./chunk-MYPDVZCO.js";
|
|
35
|
+
import {
|
|
36
|
+
BUILTIN_FAMILY_METADATA,
|
|
37
|
+
PACKAGE_VERSION,
|
|
38
|
+
ParsedDiagramFamilyMismatchError,
|
|
39
|
+
RENDER_CONTRACT_VERSION,
|
|
40
|
+
RENDER_OUTPUT_DESCRIPTORS,
|
|
41
|
+
RenderCapabilityError,
|
|
42
|
+
architectureVisualOverridesTypeScriptDeclaration,
|
|
43
|
+
asArchitecture,
|
|
44
|
+
asClass,
|
|
45
|
+
asEr,
|
|
46
|
+
asFlowchart,
|
|
47
|
+
asGantt,
|
|
48
|
+
asGitGraph,
|
|
49
|
+
asJourney,
|
|
50
|
+
asMindmap,
|
|
51
|
+
asPie,
|
|
52
|
+
asQuadrant,
|
|
53
|
+
asRadar,
|
|
54
|
+
asSequence,
|
|
55
|
+
asState,
|
|
56
|
+
asTimeline,
|
|
57
|
+
asXyChart,
|
|
58
|
+
knownBuiltinFamilies,
|
|
59
|
+
limitJsonConfigDiagnostics,
|
|
60
|
+
normalizePortablePngBackground,
|
|
61
|
+
parseRegisteredMermaid,
|
|
62
|
+
pngOutputOptionsJsonSchema,
|
|
63
|
+
projectNativePngOutputPolicyInput,
|
|
64
|
+
resolveStyleStack,
|
|
65
|
+
safeCssColor,
|
|
66
|
+
safeCssFontFamily,
|
|
67
|
+
safeCssPaint,
|
|
68
|
+
serializeMermaid,
|
|
69
|
+
sharedRenderOptionsJsonSchema,
|
|
70
|
+
sharedRenderOptionsTypeScriptDeclaration,
|
|
71
|
+
styleSpecTypeScriptDeclaration,
|
|
72
|
+
validateJsonConfigAdmission,
|
|
73
|
+
validateSerializableRenderOptions
|
|
74
|
+
} from "./chunk-BJYMBT4I.js";
|
|
75
|
+
|
|
76
|
+
// src/mcp/server.ts
|
|
77
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
78
|
+
import { createServer } from "http";
|
|
79
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
80
|
+
import { join as join2 } from "path";
|
|
81
|
+
import { URL } from "url";
|
|
82
|
+
|
|
83
|
+
// src/mcp/sandbox.ts
|
|
84
|
+
import vm from "vm";
|
|
85
|
+
|
|
86
|
+
// src/mcp/facade.ts
|
|
87
|
+
import { parse as parseJavaScript } from "acorn";
|
|
88
|
+
function createTracingMermaid(trace, makeSandboxError, isClosed, hostPolicy, beforeSdkCall) {
|
|
89
|
+
const diagramIds = /* @__PURE__ */ new WeakMap();
|
|
90
|
+
const trusted = /* @__PURE__ */ new WeakSet();
|
|
91
|
+
const hardened = /* @__PURE__ */ new WeakMap();
|
|
92
|
+
const raw = /* @__PURE__ */ new WeakMap();
|
|
93
|
+
let nextId = 1;
|
|
94
|
+
const forbidden = (prop) => prop === "constructor" || prop === "__proto__" || prop === "prototype";
|
|
95
|
+
const protoFor = (target) => Object.isExtensible(target) ? null : Reflect.getPrototypeOf(target);
|
|
96
|
+
const sandboxError = (message) => makeSandboxError ? makeSandboxError(message) : new Error(message);
|
|
97
|
+
const assertOpen = () => {
|
|
98
|
+
if (isClosed?.()) throw sandboxError("Code Mode SDK calls are not allowed while returning results");
|
|
99
|
+
const blocked = beforeSdkCall?.();
|
|
100
|
+
if (blocked) throw sandboxError(blocked);
|
|
101
|
+
};
|
|
102
|
+
const readonly = () => {
|
|
103
|
+
throw sandboxError("Code Mode SDK results are read-only; use mermaid.mutate(...) for structured edits");
|
|
104
|
+
};
|
|
105
|
+
const arrayMutators = /* @__PURE__ */ new Set(["copyWithin", "fill", "pop", "push", "reverse", "shift", "sort", "splice", "unshift"]);
|
|
106
|
+
const arrayCallbackMethods = /* @__PURE__ */ new Set(["forEach", "map", "filter", "find", "findIndex", "findLast", "findLastIndex", "some", "every", "flatMap"]);
|
|
107
|
+
const rawOf = (value) => value && (typeof value === "object" || typeof value === "function") && raw.has(value) ? raw.get(value) : value;
|
|
108
|
+
const isMap = (value) => value instanceof Map || Object.prototype.toString.call(value) === "[object Map]";
|
|
109
|
+
const isSet = (value) => value instanceof Set || Object.prototype.toString.call(value) === "[object Set]";
|
|
110
|
+
const structuredErrorMetadata = /* @__PURE__ */ new WeakMap();
|
|
111
|
+
const structuredErrorFields = (error) => {
|
|
112
|
+
if (typeof error !== "object" || error === null) return void 0;
|
|
113
|
+
const carried = structuredErrorMetadata.get(error);
|
|
114
|
+
if (carried) return carried;
|
|
115
|
+
let fields;
|
|
116
|
+
if (error instanceof RenderCapabilityError) {
|
|
117
|
+
fields = {
|
|
118
|
+
name: error.name,
|
|
119
|
+
code: error.code,
|
|
120
|
+
output: error.output,
|
|
121
|
+
family: error.family,
|
|
122
|
+
decision: error.decision
|
|
123
|
+
};
|
|
124
|
+
} else if (error instanceof ParsedDiagramFamilyMismatchError) {
|
|
125
|
+
fields = {
|
|
126
|
+
name: error.name,
|
|
127
|
+
code: error.code,
|
|
128
|
+
expectedFamilyId: error.expectedFamilyId,
|
|
129
|
+
detectedFamilyId: error.detectedFamilyId
|
|
130
|
+
};
|
|
131
|
+
} else {
|
|
132
|
+
const renderDiagnostic = projectKnownRenderErrorDiagnostic(error);
|
|
133
|
+
if (!renderDiagnostic) return void 0;
|
|
134
|
+
fields = {
|
|
135
|
+
name: renderDiagnostic.code === "ASCII_TARGET_WIDTH_IMPOSSIBLE" ? "AsciiWidthError" : "MermaidFamilyDetectionError",
|
|
136
|
+
...renderDiagnostic
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
if (!fields) return void 0;
|
|
140
|
+
const encoded = JSON.stringify(fields);
|
|
141
|
+
return encoded === void 0 ? void 0 : JSON.parse(encoded);
|
|
142
|
+
};
|
|
143
|
+
const extensibleDataEnvelope = (value) => {
|
|
144
|
+
if (Object.isExtensible(value)) return value;
|
|
145
|
+
if (Array.isArray(value)) return value.slice();
|
|
146
|
+
const prototype = Reflect.getPrototypeOf(value);
|
|
147
|
+
return prototype === Object.prototype || prototype === null ? Object.assign(Object.create(prototype), value) : value;
|
|
148
|
+
};
|
|
149
|
+
const hostCall = (fn, genericRenderFailure = false) => {
|
|
150
|
+
try {
|
|
151
|
+
return fn();
|
|
152
|
+
} catch (e) {
|
|
153
|
+
const fields = structuredErrorFields(e) ?? (genericRenderFailure ? { name: "RenderError", ...projectRenderErrorDiagnostic(e) } : void 0);
|
|
154
|
+
const wrapped = sandboxError(fields?.message ?? e.message);
|
|
155
|
+
if (fields) {
|
|
156
|
+
structuredErrorMetadata.set(wrapped, fields);
|
|
157
|
+
for (const [field, value] of Object.entries(fields)) {
|
|
158
|
+
Object.defineProperty(wrapped, field, {
|
|
159
|
+
value: harden(value),
|
|
160
|
+
enumerable: true,
|
|
161
|
+
writable: false,
|
|
162
|
+
configurable: false
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
throw wrapped;
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
const jsonClone = (value) => hostCall(() => {
|
|
170
|
+
if (!value || typeof value !== "object" && typeof value !== "function") return value;
|
|
171
|
+
const encoded = JSON.stringify(value);
|
|
172
|
+
return encoded === void 0 ? void 0 : JSON.parse(encoded);
|
|
173
|
+
});
|
|
174
|
+
const iteratorOf = (items) => {
|
|
175
|
+
let index = 0;
|
|
176
|
+
const iterator = {
|
|
177
|
+
[Symbol.iterator]() {
|
|
178
|
+
return this;
|
|
179
|
+
},
|
|
180
|
+
next() {
|
|
181
|
+
return index < items.length ? { value: items[index++], done: false } : { value: void 0, done: true };
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
return harden(iterator);
|
|
185
|
+
};
|
|
186
|
+
function descriptorFor(target, prop) {
|
|
187
|
+
if (forbidden(prop)) return void 0;
|
|
188
|
+
const desc = Reflect.getOwnPropertyDescriptor(target, prop);
|
|
189
|
+
if (!desc) return void 0;
|
|
190
|
+
if ("value" in desc) return { ...desc, value: harden(desc.value) };
|
|
191
|
+
return { ...desc, get: desc.get ? harden(desc.get) : void 0, set: desc.set ? harden(readonly) : void 0 };
|
|
192
|
+
}
|
|
193
|
+
const isDiagramLike = (value) => Boolean(value && typeof value === "object" && "body" in value && "canonicalSource" in value);
|
|
194
|
+
const idOf = (value) => {
|
|
195
|
+
value = rawOf(value);
|
|
196
|
+
if (!isDiagramLike(value)) return void 0;
|
|
197
|
+
const obj = value;
|
|
198
|
+
let id = diagramIds.get(obj);
|
|
199
|
+
if (!id) {
|
|
200
|
+
id = nextId++;
|
|
201
|
+
diagramIds.set(obj, id);
|
|
202
|
+
}
|
|
203
|
+
return id;
|
|
204
|
+
};
|
|
205
|
+
const trustDiagram = (value) => {
|
|
206
|
+
value = rawOf(value);
|
|
207
|
+
if (isDiagramLike(value)) trusted.add(value);
|
|
208
|
+
};
|
|
209
|
+
const requireTrustedDiagram = (value, verb) => {
|
|
210
|
+
value = rawOf(value);
|
|
211
|
+
if (value && (typeof value === "object" || typeof value === "function") && !trusted.has(value)) {
|
|
212
|
+
throw sandboxError(`Code Mode ${verb} input must come from mermaid.parseRegisteredMermaid(...) or mermaid.mutate(...)`);
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
const rememberRaw = (proxy2, target) => {
|
|
216
|
+
raw.set(proxy2, target);
|
|
217
|
+
if (isDiagramLike(target)) diagramIds.set(proxy2, idOf(target));
|
|
218
|
+
return proxy2;
|
|
219
|
+
};
|
|
220
|
+
const harden = (value) => {
|
|
221
|
+
if (!value || typeof value !== "object" && typeof value !== "function") return value;
|
|
222
|
+
const unwrapped = rawOf(value);
|
|
223
|
+
if (unwrapped !== value) return harden(unwrapped);
|
|
224
|
+
const obj = value;
|
|
225
|
+
const cached = hardened.get(obj);
|
|
226
|
+
if (cached) return cached;
|
|
227
|
+
if (isMap(value)) {
|
|
228
|
+
let proxy3;
|
|
229
|
+
proxy3 = new Proxy(value, {
|
|
230
|
+
get(target, prop) {
|
|
231
|
+
if (forbidden(prop)) return void 0;
|
|
232
|
+
if (prop === "size") return target.size;
|
|
233
|
+
if (prop === "get") return harden((key) => harden(target.get(rawOf(key))));
|
|
234
|
+
if (prop === "set" || prop === "delete" || prop === "clear" || prop === "getOrInsert" || prop === "getOrInsertComputed") return harden(readonly);
|
|
235
|
+
if (prop === "has") return harden((key) => target.has(rawOf(key)));
|
|
236
|
+
if (prop === Symbol.iterator || prop === "entries") return harden(() => iteratorOf(Array.from(target.entries(), ([k, v]) => harden([harden(k), harden(v)]))));
|
|
237
|
+
if (prop === "values") return harden(() => iteratorOf(Array.from(target.values(), (v) => harden(v))));
|
|
238
|
+
if (prop === "keys") return harden(() => iteratorOf(Array.from(target.keys(), (k) => harden(k))));
|
|
239
|
+
if (prop === "forEach") return harden((cb, thisArg) => {
|
|
240
|
+
for (const [k, v] of target.entries()) cb.call(thisArg, harden(v), harden(k), proxy3);
|
|
241
|
+
});
|
|
242
|
+
const got = hostCall(() => Reflect.get(target, prop, target));
|
|
243
|
+
return typeof got === "function" ? void 0 : harden(got);
|
|
244
|
+
},
|
|
245
|
+
set() {
|
|
246
|
+
return readonly();
|
|
247
|
+
},
|
|
248
|
+
deleteProperty() {
|
|
249
|
+
return readonly();
|
|
250
|
+
},
|
|
251
|
+
defineProperty() {
|
|
252
|
+
return readonly();
|
|
253
|
+
},
|
|
254
|
+
setPrototypeOf() {
|
|
255
|
+
return readonly();
|
|
256
|
+
},
|
|
257
|
+
getOwnPropertyDescriptor(target, prop) {
|
|
258
|
+
return descriptorFor(target, prop);
|
|
259
|
+
},
|
|
260
|
+
preventExtensions() {
|
|
261
|
+
return readonly();
|
|
262
|
+
},
|
|
263
|
+
getPrototypeOf(target) {
|
|
264
|
+
return protoFor(target);
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
hardened.set(obj, proxy3);
|
|
268
|
+
return rememberRaw(proxy3, value);
|
|
269
|
+
}
|
|
270
|
+
if (isSet(value)) {
|
|
271
|
+
let proxy3;
|
|
272
|
+
proxy3 = new Proxy(value, {
|
|
273
|
+
get(target, prop) {
|
|
274
|
+
if (forbidden(prop)) return void 0;
|
|
275
|
+
if (prop === "size") return target.size;
|
|
276
|
+
if (prop === "add" || prop === "delete" || prop === "clear") return harden(readonly);
|
|
277
|
+
if (prop === "has") return harden((val) => target.has(rawOf(val)));
|
|
278
|
+
if (prop === Symbol.iterator || prop === "values" || prop === "keys") return harden(() => iteratorOf(Array.from(target.values(), (v) => harden(v))));
|
|
279
|
+
if (prop === "entries") return harden(() => iteratorOf(Array.from(target.values(), (v) => harden([harden(v), harden(v)]))));
|
|
280
|
+
if (prop === "forEach") return harden((cb, thisArg) => {
|
|
281
|
+
for (const v of target.values()) cb.call(thisArg, harden(v), harden(v), proxy3);
|
|
282
|
+
});
|
|
283
|
+
const got = hostCall(() => Reflect.get(target, prop, target));
|
|
284
|
+
return typeof got === "function" ? void 0 : harden(got);
|
|
285
|
+
},
|
|
286
|
+
set() {
|
|
287
|
+
return readonly();
|
|
288
|
+
},
|
|
289
|
+
deleteProperty() {
|
|
290
|
+
return readonly();
|
|
291
|
+
},
|
|
292
|
+
defineProperty() {
|
|
293
|
+
return readonly();
|
|
294
|
+
},
|
|
295
|
+
setPrototypeOf() {
|
|
296
|
+
return readonly();
|
|
297
|
+
},
|
|
298
|
+
getOwnPropertyDescriptor(target, prop) {
|
|
299
|
+
return descriptorFor(target, prop);
|
|
300
|
+
},
|
|
301
|
+
preventExtensions() {
|
|
302
|
+
return readonly();
|
|
303
|
+
},
|
|
304
|
+
getPrototypeOf(target) {
|
|
305
|
+
return protoFor(target);
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
hardened.set(obj, proxy3);
|
|
309
|
+
return rememberRaw(proxy3, value);
|
|
310
|
+
}
|
|
311
|
+
if (typeof value === "function") {
|
|
312
|
+
const original = value;
|
|
313
|
+
const callable = (...args) => harden(hostCall(() => Reflect.apply(original, void 0, args)));
|
|
314
|
+
const proxy3 = new Proxy(callable, {
|
|
315
|
+
apply(_target, thisArg, args) {
|
|
316
|
+
return harden(hostCall(() => Reflect.apply(original, thisArg, args)));
|
|
317
|
+
},
|
|
318
|
+
get(_target, prop, receiver) {
|
|
319
|
+
if (forbidden(prop)) return void 0;
|
|
320
|
+
if (!Reflect.getOwnPropertyDescriptor(original, prop)) return void 0;
|
|
321
|
+
return harden(hostCall(() => Reflect.get(original, prop, receiver)));
|
|
322
|
+
},
|
|
323
|
+
set() {
|
|
324
|
+
return readonly();
|
|
325
|
+
},
|
|
326
|
+
deleteProperty() {
|
|
327
|
+
return readonly();
|
|
328
|
+
},
|
|
329
|
+
defineProperty() {
|
|
330
|
+
return readonly();
|
|
331
|
+
},
|
|
332
|
+
setPrototypeOf() {
|
|
333
|
+
return readonly();
|
|
334
|
+
},
|
|
335
|
+
getOwnPropertyDescriptor(_target, prop) {
|
|
336
|
+
return descriptorFor(original, prop);
|
|
337
|
+
},
|
|
338
|
+
preventExtensions() {
|
|
339
|
+
return readonly();
|
|
340
|
+
},
|
|
341
|
+
getPrototypeOf() {
|
|
342
|
+
return protoFor(original);
|
|
343
|
+
}
|
|
344
|
+
});
|
|
345
|
+
hardened.set(obj, proxy3);
|
|
346
|
+
return rememberRaw(proxy3, value);
|
|
347
|
+
}
|
|
348
|
+
const proxyTarget = extensibleDataEnvelope(value);
|
|
349
|
+
let proxy2;
|
|
350
|
+
proxy2 = new Proxy(proxyTarget, {
|
|
351
|
+
get(target, prop, receiver) {
|
|
352
|
+
if (forbidden(prop)) return void 0;
|
|
353
|
+
if (Array.isArray(target)) {
|
|
354
|
+
if (typeof prop === "string" && arrayMutators.has(prop)) return harden(readonly);
|
|
355
|
+
if (prop === Symbol.iterator || prop === "values") return harden(() => iteratorOf(Array.prototype.map.call(target, (v) => harden(v))));
|
|
356
|
+
if (prop === "entries") return harden(() => iteratorOf(Array.prototype.map.call(target, (v, i) => harden([i, harden(v)]))));
|
|
357
|
+
if (prop === "keys") return harden(() => iteratorOf(Array.from({ length: target.length }, (_, i) => i)));
|
|
358
|
+
if (typeof prop === "string" && arrayCallbackMethods.has(prop)) {
|
|
359
|
+
return harden((cb, thisArg) => {
|
|
360
|
+
const values = Array.prototype.map.call(target, (v) => harden(v));
|
|
361
|
+
const call = (v, i) => cb.call(thisArg, v, i, proxy2);
|
|
362
|
+
if (prop === "forEach") {
|
|
363
|
+
values.forEach(call);
|
|
364
|
+
return void 0;
|
|
365
|
+
}
|
|
366
|
+
if (prop === "map") return values.map(call);
|
|
367
|
+
if (prop === "filter") return values.filter((v, i) => call(v, i));
|
|
368
|
+
if (prop === "find") return values.find((v, i) => Boolean(call(v, i)));
|
|
369
|
+
if (prop === "findIndex") return values.findIndex((v, i) => Boolean(call(v, i)));
|
|
370
|
+
if (prop === "findLast") {
|
|
371
|
+
for (let i = values.length - 1; i >= 0; i--) if (call(values[i], i)) return values[i];
|
|
372
|
+
return void 0;
|
|
373
|
+
}
|
|
374
|
+
if (prop === "findLastIndex") {
|
|
375
|
+
for (let i = values.length - 1; i >= 0; i--) if (call(values[i], i)) return i;
|
|
376
|
+
return -1;
|
|
377
|
+
}
|
|
378
|
+
if (prop === "some") return values.some((v, i) => Boolean(call(v, i)));
|
|
379
|
+
if (prop === "every") return values.every((v, i) => Boolean(call(v, i)));
|
|
380
|
+
if (prop === "flatMap") return values.flatMap(call);
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
if (prop === "toSorted") {
|
|
384
|
+
return harden((compare) => {
|
|
385
|
+
const values = Array.prototype.map.call(target, (v) => harden(v));
|
|
386
|
+
return compare ? values.slice().sort((a, b) => compare(a, b)) : values.slice().sort();
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
if (prop === "toReversed") return harden(() => Array.prototype.map.call(target, (v) => harden(v)).reverse());
|
|
390
|
+
if (prop === "toSpliced") return harden(function(start, deleteCount, ...items) {
|
|
391
|
+
const values = Array.prototype.map.call(target, (v) => harden(v));
|
|
392
|
+
const copy = values.slice();
|
|
393
|
+
if (arguments.length === 1) copy.splice(start);
|
|
394
|
+
else copy.splice(start, deleteCount ?? 0, ...items.map((item) => harden(rawOf(item))));
|
|
395
|
+
return copy;
|
|
396
|
+
});
|
|
397
|
+
if (prop === "with") return harden((index, item) => {
|
|
398
|
+
const values = Array.prototype.map.call(target, (v) => harden(v));
|
|
399
|
+
const normalized = index < 0 ? values.length + index : index;
|
|
400
|
+
if (normalized < 0 || normalized >= values.length) throw sandboxError("Invalid array index");
|
|
401
|
+
const copy = values.slice();
|
|
402
|
+
copy[normalized] = harden(rawOf(item));
|
|
403
|
+
return copy;
|
|
404
|
+
});
|
|
405
|
+
if (prop === "reduce" || prop === "reduceRight") {
|
|
406
|
+
return harden(function(cb, initial) {
|
|
407
|
+
const values = Array.prototype.map.call(target, (v) => harden(v));
|
|
408
|
+
const reducer = (previous, current, index) => cb(previous, current, index, proxy2);
|
|
409
|
+
return arguments.length >= 2 ? prop === "reduce" ? values.reduce(reducer, initial) : values.reduceRight(reducer, initial) : prop === "reduce" ? values.reduce(reducer) : values.reduceRight(reducer);
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
if (!Reflect.getOwnPropertyDescriptor(target, prop)) return void 0;
|
|
414
|
+
const got = hostCall(() => Reflect.get(target, prop, receiver));
|
|
415
|
+
return typeof got === "function" ? void 0 : harden(got);
|
|
416
|
+
},
|
|
417
|
+
set() {
|
|
418
|
+
return readonly();
|
|
419
|
+
},
|
|
420
|
+
deleteProperty() {
|
|
421
|
+
return readonly();
|
|
422
|
+
},
|
|
423
|
+
defineProperty() {
|
|
424
|
+
return readonly();
|
|
425
|
+
},
|
|
426
|
+
setPrototypeOf() {
|
|
427
|
+
return readonly();
|
|
428
|
+
},
|
|
429
|
+
has(target, prop) {
|
|
430
|
+
return forbidden(prop) ? false : Reflect.has(target, prop);
|
|
431
|
+
},
|
|
432
|
+
getOwnPropertyDescriptor(target, prop) {
|
|
433
|
+
return descriptorFor(target, prop);
|
|
434
|
+
},
|
|
435
|
+
preventExtensions() {
|
|
436
|
+
return readonly();
|
|
437
|
+
},
|
|
438
|
+
getPrototypeOf(target) {
|
|
439
|
+
return protoFor(target);
|
|
440
|
+
}
|
|
441
|
+
});
|
|
442
|
+
hardened.set(obj, proxy2);
|
|
443
|
+
if (proxyTarget !== obj) hardened.set(proxyTarget, proxy2);
|
|
444
|
+
return rememberRaw(proxy2, value);
|
|
445
|
+
};
|
|
446
|
+
const fingerprint = (value) => fingerprintDiagram(rawOf(value));
|
|
447
|
+
const bodyKind = (value) => {
|
|
448
|
+
const kind = value?.body?.kind;
|
|
449
|
+
return kind != null && FAMILY_KINDS.has(kind) ? kind : "opaque";
|
|
450
|
+
};
|
|
451
|
+
const push = (call) => {
|
|
452
|
+
trace?.push(call);
|
|
453
|
+
};
|
|
454
|
+
const sdkTarget = {
|
|
455
|
+
parseRegisteredMermaid,
|
|
456
|
+
createMermaid,
|
|
457
|
+
buildMermaid,
|
|
458
|
+
asFlowchart,
|
|
459
|
+
asState,
|
|
460
|
+
asSequence,
|
|
461
|
+
asTimeline,
|
|
462
|
+
asClass,
|
|
463
|
+
asEr,
|
|
464
|
+
asJourney,
|
|
465
|
+
asArchitecture,
|
|
466
|
+
asXyChart,
|
|
467
|
+
asPie,
|
|
468
|
+
asQuadrant,
|
|
469
|
+
asGantt,
|
|
470
|
+
asMindmap,
|
|
471
|
+
asGitGraph,
|
|
472
|
+
asRadar,
|
|
473
|
+
mutate,
|
|
474
|
+
verifyMermaid,
|
|
475
|
+
analyzeMermaid,
|
|
476
|
+
analyzeMermaidSource,
|
|
477
|
+
describeMermaidFacts,
|
|
478
|
+
describeMermaidFactsSource,
|
|
479
|
+
checkMermaid,
|
|
480
|
+
checkMermaidSource,
|
|
481
|
+
serializeMermaid,
|
|
482
|
+
renderMermaidSVG,
|
|
483
|
+
renderMermaidSVGWithReceipt,
|
|
484
|
+
renderMermaidASCII,
|
|
485
|
+
renderMermaidASCIIWithReceipt,
|
|
486
|
+
layoutMermaidWithReceipt,
|
|
487
|
+
// Read-only op discovery: field shapes / enum values / constraint notes and
|
|
488
|
+
// compact signatures for a family's ops, so a Code Mode script can look up
|
|
489
|
+
// exact op shapes at runtime instead of guessing (or triggering INVALID_OP).
|
|
490
|
+
describeOps,
|
|
491
|
+
opSignatures
|
|
492
|
+
};
|
|
493
|
+
const sdkProps = new Set(Reflect.ownKeys(sdkTarget));
|
|
494
|
+
const sdkValues = /* @__PURE__ */ new Map();
|
|
495
|
+
const sdkValue = (target, prop) => {
|
|
496
|
+
if (forbidden(prop) || !sdkProps.has(prop)) return void 0;
|
|
497
|
+
if (sdkValues.has(prop)) return sdkValues.get(prop);
|
|
498
|
+
let value;
|
|
499
|
+
if (prop === "parseRegisteredMermaid") value = harden((source) => {
|
|
500
|
+
assertOpen();
|
|
501
|
+
if (typeof source !== "string") throw sandboxError(`Code Mode ${prop} source must be a string`);
|
|
502
|
+
const r = hostCall(() => target.parseRegisteredMermaid(source));
|
|
503
|
+
if (r.ok) trustDiagram(r.value);
|
|
504
|
+
const diagram = r.ok ? idOf(r.value) : void 0;
|
|
505
|
+
push({ verb: "parse", diagram, source: r.ok ? r.value.body.kind === "opaque" ? r.value.body.source : r.value.canonicalSource : void 0 });
|
|
506
|
+
return harden(r);
|
|
507
|
+
});
|
|
508
|
+
else if (prop === "createMermaid") value = harden((kind, opts) => {
|
|
509
|
+
assertOpen();
|
|
510
|
+
const r = hostCall(() => target.createMermaid(kind, jsonClone(opts)));
|
|
511
|
+
trustDiagram(r);
|
|
512
|
+
push({ verb: "create", family: FAMILY_KINDS.has(kind) ? kind : void 0, diagram: idOf(r), ok: true });
|
|
513
|
+
return harden(r);
|
|
514
|
+
});
|
|
515
|
+
else if (prop === "buildMermaid") value = harden((kind, ops, opts) => {
|
|
516
|
+
assertOpen();
|
|
517
|
+
const opsForHost = jsonClone(ops);
|
|
518
|
+
const r = hostCall(() => buildChecked(kind, opsForHost, jsonClone(opts)));
|
|
519
|
+
if (r.ok) trustDiagram(r.value);
|
|
520
|
+
push({
|
|
521
|
+
verb: "create",
|
|
522
|
+
family: FAMILY_KINDS.has(kind) ? kind : void 0,
|
|
523
|
+
diagram: r.ok ? idOf(r.value) : void 0,
|
|
524
|
+
ops: Array.isArray(ops) ? ops.length : void 0,
|
|
525
|
+
ok: r.ok
|
|
526
|
+
});
|
|
527
|
+
return harden(r);
|
|
528
|
+
});
|
|
529
|
+
else if (typeof prop === "string" && prop.startsWith("as") && prop in target) {
|
|
530
|
+
value = harden((d) => {
|
|
531
|
+
assertOpen();
|
|
532
|
+
requireTrustedDiagram(d, String(prop));
|
|
533
|
+
const input = idOf(d);
|
|
534
|
+
const r = hostCall(() => target[prop](rawOf(d)));
|
|
535
|
+
if (r) trustDiagram(r);
|
|
536
|
+
push({ verb: "narrow", family: narrowerFamily(prop), input, ok: r !== null });
|
|
537
|
+
return harden(r);
|
|
538
|
+
});
|
|
539
|
+
} else if (prop === "mutate") value = harden((d, op) => {
|
|
540
|
+
assertOpen();
|
|
541
|
+
requireTrustedDiagram(d, "mutate");
|
|
542
|
+
const opForHost = jsonClone(op);
|
|
543
|
+
const call = {
|
|
544
|
+
verb: "mutate",
|
|
545
|
+
body: bodyKind(rawOf(d)),
|
|
546
|
+
input: idOf(d),
|
|
547
|
+
opKind: typeof opForHost === "object" && opForHost && "kind" in opForHost ? String(opForHost.kind) : void 0
|
|
548
|
+
};
|
|
549
|
+
push(call);
|
|
550
|
+
const r = hostCall(() => mutateChecked(rawOf(d), opForHost));
|
|
551
|
+
if (r.ok) {
|
|
552
|
+
trustDiagram(r.value);
|
|
553
|
+
call.output = idOf(r.value);
|
|
554
|
+
call.fingerprint = fingerprint(r.value);
|
|
555
|
+
}
|
|
556
|
+
return harden(r);
|
|
557
|
+
});
|
|
558
|
+
else if (prop === "verifyMermaid") value = harden((input, opts) => {
|
|
559
|
+
assertOpen();
|
|
560
|
+
requireTrustedDiagram(input, "verifyMermaid");
|
|
561
|
+
const r = hostCall(() => target.verifyMermaid(rawOf(input), opts));
|
|
562
|
+
const diagram = idOf(input);
|
|
563
|
+
push({ verb: "verify", diagram, ok: r.ok, inspected: false, fingerprint: fingerprint(input) });
|
|
564
|
+
return harden(new Proxy(r, {
|
|
565
|
+
get(verifyTarget, verifyProp, verifyReceiver) {
|
|
566
|
+
if (isClosed?.()) throw sandboxError("Code Mode SDK calls are not allowed while returning results");
|
|
567
|
+
if (verifyProp === "ok" || verifyProp === "warnings" || verifyProp === "layout") {
|
|
568
|
+
push({ verb: "verify_inspect", diagram, property: verifyProp });
|
|
569
|
+
}
|
|
570
|
+
if (forbidden(verifyProp)) return void 0;
|
|
571
|
+
return harden(hostCall(() => Reflect.get(verifyTarget, verifyProp, verifyReceiver)));
|
|
572
|
+
},
|
|
573
|
+
set() {
|
|
574
|
+
return readonly();
|
|
575
|
+
},
|
|
576
|
+
deleteProperty() {
|
|
577
|
+
return readonly();
|
|
578
|
+
},
|
|
579
|
+
defineProperty() {
|
|
580
|
+
return readonly();
|
|
581
|
+
},
|
|
582
|
+
setPrototypeOf() {
|
|
583
|
+
return readonly();
|
|
584
|
+
},
|
|
585
|
+
getOwnPropertyDescriptor(target2, prop2) {
|
|
586
|
+
return descriptorFor(target2, prop2);
|
|
587
|
+
},
|
|
588
|
+
preventExtensions() {
|
|
589
|
+
return readonly();
|
|
590
|
+
},
|
|
591
|
+
getPrototypeOf(target2) {
|
|
592
|
+
return protoFor(target2);
|
|
593
|
+
}
|
|
594
|
+
}));
|
|
595
|
+
});
|
|
596
|
+
else if (prop === "analyzeMermaid") value = harden((d) => {
|
|
597
|
+
assertOpen();
|
|
598
|
+
requireTrustedDiagram(d, "analyzeMermaid");
|
|
599
|
+
const r = hostCall(() => target.analyzeMermaid(rawOf(d)));
|
|
600
|
+
push({ verb: "analyze", diagram: idOf(d), ok: true, fingerprint: fingerprint(d) });
|
|
601
|
+
return harden(r);
|
|
602
|
+
});
|
|
603
|
+
else if (prop === "analyzeMermaidSource") value = harden((source) => {
|
|
604
|
+
assertOpen();
|
|
605
|
+
if (typeof source !== "string") throw sandboxError("Code Mode analyzeMermaidSource source must be a string");
|
|
606
|
+
const r = hostCall(() => target.analyzeMermaidSource(source));
|
|
607
|
+
push({ verb: "analyze", source, ok: r.ok });
|
|
608
|
+
return harden(r);
|
|
609
|
+
});
|
|
610
|
+
else if (prop === "describeMermaidFacts") value = harden((d) => {
|
|
611
|
+
assertOpen();
|
|
612
|
+
requireTrustedDiagram(d, "describeMermaidFacts");
|
|
613
|
+
const r = hostCall(() => target.describeMermaidFacts(rawOf(d)));
|
|
614
|
+
push({ verb: "facts", diagram: idOf(d), ok: true, fingerprint: fingerprint(d) });
|
|
615
|
+
return harden(r);
|
|
616
|
+
});
|
|
617
|
+
else if (prop === "describeMermaidFactsSource") value = harden((source) => {
|
|
618
|
+
assertOpen();
|
|
619
|
+
if (typeof source !== "string") throw sandboxError("Code Mode describeMermaidFactsSource source must be a string");
|
|
620
|
+
const r = hostCall(() => target.describeMermaidFactsSource(source));
|
|
621
|
+
push({ verb: "facts", source, ok: r.ok });
|
|
622
|
+
return harden(r);
|
|
623
|
+
});
|
|
624
|
+
else if (prop === "checkMermaid") value = harden((d, spec) => {
|
|
625
|
+
assertOpen();
|
|
626
|
+
requireTrustedDiagram(d, "checkMermaid");
|
|
627
|
+
const r = hostCall(() => target.checkMermaid(rawOf(d), jsonClone(spec)));
|
|
628
|
+
push({ verb: "check", diagram: idOf(d), ok: r.ok, fingerprint: fingerprint(d) });
|
|
629
|
+
return harden(r);
|
|
630
|
+
});
|
|
631
|
+
else if (prop === "checkMermaidSource") value = harden((source, spec) => {
|
|
632
|
+
assertOpen();
|
|
633
|
+
if (typeof source !== "string") throw sandboxError("Code Mode checkMermaidSource source must be a string");
|
|
634
|
+
const r = hostCall(() => target.checkMermaidSource(source, jsonClone(spec)));
|
|
635
|
+
push({ verb: "check", source, ok: r.ok ? r.value.ok : false });
|
|
636
|
+
return harden(r);
|
|
637
|
+
});
|
|
638
|
+
else if (prop === "serializeMermaid") value = harden((d) => {
|
|
639
|
+
assertOpen();
|
|
640
|
+
requireTrustedDiagram(d, "serializeMermaid");
|
|
641
|
+
const source = hostCall(() => target.serializeMermaid(rawOf(d)));
|
|
642
|
+
push({ verb: "serialize", diagram: idOf(d), source, fingerprint: fingerprint(d) });
|
|
643
|
+
return source;
|
|
644
|
+
});
|
|
645
|
+
else if (prop === "renderMermaidSVG" || prop === "renderMermaidSVGWithReceipt" || prop === "renderMermaidASCII" || prop === "renderMermaidASCIIWithReceipt" || prop === "layoutMermaidWithReceipt") value = harden((input, opts) => {
|
|
646
|
+
assertOpen();
|
|
647
|
+
if (input && typeof input === "object") requireTrustedDiagram(input, String(prop));
|
|
648
|
+
const isAscii = prop === "renderMermaidASCII" || prop === "renderMermaidASCIIWithReceipt";
|
|
649
|
+
const configCallback = opts?.onConfigDiagnostic;
|
|
650
|
+
const projectionCallback = isAscii ? opts?.onProjectionDiagnostic : void 0;
|
|
651
|
+
if (configCallback !== void 0 && typeof configCallback !== "function") {
|
|
652
|
+
throw sandboxError(`Code Mode ${String(prop)} onConfigDiagnostic must be a function`);
|
|
653
|
+
}
|
|
654
|
+
if (projectionCallback !== void 0 && typeof projectionCallback !== "function") {
|
|
655
|
+
throw sandboxError(`Code Mode ${String(prop)} onProjectionDiagnostic must be a function`);
|
|
656
|
+
}
|
|
657
|
+
let cloned = jsonClone(opts);
|
|
658
|
+
if (hostPolicy) cloned = { ...cloned ?? {}, ...hostPolicy.render };
|
|
659
|
+
const configDiagnostics = [];
|
|
660
|
+
const projectionDiagnostics = [];
|
|
661
|
+
if (configCallback || projectionCallback) cloned ??= {};
|
|
662
|
+
if (configCallback) {
|
|
663
|
+
cloned.onConfigDiagnostic = (diagnostic) => configDiagnostics.push(diagnostic);
|
|
664
|
+
}
|
|
665
|
+
if (projectionCallback) {
|
|
666
|
+
cloned.onProjectionDiagnostic = (diagnostic) => projectionDiagnostics.push(diagnostic);
|
|
667
|
+
}
|
|
668
|
+
const rendered = hostCall(
|
|
669
|
+
() => target[prop](rawOf(input), cloned),
|
|
670
|
+
true
|
|
671
|
+
);
|
|
672
|
+
for (const diagnostic of configDiagnostics) configCallback(harden(jsonClone(diagnostic)));
|
|
673
|
+
for (const diagnostic of projectionDiagnostics) projectionCallback(harden(jsonClone(diagnostic)));
|
|
674
|
+
return jsonClone(rendered);
|
|
675
|
+
});
|
|
676
|
+
else if (prop === "describeOps" || prop === "opSignatures") value = harden((family) => {
|
|
677
|
+
assertOpen();
|
|
678
|
+
if (typeof family !== "string") throw sandboxError(`Code Mode ${String(prop)} family must be a string`);
|
|
679
|
+
if (!hasOpSchema(family)) {
|
|
680
|
+
const families = knownBuiltinFamilies().filter(hasOpSchema);
|
|
681
|
+
throw sandboxError(`Code Mode ${String(prop)} family must be one of: ${families.join(", ")}`);
|
|
682
|
+
}
|
|
683
|
+
return harden(hostCall(() => target[prop](family)));
|
|
684
|
+
});
|
|
685
|
+
sdkValues.set(prop, value);
|
|
686
|
+
return value;
|
|
687
|
+
};
|
|
688
|
+
const marshalResult = (value) => {
|
|
689
|
+
try {
|
|
690
|
+
const rawObj = rawOf(value);
|
|
691
|
+
const candidate = rawObj && typeof rawObj === "object" && !Array.isArray(rawObj) && rawObj.ok === true && "value" in rawObj ? rawOf(rawObj.value) : rawObj;
|
|
692
|
+
if (isDiagramLike(candidate) && trusted.has(candidate)) {
|
|
693
|
+
const d = candidate;
|
|
694
|
+
const verification = verifyMermaid(d);
|
|
695
|
+
const summary = verifySummary(verification);
|
|
696
|
+
if (!verification.ok) {
|
|
697
|
+
return {
|
|
698
|
+
ok: false,
|
|
699
|
+
family: d.kind,
|
|
700
|
+
error: {
|
|
701
|
+
code: "VERIFY_FAILED",
|
|
702
|
+
message: "returned diagram failed verify; source was not emitted",
|
|
703
|
+
details: summary.warnings
|
|
704
|
+
}
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
return { ok: true, family: d.kind, source: serializeMermaid(d), verify: summary };
|
|
708
|
+
}
|
|
709
|
+
} catch {
|
|
710
|
+
}
|
|
711
|
+
return value;
|
|
712
|
+
};
|
|
713
|
+
const proxy = new Proxy(sdkTarget, {
|
|
714
|
+
get(target, prop) {
|
|
715
|
+
return sdkValue(target, prop);
|
|
716
|
+
},
|
|
717
|
+
set() {
|
|
718
|
+
return readonly();
|
|
719
|
+
},
|
|
720
|
+
deleteProperty() {
|
|
721
|
+
return readonly();
|
|
722
|
+
},
|
|
723
|
+
defineProperty() {
|
|
724
|
+
return readonly();
|
|
725
|
+
},
|
|
726
|
+
setPrototypeOf() {
|
|
727
|
+
return readonly();
|
|
728
|
+
},
|
|
729
|
+
getOwnPropertyDescriptor(target, prop) {
|
|
730
|
+
if (!sdkProps.has(prop) || forbidden(prop)) return void 0;
|
|
731
|
+
const desc = Reflect.getOwnPropertyDescriptor(target, prop);
|
|
732
|
+
return desc && "value" in desc ? { ...desc, value: sdkValue(target, prop) } : void 0;
|
|
733
|
+
},
|
|
734
|
+
preventExtensions() {
|
|
735
|
+
return readonly();
|
|
736
|
+
},
|
|
737
|
+
getPrototypeOf(target) {
|
|
738
|
+
return protoFor(target);
|
|
739
|
+
},
|
|
740
|
+
has(_target, prop) {
|
|
741
|
+
return sdkProps.has(prop) && !forbidden(prop);
|
|
742
|
+
},
|
|
743
|
+
ownKeys() {
|
|
744
|
+
return Array.from(sdkProps);
|
|
745
|
+
}
|
|
746
|
+
});
|
|
747
|
+
resultMarshallers.set(proxy, marshalResult);
|
|
748
|
+
return proxy;
|
|
749
|
+
}
|
|
750
|
+
var resultMarshallers = /* @__PURE__ */ new WeakMap();
|
|
751
|
+
function marshalCodeModeResult(sdk, value) {
|
|
752
|
+
const fn = sdk && typeof sdk === "object" ? resultMarshallers.get(sdk) : void 0;
|
|
753
|
+
return fn ? fn(value) : value;
|
|
754
|
+
}
|
|
755
|
+
var CODE_MODE_RETURN_HINT = "return plain data instead \u2014 e.g. serializeMermaid(d) for a diagram, or read the fields you need (verifyMermaid(d).ok, .warnings); the declarative mutate/build tools return a ready { ok, family, source, verify } envelope";
|
|
756
|
+
function fingerprintDiagram(value) {
|
|
757
|
+
if (!value || typeof value !== "object") return void 0;
|
|
758
|
+
if (!("body" in value) || !("canonicalSource" in value)) return void 0;
|
|
759
|
+
try {
|
|
760
|
+
return JSON.stringify(value, jsonReplacer);
|
|
761
|
+
} catch {
|
|
762
|
+
return void 0;
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
var FAMILY_KINDS = new Set(BUILTIN_FAMILY_METADATA.map((m) => m.id));
|
|
766
|
+
var NARROWER_TO_FAMILY = new Map(
|
|
767
|
+
BUILTIN_FAMILY_METADATA.map((m) => [m.narrower, m.id])
|
|
768
|
+
);
|
|
769
|
+
function narrowerFamily(prop) {
|
|
770
|
+
const family = NARROWER_TO_FAMILY.get(String(prop));
|
|
771
|
+
if (!family) throw new Error(`Unknown Code Mode narrower: ${String(prop)}`);
|
|
772
|
+
return family;
|
|
773
|
+
}
|
|
774
|
+
function expressionFirstWraps(code) {
|
|
775
|
+
const t = code.trim();
|
|
776
|
+
const stripped = t.replace(/;\s*$/, "");
|
|
777
|
+
const expressionWrapped = `(() => { return (
|
|
778
|
+
${stripped}
|
|
779
|
+
) })()`;
|
|
780
|
+
const statementWrapped = `(() => { ${code} })()`;
|
|
781
|
+
try {
|
|
782
|
+
new Function(`return (
|
|
783
|
+
${stripped}
|
|
784
|
+
)`);
|
|
785
|
+
return [expressionWrapped];
|
|
786
|
+
} catch {
|
|
787
|
+
return [statementWrapped];
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
function unsupportedCodeReason(code) {
|
|
791
|
+
const ast = parseCodeModeAst(code);
|
|
792
|
+
if (!ast) return void 0;
|
|
793
|
+
let usesAsync = false;
|
|
794
|
+
let usesRealm = false;
|
|
795
|
+
walkJavaScriptAst(ast, (node, parent, parentKey) => {
|
|
796
|
+
if (node.type === "ImportExpression" || node.type === "AwaitExpression") usesAsync = true;
|
|
797
|
+
if ("async" in node && node.async === true) usesAsync = true;
|
|
798
|
+
if (node.type !== "Identifier" || !("name" in node) || typeof node.name !== "string") return;
|
|
799
|
+
const propertyNameOnly = parentKey === "key" && parent?.computed !== true || parent?.type === "MemberExpression" && parentKey === "property" && parent.computed === false;
|
|
800
|
+
if (propertyNameOnly && !(node.name === "fromAsync" && parent?.type === "MemberExpression" && parent.object?.type === "Identifier" && parent.object.name === "Array")) return;
|
|
801
|
+
if (SYNC_UNSUPPORTED_IDENTIFIERS.has(node.name)) usesAsync = true;
|
|
802
|
+
if (REALM_UNSUPPORTED_IDENTIFIERS.has(node.name)) usesRealm = true;
|
|
803
|
+
});
|
|
804
|
+
if (usesAsync) {
|
|
805
|
+
return "Code Mode is synchronous; async/await, Promise jobs, finalizers, queueMicrotask, Array.fromAsync, and dynamic import are not supported";
|
|
806
|
+
}
|
|
807
|
+
if (usesRealm) {
|
|
808
|
+
return "Code Mode does not expose blocking or realm-creating globals";
|
|
809
|
+
}
|
|
810
|
+
return void 0;
|
|
811
|
+
}
|
|
812
|
+
var SYNC_UNSUPPORTED_IDENTIFIERS = /* @__PURE__ */ new Set([
|
|
813
|
+
"async",
|
|
814
|
+
"await",
|
|
815
|
+
"Promise",
|
|
816
|
+
"AsyncDisposableStack",
|
|
817
|
+
"FinalizationRegistry",
|
|
818
|
+
"WeakRef",
|
|
819
|
+
"fromAsync",
|
|
820
|
+
"queueMicrotask"
|
|
821
|
+
]);
|
|
822
|
+
var REALM_UNSUPPORTED_IDENTIFIERS = /* @__PURE__ */ new Set(["Atomics", "SharedArrayBuffer", "ShadowRealm", "WebAssembly"]);
|
|
823
|
+
function parseCodeModeAst(code) {
|
|
824
|
+
const stripped = code.trim().replace(/;\s*$/, "");
|
|
825
|
+
const candidates = [
|
|
826
|
+
`(${stripped})`,
|
|
827
|
+
`function __agent_code__(){
|
|
828
|
+
${code}
|
|
829
|
+
}`,
|
|
830
|
+
// Parse otherwise-invalid top-level await so it is rejected before an
|
|
831
|
+
// isolate/CPU budget is allocated and telemetry remains truthful.
|
|
832
|
+
`async function __agent_code__(){
|
|
833
|
+
${code}
|
|
834
|
+
}`
|
|
835
|
+
];
|
|
836
|
+
for (const candidate of candidates) {
|
|
837
|
+
try {
|
|
838
|
+
return parseJavaScript(candidate, { ecmaVersion: "latest", sourceType: "script" });
|
|
839
|
+
} catch {
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
return void 0;
|
|
843
|
+
}
|
|
844
|
+
function walkJavaScriptAst(node, visit, parent, parentKey) {
|
|
845
|
+
const record = node;
|
|
846
|
+
visit(record, parent, parentKey);
|
|
847
|
+
for (const [key, value] of Object.entries(record)) {
|
|
848
|
+
if (key === "start" || key === "end" || key === "type") continue;
|
|
849
|
+
if (Array.isArray(value)) {
|
|
850
|
+
for (const child of value) if (isAcornNode(child)) walkJavaScriptAst(child, visit, record, key);
|
|
851
|
+
} else if (isAcornNode(value)) {
|
|
852
|
+
walkJavaScriptAst(value, visit, record, key);
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
function isAcornNode(value) {
|
|
857
|
+
return Boolean(value && typeof value === "object" && "type" in value && typeof value.type === "string");
|
|
858
|
+
}
|
|
859
|
+
function jsonReplacer(_k, v) {
|
|
860
|
+
if (v instanceof Map) return Object.fromEntries(v);
|
|
861
|
+
if (v instanceof Set) return Array.from(v);
|
|
862
|
+
return v;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
// src/mcp/execute-limits.ts
|
|
866
|
+
var DEFAULT_EXECUTE_TIMEOUT_MS = 5e3;
|
|
867
|
+
var DEFAULT_EXECUTE_OUTPUT_LIMITS = Object.freeze({
|
|
868
|
+
maxResultBytes: 2 * 1024 * 1024,
|
|
869
|
+
maxLogEntries: 1e3,
|
|
870
|
+
maxLogBytes: 256 * 1024
|
|
871
|
+
});
|
|
872
|
+
var LOGS_TRUNCATED_MARKER = "\u2026[logs truncated: hosted execute caps console output]";
|
|
873
|
+
function normalizeExecuteOutputLimits(value) {
|
|
874
|
+
if (value === void 0) return DEFAULT_EXECUTE_OUTPUT_LIMITS;
|
|
875
|
+
for (const field of ["maxResultBytes", "maxLogEntries", "maxLogBytes"]) {
|
|
876
|
+
if (!Number.isSafeInteger(value[field]) || value[field] <= 0) {
|
|
877
|
+
throw new TypeError(`Code Mode ${field} must be a positive safe integer`);
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
return Object.freeze({ ...value });
|
|
881
|
+
}
|
|
882
|
+
function utf8ByteLength(value) {
|
|
883
|
+
let bytes = 0;
|
|
884
|
+
for (const char of value) {
|
|
885
|
+
const code = char.codePointAt(0);
|
|
886
|
+
bytes += code <= 127 ? 1 : code <= 2047 ? 2 : code <= 65535 ? 3 : 4;
|
|
887
|
+
}
|
|
888
|
+
return bytes;
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
// src/mcp/sandbox.ts
|
|
892
|
+
var MIN_BUN_SDK_TIMEOUT_MS = 1e3;
|
|
893
|
+
var SAFE_GLOBALS = {};
|
|
894
|
+
async function executeInSandbox(code, opts = {}) {
|
|
895
|
+
try {
|
|
896
|
+
return runInSandbox(code, opts);
|
|
897
|
+
} finally {
|
|
898
|
+
await new Promise((resolve2) => setTimeout(resolve2, 0));
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
function runInSandbox(code, opts = {}) {
|
|
902
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_EXECUTE_TIMEOUT_MS;
|
|
903
|
+
const outputLimits = normalizeExecuteOutputLimits(opts.outputLimits);
|
|
904
|
+
const startedAt = performance.now();
|
|
905
|
+
const remainingBudgetMs = () => Math.floor(timeoutMs - (performance.now() - startedAt));
|
|
906
|
+
const trace = [];
|
|
907
|
+
const early = (error) => opts.trace ? { ok: false, error, logs: [], trace } : { ok: false, error, logs: [] };
|
|
908
|
+
const unsupported = unsupportedCodeReason(code);
|
|
909
|
+
if (unsupported) return early(unsupported);
|
|
910
|
+
const sandbox = {
|
|
911
|
+
...SAFE_GLOBALS,
|
|
912
|
+
eval: void 0,
|
|
913
|
+
Function: void 0
|
|
914
|
+
};
|
|
915
|
+
const context = vm.createContext(sandbox, {
|
|
916
|
+
name: "agentic-mermaid-codemode",
|
|
917
|
+
microtaskMode: "afterEvaluate",
|
|
918
|
+
codeGeneration: { strings: false, wasm: false }
|
|
919
|
+
});
|
|
920
|
+
vm.runInContext(`
|
|
921
|
+
(() => {
|
|
922
|
+
const logs = []
|
|
923
|
+
let logBytes = 0
|
|
924
|
+
let logsTruncated = false
|
|
925
|
+
const maxLogEntries = ${outputLimits.maxLogEntries}
|
|
926
|
+
const maxLogBytes = ${outputLimits.maxLogBytes}
|
|
927
|
+
const truncationMarker = ${JSON.stringify(LOGS_TRUNCATED_MARKER)}
|
|
928
|
+
const safeString = String
|
|
929
|
+
const safeStringify = JSON.stringify.bind(JSON)
|
|
930
|
+
const objectToString = Object.prototype.toString
|
|
931
|
+
const safeToStringTag = (v) => objectToString.call(v)
|
|
932
|
+
const safeFromEntries = Object.fromEntries.bind(Object)
|
|
933
|
+
const safeArrayFrom = Array.from.bind(Array)
|
|
934
|
+
const utf8Bytes = (value) => {
|
|
935
|
+
let bytes = 0
|
|
936
|
+
for (const char of value) {
|
|
937
|
+
const code = char.codePointAt(0)
|
|
938
|
+
bytes += code <= 0x7f ? 1 : code <= 0x7ff ? 2 : code <= 0xffff ? 3 : 4
|
|
939
|
+
}
|
|
940
|
+
return bytes
|
|
941
|
+
}
|
|
942
|
+
const truncateUtf8 = (value, maximum) => {
|
|
943
|
+
let bytes = 0
|
|
944
|
+
let output = ''
|
|
945
|
+
for (const char of value) {
|
|
946
|
+
const code = char.codePointAt(0)
|
|
947
|
+
const width = code <= 0x7f ? 1 : code <= 0x7ff ? 2 : code <= 0xffff ? 3 : 4
|
|
948
|
+
if (bytes + width > maximum) break
|
|
949
|
+
output += char
|
|
950
|
+
bytes += width
|
|
951
|
+
}
|
|
952
|
+
return output
|
|
953
|
+
}
|
|
954
|
+
const str = (v) => {
|
|
955
|
+
if (typeof v === 'string') return v
|
|
956
|
+
try {
|
|
957
|
+
const json = safeStringify(v)
|
|
958
|
+
return typeof json === 'string' ? json : safeString(v)
|
|
959
|
+
} catch {
|
|
960
|
+
try { return safeString(v) } catch { return '[unprintable]' }
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
const appendLog = (args) => {
|
|
964
|
+
if (logsTruncated) return
|
|
965
|
+
if (logs.length >= maxLogEntries || logBytes >= maxLogBytes) {
|
|
966
|
+
logsTruncated = true
|
|
967
|
+
logs.push(truncationMarker)
|
|
968
|
+
return
|
|
969
|
+
}
|
|
970
|
+
let line = args.map(str).join(' ')
|
|
971
|
+
const remaining = maxLogBytes - logBytes
|
|
972
|
+
if (utf8Bytes(line) > remaining) {
|
|
973
|
+
line = truncateUtf8(line, remaining)
|
|
974
|
+
logsTruncated = true
|
|
975
|
+
}
|
|
976
|
+
logBytes += utf8Bytes(line)
|
|
977
|
+
logs.push(line)
|
|
978
|
+
if (logsTruncated) logs.push(truncationMarker)
|
|
979
|
+
}
|
|
980
|
+
Object.defineProperty(globalThis, '__pi_read_logs', {
|
|
981
|
+
value: () => safeStringify(logs),
|
|
982
|
+
writable: false,
|
|
983
|
+
configurable: false,
|
|
984
|
+
})
|
|
985
|
+
Object.defineProperty(globalThis, '__pi_result', { value: undefined, writable: true, configurable: false })
|
|
986
|
+
Object.defineProperty(globalThis, '__pi_error', { value: undefined, writable: true, configurable: false })
|
|
987
|
+
Object.defineProperty(globalThis, '__pi_format_error', {
|
|
988
|
+
value: (err) => {
|
|
989
|
+
try {
|
|
990
|
+
if (typeof err === 'string') return err
|
|
991
|
+
if (err && (typeof err === 'object' || typeof err === 'function')) {
|
|
992
|
+
const message = err.message
|
|
993
|
+
if (typeof message === 'string') return message
|
|
994
|
+
}
|
|
995
|
+
return safeString(err)
|
|
996
|
+
} catch { return 'sandbox error' }
|
|
997
|
+
},
|
|
998
|
+
writable: false,
|
|
999
|
+
configurable: false,
|
|
1000
|
+
})
|
|
1001
|
+
Object.defineProperty(globalThis, '__pi_stringify_result', {
|
|
1002
|
+
value: (value) => safeStringify(value, (_k, v) => {
|
|
1003
|
+
const tag = safeToStringTag(v)
|
|
1004
|
+
if (tag === '[object Map]') return safeFromEntries(v)
|
|
1005
|
+
if (tag === '[object Set]') return safeArrayFrom(v)
|
|
1006
|
+
return v
|
|
1007
|
+
}),
|
|
1008
|
+
writable: false,
|
|
1009
|
+
configurable: false,
|
|
1010
|
+
})
|
|
1011
|
+
Object.defineProperty(globalThis, 'console', {
|
|
1012
|
+
value: Object.freeze({
|
|
1013
|
+
log: (...a) => appendLog(a),
|
|
1014
|
+
error: (...a) => appendLog(a),
|
|
1015
|
+
warn: (...a) => appendLog(a),
|
|
1016
|
+
}),
|
|
1017
|
+
writable: false,
|
|
1018
|
+
configurable: false,
|
|
1019
|
+
})
|
|
1020
|
+
try {
|
|
1021
|
+
Object.defineProperty(Error, 'prepareStackTrace', { value: undefined, writable: false, configurable: false })
|
|
1022
|
+
Object.freeze(Error)
|
|
1023
|
+
Object.freeze(Error.prototype)
|
|
1024
|
+
} catch {}
|
|
1025
|
+
try { Object.defineProperty(Array, 'fromAsync', { value: undefined, writable: false, configurable: false }) } catch {}
|
|
1026
|
+
try { Object.freeze(Promise.prototype); Object.freeze(Promise) } catch {}
|
|
1027
|
+
for (const name of [
|
|
1028
|
+
'constructor', '__proto__', 'eval', 'Function', 'Promise',
|
|
1029
|
+
'Atomics', 'SharedArrayBuffer', 'ShadowRealm', 'WebAssembly',
|
|
1030
|
+
'queueMicrotask', 'setTimeout', 'setInterval', 'setImmediate',
|
|
1031
|
+
'fetch', 'process', 'require', 'Bun', 'Worker', 'MessageChannel', 'MessagePort',
|
|
1032
|
+
'AsyncDisposableStack', 'DisposableStack', 'SuppressedError', 'FinalizationRegistry', 'WeakRef',
|
|
1033
|
+
]) {
|
|
1034
|
+
try { Object.defineProperty(globalThis, name, { value: undefined, writable: false, configurable: false }) } catch {}
|
|
1035
|
+
}
|
|
1036
|
+
try { Object.setPrototypeOf(globalThis, null) } catch {}
|
|
1037
|
+
})()
|
|
1038
|
+
`, context);
|
|
1039
|
+
const makeSandboxError = vm.runInContext(`(message) => new Error(String(message))`, context);
|
|
1040
|
+
let sdkClosed = false;
|
|
1041
|
+
let sdkCallOccurred = false;
|
|
1042
|
+
sandbox.mermaid = createTracingMermaid(
|
|
1043
|
+
opts.trace ? trace : void 0,
|
|
1044
|
+
makeSandboxError,
|
|
1045
|
+
() => sdkClosed,
|
|
1046
|
+
void 0,
|
|
1047
|
+
() => {
|
|
1048
|
+
sdkCallOccurred = true;
|
|
1049
|
+
if (typeof Bun === "undefined") return void 0;
|
|
1050
|
+
const remaining = remainingBudgetMs();
|
|
1051
|
+
return remaining < MIN_BUN_SDK_TIMEOUT_MS ? `Code Mode SDK calls under Bun require at least ${MIN_BUN_SDK_TIMEOUT_MS}ms of remaining timeout budget (timeoutMs >= ${MIN_BUN_SDK_TIMEOUT_MS})` : void 0;
|
|
1052
|
+
}
|
|
1053
|
+
);
|
|
1054
|
+
const readLogs = () => {
|
|
1055
|
+
try {
|
|
1056
|
+
const encoded = vm.runInContext(`__pi_read_logs()`, context, { timeout: 50 });
|
|
1057
|
+
if (typeof encoded !== "string") return [];
|
|
1058
|
+
const value = JSON.parse(encoded);
|
|
1059
|
+
return Array.isArray(value) ? value.filter((v) => typeof v === "string") : [];
|
|
1060
|
+
} catch {
|
|
1061
|
+
return [];
|
|
1062
|
+
}
|
|
1063
|
+
};
|
|
1064
|
+
const errorMessage = (err) => {
|
|
1065
|
+
try {
|
|
1066
|
+
sandbox.__pi_error = err;
|
|
1067
|
+
const message = vm.runInContext(`__pi_format_error(__pi_error)`, context, { timeout: 50 });
|
|
1068
|
+
return typeof message === "string" ? message : "sandbox error";
|
|
1069
|
+
} catch (e) {
|
|
1070
|
+
return e instanceof Error ? e.message : "sandbox error";
|
|
1071
|
+
} finally {
|
|
1072
|
+
try {
|
|
1073
|
+
sandbox.__pi_error = void 0;
|
|
1074
|
+
} catch {
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
};
|
|
1078
|
+
const wraps = expressionFirstWraps(code);
|
|
1079
|
+
let result;
|
|
1080
|
+
let runErr = null;
|
|
1081
|
+
for (const wrapped of wraps) {
|
|
1082
|
+
let script;
|
|
1083
|
+
try {
|
|
1084
|
+
script = new vm.Script(wrapped, { filename: "codemode.ts" });
|
|
1085
|
+
} catch (e) {
|
|
1086
|
+
runErr = e;
|
|
1087
|
+
break;
|
|
1088
|
+
}
|
|
1089
|
+
try {
|
|
1090
|
+
const remainingMs = remainingBudgetMs();
|
|
1091
|
+
if (remainingMs <= 0) throw new Error(`Code Mode execution timed out after ${timeoutMs}ms`);
|
|
1092
|
+
result = script.runInContext(context, { timeout: remainingMs });
|
|
1093
|
+
runErr = null;
|
|
1094
|
+
break;
|
|
1095
|
+
} catch (e) {
|
|
1096
|
+
runErr = e;
|
|
1097
|
+
break;
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
const withTrace = (r) => opts.trace ? { ...r, trace } : r;
|
|
1101
|
+
if (runErr) {
|
|
1102
|
+
sdkClosed = true;
|
|
1103
|
+
return withTrace({ ok: false, error: errorMessage(runErr), logs: readLogs() });
|
|
1104
|
+
}
|
|
1105
|
+
if (result === void 0) {
|
|
1106
|
+
sdkClosed = true;
|
|
1107
|
+
return withTrace({ ok: true, value: null, logs: readLogs() });
|
|
1108
|
+
}
|
|
1109
|
+
sdkClosed = true;
|
|
1110
|
+
let marshalled = result;
|
|
1111
|
+
const resultMayContainSdkValue = sdkCallOccurred && result !== null && (typeof result === "object" || typeof result === "function");
|
|
1112
|
+
if (resultMayContainSdkValue) {
|
|
1113
|
+
const remainingMs = remainingBudgetMs();
|
|
1114
|
+
if (remainingMs <= 0 || typeof Bun !== "undefined" && remainingMs < MIN_BUN_SDK_TIMEOUT_MS) {
|
|
1115
|
+
return withTrace({ ok: false, error: `Code Mode execution timed out after ${timeoutMs}ms while committing the result`, logs: readLogs() });
|
|
1116
|
+
}
|
|
1117
|
+
const marshalKey = `__pi_marshal_${Date.now().toString(36)}_${Math.random().toString(36).slice(2)}`;
|
|
1118
|
+
const marshalTarget = () => marshalCodeModeResult(sandbox.mermaid, result);
|
|
1119
|
+
const safeMarshal = new Proxy(marshalTarget, {
|
|
1120
|
+
apply: (target) => Reflect.apply(target, void 0, []),
|
|
1121
|
+
get(target, prop, receiver) {
|
|
1122
|
+
if (prop === "constructor" || prop === "__proto__" || prop === "prototype") return void 0;
|
|
1123
|
+
return Object.prototype.hasOwnProperty.call(target, prop) ? Reflect.get(target, prop, receiver) : void 0;
|
|
1124
|
+
},
|
|
1125
|
+
getPrototypeOf: () => null,
|
|
1126
|
+
has: (target, prop) => Object.prototype.hasOwnProperty.call(target, prop) && prop !== "constructor" && prop !== "__proto__" && prop !== "prototype",
|
|
1127
|
+
set: () => false,
|
|
1128
|
+
defineProperty: () => false,
|
|
1129
|
+
deleteProperty: () => false,
|
|
1130
|
+
setPrototypeOf: () => false
|
|
1131
|
+
});
|
|
1132
|
+
try {
|
|
1133
|
+
if (!Reflect.defineProperty(sandbox, marshalKey, {
|
|
1134
|
+
value: safeMarshal,
|
|
1135
|
+
enumerable: false,
|
|
1136
|
+
writable: false,
|
|
1137
|
+
configurable: true
|
|
1138
|
+
})) throw new Error("could not install the result commit guard");
|
|
1139
|
+
marshalled = vm.runInContext(`globalThis[${JSON.stringify(marshalKey)}]()`, context, { timeout: remainingMs });
|
|
1140
|
+
} catch (e) {
|
|
1141
|
+
const message = errorMessage(e);
|
|
1142
|
+
return withTrace({
|
|
1143
|
+
ok: false,
|
|
1144
|
+
error: /timed out/i.test(message) ? `Code Mode execution timed out after ${timeoutMs}ms while committing the result` : `could not commit Code Mode result: ${message}`,
|
|
1145
|
+
logs: readLogs()
|
|
1146
|
+
});
|
|
1147
|
+
} finally {
|
|
1148
|
+
try {
|
|
1149
|
+
Reflect.deleteProperty(sandbox, marshalKey);
|
|
1150
|
+
} catch {
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
let stringified;
|
|
1155
|
+
try {
|
|
1156
|
+
const stringifyTimeoutMs = remainingBudgetMs();
|
|
1157
|
+
if (stringifyTimeoutMs <= 0) {
|
|
1158
|
+
return withTrace({ ok: false, error: `Code Mode execution timed out after ${timeoutMs}ms while serializing the result`, logs: readLogs() });
|
|
1159
|
+
}
|
|
1160
|
+
sandbox.__pi_result = marshalled;
|
|
1161
|
+
stringified = vm.runInContext("__pi_stringify_result(__pi_result)", context, { timeout: stringifyTimeoutMs });
|
|
1162
|
+
} catch (e) {
|
|
1163
|
+
return withTrace({ ok: false, error: `non-serializable: ${errorMessage(e)} \u2014 ${CODE_MODE_RETURN_HINT}`, logs: readLogs() });
|
|
1164
|
+
} finally {
|
|
1165
|
+
try {
|
|
1166
|
+
sandbox.__pi_result = void 0;
|
|
1167
|
+
} catch {
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
if (stringified === void 0) return withTrace({ ok: true, value: null, logs: readLogs() });
|
|
1171
|
+
if (typeof stringified !== "string") return withTrace({ ok: false, error: "non-serializable: result did not stringify to JSON text", logs: readLogs() });
|
|
1172
|
+
if (utf8ByteLength(stringified) > outputLimits.maxResultBytes) {
|
|
1173
|
+
return withTrace({ ok: false, error: `sandbox result exceeded ${outputLimits.maxResultBytes} bytes; reduce console output or returned data`, logs: readLogs() });
|
|
1174
|
+
}
|
|
1175
|
+
try {
|
|
1176
|
+
return withTrace({ ok: true, value: JSON.parse(stringified), logs: readLogs() });
|
|
1177
|
+
} catch (e) {
|
|
1178
|
+
return withTrace({ ok: false, error: `non-serializable: ${e instanceof Error ? e.message : "invalid JSON"}`, logs: readLogs() });
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
// src/mcp/protocol.ts
|
|
1183
|
+
function preserveExactJsonRpcIds(body) {
|
|
1184
|
+
const replacements = [];
|
|
1185
|
+
const stack = [];
|
|
1186
|
+
let i = 0;
|
|
1187
|
+
const stringEnd = (start) => {
|
|
1188
|
+
let j = start + 1;
|
|
1189
|
+
while (j < body.length) {
|
|
1190
|
+
if (body[j] === "\\") {
|
|
1191
|
+
j += 2;
|
|
1192
|
+
continue;
|
|
1193
|
+
}
|
|
1194
|
+
if (body[j] === '"') return j + 1;
|
|
1195
|
+
j++;
|
|
1196
|
+
}
|
|
1197
|
+
return j;
|
|
1198
|
+
};
|
|
1199
|
+
while (i < body.length) {
|
|
1200
|
+
const ch = body[i];
|
|
1201
|
+
if (ch === '"') {
|
|
1202
|
+
const end = stringEnd(i);
|
|
1203
|
+
let key;
|
|
1204
|
+
try {
|
|
1205
|
+
key = JSON.parse(body.slice(i, end));
|
|
1206
|
+
} catch {
|
|
1207
|
+
i = end;
|
|
1208
|
+
continue;
|
|
1209
|
+
}
|
|
1210
|
+
const rpcObject = stack.length === 1 && stack[0] === "{" || stack.length === 2 && stack[0] === "[" && stack[1] === "{";
|
|
1211
|
+
let cursor = end;
|
|
1212
|
+
while (/\s/.test(body[cursor] ?? "")) cursor++;
|
|
1213
|
+
if (rpcObject && key === "id" && body[cursor] === ":") {
|
|
1214
|
+
cursor++;
|
|
1215
|
+
while (/\s/.test(body[cursor] ?? "")) cursor++;
|
|
1216
|
+
const number = body.slice(cursor).match(/^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?/)?.[0];
|
|
1217
|
+
if (number && !isSafelyRoundTrippableInteger(number)) {
|
|
1218
|
+
let sentinel = `__agentic_mermaid_exact_id_${replacements.length}__`;
|
|
1219
|
+
while (body.includes(sentinel)) sentinel += "_";
|
|
1220
|
+
replacements.push({ sentinel, raw: number, start: cursor, end: cursor + number.length });
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
i = end;
|
|
1224
|
+
continue;
|
|
1225
|
+
}
|
|
1226
|
+
if (ch === "{" || ch === "[") stack.push(ch);
|
|
1227
|
+
else if (ch === "}" || ch === "]") stack.pop();
|
|
1228
|
+
i++;
|
|
1229
|
+
}
|
|
1230
|
+
let protectedBody = body;
|
|
1231
|
+
for (const replacement of replacements.slice().reverse()) {
|
|
1232
|
+
protectedBody = protectedBody.slice(0, replacement.start) + JSON.stringify(replacement.sentinel) + protectedBody.slice(replacement.end);
|
|
1233
|
+
}
|
|
1234
|
+
return { body: protectedBody, ids: replacements.map(({ sentinel, raw }) => ({ sentinel, raw })) };
|
|
1235
|
+
}
|
|
1236
|
+
function isSafelyRoundTrippableInteger(token) {
|
|
1237
|
+
if (!/^-?(?:0|[1-9]\d*)$/.test(token)) return false;
|
|
1238
|
+
if (token === "-0") return false;
|
|
1239
|
+
try {
|
|
1240
|
+
const value = BigInt(token);
|
|
1241
|
+
return value <= BigInt(Number.MAX_SAFE_INTEGER) && value >= BigInt(Number.MIN_SAFE_INTEGER);
|
|
1242
|
+
} catch {
|
|
1243
|
+
return false;
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
function isJsonContentType(value) {
|
|
1247
|
+
if (!value) return false;
|
|
1248
|
+
const token = String.raw`[!#$%&'*+.^_\x60|~0-9A-Za-z-]+`;
|
|
1249
|
+
const quoted = String.raw`"(?:[^"\\]|\\.)*"`;
|
|
1250
|
+
return new RegExp(String.raw`^\s*application/json\s*(?:;\s*${token}\s*=\s*(?:${token}|${quoted}))*\s*$`, "i").test(value);
|
|
1251
|
+
}
|
|
1252
|
+
function stringifyJsonRpc(payload, exactIds = []) {
|
|
1253
|
+
let body = JSON.stringify(payload);
|
|
1254
|
+
for (const { sentinel, raw } of exactIds) {
|
|
1255
|
+
body = body.replace(`"id":${JSON.stringify(sentinel)}`, `"id":${raw}`);
|
|
1256
|
+
}
|
|
1257
|
+
return body;
|
|
1258
|
+
}
|
|
1259
|
+
function reply(id, result) {
|
|
1260
|
+
return { jsonrpc: "2.0", id, result };
|
|
1261
|
+
}
|
|
1262
|
+
function rpcError(id, code, message) {
|
|
1263
|
+
return { jsonrpc: "2.0", id, error: { code, message } };
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
// src/mcp/tool-surface.ts
|
|
1267
|
+
function withClosedMcpInputSchema(tool) {
|
|
1268
|
+
return { ...tool, inputSchema: { ...tool.inputSchema, additionalProperties: false } };
|
|
1269
|
+
}
|
|
1270
|
+
var MCP_SERVER_NAME = "agentic-mermaid-mcp";
|
|
1271
|
+
var MCP_SERVER_VERSION = PACKAGE_VERSION;
|
|
1272
|
+
var PURE_COMPUTE_ANNOTATIONS = {
|
|
1273
|
+
readOnlyHint: true,
|
|
1274
|
+
destructiveHint: false,
|
|
1275
|
+
idempotentHint: true,
|
|
1276
|
+
openWorldHint: false
|
|
1277
|
+
};
|
|
1278
|
+
var SANDBOX_EXECUTE_ANNOTATIONS = {
|
|
1279
|
+
readOnlyHint: true,
|
|
1280
|
+
destructiveHint: false,
|
|
1281
|
+
idempotentHint: false,
|
|
1282
|
+
openWorldHint: false
|
|
1283
|
+
};
|
|
1284
|
+
var MANAGED_ARTIFACT_ANNOTATIONS = {
|
|
1285
|
+
// output=file/url creates a managed file and repeated calls can create
|
|
1286
|
+
// different time-addressed artifacts, so the tool as a whole is neither
|
|
1287
|
+
// read-only nor idempotent even though output=base64 is pure.
|
|
1288
|
+
readOnlyHint: false,
|
|
1289
|
+
destructiveHint: false,
|
|
1290
|
+
idempotentHint: false,
|
|
1291
|
+
openWorldHint: false
|
|
1292
|
+
};
|
|
1293
|
+
var EXECUTE_TIMEOUT_ERROR = "execute timeoutMs must be a positive integer";
|
|
1294
|
+
function isValidExecuteTimeout(value) {
|
|
1295
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value > 0;
|
|
1296
|
+
}
|
|
1297
|
+
function mcpRenderOptionSchemaProperties(optionsDescription) {
|
|
1298
|
+
return {
|
|
1299
|
+
options: { ...sharedRenderOptionsJsonSchema(), description: optionsDescription }
|
|
1300
|
+
};
|
|
1301
|
+
}
|
|
1302
|
+
function projectMcpRenderOptions(args) {
|
|
1303
|
+
const nested = args.options === void 0 ? {} : args.options;
|
|
1304
|
+
const nestedProblems = validateSerializableRenderOptions(nested);
|
|
1305
|
+
if (nestedProblems.length > 0) throw new Error(`invalid render options: ${nestedProblems.join("; ")}`);
|
|
1306
|
+
const projected = { ...nested };
|
|
1307
|
+
const problems = validateSerializableRenderOptions(projected);
|
|
1308
|
+
if (problems.length > 0) throw new Error(`invalid render options: ${problems.join("; ")}`);
|
|
1309
|
+
return projected;
|
|
1310
|
+
}
|
|
1311
|
+
var FORBIDDEN_JSON_KEYS = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
|
|
1312
|
+
function plainJsonObject(value) {
|
|
1313
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
1314
|
+
const prototype = Object.getPrototypeOf(value);
|
|
1315
|
+
return prototype === Object.prototype || prototype === null;
|
|
1316
|
+
}
|
|
1317
|
+
function schemaRecord(value) {
|
|
1318
|
+
return plainJsonObject(value) ? value : void 0;
|
|
1319
|
+
}
|
|
1320
|
+
function dereferenceSchema(schema, root) {
|
|
1321
|
+
const reference = schema.$ref;
|
|
1322
|
+
if (typeof reference !== "string" || !reference.startsWith("#/")) return void 0;
|
|
1323
|
+
let cursor = root;
|
|
1324
|
+
for (const encoded of reference.slice(2).split("/")) {
|
|
1325
|
+
if (!plainJsonObject(cursor)) return void 0;
|
|
1326
|
+
cursor = cursor[encoded.replaceAll("~1", "/").replaceAll("~0", "~")];
|
|
1327
|
+
}
|
|
1328
|
+
return schemaRecord(cursor);
|
|
1329
|
+
}
|
|
1330
|
+
function validateSchemaValue(value, schema, inheritedRoot, path, ancestors) {
|
|
1331
|
+
const root = schemaRecord(schema.$defs) ? schema : inheritedRoot;
|
|
1332
|
+
if (schema.$ref !== void 0) {
|
|
1333
|
+
const resolved = dereferenceSchema(schema, root);
|
|
1334
|
+
return resolved ? validateSchemaValue(value, resolved, root, path, ancestors) : [{ path, message: `uses unresolved schema reference ${String(schema.$ref)}` }];
|
|
1335
|
+
}
|
|
1336
|
+
const anyOf = Array.isArray(schema.anyOf) ? schema.anyOf.map(schemaRecord).filter((entry) => entry !== void 0) : [];
|
|
1337
|
+
if (anyOf.length > 0) {
|
|
1338
|
+
const alternatives = anyOf.map((candidate) => validateSchemaValue(value, candidate, root, path, new Set(ancestors)));
|
|
1339
|
+
if (!alternatives.some((problems) => problems.length === 0)) {
|
|
1340
|
+
const expectation = schema["x-agentic-mermaid-validation-expectation"];
|
|
1341
|
+
if (typeof expectation === "string") return [{ path, message: `must be ${expectation}` }];
|
|
1342
|
+
return alternatives.sort((left, right) => left.length - right.length)[0] ?? [{ path, message: "must match an allowed shape" }];
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
const oneOf = Array.isArray(schema.oneOf) ? schema.oneOf.map(schemaRecord).filter((entry) => entry !== void 0) : [];
|
|
1346
|
+
if (oneOf.length > 0) {
|
|
1347
|
+
const matching = oneOf.filter((candidate) => validateSchemaValue(value, candidate, root, path, new Set(ancestors)).length === 0);
|
|
1348
|
+
if (matching.length !== 1) {
|
|
1349
|
+
return [{ path, message: `must match exactly one allowed shape (matched ${matching.length})` }];
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
if (Object.prototype.hasOwnProperty.call(schema, "const") && !Object.is(value, schema.const)) {
|
|
1353
|
+
return [{ path, message: `must equal ${String(schema.const)}` }];
|
|
1354
|
+
}
|
|
1355
|
+
if (Array.isArray(schema.enum) && !schema.enum.some((candidate) => Object.is(candidate, value))) {
|
|
1356
|
+
return [{ path, message: `must be one of ${schema.enum.map(String).join(" | ")}` }];
|
|
1357
|
+
}
|
|
1358
|
+
const type = schema.type;
|
|
1359
|
+
const typeIsValid = type === void 0 || type === "null" && value === null || type === "string" && typeof value === "string" || type === "number" && typeof value === "number" && Number.isFinite(value) || type === "integer" && typeof value === "number" && Number.isFinite(value) && Number.isInteger(value) || type === "boolean" && typeof value === "boolean" || type === "array" && Array.isArray(value) || type === "object" && plainJsonObject(value);
|
|
1360
|
+
if (!typeIsValid) {
|
|
1361
|
+
const expected = type === "number" ? "a finite number" : type === "integer" ? "a finite integer" : type === "object" ? "a plain JSON object" : type === "array" ? "an array" : type === "null" ? "null" : `a ${String(type)}`;
|
|
1362
|
+
return [{ path, message: `must be ${expected}` }];
|
|
1363
|
+
}
|
|
1364
|
+
if (typeof value === "number") {
|
|
1365
|
+
if (typeof schema.minimum === "number" && value < schema.minimum) return [{ path, message: `must be at least ${schema.minimum}` }];
|
|
1366
|
+
if (typeof schema.exclusiveMinimum === "number" && value <= schema.exclusiveMinimum) return [{ path, message: `must be greater than ${schema.exclusiveMinimum}` }];
|
|
1367
|
+
if (typeof schema.maximum === "number" && value > schema.maximum) return [{ path, message: `must be at most ${schema.maximum}` }];
|
|
1368
|
+
}
|
|
1369
|
+
if (typeof value === "string") {
|
|
1370
|
+
if (typeof schema.minLength === "number" && value.length < schema.minLength) {
|
|
1371
|
+
return [{ path, message: `must contain at least ${schema.minLength} character${schema.minLength === 1 ? "" : "s"}` }];
|
|
1372
|
+
}
|
|
1373
|
+
if (typeof schema.pattern === "string") {
|
|
1374
|
+
try {
|
|
1375
|
+
if (!new RegExp(schema.pattern, "u").test(value)) {
|
|
1376
|
+
if (schema["x-agentic-mermaid-runtime-validator"] === "portablePngBackground") {
|
|
1377
|
+
return [{ path, message: "must be a portable basic color keyword or 3, 4, 6, or 8 digit hex color" }];
|
|
1378
|
+
}
|
|
1379
|
+
return [{ path, message: `must match ${schema.pattern}` }];
|
|
1380
|
+
}
|
|
1381
|
+
} catch {
|
|
1382
|
+
return [{ path, message: "uses an invalid schema pattern" }];
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
1385
|
+
if (typeof schema.maxLength === "number" && value.length > schema.maxLength) {
|
|
1386
|
+
return [{ path, message: `must contain at most ${schema.maxLength} characters` }];
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
if (Array.isArray(value) && type === "array") {
|
|
1390
|
+
if (typeof schema.minItems === "number" && value.length < schema.minItems) {
|
|
1391
|
+
return [{ path, message: `must contain at least ${schema.minItems} item${schema.minItems === 1 ? "" : "s"}` }];
|
|
1392
|
+
}
|
|
1393
|
+
if (typeof schema.maxItems === "number" && value.length > schema.maxItems) {
|
|
1394
|
+
return [{ path, message: `must contain at most ${schema.maxItems} items` }];
|
|
1395
|
+
}
|
|
1396
|
+
if (ancestors.has(value)) return [{ path, message: "must be acyclic" }];
|
|
1397
|
+
const itemSchema = schemaRecord(schema.items);
|
|
1398
|
+
if (itemSchema) {
|
|
1399
|
+
ancestors.add(value);
|
|
1400
|
+
try {
|
|
1401
|
+
const problems = value.flatMap((item, index) => validateSchemaValue(item, itemSchema, root, [...path, index], ancestors));
|
|
1402
|
+
if (problems.length > 0) return problems;
|
|
1403
|
+
} finally {
|
|
1404
|
+
ancestors.delete(value);
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
if (plainJsonObject(value) && (type === "object" || schema.properties !== void 0 || schema.required !== void 0 || schema.additionalProperties !== void 0)) {
|
|
1409
|
+
if (ancestors.has(value)) return [{ path, message: "must be acyclic" }];
|
|
1410
|
+
ancestors.add(value);
|
|
1411
|
+
try {
|
|
1412
|
+
const properties = schemaRecord(schema.properties) ?? {};
|
|
1413
|
+
const required = Array.isArray(schema.required) ? schema.required.filter((entry) => typeof entry === "string") : [];
|
|
1414
|
+
const problems = [];
|
|
1415
|
+
for (const key of required) {
|
|
1416
|
+
if (!Object.prototype.hasOwnProperty.call(value, key)) {
|
|
1417
|
+
problems.push({ path: [...path, key], message: "is required" });
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
for (const [key, child] of Object.entries(value)) {
|
|
1421
|
+
if (FORBIDDEN_JSON_KEYS.has(key)) {
|
|
1422
|
+
problems.push({ path: [...path, key], message: "uses a forbidden prototype key" });
|
|
1423
|
+
continue;
|
|
1424
|
+
}
|
|
1425
|
+
const propertySchema = schemaRecord(properties[key]);
|
|
1426
|
+
if (propertySchema) {
|
|
1427
|
+
problems.push(...validateSchemaValue(child, propertySchema, root, [...path, key], ancestors));
|
|
1428
|
+
continue;
|
|
1429
|
+
}
|
|
1430
|
+
if (schema.additionalProperties === false) {
|
|
1431
|
+
problems.push({ path: [...path, key], message: "is not allowed" });
|
|
1432
|
+
continue;
|
|
1433
|
+
}
|
|
1434
|
+
const additionalSchema = schemaRecord(schema.additionalProperties);
|
|
1435
|
+
if (additionalSchema) {
|
|
1436
|
+
problems.push(...validateSchemaValue(child, additionalSchema, root, [...path, key], ancestors));
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
if (problems.length > 0) return problems;
|
|
1440
|
+
} finally {
|
|
1441
|
+
ancestors.delete(value);
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
const runtimeValidator = schema["x-agentic-mermaid-runtime-validator"];
|
|
1445
|
+
if (runtimeValidator === "safeCssColor" && safeCssColor(value) === void 0) {
|
|
1446
|
+
return [{ path, message: "must be a safe, non-fetching CSS color" }];
|
|
1447
|
+
}
|
|
1448
|
+
if (runtimeValidator === "portablePngBackground" && normalizePortablePngBackground(value) === void 0) {
|
|
1449
|
+
return [{ path, message: "must be a portable basic color keyword or 3, 4, 6, or 8 digit hex color" }];
|
|
1450
|
+
}
|
|
1451
|
+
if (runtimeValidator === "safeCssPaint" && safeCssPaint(value) === void 0) {
|
|
1452
|
+
return [{ path, message: "must be a safe, non-fetching CSS paint" }];
|
|
1453
|
+
}
|
|
1454
|
+
if (runtimeValidator === "safeCssFontFamily" && safeCssFontFamily(value) === void 0) {
|
|
1455
|
+
return [{ path, message: "must be a safe, non-fetching CSS font family or stack" }];
|
|
1456
|
+
}
|
|
1457
|
+
if (runtimeValidator === "styleInput") {
|
|
1458
|
+
try {
|
|
1459
|
+
resolveStyleStack(value);
|
|
1460
|
+
} catch (error) {
|
|
1461
|
+
return [{ path, message: `is invalid: ${error instanceof Error ? error.message : String(error)}` }];
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
return [];
|
|
1465
|
+
}
|
|
1466
|
+
function formatSchemaPath(path) {
|
|
1467
|
+
if (path.length === 0) return "arguments";
|
|
1468
|
+
return path.reduce((formatted, part) => typeof part === "number" ? `${formatted}[${part}]` : `${formatted}.${part}`, "arguments");
|
|
1469
|
+
}
|
|
1470
|
+
function validateMcpToolArguments(tool, value) {
|
|
1471
|
+
if (!plainJsonObject(value)) return ["arguments must be a plain JSON object"];
|
|
1472
|
+
const admissionProblems = validateJsonConfigAdmission(value);
|
|
1473
|
+
if (admissionProblems.length > 0) {
|
|
1474
|
+
return limitJsonConfigDiagnostics(admissionProblems.map((problem) => `${formatSchemaPath(problem.path)} ${problem.message}`), "arguments");
|
|
1475
|
+
}
|
|
1476
|
+
const problems = validateSchemaValue(value, tool.inputSchema, tool.inputSchema, [], /* @__PURE__ */ new Set()).map((problem) => `${formatSchemaPath(problem.path)} ${problem.message}`);
|
|
1477
|
+
return limitJsonConfigDiagnostics(problems, "arguments");
|
|
1478
|
+
}
|
|
1479
|
+
async function dispatchMcpRequest(req, context, surface) {
|
|
1480
|
+
const raw = req;
|
|
1481
|
+
const hasId = Boolean(raw && Object.prototype.hasOwnProperty.call(raw, "id"));
|
|
1482
|
+
const rawId = raw?.id;
|
|
1483
|
+
const validId = rawId === void 0 || rawId === null || typeof rawId === "string" || typeof rawId === "number" && Number.isFinite(rawId);
|
|
1484
|
+
const valid = Boolean(raw && raw.jsonrpc === "2.0" && typeof raw.method === "string" && validId);
|
|
1485
|
+
const id = validId && rawId !== void 0 ? rawId : null;
|
|
1486
|
+
if (!valid) return rpcError(null, -32600, "invalid JSON-RPC request");
|
|
1487
|
+
const notification = !hasId;
|
|
1488
|
+
let response;
|
|
1489
|
+
switch (req.method) {
|
|
1490
|
+
case "initialize": {
|
|
1491
|
+
const protocolVersion = typeof surface.protocolVersion === "function" ? surface.protocolVersion(req.params) : surface.protocolVersion;
|
|
1492
|
+
response = reply(id, {
|
|
1493
|
+
protocolVersion,
|
|
1494
|
+
serverInfo: { name: surface.serverName ?? MCP_SERVER_NAME, version: MCP_SERVER_VERSION },
|
|
1495
|
+
capabilities: { tools: {} },
|
|
1496
|
+
instructions: surface.instructions
|
|
1497
|
+
});
|
|
1498
|
+
break;
|
|
1499
|
+
}
|
|
1500
|
+
case "notifications/initialized":
|
|
1501
|
+
response = null;
|
|
1502
|
+
break;
|
|
1503
|
+
case "ping":
|
|
1504
|
+
response = reply(id, {});
|
|
1505
|
+
break;
|
|
1506
|
+
case "tools/list":
|
|
1507
|
+
response = reply(id, { tools: surface.tools });
|
|
1508
|
+
break;
|
|
1509
|
+
case "tools/call": {
|
|
1510
|
+
if (!plainJsonObject(req.params)) {
|
|
1511
|
+
response = rpcError(id, -32602, "Invalid params: tools/call requires an object");
|
|
1512
|
+
break;
|
|
1513
|
+
}
|
|
1514
|
+
const name = req.params.name;
|
|
1515
|
+
if (typeof name !== "string") {
|
|
1516
|
+
response = rpcError(id, -32602, "Invalid params: tools/call requires `name` (string)");
|
|
1517
|
+
break;
|
|
1518
|
+
}
|
|
1519
|
+
const tool = surface.tools.find((candidate) => candidate.name === name);
|
|
1520
|
+
if (!tool) {
|
|
1521
|
+
response = rpcError(id, -32602, `Unknown tool: ${name}`);
|
|
1522
|
+
break;
|
|
1523
|
+
}
|
|
1524
|
+
const args = req.params.arguments ?? {};
|
|
1525
|
+
const problems = validateMcpToolArguments(tool, args);
|
|
1526
|
+
if (problems.length > 0) {
|
|
1527
|
+
response = rpcError(id, -32602, `Invalid arguments for ${name}: ${problems.join("; ")}`);
|
|
1528
|
+
break;
|
|
1529
|
+
}
|
|
1530
|
+
response = await surface.handleToolCall(id, { ...req.params, arguments: args }, context);
|
|
1531
|
+
break;
|
|
1532
|
+
}
|
|
1533
|
+
case "prompts/list":
|
|
1534
|
+
response = reply(id, { prompts: [] });
|
|
1535
|
+
break;
|
|
1536
|
+
case "resources/list":
|
|
1537
|
+
response = reply(id, { resources: [] });
|
|
1538
|
+
break;
|
|
1539
|
+
default:
|
|
1540
|
+
response = rpcError(id, -32601, `Method not found: ${req.method}`);
|
|
1541
|
+
}
|
|
1542
|
+
return notification ? null : response;
|
|
1543
|
+
}
|
|
1544
|
+
function createExecuteTool(options) {
|
|
1545
|
+
const hostedNote = options.hosted ? `Hosted note: execute runs in an on-demand isolate and costs more than the direct
|
|
1546
|
+
render_svg/render_ascii/render_png/verify/describe tools \u2014 prefer those for plain
|
|
1547
|
+
render/verify calls. For straightforward structured edits, prefer the declarative
|
|
1548
|
+
mutate/build tools; reserve execute for logic the ops don't express.
|
|
1549
|
+
Hosted mermaid.renderMermaidSVG*, renderMermaidASCII*, and
|
|
1550
|
+
layoutMermaidWithReceipt calls force security:'strict' and
|
|
1551
|
+
embedFontImport:false; caller code cannot weaken that host policy.
|
|
1552
|
+
|
|
1553
|
+
` : "";
|
|
1554
|
+
const timeoutDescription = options.hosted ? "Optional CPU-time budget (default 5000ms, max 30000ms)." : "Optional hard timeout (default 5000ms).";
|
|
1555
|
+
const runtime = options.hosted ? "an isolated sandbox" : "a sandboxed node:vm context";
|
|
1556
|
+
return {
|
|
1557
|
+
name: "execute",
|
|
1558
|
+
description: `Run JavaScript in ${runtime}; return a value.
|
|
1559
|
+
One call composes edits. Submit JavaScript; declaration types are guidance.
|
|
1560
|
+
No promises, async/await, dynamic import, or type annotations.
|
|
1561
|
+
${hostedNote}SDK declaration:
|
|
1562
|
+
${options.sdkDeclaration}`,
|
|
1563
|
+
inputSchema: {
|
|
1564
|
+
type: "object",
|
|
1565
|
+
additionalProperties: false,
|
|
1566
|
+
properties: {
|
|
1567
|
+
code: { type: "string", description: "JavaScript to execute; mermaid.* SDK is global." },
|
|
1568
|
+
timeoutMs: { type: "integer", minimum: 1, description: timeoutDescription }
|
|
1569
|
+
},
|
|
1570
|
+
required: ["code"]
|
|
1571
|
+
},
|
|
1572
|
+
annotations: SANDBOX_EXECUTE_ANNOTATIONS
|
|
1573
|
+
};
|
|
1574
|
+
}
|
|
1575
|
+
function createRenderPngTool(mode) {
|
|
1576
|
+
const hosted = mode === "hosted";
|
|
1577
|
+
const pngSchema = pngOutputOptionsJsonSchema(hosted ? "portable" : "native");
|
|
1578
|
+
const pngProperties = pngSchema.properties;
|
|
1579
|
+
return {
|
|
1580
|
+
name: "render_png",
|
|
1581
|
+
description: hosted ? `Rasterize a Mermaid source string to PNG. Returns { ok, png_base64 }.
|
|
1582
|
+
Hosted rendering uses resvg-wasm with bundled fonts; bytes may differ from the
|
|
1583
|
+
local napi renderer, so hosted PNG is a convenience surface, not part of the
|
|
1584
|
+
byte-determinism contract. For file/URL artifacts use the local stdio server.` : `Rasterize a Mermaid source string to PNG. By default returns base64-encoded PNG bytes.
|
|
1585
|
+
Set output to "file" or "url" to write a managed artifact instead; artifact responses include
|
|
1586
|
+
{path?, url?, mimeType, bytes, sha256}. File/URL artifacts are generated under the MCP server's
|
|
1587
|
+
artifact directory with safe names, size limits, and TTL cleanup.
|
|
1588
|
+
Uses bundled resvg + Inter (DejaVu Sans fallback) for same-machine cross-runtime determinism where verified.
|
|
1589
|
+
Agentic Mermaid outputs SVG, PNG, ASCII, Unicode, and JSON layout. For non-PNG output, use execute() with mermaid.renderMermaidSVG, mermaid.renderMermaidASCII (useAscii true for ASCII, false for Unicode), or verifyMermaid(...).layout \u2014 those are streaming text/data and don't need a dedicated tool.`,
|
|
1590
|
+
inputSchema: {
|
|
1591
|
+
type: "object",
|
|
1592
|
+
additionalProperties: false,
|
|
1593
|
+
properties: {
|
|
1594
|
+
source: { type: "string", description: "Mermaid source." },
|
|
1595
|
+
...pngProperties,
|
|
1596
|
+
...mcpRenderOptionSchemaProperties("Shared advanced RenderOptions object."),
|
|
1597
|
+
...hosted ? {} : {
|
|
1598
|
+
output: { type: "string", enum: ["base64", "file", "url"], description: "PNG return mode (default base64)." }
|
|
1599
|
+
}
|
|
1600
|
+
},
|
|
1601
|
+
required: ["source"]
|
|
1602
|
+
},
|
|
1603
|
+
annotations: hosted ? PURE_COMPUTE_ANNOTATIONS : MANAGED_ARTIFACT_ANNOTATIONS
|
|
1604
|
+
};
|
|
1605
|
+
}
|
|
1606
|
+
function createDescribeTool() {
|
|
1607
|
+
return {
|
|
1608
|
+
name: "describe",
|
|
1609
|
+
description: `Describe a Mermaid diagram. format=text returns { ok, text } with
|
|
1610
|
+
one or two summary sentences; format=json returns { ok, tree } with the AX tree;
|
|
1611
|
+
format=facts returns { ok, facts } with deterministic semantic fact lines for
|
|
1612
|
+
machine checking (for example edge A -> B : label, member Duck +quack()).`,
|
|
1613
|
+
inputSchema: {
|
|
1614
|
+
type: "object",
|
|
1615
|
+
additionalProperties: false,
|
|
1616
|
+
properties: {
|
|
1617
|
+
source: { type: "string", description: "Mermaid source." },
|
|
1618
|
+
format: { type: "string", enum: ["text", "json", "facts"], description: "text (default), json AX tree, or facts semantic read-back." }
|
|
1619
|
+
},
|
|
1620
|
+
required: ["source"]
|
|
1621
|
+
},
|
|
1622
|
+
annotations: PURE_COMPUTE_ANNOTATIONS
|
|
1623
|
+
};
|
|
1624
|
+
}
|
|
1625
|
+
|
|
1626
|
+
// src/mcp/sdk-decl.ts
|
|
1627
|
+
function sdkFamilyTypeStem(family) {
|
|
1628
|
+
return family.narrower.slice(2);
|
|
1629
|
+
}
|
|
1630
|
+
function sdkFamilyBodyType(family) {
|
|
1631
|
+
return family.id === "flowchart" ? `{ kind: 'flowchart'; graph: FlowchartGraph }` : `${sdkFamilyTypeStem(family)}Body`;
|
|
1632
|
+
}
|
|
1633
|
+
var SDK_DIAGRAM_KIND_DECLARATION = `type DiagramKind = ${BUILTIN_FAMILY_METADATA.map((family) => `'${family.id}'`).join(" | ")}`;
|
|
1634
|
+
var SDK_DIAGRAM_BODY_MEMBERS = BUILTIN_FAMILY_METADATA.map((family) => ` | ${sdkFamilyBodyType(family)}`).join("\n");
|
|
1635
|
+
var SDK_VALID_DIAGRAM_ALIASES = BUILTIN_FAMILY_METADATA.map((family) => `type ${sdkFamilyTypeStem(family)}ValidDiagram = ValidDiagram & { body: ${sdkFamilyBodyType(family)} }`).join("\n");
|
|
1636
|
+
var SDK_NARROWER_METHOD_DECLARATIONS = BUILTIN_FAMILY_METADATA.map((family) => ` ${family.narrower}(d: ValidDiagram): ${sdkFamilyTypeStem(family)}ValidDiagram | null`).join("\n");
|
|
1637
|
+
var SDK_MUTATE_METHOD_DECLARATIONS = BUILTIN_FAMILY_METADATA.map((family) => {
|
|
1638
|
+
const stem = sdkFamilyTypeStem(family);
|
|
1639
|
+
return ` mutate(d: ${stem}ValidDiagram, op: ${stem}MutationOp): Result<${stem}ValidDiagram, { code: string; message: string }>`;
|
|
1640
|
+
}).join("\n");
|
|
1641
|
+
var SDK_ANY_MUTATION_OP_DECLARATION = `type AnyMutationOp = ${BUILTIN_FAMILY_METADATA.map((family) => `${sdkFamilyTypeStem(family)}MutationOp`).join(" | ")}`;
|
|
1642
|
+
var SDK_FAMILY_CONVENTION = `// 3. mutate works on ${BUILTIN_FAMILY_METADATA.map((family) => family.id).join(", ")}. Narrow via
|
|
1643
|
+
// ${BUILTIN_FAMILY_METADATA.map((family) => family.narrower).join("/")}.`;
|
|
1644
|
+
function codeModeRenderMethodDeclarations() {
|
|
1645
|
+
const methods = /* @__PURE__ */ new Map();
|
|
1646
|
+
for (const descriptor of RENDER_OUTPUT_DESCRIPTORS) {
|
|
1647
|
+
const transport = descriptor.transports.codeMode;
|
|
1648
|
+
if (transport.availability !== "direct" && transport.availability !== "projected") continue;
|
|
1649
|
+
const output = `${descriptor.id}: ${transport.selector ?? transport.availability}`;
|
|
1650
|
+
const existing = methods.get(transport.method);
|
|
1651
|
+
if (existing) {
|
|
1652
|
+
if (existing.optionsType !== transport.optionsType || existing.returnType !== transport.returnType) {
|
|
1653
|
+
throw new Error(`Code Mode render contract for ${transport.method} has incompatible signatures`);
|
|
1654
|
+
}
|
|
1655
|
+
existing.outputs.push(output);
|
|
1656
|
+
} else {
|
|
1657
|
+
methods.set(transport.method, {
|
|
1658
|
+
optionsType: transport.optionsType,
|
|
1659
|
+
returnType: transport.returnType,
|
|
1660
|
+
outputs: [output]
|
|
1661
|
+
});
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
return [...methods].map(
|
|
1665
|
+
([method, declaration]) => ` // ${declaration.outputs.join("; ")}
|
|
1666
|
+
${method}(input: ParsedDiagram | string, opts?: ${declaration.optionsType}): ${declaration.returnType}`
|
|
1667
|
+
).join("\n");
|
|
1668
|
+
}
|
|
1669
|
+
var CODE_MODE_SHARED_RENDER_OPTIONS_DECLARATION = sharedRenderOptionsTypeScriptDeclaration();
|
|
1670
|
+
var CODE_MODE_OUTPUT_RENDER_OPTION_DECLARATIONS = `interface ConfigDiagnostic { code: 'INEFFECTIVE_CONFIG'; field: string; message: string }
|
|
1671
|
+
interface TerminalProjectionDiagnostic { code: string; feature: string; message: string }
|
|
1672
|
+
|
|
1673
|
+
interface SvgRenderOptions extends SharedRenderOptions {
|
|
1674
|
+
onConfigDiagnostic?: (diagnostic: ConfigDiagnostic) => void
|
|
1675
|
+
}
|
|
1676
|
+
|
|
1677
|
+
interface AsciiRenderOptions extends SharedRenderOptions {
|
|
1678
|
+
useAscii?: boolean
|
|
1679
|
+
paddingX?: number
|
|
1680
|
+
paddingY?: number
|
|
1681
|
+
boxBorderPadding?: number
|
|
1682
|
+
colorMode?: 'auto' | 'none' | 'ansi16' | 'ansi256' | 'truecolor' | 'html'
|
|
1683
|
+
theme?: { fg?: string; border?: string; line?: string; arrow?: string; accent?: string; bg?: string; corner?: string; junction?: string }
|
|
1684
|
+
maxWidth?: number
|
|
1685
|
+
targetWidth?: number
|
|
1686
|
+
onConfigDiagnostic?: (diagnostic: ConfigDiagnostic) => void
|
|
1687
|
+
onProjectionDiagnostic?: (diagnostic: TerminalProjectionDiagnostic) => void
|
|
1688
|
+
}
|
|
1689
|
+
|
|
1690
|
+
interface LayoutRenderOptions extends SharedRenderOptions {
|
|
1691
|
+
debug?: boolean
|
|
1692
|
+
regions?: boolean
|
|
1693
|
+
actions?: boolean
|
|
1694
|
+
onConfigDiagnostic?: (diagnostic: ConfigDiagnostic) => void
|
|
1695
|
+
}`;
|
|
1696
|
+
var CODE_MODE_RENDER_RECEIPT_DECLARATIONS = `interface RenderArtifactDiagnostic { code: string; message?: string; reference?: string; feature?: string; input?: string; canonicalId?: string; removal?: { release: string; date: string } }
|
|
1697
|
+
interface CapabilityResolution { readonly id: \`\${string}:\${string}\`; readonly range: string; readonly level: 'required' | 'preferred' | 'optional'; readonly status: 'selected' | 'unsupported' | 'incompatible'; readonly version?: string }
|
|
1698
|
+
interface CapabilityDecision { readonly version: 1; readonly accepted: boolean; readonly resolutions: readonly CapabilityResolution[] }
|
|
1699
|
+
interface RenderExecutionDecision {
|
|
1700
|
+
readonly family: { readonly id: string; readonly version: string }
|
|
1701
|
+
readonly backend: { readonly mode: 'scene'; readonly requestedId: string; readonly selectedId: string; readonly version: string; readonly hostPolicy: boolean } | { readonly mode: 'family-svg' }
|
|
1702
|
+
readonly digest: string
|
|
1703
|
+
}
|
|
1704
|
+
interface RenderRequestReceipt { version: ${RENDER_CONTRACT_VERSION}; output: 'svg' | 'png' | 'ascii' | 'unicode' | 'html' | 'layout'; sharedRequestDigest: string; requestDigest: string; appearanceDigest: string; capabilityDecision: CapabilityDecision; diagnostics?: readonly RenderArtifactDiagnostic[]; graphicalProjectionDigest?: string; executionDecision?: RenderExecutionDecision }`;
|
|
1705
|
+
function compactInterfaceDeclarations(declaration) {
|
|
1706
|
+
let compact = "";
|
|
1707
|
+
for (const sourceLine of declaration.split("\n")) {
|
|
1708
|
+
const line = sourceLine.trim();
|
|
1709
|
+
if (!line) continue;
|
|
1710
|
+
if (line.startsWith("interface ") && line.endsWith("}")) {
|
|
1711
|
+
compact += `${line}
|
|
1712
|
+
`;
|
|
1713
|
+
} else if (line.endsWith("{")) {
|
|
1714
|
+
compact += `${line} `;
|
|
1715
|
+
} else if (line === "}") {
|
|
1716
|
+
compact += `}
|
|
1717
|
+
`;
|
|
1718
|
+
} else {
|
|
1719
|
+
compact += `${line.replaceAll("?: ", "?:")};`;
|
|
1720
|
+
}
|
|
1721
|
+
}
|
|
1722
|
+
return compact.trimEnd();
|
|
1723
|
+
}
|
|
1724
|
+
var CODE_MODE_CORE_SHARED_RENDER_OPTIONS_DECLARATION = compactInterfaceDeclarations(
|
|
1725
|
+
CODE_MODE_SHARED_RENDER_OPTIONS_DECLARATION
|
|
1726
|
+
);
|
|
1727
|
+
var CODE_MODE_CORE_OUTPUT_RENDER_OPTION_DECLARATIONS = compactInterfaceDeclarations(
|
|
1728
|
+
CODE_MODE_OUTPUT_RENDER_OPTION_DECLARATIONS
|
|
1729
|
+
);
|
|
1730
|
+
var CODE_MODE_CORE_RENDER_RECEIPT_DECLARATIONS = compactInterfaceDeclarations(
|
|
1731
|
+
CODE_MODE_RENDER_RECEIPT_DECLARATIONS
|
|
1732
|
+
);
|
|
1733
|
+
var CODE_MODE_RENDER_OPTION_DECLARATIONS = `${styleSpecTypeScriptDeclaration()}
|
|
1734
|
+
|
|
1735
|
+
${architectureVisualOverridesTypeScriptDeclaration()}
|
|
1736
|
+
|
|
1737
|
+
${CODE_MODE_SHARED_RENDER_OPTIONS_DECLARATION}
|
|
1738
|
+
|
|
1739
|
+
${CODE_MODE_OUTPUT_RENDER_OPTION_DECLARATIONS}
|
|
1740
|
+
|
|
1741
|
+
${CODE_MODE_RENDER_RECEIPT_DECLARATIONS}
|
|
1742
|
+
|
|
1743
|
+
interface TerminalConnectorProjection {
|
|
1744
|
+
id: string
|
|
1745
|
+
role: string
|
|
1746
|
+
realization: 'native' | 'emulated' | 'projected' | 'lossy' | 'unsupported'
|
|
1747
|
+
topology: 'line' | 'polyline' | 'path'
|
|
1748
|
+
direction: 'forward' | 'reverse' | 'bidirectional' | 'undirected' | 'self'
|
|
1749
|
+
relationship: string
|
|
1750
|
+
markers: { start?: { id: string; shape: string }; mid: readonly { id: string; shape: string }[]; end?: { id: string; shape: string } }
|
|
1751
|
+
labels: readonly { id?: string; text: string }[]
|
|
1752
|
+
lineStyle: 'solid' | 'dotted' | 'dashed' | 'thick' | 'invisible'
|
|
1753
|
+
strokeLosses: readonly string[]
|
|
1754
|
+
diagnostics: readonly string[]
|
|
1755
|
+
}
|
|
1756
|
+
interface ResolvedTerminalStyle {
|
|
1757
|
+
version: 1
|
|
1758
|
+
colorMode: 'none' | 'ansi16' | 'ansi256' | 'truecolor' | 'html'
|
|
1759
|
+
theme: Readonly<{ fg: string; border: string; line: string; arrow: string; accent?: string; bg?: string; corner?: string; junction?: string }>
|
|
1760
|
+
diagnostics: readonly TerminalProjectionDiagnostic[]
|
|
1761
|
+
connectorProjection: { evidence: 'scene' | 'unavailable' | 'not-evaluated'; count: number; topologies: Readonly<Record<'line' | 'polyline' | 'path', number>>; realizations: Readonly<Record<string, number>>; relationships: readonly string[]; directions: readonly string[]; markerPositions: Readonly<{ start: number; mid: number; end: number }>; labelCount: number; connectors: readonly TerminalConnectorProjection[]; digest: string }
|
|
1762
|
+
digest: string
|
|
1763
|
+
}
|
|
1764
|
+
interface ResolvedTerminalOutputPolicy {
|
|
1765
|
+
version: 1
|
|
1766
|
+
useAscii: boolean
|
|
1767
|
+
paddingX: number
|
|
1768
|
+
paddingY: number
|
|
1769
|
+
boxBorderPadding: number
|
|
1770
|
+
colorMode: 'none' | 'ansi16' | 'ansi256' | 'truecolor' | 'html'
|
|
1771
|
+
theme: Readonly<{ fg?: string; border?: string; line?: string; arrow?: string; accent?: string; bg?: string; corner?: string; junction?: string }>
|
|
1772
|
+
maxWidth?: number
|
|
1773
|
+
targetWidth?: number
|
|
1774
|
+
}
|
|
1775
|
+
interface RenderedSvg { svg: string; receipt: RenderRequestReceipt }
|
|
1776
|
+
interface RenderedAscii { text: string; receipt: RenderRequestReceipt; terminalStyle: ResolvedTerminalStyle; outputPolicy: ResolvedTerminalOutputPolicy }
|
|
1777
|
+
interface RenderedLayoutArtifact { layout: VerifyResult['layout']; receipt: RenderRequestReceipt }`;
|
|
1778
|
+
var CODE_MODE_CORE_STYLE_SPEC_DECLARATION = styleSpecTypeScriptDeclaration({ compact: true }).replaceAll(" | ", "|").replaceAll(", ", ",");
|
|
1779
|
+
var CODE_MODE_CORE_RENDER_OPTION_DECLARATIONS = `${CODE_MODE_CORE_STYLE_SPEC_DECLARATION}
|
|
1780
|
+
type ArchitectureVisualOverrides = Readonly<Record<string, unknown>>
|
|
1781
|
+
|
|
1782
|
+
${CODE_MODE_CORE_SHARED_RENDER_OPTIONS_DECLARATION}
|
|
1783
|
+
|
|
1784
|
+
${CODE_MODE_CORE_OUTPUT_RENDER_OPTION_DECLARATIONS}
|
|
1785
|
+
|
|
1786
|
+
${CODE_MODE_CORE_RENDER_RECEIPT_DECLARATIONS}
|
|
1787
|
+
|
|
1788
|
+
interface RenderedSvg { svg: string; receipt: RenderRequestReceipt }
|
|
1789
|
+
interface RenderedAscii { text: string; receipt: RenderRequestReceipt; terminalStyle: Record<string, unknown>; outputPolicy: Record<string, unknown> }
|
|
1790
|
+
interface RenderedLayoutArtifact { layout: VerifyResult['layout']; receipt: RenderRequestReceipt }`;
|
|
1791
|
+
var CODE_MODE_RENDER_METHOD_DECLARATIONS = codeModeRenderMethodDeclarations();
|
|
1792
|
+
var SDK_DECLARATION = `// Mermaid agent SDK available as the global \`mermaid\`. All calls are
|
|
1793
|
+
// synchronous and pure. Compose multi-step edits in one execute() call.
|
|
1794
|
+
// Code Mode is synchronous: async/await, Promise jobs, and dynamic import are not supported.
|
|
1795
|
+
|
|
1796
|
+
type Result<T, E> = { ok: true; value: T } | { ok: false; error: E }
|
|
1797
|
+
type CheckMermaidSpec = string[] | {
|
|
1798
|
+
include?: string[]; expected?: string[]; required?: string[]; require?: string[]
|
|
1799
|
+
exclude?: string[]; absent?: string[]; forbidden?: string[]; forbid?: string[]; unexpected?: string[]
|
|
1800
|
+
exact?: boolean
|
|
1801
|
+
}
|
|
1802
|
+
interface CheckMermaidResult { ok: boolean; missing: string[]; unexpected: string[]; facts: string[] }
|
|
1803
|
+
|
|
1804
|
+
type MermaidConfigScalar = string | number | boolean | null
|
|
1805
|
+
type MermaidConfigValue = MermaidConfigScalar | MermaidConfigValue[] | { [key: string]: MermaidConfigValue | undefined }
|
|
1806
|
+
type MermaidRuntimeConfig = {
|
|
1807
|
+
[key: string]: MermaidConfigValue | undefined
|
|
1808
|
+
theme?: string
|
|
1809
|
+
fontFamily?: string
|
|
1810
|
+
themeVariables?: { [key: string]: MermaidConfigValue | undefined; fontFamily?: string }
|
|
1811
|
+
timeline?: { [key: string]: MermaidConfigValue | undefined; disableMulticolor?: boolean; sectionFills?: string[]; sectionColours?: string[] }
|
|
1812
|
+
journey?: { [key: string]: MermaidConfigValue | undefined; diagramMarginX?: number; diagramMarginY?: number; leftMargin?: number; maxLabelWidth?: number; taskMargin?: number; actorColours?: string[]; sectionFills?: string[]; sectionColours?: string[]; useMaxWidth?: boolean }
|
|
1813
|
+
flowchart?: { [key: string]: MermaidConfigValue | undefined; nodeSpacing?: number; rankSpacing?: number; wrappingWidth?: number }
|
|
1814
|
+
// State geometry consumes the faithful ELK/measured-text equivalents;
|
|
1815
|
+
// legacy Dagre calibration keys remain typed and emit INEFFECTIVE_CONFIG.
|
|
1816
|
+
state?: { [key: string]: MermaidConfigValue | undefined; titleTopMargin?: number; arrowMarkerAbsolute?: boolean; dividerMargin?: number; sizeUnit?: number; padding?: number; textHeight?: number; titleShift?: number; noteMargin?: number; nodeSpacing?: number; rankSpacing?: number; forkWidth?: number; forkHeight?: number; miniPadding?: number; fontSizeFactor?: number; fontSize?: number; labelHeight?: number; edgeLengthFactor?: string; compositTitleSize?: number; radius?: number; defaultRenderer?: 'dagre-d3' | 'dagre-wrapper' | 'elk' }
|
|
1817
|
+
class?: { [key: string]: MermaidConfigValue | undefined; nodeSpacing?: number; rankSpacing?: number }
|
|
1818
|
+
er?: { [key: string]: MermaidConfigValue | undefined; layoutDirection?: string; nodeSpacing?: number; rankSpacing?: number }
|
|
1819
|
+
architecture?: { [key: string]: MermaidConfigValue | undefined; padding?: number; iconSize?: number; fontSize?: number; nodeSeparation?: number; idealEdgeLengthMultiplier?: number }
|
|
1820
|
+
xyChart?: { [key: string]: MermaidConfigValue | undefined }
|
|
1821
|
+
pie?: { [key: string]: MermaidConfigValue | undefined; textPosition?: number; donutHole?: number; legendPosition?: 'top' | 'bottom' | 'left' | 'right' | 'center' }
|
|
1822
|
+
quadrantChart?: { [key: string]: MermaidConfigValue | undefined; chartWidth?: number; chartHeight?: number; pointRadius?: number; useMaxWidth?: boolean }
|
|
1823
|
+
gantt?: { [key: string]: MermaidConfigValue | undefined; displayMode?: string }
|
|
1824
|
+
mindmap?: { [key: string]: MermaidConfigValue | undefined; padding?: number; maxNodeWidth?: number }
|
|
1825
|
+
gitGraph?: { [key: string]: MermaidConfigValue | undefined; showBranches?: boolean; showCommitLabel?: boolean; mainBranchName?: string; mainBranchOrder?: number; parallelCommits?: boolean; rotateCommitLabel?: boolean }
|
|
1826
|
+
radar?: { [key: string]: MermaidConfigValue | undefined; width?: number; height?: number; marginTop?: number; marginRight?: number; marginBottom?: number; marginLeft?: number; axisScaleFactor?: number; axisLabelFactor?: number; curveTension?: number; useMaxWidth?: boolean; tickLabels?: boolean }
|
|
1827
|
+
// Wired sequence keys (unlisted documented keys are accepted and named by
|
|
1828
|
+
// verify's INEFFECTIVE_CONFIG lint \u2014 see src/sequence/config.ts).
|
|
1829
|
+
sequence?: { [key: string]: MermaidConfigValue | undefined; actorMargin?: number; width?: number; height?: number; diagramMarginX?: number; diagramMarginY?: number; messageMargin?: number; noteMargin?: number; activationWidth?: number; showSequenceNumbers?: boolean }
|
|
1830
|
+
useMaxWidth?: boolean
|
|
1831
|
+
useWidth?: number
|
|
1832
|
+
/** Recognized for upstream accounting but public renderers reject raw CSS; use StyleSpec. */
|
|
1833
|
+
themeCSS?: string
|
|
1834
|
+
}
|
|
1835
|
+
|
|
1836
|
+
${CODE_MODE_RENDER_OPTION_DECLARATIONS}
|
|
1837
|
+
|
|
1838
|
+
${SDK_DIAGRAM_KIND_DECLARATION}
|
|
1839
|
+
type ExternalFamilyId = \`family:\${string}\`
|
|
1840
|
+
|
|
1841
|
+
interface ExtensionCompatibility {
|
|
1842
|
+
readonly [contract: string]: string | undefined
|
|
1843
|
+
readonly core?: string
|
|
1844
|
+
readonly scene?: string
|
|
1845
|
+
}
|
|
1846
|
+
|
|
1847
|
+
interface ExtensionProvenance {
|
|
1848
|
+
readonly owner: string
|
|
1849
|
+
readonly source: string
|
|
1850
|
+
readonly reference?: string
|
|
1851
|
+
}
|
|
1852
|
+
|
|
1853
|
+
interface ExtensionIdentity<Kind extends string = string> {
|
|
1854
|
+
readonly id: \`\${Kind}:\${string}\`
|
|
1855
|
+
readonly kind: Kind
|
|
1856
|
+
readonly version: string
|
|
1857
|
+
readonly compatibility: ExtensionCompatibility
|
|
1858
|
+
readonly provenance: ExtensionProvenance
|
|
1859
|
+
}
|
|
1860
|
+
|
|
1861
|
+
interface SourceSpanPoint {
|
|
1862
|
+
/** UTF-16 offset in the exact authored source; line and col are one-based. */
|
|
1863
|
+
readonly offset: number
|
|
1864
|
+
readonly line: number
|
|
1865
|
+
readonly col: number
|
|
1866
|
+
}
|
|
1867
|
+
|
|
1868
|
+
interface SourceSpan {
|
|
1869
|
+
readonly start: SourceSpanPoint
|
|
1870
|
+
readonly end: SourceSpanPoint
|
|
1871
|
+
}
|
|
1872
|
+
|
|
1873
|
+
interface PreservedSourceSpans {
|
|
1874
|
+
readonly source: SourceSpan
|
|
1875
|
+
readonly wrapper?: SourceSpan
|
|
1876
|
+
readonly frontmatter?: SourceSpan
|
|
1877
|
+
readonly initDirectives?: readonly SourceSpan[]
|
|
1878
|
+
readonly accessibilityDirectives?: readonly SourceSpan[]
|
|
1879
|
+
readonly header: SourceSpan
|
|
1880
|
+
readonly body: SourceSpan
|
|
1881
|
+
}
|
|
1882
|
+
|
|
1883
|
+
interface SourceLocation { readonly line: number; readonly col: number }
|
|
1884
|
+
interface SourceMapSpans {
|
|
1885
|
+
readonly preserved: PreservedSourceSpans
|
|
1886
|
+
readonly nodes: ReadonlyMap<string, SourceSpan>
|
|
1887
|
+
readonly edges: ReadonlyMap<string, SourceSpan>
|
|
1888
|
+
readonly groups: ReadonlyMap<string, SourceSpan>
|
|
1889
|
+
readonly labels: ReadonlyMap<string, SourceSpan>
|
|
1890
|
+
}
|
|
1891
|
+
interface SourceMap {
|
|
1892
|
+
readonly nodes: ReadonlyMap<string, SourceLocation>
|
|
1893
|
+
readonly edges: ReadonlyMap<string, SourceLocation>
|
|
1894
|
+
readonly groups: ReadonlyMap<string, SourceLocation>
|
|
1895
|
+
readonly labels: ReadonlyMap<string, SourceLocation>
|
|
1896
|
+
readonly spans?: SourceMapSpans
|
|
1897
|
+
}
|
|
1898
|
+
|
|
1899
|
+
interface SourcePreservationReceipt {
|
|
1900
|
+
readonly version: 1
|
|
1901
|
+
readonly classification: 'unsupported' | 'inventory-only' | 'unknown'
|
|
1902
|
+
/** Exact authored bytes supplied to the parser. */
|
|
1903
|
+
readonly source: string
|
|
1904
|
+
readonly header: string
|
|
1905
|
+
readonly upstreamFamilyId?: string
|
|
1906
|
+
readonly mermaidVersion: string
|
|
1907
|
+
readonly spans?: PreservedSourceSpans
|
|
1908
|
+
}
|
|
1909
|
+
|
|
1910
|
+
interface ParseError {
|
|
1911
|
+
readonly code: string
|
|
1912
|
+
readonly message: string
|
|
1913
|
+
readonly line?: number
|
|
1914
|
+
readonly col?: number
|
|
1915
|
+
readonly preservation?: SourcePreservationReceipt
|
|
1916
|
+
readonly help?: string
|
|
1917
|
+
}
|
|
1918
|
+
|
|
1919
|
+
interface ValidDiagram {
|
|
1920
|
+
readonly kind: DiagramKind
|
|
1921
|
+
readonly source: SourceMap
|
|
1922
|
+
readonly meta: {
|
|
1923
|
+
frontmatter?: Record<string, unknown>
|
|
1924
|
+
initDirectives: { raw: string; parsed: Record<string, unknown> }[]
|
|
1925
|
+
comments: { text: string; line: number }[]
|
|
1926
|
+
wrapperSource?: string
|
|
1927
|
+
droppedComments?: { text: string; line: number }[]
|
|
1928
|
+
accessibility: { title?: string; descr?: string }
|
|
1929
|
+
}
|
|
1930
|
+
readonly body:
|
|
1931
|
+
${SDK_DIAGRAM_BODY_MEMBERS}
|
|
1932
|
+
| { kind: 'opaque'; family: DiagramKind; source: string }
|
|
1933
|
+
readonly canonicalSource: string // normalized renderer input; opaque fidelity uses body.source
|
|
1934
|
+
}
|
|
1935
|
+
|
|
1936
|
+
interface ExtensionDiagramBody {
|
|
1937
|
+
readonly kind: 'extension'
|
|
1938
|
+
readonly family: ExternalFamilyId
|
|
1939
|
+
readonly source: string
|
|
1940
|
+
readonly data?: unknown
|
|
1941
|
+
}
|
|
1942
|
+
|
|
1943
|
+
interface ExtensionValidDiagram {
|
|
1944
|
+
readonly kind: ExternalFamilyId
|
|
1945
|
+
readonly descriptorIdentity: ExtensionIdentity<'family'>
|
|
1946
|
+
readonly meta: ValidDiagram['meta']
|
|
1947
|
+
readonly body: ExtensionDiagramBody
|
|
1948
|
+
readonly source: SourceMap
|
|
1949
|
+
readonly canonicalSource: string
|
|
1950
|
+
}
|
|
1951
|
+
|
|
1952
|
+
interface PreservedValidDiagram {
|
|
1953
|
+
readonly kind: ExternalFamilyId
|
|
1954
|
+
readonly meta: ValidDiagram['meta']
|
|
1955
|
+
readonly body: {
|
|
1956
|
+
readonly kind: 'preserved'
|
|
1957
|
+
readonly representation: 'opaque' | 'unknown'
|
|
1958
|
+
readonly source: string
|
|
1959
|
+
readonly preservation: SourcePreservationReceipt
|
|
1960
|
+
readonly spans: PreservedSourceSpans
|
|
1961
|
+
readonly diagnostic: {
|
|
1962
|
+
readonly code: 'UNSUPPORTED_FAMILY' | 'UNKNOWN_HEADER' | 'FAMILY_DESCRIPTOR_MISMATCH'
|
|
1963
|
+
readonly message: string
|
|
1964
|
+
readonly help: string
|
|
1965
|
+
}
|
|
1966
|
+
}
|
|
1967
|
+
readonly source: SourceMap
|
|
1968
|
+
readonly canonicalSource: string
|
|
1969
|
+
}
|
|
1970
|
+
|
|
1971
|
+
type ParsedDiagram = ValidDiagram | ExtensionValidDiagram | PreservedValidDiagram
|
|
1972
|
+
|
|
1973
|
+
${SDK_VALID_DIAGRAM_ALIASES}
|
|
1974
|
+
|
|
1975
|
+
interface FlowchartGraph {
|
|
1976
|
+
direction: 'TD' | 'TB' | 'LR' | 'BT' | 'RL'
|
|
1977
|
+
// semanticShape/authoredShape carry Mermaid v11 @{ shape } metadata: shape
|
|
1978
|
+
// stays the drawn geometry, semanticShape the canonical v11 id, and
|
|
1979
|
+
// authoredShape the exact spelling that serializes back.
|
|
1980
|
+
nodes: Map<string, { id: string; label: string; shape: string; semanticShape?: string; authoredShape?: string; markdownLabel?: true; icon?: string; image?: string; iconForm?: 'square' | 'circle' | 'rounded'; href?: string }>
|
|
1981
|
+
edges: {
|
|
1982
|
+
// id = authored v11.6 edge ID ('e1@-->'): round-trips verbatim and is a
|
|
1983
|
+
// valid remove_edge/set_label target selector.
|
|
1984
|
+
id?: string
|
|
1985
|
+
source: string; target: string; label?: string; style: string
|
|
1986
|
+
hasArrowStart?: boolean; hasArrowEnd?: boolean; startMarker?: string; endMarker?: string
|
|
1987
|
+
curve?: string; animate?: boolean; animation?: 'fast' | 'slow'; length?: number
|
|
1988
|
+
}[]
|
|
1989
|
+
subgraphs: { id: string; label: string; nodeIds: string[]; children: FlowchartGraph['subgraphs']; direction?: FlowchartGraph['direction']; concurrencyRegion?: true }[]
|
|
1990
|
+
classDefs: Map<string, Record<string, string>>
|
|
1991
|
+
classAssignments: Map<string, string>
|
|
1992
|
+
nodeStyles: Map<string, Record<string, string>>
|
|
1993
|
+
linkStyles: Map<number | 'default', Record<string, string>>
|
|
1994
|
+
}
|
|
1995
|
+
|
|
1996
|
+
interface StateNode { id: string; label?: string; declaredBare?: true; stereotype?: 'fork' | 'join' | 'choice' | 'history' | 'deep-history'; states?: StateNode[]; transitions?: StateTransition[]; regions?: StateRegion[]; className?: string; style?: Record<string, string>; direction?: 'TD' | 'TB' | 'LR' | 'BT' | 'RL' }
|
|
1997
|
+
interface StateRegion { states: StateNode[]; transitions: StateTransition[]; direction?: 'TD' | 'TB' | 'LR' | 'BT' | 'RL' }
|
|
1998
|
+
interface StateTransition { from: string; to: string; label?: string; style?: Record<string, string> } // from/to may be '[*]' or a history ref ('[H]', 'X[H*]')
|
|
1999
|
+
interface StateNote { target: string; side: 'left' | 'right'; text: string }
|
|
2000
|
+
interface StateBody { kind: 'state'; states: StateNode[]; transitions: StateTransition[]; notes?: StateNote[]; direction?: 'TD' | 'TB' | 'LR' | 'BT' | 'RL'; classDefs?: Record<string, Record<string, string>>; defaultTransitionStyle?: Record<string, string> }
|
|
2001
|
+
|
|
2002
|
+
type SeqParticipantKind = 'participant' | 'actor' | 'boundary' | 'control' | 'entity' | 'database' | 'collections' | 'queue'
|
|
2003
|
+
interface SeqParticipant { id: string; label: string; kind: SeqParticipantKind; declaration?: 'participant' | 'actor'; links?: Record<string, string> }
|
|
2004
|
+
interface SeqMessage { from: string; to: string; text: string; style: string; arrow?: string; centralStart?: boolean; centralEnd?: boolean; activate?: boolean; deactivate?: boolean }
|
|
2005
|
+
type SeqFragmentKind = 'alt' | 'opt' | 'loop' | 'par'
|
|
2006
|
+
interface SeqFragmentBranch { label?: string; messages: SeqMessage[] }
|
|
2007
|
+
interface SeqFragment { fragmentKind: SeqFragmentKind; label?: string; branches: SeqFragmentBranch[]; rawLines?: string[] }
|
|
2008
|
+
// BUILD-18: ordered statement list. participant/message refs index into the
|
|
2009
|
+
// participants/messages arrays; direct-message common fragments are typed;
|
|
2010
|
+
// opaque-block carries all remaining unmodeled lines verbatim.
|
|
2011
|
+
type SequenceStatement =
|
|
2012
|
+
| { kind: 'participant'; ref: number }
|
|
2013
|
+
| { kind: 'message'; ref: number }
|
|
2014
|
+
| { kind: 'actor-links'; actorId: string; links: Record<string, string> }
|
|
2015
|
+
| { kind: 'fragment'; fragment: SeqFragment }
|
|
2016
|
+
| { kind: 'opaque-block'; lines: string[] }
|
|
2017
|
+
interface SequenceBody { kind: 'sequence'; participants: SeqParticipant[]; messages: SeqMessage[]; statements: SequenceStatement[] }
|
|
2018
|
+
|
|
2019
|
+
interface TimelineEvent { id: string; text: string }
|
|
2020
|
+
interface TimelinePeriod { id: string; label: string; events: TimelineEvent[] }
|
|
2021
|
+
interface TimelineSection { id: string; label?: string; periods: TimelinePeriod[] }
|
|
2022
|
+
// direction: explicit \`timeline TD\`/\`timeline LR\` header token (TD = vertical, upstream PR #7270); undefined = LR default.
|
|
2023
|
+
interface TimelineBody { kind: 'timeline'; direction?: 'LR' | 'TD'; title?: string; accessibilityTitle?: string; accessibilityDescription?: string; sections: TimelineSection[] }
|
|
2024
|
+
|
|
2025
|
+
interface ClassNode { id: string; generic?: string; label?: string; members: string[]; namespace?: string; className?: string; style?: Record<string, string>; href?: string }
|
|
2026
|
+
type ClassRelationKind = 'inheritance' | 'composition' | 'aggregation' | 'association' | 'dependency' | 'realization' | 'link-solid' | 'link-dashed' | 'lollipop'
|
|
2027
|
+
interface ClassRelation { from: string; to: string; kind: ClassRelationKind; label?: string; fromCardinality?: string; toCardinality?: string; markerAt?: 'from' | 'to' | 'both'; fromKind?: ClassRelationKind; toKind?: ClassRelationKind }
|
|
2028
|
+
interface ClassNote { text: string; for?: string }
|
|
2029
|
+
// namespace paths are dot-joined (e.g. 'Platform.Auth'); namespaces render as
|
|
2030
|
+
// compound boxes and serialize to "namespace path { ... }" blocks.
|
|
2031
|
+
interface ClassNamespaceDecl { name: string; label?: string }
|
|
2032
|
+
interface ClassBody { kind: 'class'; title?: string; classes: ClassNode[]; relations: ClassRelation[]; notes: ClassNote[]; namespaces?: ClassNamespaceDecl[]; classDefs?: Record<string, Record<string, string>> }
|
|
2033
|
+
|
|
2034
|
+
type ErCardinality = 'one-only' | 'zero-or-one' | 'zero-or-many' | 'one-or-many'
|
|
2035
|
+
interface ErAttribute { text: string }
|
|
2036
|
+
interface ErEntity { id: string; label?: string; attributes: ErAttribute[]; className?: string; style?: Record<string, string>; groupId?: string }
|
|
2037
|
+
interface ErGroup { id: string; label: string; parentId?: string; direction?: 'TD' | 'TB' | 'LR' | 'BT' | 'RL' }
|
|
2038
|
+
interface ErRelation { from: string; to: string; leftCard: ErCardinality; rightCard: ErCardinality; dashed: boolean; label?: string }
|
|
2039
|
+
type ErStatement = { kind: 'entity'; id: string } | { kind: 'relation'; ref: number } | { kind: 'direction'; groupId?: string } | { kind: 'group-open'; id: string } | { kind: 'group-close'; id: string } | { kind: 'opaque'; lines: string[] }
|
|
2040
|
+
interface ErBody { kind: 'er'; entities: ErEntity[]; relations: ErRelation[]; groups?: ErGroup[]; direction?: 'TD' | 'TB' | 'LR' | 'BT' | 'RL'; classDefs?: Record<string, Record<string, string>>; statements?: ErStatement[] }
|
|
2041
|
+
|
|
2042
|
+
interface JourneyTask { id: string; text: string; score: number; actors: string[] }
|
|
2043
|
+
interface JourneySection { id: string; label?: string; tasks: JourneyTask[] }
|
|
2044
|
+
interface JourneyBody { kind: 'journey'; title?: string; accessibilityTitle?: string; accessibilityDescription?: string; sections: JourneySection[] }
|
|
2045
|
+
|
|
2046
|
+
type ArchitectureSide = 'L' | 'R' | 'T' | 'B'
|
|
2047
|
+
type ArchitectureEndpointBoundary = 'item' | 'group'
|
|
2048
|
+
interface ArchitectureGroup { id: string; label: string; icon?: string; parentId?: string }
|
|
2049
|
+
interface ArchitectureService { id: string; label: string; icon?: string; parentId?: string }
|
|
2050
|
+
interface ArchitectureJunction { id: string; parentId?: string }
|
|
2051
|
+
interface ArchitectureEndpoint { id: string; side: ArchitectureSide; boundary?: ArchitectureEndpointBoundary }
|
|
2052
|
+
interface ArchitectureEdge { source: ArchitectureEndpoint; target: ArchitectureEndpoint; label?: string; hasArrowStart: boolean; hasArrowEnd: boolean }
|
|
2053
|
+
// alignments: upstream v11.16.0 "align row|column" directives, preserved
|
|
2054
|
+
// losslessly and honored as deterministic center-coordinate constraints.
|
|
2055
|
+
interface ArchitectureAlignment { axis: 'row' | 'column'; members: string[] }
|
|
2056
|
+
interface ArchitectureBody { kind: 'architecture'; title?: string; accessibilityTitle?: string; accessibilityDescription?: string; groups: ArchitectureGroup[]; services: ArchitectureService[]; junctions: ArchitectureJunction[]; edges: ArchitectureEdge[]; alignments?: ArchitectureAlignment[] }
|
|
2057
|
+
|
|
2058
|
+
interface XyChartAxis { name?: string; categories?: string[]; range?: { min: number; max: number } }
|
|
2059
|
+
interface XyChartSeries { id: string; kind: 'bar' | 'line'; name?: string; values: number[]; pointLabels?: Array<string | undefined> }
|
|
2060
|
+
interface XyChartBody { kind: 'xychart'; accessibilityTitle?: string; accessibilityDescription?: string; title?: string; horizontal?: boolean; xAxis?: XyChartAxis; yAxis?: XyChartAxis; series: XyChartSeries[] }
|
|
2061
|
+
|
|
2062
|
+
interface PieSlice { id: string; label: string; value: number } // value > 0
|
|
2063
|
+
interface PieBody { kind: 'pie'; accessibilityTitle?: string; accessibilityDescription?: string; title?: string; showData: boolean; slices: PieSlice[] }
|
|
2064
|
+
|
|
2065
|
+
interface QuadrantAxis { near: string; far?: string }
|
|
2066
|
+
// Upstream per-point styling (direct \`radius:/color:/stroke-color:/stroke-width:\`
|
|
2067
|
+
// tails, \`classDef\` tables, \`:::class\` assignments) is structured content:
|
|
2068
|
+
// preserved by every op and serialized canonically. strokeWidth may carry px.
|
|
2069
|
+
interface QuadrantPointStyle { radius?: number; color?: string; strokeColor?: string; strokeWidth?: string; extra?: string[] }
|
|
2070
|
+
interface QuadrantPoint { label: string; x: number; y: number; className?: string; style?: QuadrantPointStyle } // x,y in [0,1]
|
|
2071
|
+
// quadrants indexed 0-based; index n-1 holds Mermaid quadrant-n
|
|
2072
|
+
// (1=top-right, 2=top-left, 3=bottom-left, 4=bottom-right)
|
|
2073
|
+
interface QuadrantBody { kind: 'quadrant'; accessibilityTitle?: string; accessibilityDescription?: string; title?: string; xAxis?: QuadrantAxis; yAxis?: QuadrantAxis; quadrants: [string?, string?, string?, string?]; points: QuadrantPoint[]; classDefs?: Record<string, QuadrantPointStyle> }
|
|
2074
|
+
|
|
2075
|
+
// Radar (spider) chart: axes = spokes; curves = one value per axis (axis order).
|
|
2076
|
+
interface RadarBodyAxis { id: string; label: string }
|
|
2077
|
+
interface RadarBodyCurve { id: string; label: string; values: number[] }
|
|
2078
|
+
interface RadarBody { kind: 'radar'; title?: string; axes: RadarBodyAxis[]; curves: RadarBodyCurve[]; min: number; max?: number; ticks: number; graticule: 'circle' | 'polygon'; showLegend: boolean }
|
|
2079
|
+
|
|
2080
|
+
type GanttTaskTag = 'active' | 'done' | 'crit' | 'milestone' | 'vert'
|
|
2081
|
+
// start: a date in the diagram's dateFormat or 'after id\u2026'; undefined = previous task's end.
|
|
2082
|
+
// end: a date, a duration token ('3d', '2w'), or 'until id\u2026'.
|
|
2083
|
+
interface GanttTask { id: string; taskId?: string; label: string; tags: GanttTaskTag[]; start?: string; end: string }
|
|
2084
|
+
interface GanttSection { id: string; label?: string; tasks: GanttTask[] }
|
|
2085
|
+
// Segment-preserving body: calendar directives (dateFormat, excludes, weekend\u2026),
|
|
2086
|
+
// click lines, and comments ride along VERBATIM as opaque-block segments \u2014 they
|
|
2087
|
+
// are preserved, not typed-editable. Tasks inside opaque segments are invisible
|
|
2088
|
+
// to mutation ops.
|
|
2089
|
+
type GanttStatement =
|
|
2090
|
+
| { kind: 'title' }
|
|
2091
|
+
| { kind: 'section'; ref: number }
|
|
2092
|
+
| { kind: 'task'; section: number; ref: number }
|
|
2093
|
+
| { kind: 'opaque-block'; lines: string[] }
|
|
2094
|
+
interface GanttBody { kind: 'gantt'; title?: string; sections: GanttSection[]; statements?: GanttStatement[] }
|
|
2095
|
+
|
|
2096
|
+
type MindmapShape = 'default' | 'rect' | 'rounded' | 'circle' | 'cloud' | 'bang' | 'hexagon'
|
|
2097
|
+
interface MindmapNode { id: string; label: string; shape: MindmapShape; markdown?: true; icon?: string; className?: string; children: MindmapNode[] }
|
|
2098
|
+
interface MindmapBody { kind: 'mindmap'; root: MindmapNode; accessibilityTitle?: string; accessibilityDescription?: string }
|
|
2099
|
+
|
|
2100
|
+
type GitGraphCommitType = 'NORMAL' | 'REVERSE' | 'HIGHLIGHT' | 'MERGE' | 'CHERRY_PICK'
|
|
2101
|
+
interface GitGraphCommit { id: string; message?: string; type: GitGraphCommitType; customType?: 'NORMAL' | 'REVERSE' | 'HIGHLIGHT'; tags: string[]; parents: string[]; branch: string; sequence: number; source: 'commit' | 'merge' | 'cherry-pick'; mergeBranch?: string; cherrySource?: string; cherryParent?: string; syntheticCherryTag?: string; customId: boolean }
|
|
2102
|
+
interface GitGraphBranch { name: string; order: number; sequence: number; head?: string }
|
|
2103
|
+
type GitGraphStatement = { kind: 'commit'; ref: string } | { kind: 'branch'; name: string; order?: number } | { kind: 'checkout'; branch: string; keyword: 'checkout' | 'switch' } | { kind: 'merge'; ref: string; branch: string } | { kind: 'cherry-pick'; ref: string; source: string; parent?: string }
|
|
2104
|
+
interface GitGraphBody { kind: 'gitgraph'; direction: 'LR' | 'TB' | 'BT'; mainBranchName: string; title?: string; commits: GitGraphCommit[]; branches: GitGraphBranch[]; statements: GitGraphStatement[]; accessibilityTitle?: string; accessibilityDescription?: string }
|
|
2105
|
+
|
|
2106
|
+
type FlowchartMutationOp =
|
|
2107
|
+
// shape also accepts any Mermaid v11 @{ shape } name/alias (e.g. 'manual-input')
|
|
2108
|
+
| { kind: 'add_node'; id: string; label: string; shape?: string; parent?: string }
|
|
2109
|
+
| { kind: 'remove_node'; id: string }
|
|
2110
|
+
| { kind: 'rename_node'; from: string; to: string }
|
|
2111
|
+
| { kind: 'set_label'; target: string; label: string } // target: node id, authored edge ID (e1), or 'from->to'/'from->to#k'
|
|
2112
|
+
| { kind: 'add_edge'; from: string; to: string; label?: string; style?: 'solid' | 'dotted' | 'thick' | 'invisible' }
|
|
2113
|
+
| { kind: 'remove_edge'; id: string } // id: authored edge ID (e1), or 'from->to'/'from->to#k'
|
|
2114
|
+
| { kind: 'set_shape'; id: string; shape: string } // geometry name or v11 @{ shape } name/alias
|
|
2115
|
+
| { kind: 'set_direction'; direction: 'TD' | 'TB' | 'LR' | 'BT' | 'RL'; subgraph?: string } // omit subgraph = diagram direction
|
|
2116
|
+
| { kind: 'add_subgraph'; id: string; label?: string; parent?: string; members?: string[] } // members MOVE into the new subgraph
|
|
2117
|
+
| { kind: 'remove_subgraph'; id: string; removeMembers?: boolean } // default dissolves; true deletes members + their edges
|
|
2118
|
+
| { kind: 'move_node'; id: string; subgraph: string | null } // null = top level
|
|
2119
|
+
| { kind: 'define_class'; name: string; style: string } // CSS-like pairs 'fill:#f96,stroke:#333'
|
|
2120
|
+
| { kind: 'set_node_class'; id: string; className: string | null } // null removes the assignment
|
|
2121
|
+
| { kind: 'set_node_style'; id: string; style: string | null } // null clears the inline style
|
|
2122
|
+
|
|
2123
|
+
type StateMutationOp =
|
|
2124
|
+
| { kind: 'add_state'; id: string; label?: string | null; parent?: string | null; region?: number }
|
|
2125
|
+
| { kind: 'remove_state'; id: string; recursive?: boolean } // recursive: true removes a non-empty composite's whole subtree
|
|
2126
|
+
| { kind: 'rename_state'; from: string; to: string }
|
|
2127
|
+
| { kind: 'set_state_label'; id: string; label: string | null }
|
|
2128
|
+
| { kind: 'add_transition'; from: string; to: string; label?: string | null; parent?: string | null; region?: number } // from/to may be '[*]' or a history ref ('X[H]')
|
|
2129
|
+
| { kind: 'remove_transition'; index?: number; from?: string; to?: string; parent?: string | null; region?: number }
|
|
2130
|
+
| { kind: 'set_transition_label'; index?: number; from?: string; to?: string; label: string | null; parent?: string | null; region?: number }
|
|
2131
|
+
| { kind: 'make_composite'; id: string; members: string[]; label?: string | null }
|
|
2132
|
+
| { kind: 'set_direction'; direction: 'TD' | 'TB' | 'LR' | 'BT' | 'RL'; state?: string | null } // omit state = diagram direction; composite id = its override
|
|
2133
|
+
| { kind: 'move_state'; id: string; parent: string | null; region?: number } // null = top level; region addresses a concurrent composite
|
|
2134
|
+
| { kind: 'dissolve_composite'; id: string } // hoists children + inner transitions; rejects while referenced
|
|
2135
|
+
| { kind: 'add_note'; target: string; side?: 'left' | 'right'; text: string } // side defaults to 'right'
|
|
2136
|
+
| { kind: 'remove_note'; index: number }
|
|
2137
|
+
| { kind: 'set_note_text'; index: number; text: string }
|
|
2138
|
+
| { kind: 'define_class'; name: string; style: string }
|
|
2139
|
+
| { kind: 'set_state_class'; id: string; className: string | null }
|
|
2140
|
+
| { kind: 'set_state_style'; id: string; style: string | null }
|
|
2141
|
+
| { kind: 'set_transition_style'; index?: number; default?: boolean; style: string | null; parent?: string | null; region?: number }
|
|
2142
|
+
|
|
2143
|
+
type SequenceMutationOp =
|
|
2144
|
+
| { kind: 'add_participant'; id: string; label?: string; participantKind?: 'participant' | 'actor' }
|
|
2145
|
+
| { kind: 'remove_participant'; id: string }
|
|
2146
|
+
| { kind: 'add_message'; from: string; to: string; text: string; style?: 'sync' | 'reply' | 'async' | 'async-dashed' | 'lost' | 'lost-dashed'; index?: number } // index = top-level insert position; omitted = append
|
|
2147
|
+
| { kind: 'remove_message'; index: number }
|
|
2148
|
+
| { kind: 'set_message_text'; index: number; text: string }
|
|
2149
|
+
| { kind: 'move_message'; from: number; to: number } // top-level indices
|
|
2150
|
+
| { kind: 'set_participant_label'; id: string; label: string }
|
|
2151
|
+
| { kind: 'add_fragment'; fragmentKind: SeqFragmentKind; label?: string; index?: number }
|
|
2152
|
+
| { kind: 'remove_fragment'; index: number }
|
|
2153
|
+
| { kind: 'set_fragment_label'; index: number; label: string | null }
|
|
2154
|
+
| { kind: 'add_fragment_branch'; fragmentIndex: number; label?: string }
|
|
2155
|
+
| { kind: 'set_fragment_branch_label'; fragmentIndex: number; branchIndex: number; label: string | null }
|
|
2156
|
+
| { kind: 'add_fragment_message'; fragmentIndex: number; branchIndex?: number; from: string; to: string; text: string; style?: 'sync' | 'reply' | 'async' | 'async-dashed' | 'lost' | 'lost-dashed'; index?: number }
|
|
2157
|
+
| { kind: 'remove_fragment_message'; fragmentIndex: number; branchIndex?: number; index: number }
|
|
2158
|
+
| { kind: 'set_fragment_message_text'; fragmentIndex: number; branchIndex?: number; index: number; text: string }
|
|
2159
|
+
|
|
2160
|
+
type TimelineMutationOp =
|
|
2161
|
+
| { kind: 'set_title'; title: string | null }
|
|
2162
|
+
| { kind: 'add_section'; label: string; index?: number } // index = insert position; omit to append
|
|
2163
|
+
| { kind: 'remove_section'; index: number }
|
|
2164
|
+
| { kind: 'set_section_label'; index: number; label: string }
|
|
2165
|
+
| { kind: 'add_period'; sectionIndex: number; label: string; events?: string[]; index?: number }
|
|
2166
|
+
| { kind: 'remove_period'; sectionIndex: number; periodIndex: number }
|
|
2167
|
+
| { kind: 'set_period_label'; sectionIndex: number; periodIndex: number; label: string }
|
|
2168
|
+
| { kind: 'add_event'; sectionIndex: number; periodIndex: number; text: string; index?: number }
|
|
2169
|
+
| { kind: 'remove_event'; sectionIndex: number; periodIndex: number; eventIndex: number }
|
|
2170
|
+
| { kind: 'set_event_text'; sectionIndex: number; periodIndex: number; eventIndex: number; text: string }
|
|
2171
|
+
// Chronology reorder (journey move_task/move_section convention): toIndex is
|
|
2172
|
+
// the insert position in the target container, applied after removal.
|
|
2173
|
+
| { kind: 'move_period'; fromSection: number; fromIndex: number; toSection: number; toIndex: number }
|
|
2174
|
+
| { kind: 'move_event'; fromSection: number; fromPeriod: number; fromIndex: number; toSection: number; toPeriod: number; toIndex: number }
|
|
2175
|
+
| { kind: 'move_section'; from: number; to: number }
|
|
2176
|
+
| { kind: 'set_accessibility_title'; title: string | null }
|
|
2177
|
+
| { kind: 'set_accessibility_description'; description: string | null }
|
|
2178
|
+
|
|
2179
|
+
type ClassMutationOp =
|
|
2180
|
+
| { kind: 'set_title'; title: string | null }
|
|
2181
|
+
| { kind: 'add_class'; id: string; label?: string; generic?: string; members?: string[]; namespace?: string }
|
|
2182
|
+
| { kind: 'remove_class'; id: string }
|
|
2183
|
+
| { kind: 'rename_class'; from: string; to: string }
|
|
2184
|
+
| { kind: 'set_class_generic'; class: string; generic: string | null }
|
|
2185
|
+
| { kind: 'add_member'; class: string; text: string }
|
|
2186
|
+
| { kind: 'remove_member'; class: string; index: number }
|
|
2187
|
+
| { kind: 'add_relation'; from: string; to: string; relKind: ClassRelationKind; label?: string }
|
|
2188
|
+
| { kind: 'remove_relation'; index: number }
|
|
2189
|
+
| { kind: 'add_note'; text: string; for?: string }
|
|
2190
|
+
| { kind: 'remove_note'; index: number }
|
|
2191
|
+
| { kind: 'set_class_namespace'; class: string; namespace: string | null }
|
|
2192
|
+
| { kind: 'define_class'; name: string; style: string }
|
|
2193
|
+
| { kind: 'set_css_class'; class: string; className: string | null }
|
|
2194
|
+
| { kind: 'set_class_style'; class: string; style: string | null }
|
|
2195
|
+
|
|
2196
|
+
type ErMutationOp =
|
|
2197
|
+
| { kind: 'add_entity'; id: string; label?: string; attributes?: string[] }
|
|
2198
|
+
| { kind: 'remove_entity'; id: string }
|
|
2199
|
+
| { kind: 'rename_entity'; from: string; to: string }
|
|
2200
|
+
| { kind: 'set_entity_label'; entity: string; label: string | null }
|
|
2201
|
+
| { kind: 'add_attribute'; entity: string; text: string }
|
|
2202
|
+
| { kind: 'remove_attribute'; entity: string; index: number }
|
|
2203
|
+
| { kind: 'add_relation'; from: string; to: string; leftCard: ErCardinality; rightCard: ErCardinality; dashed?: boolean; label?: string }
|
|
2204
|
+
| { kind: 'remove_relation'; index: number }
|
|
2205
|
+
| { kind: 'set_direction'; direction: 'TD' | 'TB' | 'LR' | 'BT' | 'RL' }
|
|
2206
|
+
| { kind: 'define_class'; name: string; style: string }
|
|
2207
|
+
| { kind: 'set_entity_class'; entity: string; className: string | null }
|
|
2208
|
+
| { kind: 'set_entity_style'; entity: string; style: string | null }
|
|
2209
|
+
|
|
2210
|
+
type JourneyMutationOp =
|
|
2211
|
+
| { kind: 'set_title'; title: string | null }
|
|
2212
|
+
| { kind: 'add_section'; label: string; index?: number }
|
|
2213
|
+
| { kind: 'remove_section'; index: number }
|
|
2214
|
+
| { kind: 'set_section_label'; index: number; label: string }
|
|
2215
|
+
| { kind: 'add_task'; sectionIndex: number; text: string; score: number; actors?: string[]; index?: number }
|
|
2216
|
+
| { kind: 'remove_task'; sectionIndex: number; taskIndex: number }
|
|
2217
|
+
| { kind: 'set_task_text'; sectionIndex: number; taskIndex: number; text: string }
|
|
2218
|
+
| { kind: 'set_task_score'; sectionIndex: number; taskIndex: number; score: number }
|
|
2219
|
+
| { kind: 'set_task_actors'; sectionIndex: number; taskIndex: number; actors: string[] }
|
|
2220
|
+
| { kind: 'rename_actor'; from: string; to: string }
|
|
2221
|
+
| { kind: 'move_task'; fromSection: number; fromIndex: number; toSection: number; toIndex: number }
|
|
2222
|
+
| { kind: 'move_section'; from: number; to: number }
|
|
2223
|
+
| { kind: 'set_accessibility_title'; title: string | null }
|
|
2224
|
+
| { kind: 'set_accessibility_description'; description: string | null }
|
|
2225
|
+
|
|
2226
|
+
type ArchitectureMutationOp =
|
|
2227
|
+
| { kind: 'set_title'; title: string | null }
|
|
2228
|
+
| { kind: 'set_accessibility_title'; title: string | null }
|
|
2229
|
+
| { kind: 'set_accessibility_description'; description: string | null }
|
|
2230
|
+
| { kind: 'add_service'; id: string; label?: string; icon?: string | null; group?: string | null }
|
|
2231
|
+
| { kind: 'remove_service'; id: string }
|
|
2232
|
+
| { kind: 'rename_service'; from: string; to: string }
|
|
2233
|
+
| { kind: 'set_service_label'; id: string; label: string }
|
|
2234
|
+
| { kind: 'set_service_icon'; id: string; icon: string | null }
|
|
2235
|
+
| { kind: 'move_service'; id: string; group: string | null }
|
|
2236
|
+
| { kind: 'add_junction'; id: string; group?: string | null }
|
|
2237
|
+
| { kind: 'remove_junction'; id: string }
|
|
2238
|
+
| { kind: 'rename_junction'; from: string; to: string }
|
|
2239
|
+
| { kind: 'move_junction'; id: string; group: string | null }
|
|
2240
|
+
| { kind: 'add_group'; id: string; label?: string; icon?: string | null; parent?: string | null }
|
|
2241
|
+
| { kind: 'set_group_label'; id: string; label: string }
|
|
2242
|
+
| { kind: 'remove_group'; id: string }
|
|
2243
|
+
| { kind: 'add_edge'; from: string; to: string; fromSide: ArchitectureSide; toSide: ArchitectureSide; fromBoundary?: ArchitectureEndpointBoundary; toBoundary?: ArchitectureEndpointBoundary; label?: string | null; hasArrowStart?: boolean; hasArrowEnd?: boolean }
|
|
2244
|
+
| { kind: 'update_edge'; index: number; from?: string; to?: string; fromSide?: ArchitectureSide; toSide?: ArchitectureSide; fromBoundary?: ArchitectureEndpointBoundary; toBoundary?: ArchitectureEndpointBoundary; label?: string | null; hasArrowStart?: boolean; hasArrowEnd?: boolean }
|
|
2245
|
+
| { kind: 'remove_edge'; index?: number; id?: string }
|
|
2246
|
+
|
|
2247
|
+
type XyChartAxisSpec = { name?: string | null; categories?: string[]; range?: { min: number; max: number } }
|
|
2248
|
+
type XyChartMutationOp =
|
|
2249
|
+
| { kind: 'set_title'; title: string | null }
|
|
2250
|
+
| { kind: 'set_x_axis'; axis: XyChartAxisSpec | null }
|
|
2251
|
+
| { kind: 'set_y_axis'; axis: XyChartAxisSpec | null }
|
|
2252
|
+
| { kind: 'add_series'; kind2: 'bar' | 'line'; name?: string | null; values: number[] }
|
|
2253
|
+
| { kind: 'remove_series'; index: number }
|
|
2254
|
+
| { kind: 'set_series_values'; index: number; values: number[] }
|
|
2255
|
+
| { kind: 'set_series_name'; index: number; name: string | null }
|
|
2256
|
+
| { kind: 'reorder_series'; from: number; to: number }
|
|
2257
|
+
| { kind: 'set_orientation'; horizontal: boolean }
|
|
2258
|
+
| { kind: 'set_data_point'; seriesIndex: number; index: number; value: number }
|
|
2259
|
+
|
|
2260
|
+
type PieMutationOp =
|
|
2261
|
+
| { kind: 'set_title'; title: string | null }
|
|
2262
|
+
| { kind: 'set_show_data'; showData: boolean }
|
|
2263
|
+
| { kind: 'add_slice'; label: string; value: number } // value > 0
|
|
2264
|
+
| { kind: 'remove_slice'; label: string }
|
|
2265
|
+
| { kind: 'rename_slice'; from: string; to: string }
|
|
2266
|
+
| { kind: 'set_slice_value'; label: string; value: number }
|
|
2267
|
+
| { kind: 'reorder_slice'; from: number; to: number }
|
|
2268
|
+
|
|
2269
|
+
type QuadrantMutationOp =
|
|
2270
|
+
| { kind: 'set_title'; title: string | null }
|
|
2271
|
+
| { kind: 'set_axis_labels'; axis: 'x' | 'y'; near: string | null; far?: string | null }
|
|
2272
|
+
| { kind: 'set_quadrant_label'; quadrant: number; label: string | null } // quadrant 1..4
|
|
2273
|
+
| { kind: 'add_point'; label: string; x: number; y: number } // x,y in [0,1]
|
|
2274
|
+
| { kind: 'remove_point'; label: string }
|
|
2275
|
+
| { kind: 'move_point'; label: string; x: number; y: number }
|
|
2276
|
+
| { kind: 'rename_point'; from: string; to: string }
|
|
2277
|
+
|
|
2278
|
+
type MindmapMutationOp =
|
|
2279
|
+
| { kind: 'add_node'; id: string; label: string; parent: string; shape?: MindmapShape; index?: number }
|
|
2280
|
+
| { kind: 'remove_node'; id: string; recursive?: boolean }
|
|
2281
|
+
| { kind: 'rename_node'; from: string; to: string }
|
|
2282
|
+
| { kind: 'set_label'; id: string; label: string }
|
|
2283
|
+
| { kind: 'move_node'; id: string; parent: string; index?: number }
|
|
2284
|
+
| { kind: 'set_shape'; id: string; shape: MindmapShape }
|
|
2285
|
+
| { kind: 'set_icon'; id: string; icon: string | null }
|
|
2286
|
+
| { kind: 'set_node_class'; id: string; className: string | null }
|
|
2287
|
+
| { kind: 'set_accessibility_title'; title: string | null }
|
|
2288
|
+
| { kind: 'set_accessibility_description'; description: string | null }
|
|
2289
|
+
|
|
2290
|
+
type GitGraphMutationOp =
|
|
2291
|
+
| { kind: 'append_commit'; id?: string; message?: string; type?: 'NORMAL' | 'REVERSE' | 'HIGHLIGHT'; tags?: string[] }
|
|
2292
|
+
| { kind: 'create_branch'; name: string; order?: number }
|
|
2293
|
+
| { kind: 'checkout_branch'; name: string }
|
|
2294
|
+
| { kind: 'merge_branch'; name: string; id?: string; type?: 'NORMAL' | 'REVERSE' | 'HIGHLIGHT'; tags?: string[] }
|
|
2295
|
+
| { kind: 'cherry_pick'; id: string; parent?: string; tags?: string[] }
|
|
2296
|
+
| { kind: 'set_commit_message'; id: string; message: string | null }
|
|
2297
|
+
| { kind: 'set_commit_type'; id: string; type: 'NORMAL' | 'REVERSE' | 'HIGHLIGHT' }
|
|
2298
|
+
| { kind: 'set_commit_tags'; id: string; tags: string[] }
|
|
2299
|
+
| { kind: 'rename_branch'; from: string; to: string }
|
|
2300
|
+
| { kind: 'set_accessibility_title'; title: string | null }
|
|
2301
|
+
| { kind: 'set_accessibility_description'; description: string | null }
|
|
2302
|
+
|
|
2303
|
+
type RadarMutationOp =
|
|
2304
|
+
| { kind: 'set_title'; title: string | null }
|
|
2305
|
+
| { kind: 'add_axis'; id: string; label?: string | null; index?: number; fill?: number }
|
|
2306
|
+
| { kind: 'remove_axis'; id: string }
|
|
2307
|
+
| { kind: 'rename_axis'; from: string; to: string }
|
|
2308
|
+
| { kind: 'set_axis_label'; id: string; label: string | null }
|
|
2309
|
+
| { kind: 'reorder_axis'; from: number; to: number }
|
|
2310
|
+
| { kind: 'add_curve'; id: string; label?: string | null; values: number[]; index?: number }
|
|
2311
|
+
| { kind: 'remove_curve'; id: string }
|
|
2312
|
+
| { kind: 'set_curve_values'; id: string; values: number[] }
|
|
2313
|
+
| { kind: 'set_curve_value'; curve: string; axis: string; value: number }
|
|
2314
|
+
| { kind: 'set_curve_label'; id: string; label: string | null }
|
|
2315
|
+
| { kind: 'rename_curve'; from: string; to: string }
|
|
2316
|
+
| { kind: 'reorder_curve'; from: number; to: number }
|
|
2317
|
+
| { kind: 'set_config'; max?: number | null; min?: number | null; ticks?: number | null; graticule?: 'circle' | 'polygon' | null; showLegend?: boolean | null }
|
|
2318
|
+
|
|
2319
|
+
type GanttMutationOp =
|
|
2320
|
+
| { kind: 'set_title'; title: string | null }
|
|
2321
|
+
| { kind: 'add_section'; label: string }
|
|
2322
|
+
| { kind: 'rename_section'; index: number; label: string }
|
|
2323
|
+
| { kind: 'remove_section'; index: number }
|
|
2324
|
+
| { kind: 'add_task'; sectionIndex: number; label: string; taskId?: string; tags?: GanttTaskTag[]; start?: string; end: string; index?: number }
|
|
2325
|
+
| { kind: 'remove_task'; sectionIndex: number; taskIndex: number }
|
|
2326
|
+
| { kind: 'rename_task'; sectionIndex: number; taskIndex: number; label: string }
|
|
2327
|
+
| { kind: 'set_task_status'; sectionIndex: number; taskIndex: number; status: 'active' | 'done' | 'crit' | null }
|
|
2328
|
+
| { kind: 'set_task_dates'; sectionIndex: number; taskIndex: number; start?: string | null; end?: string }
|
|
2329
|
+
| { kind: 'set_task_flags'; sectionIndex: number; taskIndex: number; milestone?: boolean; vert?: boolean }
|
|
2330
|
+
// set_task_id rewrites structured after/until references on rename; rejects
|
|
2331
|
+
// while referenced from opaque lines (click/comments), and null rejects
|
|
2332
|
+
// while referenced at all.
|
|
2333
|
+
| { kind: 'set_task_id'; sectionIndex: number; taskIndex: number; taskId: string | null }
|
|
2334
|
+
// Source order IS gantt scheduling semantics: moves are rejected with a
|
|
2335
|
+
// prescriptive error when they would change an implicit-start task's
|
|
2336
|
+
// predecessor (materialize an explicit start via set_task_dates first).
|
|
2337
|
+
| { kind: 'move_task'; fromSection: number; fromIndex: number; toSection: number; toIndex: number }
|
|
2338
|
+
| { kind: 'move_section'; from: number; to: number }
|
|
2339
|
+
|
|
2340
|
+
// Tier 1 (structural, reliable): EMPTY_DIAGRAM, EDGE_MISANCHORED, OFF_CANVAS,
|
|
2341
|
+
// GROUP_BREACH, UNKNOWN_SHAPE, LABEL_OVERFLOW (rendered-line char count:
|
|
2342
|
+
// <br> splits lines, XML entities decode to one char),
|
|
2343
|
+
// UNRESOLVABLE_SCHEDULE (gantt: parses but schedule cannot resolve; render would fail),
|
|
2344
|
+
// RENDER_FAILED (strict renderer rejected the canonical source).
|
|
2345
|
+
// Tier 2 (geometric, advisory): NODE_OVERLAP, ROUTE_SELF_CROSS, and the
|
|
2346
|
+
// route-contract tripwires ROUTE_HITCH, ROUTE_UNEXPLAINED_BEND,
|
|
2347
|
+
// ROUTE_LABEL_ON_SHARED_TRUNK, ROUTE_SELF_LOOP_OCCUPANCY, ROUTE_CONTAINER_MISANCHOR,
|
|
2348
|
+
// ROUTE_SHAPE_MISANCHOR, ROUTE_STALE_AFTER_NODE_MOVE.
|
|
2349
|
+
// Tier 3 (lint, advisory): DUPLICATE_EDGE, UNREACHABLE_NODE,
|
|
2350
|
+
// DECISION_BRANCH_UNLABELED, COMMENT_DROPPED, UNSUPPORTED_SYNTAX,
|
|
2351
|
+
// CONTENT_DROPPED_ON_ROUNDTRIP, INEFFECTIVE_CONFIG, LOW_CONTRAST,
|
|
2352
|
+
// BRAND_CONSTRAINT_WARNING. BRAND_CONSTRAINT_ERROR is inspect-only policy
|
|
2353
|
+
// selected explicitly by a Style constraint with action "error".
|
|
2354
|
+
type RenderedRegionKind='node'|'edge'|'label'|'canvas'|'group'|'cluster'|'lane'|'band'|'compartment'|'plot'|'ring'
|
|
2355
|
+
type DiagramActionSecurity='safe'|'unsafe'|'source-only'|'unsupported'
|
|
2356
|
+
interface RenderedRegion { id:string;kind:RenderedRegionKind;elementId?:string;parentId?:string;bounds:{x:number;y:number;w:number;h:number};sourceLine?:number }
|
|
2357
|
+
interface DiagramActionRecord { id?:string;regionId?:string;family:DiagramKind;target:string;action:'href'|'call'|'callback';raw:string;line?:number;href?:string;security:DiagramActionSecurity;executable:false;message?:string }
|
|
2358
|
+
interface RenderedLayout { version: 1; kind: DiagramKind | ExternalFamilyId; nodes: unknown[]; edges: unknown[]; groups: unknown[]; regions?: RenderedRegion[]; actions?: DiagramActionRecord[]; bounds: { w: number; h: number } }
|
|
2359
|
+
|
|
2360
|
+
type WarningCode =
|
|
2361
|
+
| 'EMPTY_DIAGRAM' | 'EDGE_MISANCHORED' | 'OFF_CANVAS' | 'GROUP_BREACH'
|
|
2362
|
+
| 'UNKNOWN_SHAPE' | 'LABEL_OVERFLOW' | 'UNRESOLVABLE_SCHEDULE' | 'RENDER_FAILED'
|
|
2363
|
+
| 'NODE_OVERLAP' | 'ROUTE_SELF_CROSS' | 'ROUTE_HITCH'
|
|
2364
|
+
| 'ROUTE_UNEXPLAINED_BEND' | 'ROUTE_LABEL_ON_SHARED_TRUNK' | 'ROUTE_SELF_LOOP_OCCUPANCY' | 'ROUTE_CONTAINER_MISANCHOR'
|
|
2365
|
+
| 'ROUTE_SHAPE_MISANCHOR' | 'ROUTE_STALE_AFTER_NODE_MOVE'
|
|
2366
|
+
| 'DUPLICATE_EDGE' | 'UNREACHABLE_NODE' | 'DECISION_BRANCH_UNLABELED' | 'COMMENT_DROPPED' | 'UNSUPPORTED_SYNTAX'
|
|
2367
|
+
| 'CONTENT_DROPPED_ON_ROUNDTRIP' | 'INEFFECTIVE_CONFIG' | 'LOW_CONTRAST'
|
|
2368
|
+
| 'BRAND_CONSTRAINT_WARNING' | 'BRAND_CONSTRAINT_ERROR'
|
|
2369
|
+
|
|
2370
|
+
interface VerifyResult {
|
|
2371
|
+
ok: boolean
|
|
2372
|
+
warnings: { code: WarningCode; [field: string]: unknown }[]
|
|
2373
|
+
layout: RenderedLayout
|
|
2374
|
+
}
|
|
2375
|
+
|
|
2376
|
+
interface DiagramAnalysis {
|
|
2377
|
+
kind: DiagramKind | ExternalFamilyId
|
|
2378
|
+
feedbackEdges: Array<{ edgeIndex: number; from: string; to: string; label?: string; routeClass: string }>
|
|
2379
|
+
actions: Array<{ id?: string; regionId?: string; family: DiagramKind; target: string; action: 'href' | 'call' | 'callback'; raw: string; line?: number; href?: string; security: 'safe' | 'unsafe' | 'source-only' | 'unsupported'; executable: false; message?: string }>
|
|
2380
|
+
gantt?: { criticalPathTaskIds: string[]; slackByTaskId: Record<string, number>; projectStart: number; projectEnd: number; entryTaskIds: string[]; sinkTaskIds: string[] }
|
|
2381
|
+
}
|
|
2382
|
+
|
|
2383
|
+
${SDK_ANY_MUTATION_OP_DECLARATION}
|
|
2384
|
+
|
|
2385
|
+
declare const mermaid: {
|
|
2386
|
+
parseRegisteredMermaid(source: string): Result<ParsedDiagram, ParseError[]>
|
|
2387
|
+
// Blank-slate authoring: createMermaid returns an empty structured diagram
|
|
2388
|
+
// for any DiagramKind (already narrowed \u2014 pass it straight to mutate).
|
|
2389
|
+
// buildMermaid folds a typed op list over that empty diagram; on failure the
|
|
2390
|
+
// error carries opIndex of the op that failed. direction applies to
|
|
2391
|
+
// flowchart/state only.
|
|
2392
|
+
createMermaid(kind: DiagramKind, opts?: { direction?: 'TD' | 'TB' | 'LR' | 'BT' | 'RL' }): ValidDiagram
|
|
2393
|
+
buildMermaid(kind: DiagramKind, ops: AnyMutationOp[], opts?: { direction?: 'TD' | 'TB' | 'LR' | 'BT' | 'RL' }): Result<ValidDiagram, { code: string; message: string; opIndex: number }>
|
|
2394
|
+
${SDK_NARROWER_METHOD_DECLARATIONS}
|
|
2395
|
+
${SDK_MUTATE_METHOD_DECLARATIONS}
|
|
2396
|
+
verifyMermaid(input: ParsedDiagram | string, opts?: { suppress?: WarningCode[]; labelCharCap?: number; renderOptions?: SharedRenderOptions }): VerifyResult
|
|
2397
|
+
analyzeMermaid(d: ValidDiagram): DiagramAnalysis
|
|
2398
|
+
analyzeMermaidSource(source: string): Result<DiagramAnalysis, { code: string; message: string }[]>
|
|
2399
|
+
describeMermaidFacts(d: ValidDiagram): string[]
|
|
2400
|
+
describeMermaidFactsSource(source: string): Result<string[], { code: string; message: string }[]>
|
|
2401
|
+
checkMermaid(d: ValidDiagram, spec: CheckMermaidSpec): CheckMermaidResult
|
|
2402
|
+
checkMermaidSource(source: string, spec: CheckMermaidSpec): Result<CheckMermaidResult, { code: string; message: string }[]>
|
|
2403
|
+
serializeMermaid(d: ParsedDiagram): string
|
|
2404
|
+
// Compatibility conveniences return only bytes/text; use the receipt-aware
|
|
2405
|
+
// methods below for transport parity and projection/security evidence.
|
|
2406
|
+
renderMermaidSVG(input: ParsedDiagram | string, opts?: SvgRenderOptions): string
|
|
2407
|
+
renderMermaidASCII(input: ParsedDiagram | string, opts?: AsciiRenderOptions): string
|
|
2408
|
+
${CODE_MODE_RENDER_METHOD_DECLARATIONS}
|
|
2409
|
+
// Op discovery \u2014 look up exact op shapes at runtime instead of guessing.
|
|
2410
|
+
// describeOps returns every op's field names, required-ness, inlined enum
|
|
2411
|
+
// values, and constraint/default notes (e.g. score "integer 1..5", shape
|
|
2412
|
+
// "default: rectangle"); opSignatures returns compact one-liners like
|
|
2413
|
+
// "add_point(label, x, y)". Read these before authoring an unfamiliar op.
|
|
2414
|
+
describeOps(family: DiagramKind): Record<string, { name: string; required: boolean; type: string; note?: string }[]>
|
|
2415
|
+
opSignatures(family: DiagramKind): string[]
|
|
2416
|
+
}
|
|
2417
|
+
|
|
2418
|
+
// Conventions:
|
|
2419
|
+
// 1. For new diagrams, use buildMermaid(kind, ops) \u2014 or createMermaid(kind)
|
|
2420
|
+
// then mutate step by step \u2014 and verify/render the result. Hand-author
|
|
2421
|
+
// Mermaid source only for syntax the typed ops do not model.
|
|
2422
|
+
// 2. For existing structured diagrams, use mutate() + verify + serializeMermaid();
|
|
2423
|
+
// do not regenerate/concatenate source when a typed op exists.
|
|
2424
|
+
${SDK_FAMILY_CONVENTION}
|
|
2425
|
+
// State owns a dedicated body (BUILD-19); asFlowchart returns null on it.
|
|
2426
|
+
// Gantt bodies are segment-preserving: directives/click/comment lines ride
|
|
2427
|
+
// along verbatim as opaque-block segments and are edited as source only.
|
|
2428
|
+
// Opaque-fallback
|
|
2429
|
+
// bodies (unmodeled syntax) are source-level only; if explicitly edited as
|
|
2430
|
+
// text, re-parse and verify before returning.
|
|
2431
|
+
// 4. verify.ok is structural, not a visual-quality score; inspect warnings/layout or render artifacts for layout quality.
|
|
2432
|
+
// For semantic task correctness, read back deterministic facts or check them:
|
|
2433
|
+
// checkMermaid(d, ['edge Processing -> [*] : done', 'member Duck +quack()']).
|
|
2434
|
+
// 5. Layout is deterministic and never seeded. The render option seed only
|
|
2435
|
+
// re-rolls stochastic ink of styled looks (render option style: a name,
|
|
2436
|
+
// an inline style record, or a stack merged left-to-right; a colors-only
|
|
2437
|
+
// style is a theme); geometry is identical for identical input. Gantt
|
|
2438
|
+
// never reads the wall clock; pass render option ganttToday to draw a
|
|
2439
|
+
// deterministic today marker.
|
|
2440
|
+
`;
|
|
2441
|
+
|
|
2442
|
+
// src/mcp/sdk-discovery.ts
|
|
2443
|
+
var SDK_FAMILIES = BUILTIN_FAMILY_METADATA.map((family) => family.id);
|
|
2444
|
+
var SDK_FAMILY_SET = new Set(SDK_FAMILIES);
|
|
2445
|
+
var DIAGRAM_KIND = SDK_FAMILIES.map((family) => `'${family}'`).join(" | ");
|
|
2446
|
+
var NARROWERS = BUILTIN_FAMILY_METADATA.map((family) => ` ${family.narrower}(diagram: ValidDiagram): ValidDiagram | null`).join("\n");
|
|
2447
|
+
var SDK_CORE_DECLARATION = `type DiagramKind = ${DIAGRAM_KIND}
|
|
2448
|
+
type MutationOp = { kind: string; [field: string]: unknown }
|
|
2449
|
+
type Result<T, E = { code: string; message: string }> = { ok: true; value: T } | { ok: false; error: E }
|
|
2450
|
+
interface SourceLocation { readonly line: number; readonly col: number }
|
|
2451
|
+
interface SourceMapSpans { readonly preserved: PreservedSourceSpans; readonly nodes: ReadonlyMap<string, SourceSpan>; readonly edges: ReadonlyMap<string, SourceSpan>; readonly groups: ReadonlyMap<string, SourceSpan>; readonly labels: ReadonlyMap<string, SourceSpan> }
|
|
2452
|
+
interface SourceMap { readonly nodes: ReadonlyMap<string, SourceLocation>; readonly edges: ReadonlyMap<string, SourceLocation>; readonly groups: ReadonlyMap<string, SourceLocation>; readonly labels: ReadonlyMap<string, SourceLocation>; readonly spans?: SourceMapSpans }
|
|
2453
|
+
interface ValidDiagram { readonly kind: DiagramKind; readonly source: SourceMap }
|
|
2454
|
+
type ExternalFamilyId = \`family:\${string}\`
|
|
2455
|
+
interface ExtensionCompatibility {
|
|
2456
|
+
readonly [contract: string]: string | undefined
|
|
2457
|
+
readonly core?: string
|
|
2458
|
+
readonly scene?: string
|
|
2459
|
+
}
|
|
2460
|
+
interface ExtensionProvenance { readonly owner: string; readonly source: string; readonly reference?: string }
|
|
2461
|
+
interface ExtensionIdentity<Kind extends string = string> {
|
|
2462
|
+
readonly id: \`\${Kind}:\${string}\`
|
|
2463
|
+
readonly kind: Kind
|
|
2464
|
+
readonly version: string
|
|
2465
|
+
readonly compatibility: ExtensionCompatibility
|
|
2466
|
+
readonly provenance: ExtensionProvenance
|
|
2467
|
+
}
|
|
2468
|
+
interface SourceSpanPoint { readonly offset: number; readonly line: number; readonly col: number }
|
|
2469
|
+
interface SourceSpan { readonly start: SourceSpanPoint; readonly end: SourceSpanPoint }
|
|
2470
|
+
interface PreservedSourceSpans {
|
|
2471
|
+
readonly source: SourceSpan
|
|
2472
|
+
readonly wrapper?: SourceSpan
|
|
2473
|
+
readonly frontmatter?: SourceSpan
|
|
2474
|
+
readonly initDirectives?: readonly SourceSpan[]
|
|
2475
|
+
readonly accessibilityDirectives?: readonly SourceSpan[]
|
|
2476
|
+
readonly header: SourceSpan
|
|
2477
|
+
readonly body: SourceSpan
|
|
2478
|
+
}
|
|
2479
|
+
interface SourcePreservationReceipt {
|
|
2480
|
+
readonly version: 1
|
|
2481
|
+
readonly classification: 'unsupported' | 'inventory-only' | 'unknown'
|
|
2482
|
+
readonly source: string
|
|
2483
|
+
readonly header: string
|
|
2484
|
+
readonly upstreamFamilyId?: string
|
|
2485
|
+
readonly mermaidVersion: string
|
|
2486
|
+
readonly spans?: PreservedSourceSpans
|
|
2487
|
+
}
|
|
2488
|
+
interface ParseError {
|
|
2489
|
+
readonly code: string
|
|
2490
|
+
readonly message: string
|
|
2491
|
+
readonly line?: number
|
|
2492
|
+
readonly col?: number
|
|
2493
|
+
readonly preservation?: SourcePreservationReceipt
|
|
2494
|
+
readonly help?: string
|
|
2495
|
+
}
|
|
2496
|
+
interface ExtensionValidDiagram {
|
|
2497
|
+
readonly kind: ExternalFamilyId
|
|
2498
|
+
readonly descriptorIdentity: ExtensionIdentity<'family'>
|
|
2499
|
+
readonly source: SourceMap
|
|
2500
|
+
}
|
|
2501
|
+
interface PreservedValidDiagram {
|
|
2502
|
+
readonly kind: ExternalFamilyId
|
|
2503
|
+
readonly source: SourceMap
|
|
2504
|
+
readonly body: {
|
|
2505
|
+
readonly kind: 'preserved'
|
|
2506
|
+
readonly representation: 'opaque' | 'unknown'
|
|
2507
|
+
readonly source: string
|
|
2508
|
+
readonly preservation: SourcePreservationReceipt
|
|
2509
|
+
readonly spans: PreservedSourceSpans
|
|
2510
|
+
readonly diagnostic: {
|
|
2511
|
+
readonly code: 'UNSUPPORTED_FAMILY' | 'UNKNOWN_HEADER' | 'FAMILY_DESCRIPTOR_MISMATCH'
|
|
2512
|
+
readonly message: string
|
|
2513
|
+
readonly help: string
|
|
2514
|
+
}
|
|
2515
|
+
}
|
|
2516
|
+
}
|
|
2517
|
+
type ParsedDiagram = ValidDiagram | ExtensionValidDiagram | PreservedValidDiagram
|
|
2518
|
+
type RenderedRegionKind='node'|'edge'|'label'|'canvas'|'group'|'cluster'|'lane'|'band'|'compartment'|'plot'|'ring'
|
|
2519
|
+
type DiagramActionSecurity='safe'|'unsafe'|'source-only'|'unsupported'
|
|
2520
|
+
interface RenderedRegion { id:string;kind:RenderedRegionKind;elementId?:string;parentId?:string;bounds:{x:number;y:number;w:number;h:number};sourceLine?:number }
|
|
2521
|
+
interface DiagramActionRecord { id?:string;regionId?:string;family:DiagramKind;target:string;action:'href'|'call'|'callback';raw:string;line?:number;href?:string;security:DiagramActionSecurity;executable:false;message?:string }
|
|
2522
|
+
interface RenderedLayout { version: 1; kind: DiagramKind | ExternalFamilyId; bounds: { w: number; h: number }; nodes: unknown[]; edges: unknown[]; groups: unknown[]; regions?: RenderedRegion[]; actions?: DiagramActionRecord[] }
|
|
2523
|
+
interface VerifyResult { ok: boolean; warnings: unknown[]; layout: RenderedLayout }
|
|
2524
|
+
type CheckMermaidSpec = string[] | { include?: string[]; exclude?: string[]; exact?: boolean }
|
|
2525
|
+
interface CheckMermaidResult { ok: boolean; missing: string[]; unexpected: string[]; facts: string[] }
|
|
2526
|
+
type MermaidConfigScalar = string | number | boolean | null
|
|
2527
|
+
type MermaidConfigValue = MermaidConfigScalar | MermaidConfigValue[] | { [key: string]: MermaidConfigValue | undefined }
|
|
2528
|
+
type MermaidRuntimeConfig = { [key: string]: MermaidConfigValue | undefined }
|
|
2529
|
+
|
|
2530
|
+
${CODE_MODE_CORE_RENDER_OPTION_DECLARATIONS}
|
|
2531
|
+
|
|
2532
|
+
declare const mermaid: {
|
|
2533
|
+
parseRegisteredMermaid(source: string): Result<ParsedDiagram, ParseError[]>
|
|
2534
|
+
createMermaid(kind: DiagramKind, opts?: { direction?: 'TD' | 'TB' | 'LR' | 'BT' | 'RL' }): ValidDiagram
|
|
2535
|
+
buildMermaid(kind: DiagramKind, ops: MutationOp[], opts?: { direction?: 'TD' | 'TB' | 'LR' | 'BT' | 'RL' }): Result<ValidDiagram, { code: string; message: string; opIndex: number }>
|
|
2536
|
+
${NARROWERS}
|
|
2537
|
+
mutate(diagram: ValidDiagram, op: MutationOp): Result<ValidDiagram>
|
|
2538
|
+
verifyMermaid(input: ParsedDiagram | string, opts?: { suppress?: string[]; labelCharCap?: number; renderOptions?: SharedRenderOptions }): VerifyResult
|
|
2539
|
+
analyzeMermaid(diagram: ValidDiagram): Record<string, unknown>
|
|
2540
|
+
analyzeMermaidSource(source: string): Result<Record<string, unknown>>
|
|
2541
|
+
describeMermaidFacts(diagram: ValidDiagram): string[]
|
|
2542
|
+
describeMermaidFactsSource(source: string): Result<string[]>
|
|
2543
|
+
checkMermaid(diagram: ValidDiagram, spec: CheckMermaidSpec): CheckMermaidResult
|
|
2544
|
+
checkMermaidSource(source: string, spec: CheckMermaidSpec): Result<CheckMermaidResult>
|
|
2545
|
+
serializeMermaid(diagram: ParsedDiagram): string
|
|
2546
|
+
renderMermaidSVG(input: ParsedDiagram | string, opts?: SvgRenderOptions): string
|
|
2547
|
+
renderMermaidSVGWithReceipt(input: ParsedDiagram | string, opts?: SvgRenderOptions): RenderedSvg
|
|
2548
|
+
renderMermaidASCII(input: ParsedDiagram | string, opts?: AsciiRenderOptions): string
|
|
2549
|
+
renderMermaidASCIIWithReceipt(input: ParsedDiagram | string, opts?: AsciiRenderOptions): RenderedAscii
|
|
2550
|
+
layoutMermaidWithReceipt(input: ParsedDiagram | string, opts?: LayoutRenderOptions): RenderedLayoutArtifact
|
|
2551
|
+
describeOps(family: DiagramKind): Record<string, { name: string; required: boolean; type: string; note?: string }[]>
|
|
2552
|
+
opSignatures(family: DiagramKind): string[]
|
|
2553
|
+
}`;
|
|
2554
|
+
function createDescribeSdkTool() {
|
|
2555
|
+
return {
|
|
2556
|
+
name: "describe_sdk",
|
|
2557
|
+
description: `Return version-matched mutation operations for one diagram family.
|
|
2558
|
+
Use detail=signatures for the compact op menu or detail=fields (default) for exact
|
|
2559
|
+
field types, required flags, enum values, defaults, and constraints. Call this
|
|
2560
|
+
before build, mutate, or execute when the family schema is not already known.`,
|
|
2561
|
+
inputSchema: {
|
|
2562
|
+
type: "object",
|
|
2563
|
+
properties: {
|
|
2564
|
+
family: { type: "string", enum: [...SDK_FAMILIES], description: "Diagram family whose mutation operations are needed." },
|
|
2565
|
+
detail: { type: "string", enum: ["signatures", "fields"], description: "signatures for a compact menu; fields for the complete schema (default)." }
|
|
2566
|
+
},
|
|
2567
|
+
required: ["family"]
|
|
2568
|
+
},
|
|
2569
|
+
annotations: PURE_COMPUTE_ANNOTATIONS
|
|
2570
|
+
};
|
|
2571
|
+
}
|
|
2572
|
+
function describeSdkPayload(args) {
|
|
2573
|
+
const family = args.family;
|
|
2574
|
+
if (typeof family !== "string" || !SDK_FAMILY_SET.has(family)) {
|
|
2575
|
+
throw new Error(`describe_sdk family must be one of: ${SDK_FAMILIES.join(", ")}`);
|
|
2576
|
+
}
|
|
2577
|
+
const detail = args.detail ?? "fields";
|
|
2578
|
+
if (detail !== "signatures" && detail !== "fields") {
|
|
2579
|
+
throw new Error("describe_sdk detail must be one of: signatures, fields");
|
|
2580
|
+
}
|
|
2581
|
+
if (detail === "signatures") {
|
|
2582
|
+
return { ok: true, family, detail, signatures: opSignatures(family) };
|
|
2583
|
+
}
|
|
2584
|
+
return { ok: true, family, detail, ops: describeOps(family) };
|
|
2585
|
+
}
|
|
2586
|
+
|
|
2587
|
+
// src/mcp/describe-payload.ts
|
|
2588
|
+
var EXTERNAL_FAMILY_DESCRIBE_UNAVAILABLE_CODE = "EXTERNAL_FAMILY_DESCRIBE_UNAVAILABLE";
|
|
2589
|
+
function externalFamilyDescribeUnavailable(family) {
|
|
2590
|
+
return Object.freeze({
|
|
2591
|
+
code: EXTERNAL_FAMILY_DESCRIBE_UNAVAILABLE_CODE,
|
|
2592
|
+
capability: "describe",
|
|
2593
|
+
family,
|
|
2594
|
+
message: `Registered external family "${family}" has no semantic describe contract in FamilyDescriptor v2.`
|
|
2595
|
+
});
|
|
2596
|
+
}
|
|
2597
|
+
function isExtensionDiagram(diagram) {
|
|
2598
|
+
return diagram.body.kind === "extension";
|
|
2599
|
+
}
|
|
2600
|
+
function isPreservedDiagram(diagram) {
|
|
2601
|
+
return diagram.body.kind === "preserved";
|
|
2602
|
+
}
|
|
2603
|
+
function mcpDescribeFormat(args) {
|
|
2604
|
+
const format = args.format ?? "text";
|
|
2605
|
+
if (format === "text" || format === "json" || format === "facts") return format;
|
|
2606
|
+
throw new Error("describe format must be one of: text, json, facts");
|
|
2607
|
+
}
|
|
2608
|
+
function mcpDescribePayload(source, args) {
|
|
2609
|
+
const format = mcpDescribeFormat(args);
|
|
2610
|
+
const parsed = parseRegisteredMermaid(source);
|
|
2611
|
+
if (!parsed.ok) {
|
|
2612
|
+
if (format === "text") return { ok: true, text: describeMermaidSource(source) };
|
|
2613
|
+
return { ok: false, errors: parsed.error };
|
|
2614
|
+
}
|
|
2615
|
+
const diagram = parsed.value;
|
|
2616
|
+
if (isPreservedDiagram(diagram)) {
|
|
2617
|
+
return {
|
|
2618
|
+
ok: false,
|
|
2619
|
+
family: diagram.body.preservation.upstreamFamilyId ?? diagram.kind,
|
|
2620
|
+
error: {
|
|
2621
|
+
...diagram.body.diagnostic,
|
|
2622
|
+
preservation: diagram.body.preservation
|
|
2623
|
+
}
|
|
2624
|
+
};
|
|
2625
|
+
}
|
|
2626
|
+
if (isExtensionDiagram(diagram)) {
|
|
2627
|
+
return {
|
|
2628
|
+
ok: false,
|
|
2629
|
+
family: diagram.kind,
|
|
2630
|
+
error: externalFamilyDescribeUnavailable(diagram.kind)
|
|
2631
|
+
};
|
|
2632
|
+
}
|
|
2633
|
+
const verification = verifyMermaid(diagram);
|
|
2634
|
+
if (!verification.ok) {
|
|
2635
|
+
return {
|
|
2636
|
+
ok: false,
|
|
2637
|
+
family: diagram.kind,
|
|
2638
|
+
warnings: verification.warnings
|
|
2639
|
+
};
|
|
2640
|
+
}
|
|
2641
|
+
if (format === "text") return { ok: true, text: describeMermaid(diagram), warnings: verification.warnings };
|
|
2642
|
+
if (format === "facts") return { ok: true, facts: describeMermaidFacts(diagram), warnings: verification.warnings };
|
|
2643
|
+
return { ok: true, tree: JSON.parse(describeMermaid(diagram, { format: "json" })), warnings: verification.warnings };
|
|
2644
|
+
}
|
|
2645
|
+
|
|
2646
|
+
// src/mcp/artifacts.ts
|
|
2647
|
+
import { createHash, randomUUID } from "crypto";
|
|
2648
|
+
import {
|
|
2649
|
+
closeSync,
|
|
2650
|
+
existsSync,
|
|
2651
|
+
fstatSync,
|
|
2652
|
+
fsyncSync,
|
|
2653
|
+
lstatSync,
|
|
2654
|
+
mkdirSync,
|
|
2655
|
+
openSync,
|
|
2656
|
+
readFileSync,
|
|
2657
|
+
readdirSync,
|
|
2658
|
+
renameSync,
|
|
2659
|
+
unlinkSync,
|
|
2660
|
+
writeFileSync,
|
|
2661
|
+
writeSync
|
|
2662
|
+
} from "fs";
|
|
2663
|
+
import { hostname, tmpdir } from "os";
|
|
2664
|
+
import { basename, join, resolve, sep } from "path";
|
|
2665
|
+
var DEFAULT_MAX_BYTES = 20 * 1024 * 1024;
|
|
2666
|
+
var DEFAULT_MAX_TOTAL_BYTES = 200 * 1024 * 1024;
|
|
2667
|
+
var DEFAULT_MAX_ARTIFACTS = 1e3;
|
|
2668
|
+
var DEFAULT_TTL_MS = 60 * 60 * 1e3;
|
|
2669
|
+
var MANIFEST_NAME = ".agentic-mermaid-artifacts-v1.json";
|
|
2670
|
+
var LOCK_NAME = ".agentic-mermaid-artifacts-v1.lock";
|
|
2671
|
+
var MAX_MANIFEST_BYTES = 1024 * 1024;
|
|
2672
|
+
var MANAGED_NAME = /^[0-9a-z]+-[0-9a-f-]{36}\.[a-z0-9_-]+$/;
|
|
2673
|
+
function positiveSafeInteger(value, field) {
|
|
2674
|
+
if (!Number.isSafeInteger(value) || value <= 0) throw new Error(`artifact ${field} must be a positive safe integer`);
|
|
2675
|
+
return value;
|
|
2676
|
+
}
|
|
2677
|
+
var ArtifactStore = class {
|
|
2678
|
+
dir;
|
|
2679
|
+
maxBytes;
|
|
2680
|
+
maxTotalBytes;
|
|
2681
|
+
maxArtifacts;
|
|
2682
|
+
ttlMs;
|
|
2683
|
+
baseUrl;
|
|
2684
|
+
now;
|
|
2685
|
+
records = /* @__PURE__ */ new Map();
|
|
2686
|
+
manifestPath;
|
|
2687
|
+
lockPath;
|
|
2688
|
+
lockToken = randomUUID();
|
|
2689
|
+
lockFd = -1;
|
|
2690
|
+
closed = false;
|
|
2691
|
+
constructor(opts = {}) {
|
|
2692
|
+
this.dir = resolve(opts.dir ?? join(tmpdir(), "agentic-mermaid-mcp-artifacts"));
|
|
2693
|
+
this.baseUrl = normalizeBaseUrl(opts.baseUrl);
|
|
2694
|
+
this.maxBytes = positiveSafeInteger(opts.maxBytes ?? DEFAULT_MAX_BYTES, "maxBytes");
|
|
2695
|
+
this.maxTotalBytes = positiveSafeInteger(opts.maxTotalBytes ?? DEFAULT_MAX_TOTAL_BYTES, "maxTotalBytes");
|
|
2696
|
+
this.maxArtifacts = positiveSafeInteger(opts.maxArtifacts ?? DEFAULT_MAX_ARTIFACTS, "maxArtifacts");
|
|
2697
|
+
this.ttlMs = positiveSafeInteger(opts.ttlMs ?? DEFAULT_TTL_MS, "ttlMs");
|
|
2698
|
+
if (this.maxBytes > this.maxTotalBytes) throw new Error("artifact maxBytes must not exceed maxTotalBytes");
|
|
2699
|
+
this.now = opts.now ?? (() => Date.now());
|
|
2700
|
+
mkdirSync(this.dir, { recursive: true, mode: 448 });
|
|
2701
|
+
this.manifestPath = join(this.dir, MANIFEST_NAME);
|
|
2702
|
+
this.lockPath = join(this.dir, LOCK_NAME);
|
|
2703
|
+
this.acquireOwnership();
|
|
2704
|
+
try {
|
|
2705
|
+
this.loadManifest();
|
|
2706
|
+
this.reconcileManagedFiles();
|
|
2707
|
+
this.cleanupExpired();
|
|
2708
|
+
} catch (error) {
|
|
2709
|
+
this.close();
|
|
2710
|
+
throw error;
|
|
2711
|
+
}
|
|
2712
|
+
}
|
|
2713
|
+
setBaseUrl(baseUrl) {
|
|
2714
|
+
this.baseUrl = normalizeBaseUrl(baseUrl);
|
|
2715
|
+
}
|
|
2716
|
+
hasBaseUrl() {
|
|
2717
|
+
return typeof this.baseUrl === "string" && this.baseUrl.length > 0;
|
|
2718
|
+
}
|
|
2719
|
+
write(bytes, opts) {
|
|
2720
|
+
this.assertOpen();
|
|
2721
|
+
if (bytes.byteLength > this.maxBytes) throw new Error(`artifact exceeds maxBytes (${bytes.byteLength} > ${this.maxBytes})`);
|
|
2722
|
+
if (bytes.byteLength > this.maxTotalBytes) throw new Error(`artifact exceeds maxTotalBytes (${bytes.byteLength} > ${this.maxTotalBytes})`);
|
|
2723
|
+
this.cleanupExpired();
|
|
2724
|
+
if (this.records.size >= this.maxArtifacts) throw new Error(`artifact store exceeds maxArtifacts (${this.records.size + 1} > ${this.maxArtifacts})`);
|
|
2725
|
+
const totalBytes = [...this.records.values()].reduce((total, record) => total + record.bytes, 0);
|
|
2726
|
+
if (totalBytes + bytes.byteLength > this.maxTotalBytes) {
|
|
2727
|
+
throw new Error(`artifact store exceeds maxTotalBytes (${totalBytes + bytes.byteLength} > ${this.maxTotalBytes})`);
|
|
2728
|
+
}
|
|
2729
|
+
const buffer = Buffer.from(bytes);
|
|
2730
|
+
const sha256 = digest(buffer);
|
|
2731
|
+
const ext = sanitizeExtension(opts.extension);
|
|
2732
|
+
const createdAt = this.now();
|
|
2733
|
+
const name = `${createdAt.toString(36)}-${randomUUID()}${ext}`;
|
|
2734
|
+
const path = safePath(this.dir, name);
|
|
2735
|
+
const temp = safePath(this.dir, `.${name}.${randomUUID()}.tmp`);
|
|
2736
|
+
const tracked = {
|
|
2737
|
+
name,
|
|
2738
|
+
path,
|
|
2739
|
+
mimeType: opts.mimeType,
|
|
2740
|
+
bytes: buffer.length,
|
|
2741
|
+
sha256,
|
|
2742
|
+
createdAt,
|
|
2743
|
+
expiresAt: createdAt + this.ttlMs
|
|
2744
|
+
};
|
|
2745
|
+
try {
|
|
2746
|
+
writeFileSync(temp, buffer, { mode: 384 });
|
|
2747
|
+
renameSync(temp, path);
|
|
2748
|
+
this.records.set(name, tracked);
|
|
2749
|
+
this.persistManifest();
|
|
2750
|
+
} catch (error) {
|
|
2751
|
+
this.records.delete(name);
|
|
2752
|
+
try {
|
|
2753
|
+
unlinkSync(temp);
|
|
2754
|
+
} catch {
|
|
2755
|
+
}
|
|
2756
|
+
try {
|
|
2757
|
+
unlinkSync(path);
|
|
2758
|
+
} catch {
|
|
2759
|
+
}
|
|
2760
|
+
throw error;
|
|
2761
|
+
}
|
|
2762
|
+
return this.publicRecord(tracked);
|
|
2763
|
+
}
|
|
2764
|
+
read(name) {
|
|
2765
|
+
this.assertOpen();
|
|
2766
|
+
let path;
|
|
2767
|
+
try {
|
|
2768
|
+
path = safePath(this.dir, name);
|
|
2769
|
+
} catch {
|
|
2770
|
+
return null;
|
|
2771
|
+
}
|
|
2772
|
+
const record = this.records.get(name);
|
|
2773
|
+
if (!record || record.path !== path) return null;
|
|
2774
|
+
const now = this.now();
|
|
2775
|
+
if (record.expiresAt <= now) {
|
|
2776
|
+
this.deleteRecord(name, path, true);
|
|
2777
|
+
return null;
|
|
2778
|
+
}
|
|
2779
|
+
if (!existsSync(path)) {
|
|
2780
|
+
this.records.delete(name);
|
|
2781
|
+
this.persistManifest();
|
|
2782
|
+
return null;
|
|
2783
|
+
}
|
|
2784
|
+
const st = lstatSync(path);
|
|
2785
|
+
if (!st.isFile() || st.isSymbolicLink() || st.size !== record.bytes) {
|
|
2786
|
+
this.deleteRecord(name, path, true);
|
|
2787
|
+
return null;
|
|
2788
|
+
}
|
|
2789
|
+
const bytes = readFileSync(path);
|
|
2790
|
+
if (digest(bytes) !== record.sha256) {
|
|
2791
|
+
this.deleteRecord(name, path, true);
|
|
2792
|
+
return null;
|
|
2793
|
+
}
|
|
2794
|
+
return {
|
|
2795
|
+
path,
|
|
2796
|
+
bytes,
|
|
2797
|
+
mimeType: record.mimeType,
|
|
2798
|
+
expiresAt: record.expiresAt,
|
|
2799
|
+
cacheMaxAgeSeconds: Math.max(0, Math.floor((record.expiresAt - now) / 1e3))
|
|
2800
|
+
};
|
|
2801
|
+
}
|
|
2802
|
+
cleanupExpired() {
|
|
2803
|
+
this.assertOpen();
|
|
2804
|
+
const cutoff = this.now();
|
|
2805
|
+
let changed = false;
|
|
2806
|
+
for (const [name, record] of this.records) {
|
|
2807
|
+
if (record.expiresAt <= cutoff) {
|
|
2808
|
+
this.deleteRecord(name, record.path, false);
|
|
2809
|
+
changed = true;
|
|
2810
|
+
}
|
|
2811
|
+
}
|
|
2812
|
+
if (changed) this.persistManifest();
|
|
2813
|
+
}
|
|
2814
|
+
/** Release exclusive ownership of the artifact directory. Idempotent. */
|
|
2815
|
+
close() {
|
|
2816
|
+
if (this.closed) return;
|
|
2817
|
+
this.closed = true;
|
|
2818
|
+
const fd = this.lockFd;
|
|
2819
|
+
this.lockFd = -1;
|
|
2820
|
+
try {
|
|
2821
|
+
if (fd < 0 || !existsSync(this.lockPath)) return;
|
|
2822
|
+
const held = fstatSync(fd);
|
|
2823
|
+
const current = lstatSync(this.lockPath);
|
|
2824
|
+
if (!current.isFile() || current.isSymbolicLink() || held.dev !== current.dev || held.ino !== current.ino) return;
|
|
2825
|
+
const marker = JSON.parse(readFileSync(this.lockPath, "utf8"));
|
|
2826
|
+
if (marker.token === this.lockToken) unlinkSync(this.lockPath);
|
|
2827
|
+
} catch {
|
|
2828
|
+
} finally {
|
|
2829
|
+
if (fd >= 0) try {
|
|
2830
|
+
closeSync(fd);
|
|
2831
|
+
} catch {
|
|
2832
|
+
}
|
|
2833
|
+
}
|
|
2834
|
+
}
|
|
2835
|
+
assertOpen() {
|
|
2836
|
+
if (this.closed || this.lockFd < 0) throw new Error("artifact store is closed");
|
|
2837
|
+
}
|
|
2838
|
+
acquireOwnership() {
|
|
2839
|
+
let fd = -1;
|
|
2840
|
+
try {
|
|
2841
|
+
fd = openSync(this.lockPath, "wx", 384);
|
|
2842
|
+
const marker = JSON.stringify({ schemaVersion: 1, pid: process.pid, host: hostname(), token: this.lockToken });
|
|
2843
|
+
writeSync(fd, marker);
|
|
2844
|
+
fsyncSync(fd);
|
|
2845
|
+
this.lockFd = fd;
|
|
2846
|
+
} catch (error) {
|
|
2847
|
+
if (fd >= 0) {
|
|
2848
|
+
try {
|
|
2849
|
+
closeSync(fd);
|
|
2850
|
+
} catch {
|
|
2851
|
+
}
|
|
2852
|
+
try {
|
|
2853
|
+
unlinkSync(this.lockPath);
|
|
2854
|
+
} catch {
|
|
2855
|
+
}
|
|
2856
|
+
}
|
|
2857
|
+
const code = error.code;
|
|
2858
|
+
if (code === "EEXIST") {
|
|
2859
|
+
throw new Error(`artifact directory is already owned; remove ${LOCK_NAME} only after confirming no server is using it`);
|
|
2860
|
+
}
|
|
2861
|
+
throw error;
|
|
2862
|
+
}
|
|
2863
|
+
}
|
|
2864
|
+
reconcileManagedFiles() {
|
|
2865
|
+
for (const name of readdirSync(this.dir)) {
|
|
2866
|
+
if (!MANAGED_NAME.test(name) || this.records.has(name)) continue;
|
|
2867
|
+
const path = safePath(this.dir, name);
|
|
2868
|
+
try {
|
|
2869
|
+
const entry = lstatSync(path);
|
|
2870
|
+
if (entry.isFile() || entry.isSymbolicLink()) unlinkSync(path);
|
|
2871
|
+
} catch {
|
|
2872
|
+
}
|
|
2873
|
+
}
|
|
2874
|
+
}
|
|
2875
|
+
publicRecord(record) {
|
|
2876
|
+
const output = {
|
|
2877
|
+
name: record.name,
|
|
2878
|
+
path: record.path,
|
|
2879
|
+
mimeType: record.mimeType,
|
|
2880
|
+
bytes: record.bytes,
|
|
2881
|
+
sha256: record.sha256
|
|
2882
|
+
};
|
|
2883
|
+
if (this.baseUrl) output.url = `${this.baseUrl}/${encodeURIComponent(record.name)}`;
|
|
2884
|
+
return output;
|
|
2885
|
+
}
|
|
2886
|
+
loadManifest() {
|
|
2887
|
+
if (!existsSync(this.manifestPath)) return;
|
|
2888
|
+
const stat = lstatSync(this.manifestPath);
|
|
2889
|
+
if (!stat.isFile() || stat.isSymbolicLink() || stat.size > MAX_MANIFEST_BYTES) {
|
|
2890
|
+
throw new Error("artifact manifest must be a bounded regular file");
|
|
2891
|
+
}
|
|
2892
|
+
let manifest;
|
|
2893
|
+
try {
|
|
2894
|
+
manifest = JSON.parse(readFileSync(this.manifestPath, "utf8"));
|
|
2895
|
+
} catch {
|
|
2896
|
+
throw new Error("artifact manifest is not valid JSON");
|
|
2897
|
+
}
|
|
2898
|
+
if (manifest?.schemaVersion !== 1 || !Array.isArray(manifest.records) || manifest.records.length > this.maxArtifacts) {
|
|
2899
|
+
throw new Error("artifact manifest has an unsupported or oversized shape");
|
|
2900
|
+
}
|
|
2901
|
+
let totalBytes = 0;
|
|
2902
|
+
for (const candidate of manifest.records) {
|
|
2903
|
+
if (!candidate || typeof candidate !== "object" || typeof candidate.name !== "string" || !MANAGED_NAME.test(candidate.name) || typeof candidate.mimeType !== "string" || candidate.mimeType.length === 0 || !Number.isSafeInteger(candidate.bytes) || candidate.bytes < 0 || typeof candidate.sha256 !== "string" || !/^[a-f0-9]{64}$/.test(candidate.sha256) || !Number.isSafeInteger(candidate.createdAt) || !Number.isSafeInteger(candidate.expiresAt) || candidate.expiresAt <= candidate.createdAt) {
|
|
2904
|
+
throw new Error("artifact manifest contains an invalid record");
|
|
2905
|
+
}
|
|
2906
|
+
const path = safePath(this.dir, candidate.name);
|
|
2907
|
+
if (!existsSync(path)) continue;
|
|
2908
|
+
const file = lstatSync(path);
|
|
2909
|
+
if (!file.isFile() || file.isSymbolicLink() || file.size !== candidate.bytes) {
|
|
2910
|
+
throw new Error(`artifact manifest record ${candidate.name} does not match a regular file`);
|
|
2911
|
+
}
|
|
2912
|
+
const bytes = readFileSync(path);
|
|
2913
|
+
if (digest(bytes) !== candidate.sha256) throw new Error(`artifact manifest record ${candidate.name} failed integrity verification`);
|
|
2914
|
+
totalBytes += candidate.bytes;
|
|
2915
|
+
if (totalBytes > this.maxTotalBytes) throw new Error("artifact manifest exceeds maxTotalBytes");
|
|
2916
|
+
this.records.set(candidate.name, { ...candidate, path });
|
|
2917
|
+
}
|
|
2918
|
+
if (this.records.size !== manifest.records.length) this.persistManifest();
|
|
2919
|
+
}
|
|
2920
|
+
persistManifest() {
|
|
2921
|
+
const records = [...this.records.values()].sort((a, b) => a.createdAt - b.createdAt || (a.name < b.name ? -1 : a.name > b.name ? 1 : 0)).map(({ name, mimeType, bytes, sha256, createdAt, expiresAt }) => ({ name, mimeType, bytes, sha256, createdAt, expiresAt }));
|
|
2922
|
+
const body = JSON.stringify({ schemaVersion: 1, records });
|
|
2923
|
+
if (Buffer.byteLength(body) > MAX_MANIFEST_BYTES) throw new Error("artifact manifest exceeds its byte budget");
|
|
2924
|
+
const temp = join(this.dir, `.${MANIFEST_NAME}.${process.pid}.${randomUUID()}.tmp`);
|
|
2925
|
+
try {
|
|
2926
|
+
writeFileSync(temp, body, { mode: 384 });
|
|
2927
|
+
renameSync(temp, this.manifestPath);
|
|
2928
|
+
} finally {
|
|
2929
|
+
try {
|
|
2930
|
+
unlinkSync(temp);
|
|
2931
|
+
} catch {
|
|
2932
|
+
}
|
|
2933
|
+
}
|
|
2934
|
+
}
|
|
2935
|
+
deleteRecord(name, path, persist) {
|
|
2936
|
+
this.records.delete(name);
|
|
2937
|
+
try {
|
|
2938
|
+
unlinkSync(path);
|
|
2939
|
+
} catch {
|
|
2940
|
+
}
|
|
2941
|
+
if (persist) this.persistManifest();
|
|
2942
|
+
}
|
|
2943
|
+
};
|
|
2944
|
+
function createArtifactStore(opts = {}) {
|
|
2945
|
+
return new ArtifactStore(opts);
|
|
2946
|
+
}
|
|
2947
|
+
function digest(bytes) {
|
|
2948
|
+
return createHash("sha256").update(bytes).digest("hex");
|
|
2949
|
+
}
|
|
2950
|
+
function normalizeBaseUrl(url) {
|
|
2951
|
+
if (!url) return void 0;
|
|
2952
|
+
return url.replace(/\/+$/, "");
|
|
2953
|
+
}
|
|
2954
|
+
function sanitizeExtension(extension) {
|
|
2955
|
+
const ext = extension.startsWith(".") ? extension : `.${extension}`;
|
|
2956
|
+
const safe = ext.toLowerCase().replace(/[^.a-z0-9_-]/g, "");
|
|
2957
|
+
return safe && safe !== "." ? safe : ".bin";
|
|
2958
|
+
}
|
|
2959
|
+
function safePath(root, name) {
|
|
2960
|
+
const leaf = basename(name);
|
|
2961
|
+
if (!leaf || leaf !== name || leaf.includes("..")) throw new Error("invalid artifact name");
|
|
2962
|
+
const path = resolve(root, leaf);
|
|
2963
|
+
const prefix = root.endsWith(sep) ? root : `${root}${sep}`;
|
|
2964
|
+
if (!path.startsWith(prefix)) throw new Error("artifact path escaped root");
|
|
2965
|
+
return path;
|
|
2966
|
+
}
|
|
2967
|
+
|
|
2968
|
+
// src/mcp/server.ts
|
|
2969
|
+
var PROTOCOL_VERSION = "2024-11-05";
|
|
2970
|
+
var MAX_RPC_BODY_BYTES = 1024 * 1024;
|
|
2971
|
+
var MAX_SANDBOX_TIMEOUT_MS = 3e4;
|
|
2972
|
+
var MAX_SSE_SESSIONS = 32;
|
|
2973
|
+
var HttpStatusError = class extends Error {
|
|
2974
|
+
constructor(status, message) {
|
|
2975
|
+
super(message);
|
|
2976
|
+
this.status = status;
|
|
2977
|
+
}
|
|
2978
|
+
status;
|
|
2979
|
+
};
|
|
2980
|
+
var LOCAL_TOOLS = [
|
|
2981
|
+
createExecuteTool({ sdkDeclaration: SDK_CORE_DECLARATION }),
|
|
2982
|
+
withClosedMcpInputSchema(createDescribeSdkTool()),
|
|
2983
|
+
createRenderPngTool("local"),
|
|
2984
|
+
createDescribeTool()
|
|
2985
|
+
];
|
|
2986
|
+
var defaultArtifactStore;
|
|
2987
|
+
var defaultArtifactStoreExitHook = false;
|
|
2988
|
+
var MCP_NARROWERS = BUILTIN_FAMILY_METADATA.map((f) => f.narrower).join("/");
|
|
2989
|
+
var LOCAL_INSTRUCTIONS = `agentic-mermaid Code Mode server. Primary tool execute runs synchronous JavaScript against the typed mermaid.* SDK in a sandbox; async/await and Promise jobs are not supported. describe_sdk progressively discloses one family's version-matched mutation schema; call it before authoring unfamiliar ops. render_png and describe are narrow helpers. render_png can return base64, managed file paths, or managed URLs when the transport config provides an artifact store. There is no mutate tool on this server: structured edits go through the SDK's mermaid.mutate(...) inside execute; narrow via ${MCP_NARROWERS}. Every built-in renderable family ships a typed path when the body narrows; only opaque fallback bodies are source-level only. Layout is deterministic; there is no layout seed (the optional style seed only re-rolls ink of styled looks, never geometry).`;
|
|
2990
|
+
var LOCAL_SURFACE = {
|
|
2991
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
2992
|
+
tools: LOCAL_TOOLS,
|
|
2993
|
+
instructions: LOCAL_INSTRUCTIONS,
|
|
2994
|
+
handleToolCall
|
|
2995
|
+
};
|
|
2996
|
+
async function handleRequest(req, context = {}) {
|
|
2997
|
+
return dispatchMcpRequest(req, context, LOCAL_SURFACE);
|
|
2998
|
+
}
|
|
2999
|
+
async function handleToolCall(id, params, context) {
|
|
3000
|
+
const p = params;
|
|
3001
|
+
const name = p?.name;
|
|
3002
|
+
const args = p?.arguments ?? {};
|
|
3003
|
+
if (name === "describe_sdk") {
|
|
3004
|
+
try {
|
|
3005
|
+
const payload = describeSdkPayload(args);
|
|
3006
|
+
return reply(id, { content: [{ type: "text", text: JSON.stringify(payload) }], isError: false });
|
|
3007
|
+
} catch (e) {
|
|
3008
|
+
return rpcError(id, -32602, e instanceof Error ? e.message : String(e));
|
|
3009
|
+
}
|
|
3010
|
+
}
|
|
3011
|
+
if (name === "execute") {
|
|
3012
|
+
const code = args.code;
|
|
3013
|
+
const requestedTimeoutMs = args.timeoutMs;
|
|
3014
|
+
if (requestedTimeoutMs !== void 0 && !isValidExecuteTimeout(requestedTimeoutMs)) {
|
|
3015
|
+
return rpcError(id, -32602, EXECUTE_TIMEOUT_ERROR);
|
|
3016
|
+
}
|
|
3017
|
+
const timeoutMs = Math.min(
|
|
3018
|
+
requestedTimeoutMs ?? DEFAULT_EXECUTE_TIMEOUT_MS,
|
|
3019
|
+
context.maxSandboxTimeoutMs ?? MAX_SANDBOX_TIMEOUT_MS
|
|
3020
|
+
);
|
|
3021
|
+
if (typeof code !== "string") return rpcError(id, -32602, "execute requires `code` (string)");
|
|
3022
|
+
const r = await executeInSandbox(code, { timeoutMs });
|
|
3023
|
+
return reply(id, { content: [{ type: "text", text: JSON.stringify(r) }], isError: !r.ok });
|
|
3024
|
+
}
|
|
3025
|
+
if (name === "render_png") return handleRenderPng(id, args, context);
|
|
3026
|
+
if (name === "describe") {
|
|
3027
|
+
const source = args.source;
|
|
3028
|
+
if (typeof source !== "string") return rpcError(id, -32602, "describe requires `source` (string)");
|
|
3029
|
+
try {
|
|
3030
|
+
const payload = mcpDescribePayload(source, args);
|
|
3031
|
+
return reply(id, { content: [{ type: "text", text: JSON.stringify(payload) }], isError: !payload.ok });
|
|
3032
|
+
} catch (e) {
|
|
3033
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
3034
|
+
const payload = { ok: false, error: { code: "DESCRIBE_FAILED", message: msg } };
|
|
3035
|
+
return reply(id, { content: [{ type: "text", text: JSON.stringify(payload) }], isError: true });
|
|
3036
|
+
}
|
|
3037
|
+
}
|
|
3038
|
+
return rpcError(id, -32602, `Unknown tool: ${name ?? "<none>"}`);
|
|
3039
|
+
}
|
|
3040
|
+
function handleRenderPng(id, args, context) {
|
|
3041
|
+
const source = args.source;
|
|
3042
|
+
const output = args.output ?? "base64";
|
|
3043
|
+
if (typeof source !== "string") return rpcError(id, -32602, "render_png requires `source` (string)");
|
|
3044
|
+
if (!["base64", "file", "url"].includes(output)) return rpcError(id, -32602, "render_png output must be one of: base64, file, url");
|
|
3045
|
+
try {
|
|
3046
|
+
const fontWarnings = [];
|
|
3047
|
+
const pngOutput = projectNativePngOutputPolicyInput(args);
|
|
3048
|
+
const rendered = renderMermaidPNGWithReceipt(source, {
|
|
3049
|
+
...projectMcpRenderOptions(args),
|
|
3050
|
+
...pngOutput,
|
|
3051
|
+
onWarning: (warning) => fontWarnings.push(warning)
|
|
3052
|
+
});
|
|
3053
|
+
const warnings = [...configWarningsForMermaid(source), ...fontWarnings].filter((warning, index, all) => all.findIndex((candidate) => JSON.stringify(candidate) === JSON.stringify(warning)) === index);
|
|
3054
|
+
if (output === "base64") {
|
|
3055
|
+
const png_base64 = Buffer.from(rendered.png).toString("base64");
|
|
3056
|
+
const payload2 = { ok: true, png_base64, receipt: rendered.receipt, runtime: rendered.runtime, warnings };
|
|
3057
|
+
return reply(id, { content: [{ type: "text", text: JSON.stringify(payload2) }], isError: false });
|
|
3058
|
+
}
|
|
3059
|
+
const store = context.artifactStore ?? getDefaultArtifactStore();
|
|
3060
|
+
if (output === "url" && !store.hasBaseUrl()) return rpcError(id, -32602, "render_png output=url requires an HTTP/SSE artifact base URL");
|
|
3061
|
+
const artifact = store.write(rendered.png, { extension: ".png", mimeType: "image/png" });
|
|
3062
|
+
const payload = { ok: true, artifact: artifactPayload(artifact, output), receipt: rendered.receipt, runtime: rendered.runtime, warnings };
|
|
3063
|
+
return reply(id, { content: [{ type: "text", text: JSON.stringify(payload) }], isError: false });
|
|
3064
|
+
} catch (e) {
|
|
3065
|
+
const payload = {
|
|
3066
|
+
ok: false,
|
|
3067
|
+
error: projectRenderErrorDiagnostic(e)
|
|
3068
|
+
};
|
|
3069
|
+
return reply(id, { content: [{ type: "text", text: JSON.stringify(payload) }], isError: true });
|
|
3070
|
+
}
|
|
3071
|
+
}
|
|
3072
|
+
function artifactPayload(artifact, output) {
|
|
3073
|
+
const base = { mimeType: artifact.mimeType, bytes: artifact.bytes, sha256: artifact.sha256 };
|
|
3074
|
+
if (output === "url") return { ...base, url: artifact.url };
|
|
3075
|
+
return { ...base, path: artifact.path };
|
|
3076
|
+
}
|
|
3077
|
+
function getDefaultArtifactStore() {
|
|
3078
|
+
defaultArtifactStore ??= createArtifactStore({ dir: join2(tmpdir2(), "agentic-mermaid-mcp-artifacts") });
|
|
3079
|
+
if (!defaultArtifactStoreExitHook) {
|
|
3080
|
+
defaultArtifactStoreExitHook = true;
|
|
3081
|
+
process.once("exit", () => defaultArtifactStore?.close());
|
|
3082
|
+
}
|
|
3083
|
+
return defaultArtifactStore;
|
|
3084
|
+
}
|
|
3085
|
+
function warmUpPngRenderer() {
|
|
3086
|
+
try {
|
|
3087
|
+
renderMermaidPNG("flowchart LR\n A --> B");
|
|
3088
|
+
} catch {
|
|
3089
|
+
}
|
|
3090
|
+
}
|
|
3091
|
+
async function runStdio(options = {}) {
|
|
3092
|
+
warmUpPngRenderer();
|
|
3093
|
+
const artifactStore = createArtifactStore({
|
|
3094
|
+
dir: options.artifactDir,
|
|
3095
|
+
maxBytes: options.maxArtifactBytes,
|
|
3096
|
+
maxTotalBytes: options.maxArtifactTotalBytes,
|
|
3097
|
+
maxArtifacts: options.maxArtifacts,
|
|
3098
|
+
ttlMs: options.artifactTtlMs
|
|
3099
|
+
});
|
|
3100
|
+
process.stdin.setEncoding("utf8");
|
|
3101
|
+
let buf = "";
|
|
3102
|
+
process.stdin.on("data", async (chunk) => {
|
|
3103
|
+
buf += chunk;
|
|
3104
|
+
let nl;
|
|
3105
|
+
while ((nl = buf.indexOf("\n")) >= 0) {
|
|
3106
|
+
const line = buf.slice(0, nl).trim();
|
|
3107
|
+
buf = buf.slice(nl + 1);
|
|
3108
|
+
if (!line) continue;
|
|
3109
|
+
try {
|
|
3110
|
+
const exact = preserveExactJsonRpcIds(line);
|
|
3111
|
+
const res = await handleRequest(JSON.parse(exact.body), { artifactStore, maxSandboxTimeoutMs: options.maxSandboxTimeoutMs });
|
|
3112
|
+
if (res) process.stdout.write(stringifyJsonRpc(res, exact.ids) + "\n");
|
|
3113
|
+
} catch (e) {
|
|
3114
|
+
process.stdout.write(JSON.stringify(rpcError(null, -32700, `parse error: ${e.message}`)) + "\n");
|
|
3115
|
+
}
|
|
3116
|
+
}
|
|
3117
|
+
});
|
|
3118
|
+
try {
|
|
3119
|
+
await new Promise((resolve2) => {
|
|
3120
|
+
process.stdin.on("end", () => resolve2());
|
|
3121
|
+
process.stdin.on("close", () => resolve2());
|
|
3122
|
+
});
|
|
3123
|
+
} finally {
|
|
3124
|
+
artifactStore.close();
|
|
3125
|
+
}
|
|
3126
|
+
}
|
|
3127
|
+
async function startHttpServer(options = {}) {
|
|
3128
|
+
warmUpPngRenderer();
|
|
3129
|
+
const host = options.host ?? "127.0.0.1";
|
|
3130
|
+
const port = options.port ?? 3e3;
|
|
3131
|
+
if (!isLoopbackHost(host) && !options.authToken) throw new Error("HTTP MCP remote bind requires --auth-token");
|
|
3132
|
+
const sessions = /* @__PURE__ */ new Map();
|
|
3133
|
+
let baseUrl = "";
|
|
3134
|
+
const maxRpcBodyBytes = options.maxRpcBodyBytes ?? MAX_RPC_BODY_BYTES;
|
|
3135
|
+
const maxSseSessions = options.maxSseSessions ?? MAX_SSE_SESSIONS;
|
|
3136
|
+
const publicOrigin = httpOrigin(options.publicUrl, "publicUrl");
|
|
3137
|
+
if (!Number.isInteger(maxSseSessions) || maxSseSessions <= 0) throw new Error("maxSseSessions must be a positive integer");
|
|
3138
|
+
const artifactStore = createArtifactStore({
|
|
3139
|
+
dir: options.artifactDir,
|
|
3140
|
+
maxBytes: options.maxArtifactBytes,
|
|
3141
|
+
maxTotalBytes: options.maxArtifactTotalBytes,
|
|
3142
|
+
maxArtifacts: options.maxArtifacts,
|
|
3143
|
+
ttlMs: options.artifactTtlMs
|
|
3144
|
+
});
|
|
3145
|
+
const context = { artifactStore, maxSandboxTimeoutMs: options.maxSandboxTimeoutMs };
|
|
3146
|
+
const server = createServer(async (req, res) => {
|
|
3147
|
+
try {
|
|
3148
|
+
const u = new URL(req.url ?? "/", baseUrl || `http://${host}:${port || 0}`);
|
|
3149
|
+
if (req.method === "GET" && u.pathname === "/health") return sendJson(res, 200, { ok: true });
|
|
3150
|
+
if (req.method === "GET" && u.pathname === "/sse") {
|
|
3151
|
+
if (!authorizeHttpAccess(req, res, baseUrl, publicOrigin, options.authToken)) return;
|
|
3152
|
+
return openSse(req, res, sessions, publicOrigin ?? baseUrl, maxSseSessions);
|
|
3153
|
+
}
|
|
3154
|
+
if (req.method === "POST" && u.pathname === "/message") {
|
|
3155
|
+
if (!authorizeHttpRpc(req, res, baseUrl, publicOrigin, options.authToken)) return;
|
|
3156
|
+
return await postSseMessage(req, res, sessions, context, maxRpcBodyBytes);
|
|
3157
|
+
}
|
|
3158
|
+
if (req.method === "POST" && u.pathname === "/rpc") {
|
|
3159
|
+
if (!authorizeHttpRpc(req, res, baseUrl, publicOrigin, options.authToken)) return;
|
|
3160
|
+
return await postRpc(req, res, context, maxRpcBodyBytes);
|
|
3161
|
+
}
|
|
3162
|
+
if (req.method === "GET" && u.pathname.startsWith("/artifacts/")) {
|
|
3163
|
+
if (!authorizeHttpAccess(req, res, baseUrl, publicOrigin, options.authToken)) return;
|
|
3164
|
+
return serveArtifact(res, artifactStore, decodeURIComponent(u.pathname.slice("/artifacts/".length)));
|
|
3165
|
+
}
|
|
3166
|
+
return sendJson(res, 404, { ok: false, error: "not found" });
|
|
3167
|
+
} catch (e) {
|
|
3168
|
+
if (e instanceof HttpStatusError) return sendJson(res, e.status, { ok: false, error: e.message });
|
|
3169
|
+
return sendJson(res, 500, { ok: false, error: e instanceof Error ? e.message : String(e) });
|
|
3170
|
+
}
|
|
3171
|
+
});
|
|
3172
|
+
try {
|
|
3173
|
+
await new Promise((resolve2, reject) => {
|
|
3174
|
+
server.once("error", reject);
|
|
3175
|
+
server.listen(port, host, () => {
|
|
3176
|
+
server.off("error", reject);
|
|
3177
|
+
resolve2();
|
|
3178
|
+
});
|
|
3179
|
+
});
|
|
3180
|
+
const address = server.address();
|
|
3181
|
+
if (!address || typeof address === "string") throw new Error("HTTP MCP server did not expose a TCP address");
|
|
3182
|
+
baseUrl = `http://${host}:${address.port}`;
|
|
3183
|
+
artifactStore.setBaseUrl(options.publicUrl ?? `${baseUrl}/artifacts`);
|
|
3184
|
+
} catch (error) {
|
|
3185
|
+
artifactStore.close();
|
|
3186
|
+
try {
|
|
3187
|
+
server.close();
|
|
3188
|
+
} catch {
|
|
3189
|
+
}
|
|
3190
|
+
throw error;
|
|
3191
|
+
}
|
|
3192
|
+
return {
|
|
3193
|
+
server,
|
|
3194
|
+
url: baseUrl,
|
|
3195
|
+
artifactStore,
|
|
3196
|
+
close: () => new Promise((resolve2, reject) => {
|
|
3197
|
+
for (const sse of sessions.values()) sse.end();
|
|
3198
|
+
sessions.clear();
|
|
3199
|
+
server.close((err) => {
|
|
3200
|
+
artifactStore.close();
|
|
3201
|
+
if (err) reject(err);
|
|
3202
|
+
else resolve2();
|
|
3203
|
+
});
|
|
3204
|
+
})
|
|
3205
|
+
};
|
|
3206
|
+
}
|
|
3207
|
+
async function runHttp(options = {}) {
|
|
3208
|
+
const started = await startHttpServer(options);
|
|
3209
|
+
process.stderr.write(`agentic-mermaid-mcp HTTP/SSE listening at ${started.url} (SSE: ${started.url}/sse, plain JSON-RPC: ${started.url}/rpc)
|
|
3210
|
+
`);
|
|
3211
|
+
return new Promise((resolve2) => {
|
|
3212
|
+
const shutdown = () => {
|
|
3213
|
+
started.close().finally(resolve2);
|
|
3214
|
+
};
|
|
3215
|
+
process.once("SIGINT", shutdown);
|
|
3216
|
+
process.once("SIGTERM", shutdown);
|
|
3217
|
+
});
|
|
3218
|
+
}
|
|
3219
|
+
function isLoopbackHost(host) {
|
|
3220
|
+
return host === "127.0.0.1" || host === "localhost" || host === "::1";
|
|
3221
|
+
}
|
|
3222
|
+
function httpOrigin(value, label) {
|
|
3223
|
+
if (!value) return void 0;
|
|
3224
|
+
let parsed;
|
|
3225
|
+
try {
|
|
3226
|
+
parsed = new URL(value);
|
|
3227
|
+
} catch {
|
|
3228
|
+
throw new Error(`${label} must be an absolute http(s) URL`);
|
|
3229
|
+
}
|
|
3230
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
3231
|
+
throw new Error(`${label} must be an absolute http(s) URL`);
|
|
3232
|
+
}
|
|
3233
|
+
return parsed.origin;
|
|
3234
|
+
}
|
|
3235
|
+
function authorizeHttpAccess(req, res, baseUrl, publicOrigin, authToken) {
|
|
3236
|
+
const origin = req.headers.origin;
|
|
3237
|
+
if (origin && origin !== baseUrl && origin !== publicOrigin) {
|
|
3238
|
+
sendJson(res, 403, { ok: false, error: "origin not allowed" });
|
|
3239
|
+
return false;
|
|
3240
|
+
}
|
|
3241
|
+
if (authToken && req.headers.authorization !== `Bearer ${authToken}`) {
|
|
3242
|
+
sendJson(res, 401, { ok: false, error: "missing or invalid bearer token" });
|
|
3243
|
+
return false;
|
|
3244
|
+
}
|
|
3245
|
+
return true;
|
|
3246
|
+
}
|
|
3247
|
+
function authorizeHttpRpc(req, res, baseUrl, publicOrigin, authToken) {
|
|
3248
|
+
if (!authorizeHttpAccess(req, res, baseUrl, publicOrigin, authToken)) return false;
|
|
3249
|
+
if (!isJsonContentType(String(req.headers["content-type"] ?? ""))) {
|
|
3250
|
+
sendJson(res, 415, { ok: false, error: "HTTP MCP JSON-RPC requires content-type application/json" });
|
|
3251
|
+
return false;
|
|
3252
|
+
}
|
|
3253
|
+
return true;
|
|
3254
|
+
}
|
|
3255
|
+
function openSse(req, res, sessions, baseUrl, maxSessions) {
|
|
3256
|
+
if (sessions.size >= maxSessions) {
|
|
3257
|
+
sendJson(res, 503, { ok: false, error: `SSE session limit reached (${maxSessions})` });
|
|
3258
|
+
return;
|
|
3259
|
+
}
|
|
3260
|
+
const sessionId = randomUUID2();
|
|
3261
|
+
sessions.set(sessionId, res);
|
|
3262
|
+
res.writeHead(200, {
|
|
3263
|
+
"Content-Type": "text/event-stream; charset=utf-8",
|
|
3264
|
+
"Cache-Control": "no-cache, no-transform",
|
|
3265
|
+
Connection: "keep-alive",
|
|
3266
|
+
"X-Accel-Buffering": "no"
|
|
3267
|
+
});
|
|
3268
|
+
res.write(`event: endpoint
|
|
3269
|
+
data: ${baseUrl}/message?sessionId=${encodeURIComponent(sessionId)}
|
|
3270
|
+
|
|
3271
|
+
`);
|
|
3272
|
+
const heartbeat = setInterval(() => res.write(": keepalive\n\n"), 25e3);
|
|
3273
|
+
req.on("close", () => {
|
|
3274
|
+
clearInterval(heartbeat);
|
|
3275
|
+
sessions.delete(sessionId);
|
|
3276
|
+
});
|
|
3277
|
+
}
|
|
3278
|
+
async function postSseMessage(req, res, sessions, context, maxBytes) {
|
|
3279
|
+
const u = new URL(req.url ?? "/", "http://localhost");
|
|
3280
|
+
const sessionId = u.searchParams.get("sessionId") ?? "";
|
|
3281
|
+
const sse = sessions.get(sessionId);
|
|
3282
|
+
if (!sse) return sendJson(res, 404, { ok: false, error: "unknown sessionId" });
|
|
3283
|
+
const body = await readRequestBody(req, maxBytes);
|
|
3284
|
+
const exact = preserveExactJsonRpcIds(body);
|
|
3285
|
+
let parsed;
|
|
3286
|
+
try {
|
|
3287
|
+
parsed = JSON.parse(exact.body);
|
|
3288
|
+
} catch {
|
|
3289
|
+
throw new HttpStatusError(400, "invalid JSON-RPC body");
|
|
3290
|
+
}
|
|
3291
|
+
const response = await handleRequest(parsed, context);
|
|
3292
|
+
if (response) {
|
|
3293
|
+
sse.write(`event: message
|
|
3294
|
+
data: ${stringifyJsonRpc(response, exact.ids)}
|
|
3295
|
+
|
|
3296
|
+
`);
|
|
3297
|
+
return sendJson(res, 202, { ok: true });
|
|
3298
|
+
}
|
|
3299
|
+
res.writeHead(202);
|
|
3300
|
+
res.end();
|
|
3301
|
+
}
|
|
3302
|
+
async function postRpc(req, res, context, maxBytes) {
|
|
3303
|
+
const body = await readRequestBody(req, maxBytes);
|
|
3304
|
+
const exact = preserveExactJsonRpcIds(body);
|
|
3305
|
+
let parsed;
|
|
3306
|
+
try {
|
|
3307
|
+
parsed = JSON.parse(exact.body);
|
|
3308
|
+
} catch {
|
|
3309
|
+
throw new HttpStatusError(400, "invalid JSON-RPC body");
|
|
3310
|
+
}
|
|
3311
|
+
const response = await handleRequest(parsed, context);
|
|
3312
|
+
if (response === null) {
|
|
3313
|
+
res.writeHead(202);
|
|
3314
|
+
res.end();
|
|
3315
|
+
return;
|
|
3316
|
+
}
|
|
3317
|
+
sendJson(res, 200, response, exact.ids);
|
|
3318
|
+
}
|
|
3319
|
+
function serveArtifact(res, store, name) {
|
|
3320
|
+
const artifact = store.read(name);
|
|
3321
|
+
if (!artifact) return sendJson(res, 404, { ok: false, error: "artifact not found" });
|
|
3322
|
+
res.writeHead(200, {
|
|
3323
|
+
"Content-Type": artifact.mimeType,
|
|
3324
|
+
"Content-Length": artifact.bytes.length,
|
|
3325
|
+
"Cache-Control": `private, max-age=${artifact.cacheMaxAgeSeconds}, immutable`
|
|
3326
|
+
});
|
|
3327
|
+
res.end(artifact.bytes);
|
|
3328
|
+
}
|
|
3329
|
+
function sendJson(res, status, payload, exactIds = []) {
|
|
3330
|
+
const body = stringifyJsonRpc(payload, exactIds);
|
|
3331
|
+
res.writeHead(status, { "Content-Type": "application/json; charset=utf-8", "Content-Length": Buffer.byteLength(body) });
|
|
3332
|
+
res.end(body);
|
|
3333
|
+
}
|
|
3334
|
+
async function readRequestBody(req, maxBytes = MAX_RPC_BODY_BYTES) {
|
|
3335
|
+
const declared = Number(req.headers["content-length"] ?? 0);
|
|
3336
|
+
if (Number.isFinite(declared) && declared > maxBytes) throw new HttpStatusError(413, `request body exceeds ${maxBytes} bytes`);
|
|
3337
|
+
const chunks = [];
|
|
3338
|
+
let bytes = 0;
|
|
3339
|
+
for await (const chunk of req) {
|
|
3340
|
+
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk));
|
|
3341
|
+
bytes += buffer.byteLength;
|
|
3342
|
+
if (bytes > maxBytes) throw new HttpStatusError(413, `request body exceeds ${maxBytes} bytes`);
|
|
3343
|
+
chunks.push(buffer);
|
|
3344
|
+
}
|
|
3345
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
3346
|
+
}
|
|
3347
|
+
|
|
3348
|
+
// src/mcp/mcp-cli.ts
|
|
3349
|
+
var MCP_CLI_HELP = `agentic-mermaid-mcp [--transport stdio|http] [--host 127.0.0.1] [--port 3000]
|
|
3350
|
+
|
|
3351
|
+
Options:
|
|
3352
|
+
--transport stdio|http Transport to run (default stdio).
|
|
3353
|
+
--host <host> HTTP/SSE bind host (default 127.0.0.1).
|
|
3354
|
+
--port <port> HTTP/SSE bind port (default 3000, 0 = ephemeral).
|
|
3355
|
+
--artifact-dir <dir> Managed artifact directory for file/url outputs.
|
|
3356
|
+
--public-url <url> Public artifact prefix; its origin is allowed by HTTP/SSE.
|
|
3357
|
+
--max-artifact-bytes <n> Maximum bytes in one artifact (default 20MiB).
|
|
3358
|
+
--max-artifact-total-bytes <n> Aggregate managed artifact bytes (default 200MiB).
|
|
3359
|
+
--max-artifacts <n> Maximum managed artifact count (default 1000).
|
|
3360
|
+
--artifact-ttl-ms <n> Artifact cleanup TTL (default 1h).
|
|
3361
|
+
--max-rpc-body-bytes <n> Max HTTP JSON-RPC body size (default 1MiB).
|
|
3362
|
+
--auth-token <token> Bearer token required for every non-health HTTP route.
|
|
3363
|
+
--max-sandbox-timeout-ms <n> Max execute timeout (default 30000).
|
|
3364
|
+
--help Show this help.
|
|
3365
|
+
`;
|
|
3366
|
+
var MCP_FLAG_SPECS = Object.freeze({
|
|
3367
|
+
help: { kind: "boolean", applicability: "both" },
|
|
3368
|
+
transport: { kind: "string", applicability: "both" },
|
|
3369
|
+
host: { kind: "string", applicability: "http" },
|
|
3370
|
+
port: { kind: "integer", applicability: "http", min: 0, max: 65535 },
|
|
3371
|
+
"artifact-dir": { kind: "string", applicability: "both" },
|
|
3372
|
+
"public-url": { kind: "string", applicability: "http" },
|
|
3373
|
+
"max-artifact-bytes": { kind: "integer", applicability: "both", min: 1 },
|
|
3374
|
+
"max-artifact-total-bytes": { kind: "integer", applicability: "both", min: 1 },
|
|
3375
|
+
"max-artifacts": { kind: "integer", applicability: "both", min: 1 },
|
|
3376
|
+
"artifact-ttl-ms": { kind: "integer", applicability: "both", min: 1 },
|
|
3377
|
+
"max-rpc-body-bytes": { kind: "integer", applicability: "http", min: 1 },
|
|
3378
|
+
"auth-token": { kind: "string", applicability: "http" },
|
|
3379
|
+
"max-sandbox-timeout-ms": { kind: "integer", applicability: "both", min: 1 }
|
|
3380
|
+
});
|
|
3381
|
+
function parseMcpFlags(argv) {
|
|
3382
|
+
const flags = {};
|
|
3383
|
+
for (let index = 0; index < argv.length; index++) {
|
|
3384
|
+
let token = argv[index];
|
|
3385
|
+
if (token === "-h") token = "--help";
|
|
3386
|
+
if (!token.startsWith("--")) throw new Error(`unexpected positional argument: ${token}`);
|
|
3387
|
+
const equals = token.indexOf("=");
|
|
3388
|
+
const name = token.slice(2, equals < 0 ? void 0 : equals);
|
|
3389
|
+
const spec = MCP_FLAG_SPECS[name];
|
|
3390
|
+
if (!spec) throw new Error(`unknown option: --${name}`);
|
|
3391
|
+
if (Object.hasOwn(flags, name)) throw new Error(`duplicate flag: --${name}`);
|
|
3392
|
+
const inline = equals < 0 ? void 0 : token.slice(equals + 1);
|
|
3393
|
+
if (spec.kind === "boolean") {
|
|
3394
|
+
if (inline !== void 0) throw new Error(`--${name} does not accept a value`);
|
|
3395
|
+
flags[name] = true;
|
|
3396
|
+
continue;
|
|
3397
|
+
}
|
|
3398
|
+
const value = inline ?? argv[++index];
|
|
3399
|
+
if (value === void 0 || value.length === 0 || value.startsWith("-")) {
|
|
3400
|
+
throw new Error(`--${name} requires a value`);
|
|
3401
|
+
}
|
|
3402
|
+
flags[name] = value;
|
|
3403
|
+
}
|
|
3404
|
+
return Object.freeze(flags);
|
|
3405
|
+
}
|
|
3406
|
+
function stringFlag(flags, name) {
|
|
3407
|
+
const value = flags[name];
|
|
3408
|
+
return typeof value === "string" ? value : void 0;
|
|
3409
|
+
}
|
|
3410
|
+
function integerFlag(flags, name) {
|
|
3411
|
+
const raw = stringFlag(flags, name);
|
|
3412
|
+
if (raw === void 0) return void 0;
|
|
3413
|
+
const candidate = MCP_FLAG_SPECS[name];
|
|
3414
|
+
if (candidate.kind !== "integer") throw new Error(`internal MCP flag authority mismatch for --${name}`);
|
|
3415
|
+
const spec = candidate;
|
|
3416
|
+
const value = Number(raw);
|
|
3417
|
+
if (!Number.isSafeInteger(value) || value < spec.min || spec.max !== void 0 && value > spec.max) {
|
|
3418
|
+
throw new Error(`--${name} must be an integer${spec.max === void 0 ? ` >= ${spec.min}` : ` between ${spec.min} and ${spec.max}`}`);
|
|
3419
|
+
}
|
|
3420
|
+
return value;
|
|
3421
|
+
}
|
|
3422
|
+
function parseMcpCliOptions(argv) {
|
|
3423
|
+
const flags = parseMcpFlags(argv);
|
|
3424
|
+
const explicitTransport = stringFlag(flags, "transport");
|
|
3425
|
+
if (explicitTransport !== void 0 && explicitTransport !== "stdio" && explicitTransport !== "http") {
|
|
3426
|
+
throw new Error(`unknown transport: ${explicitTransport}`);
|
|
3427
|
+
}
|
|
3428
|
+
const transport = explicitTransport === "http" ? "http" : "stdio";
|
|
3429
|
+
if (transport === "stdio") {
|
|
3430
|
+
const invalid = Object.keys(flags).filter((name) => MCP_FLAG_SPECS[name]?.applicability === "http");
|
|
3431
|
+
if (invalid.length > 0) throw new Error(`${invalid.map((name) => `--${name}`).join(", ")} ${invalid.length === 1 ? "is" : "are"} valid only with HTTP transport`);
|
|
3432
|
+
}
|
|
3433
|
+
return {
|
|
3434
|
+
transport,
|
|
3435
|
+
httpOptions: {
|
|
3436
|
+
host: stringFlag(flags, "host"),
|
|
3437
|
+
port: integerFlag(flags, "port"),
|
|
3438
|
+
artifactDir: stringFlag(flags, "artifact-dir"),
|
|
3439
|
+
publicUrl: stringFlag(flags, "public-url"),
|
|
3440
|
+
maxArtifactBytes: integerFlag(flags, "max-artifact-bytes"),
|
|
3441
|
+
maxArtifactTotalBytes: integerFlag(flags, "max-artifact-total-bytes"),
|
|
3442
|
+
maxArtifacts: integerFlag(flags, "max-artifacts"),
|
|
3443
|
+
artifactTtlMs: integerFlag(flags, "artifact-ttl-ms"),
|
|
3444
|
+
maxRpcBodyBytes: integerFlag(flags, "max-rpc-body-bytes"),
|
|
3445
|
+
authToken: stringFlag(flags, "auth-token"),
|
|
3446
|
+
maxSandboxTimeoutMs: integerFlag(flags, "max-sandbox-timeout-ms")
|
|
3447
|
+
}
|
|
3448
|
+
};
|
|
3449
|
+
}
|
|
3450
|
+
async function runMcpCli(argv = process.argv.slice(2), io = {}) {
|
|
3451
|
+
const stdout = io.stdout ?? process.stdout;
|
|
3452
|
+
const stderr = io.stderr ?? process.stderr;
|
|
3453
|
+
try {
|
|
3454
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
3455
|
+
stdout.write(MCP_CLI_HELP);
|
|
3456
|
+
return 0;
|
|
3457
|
+
}
|
|
3458
|
+
const { transport, httpOptions } = parseMcpCliOptions(argv);
|
|
3459
|
+
if (transport === "http") {
|
|
3460
|
+
await runHttp(httpOptions);
|
|
3461
|
+
return 0;
|
|
3462
|
+
}
|
|
3463
|
+
await runStdio({
|
|
3464
|
+
artifactDir: httpOptions.artifactDir,
|
|
3465
|
+
maxArtifactBytes: httpOptions.maxArtifactBytes,
|
|
3466
|
+
maxArtifactTotalBytes: httpOptions.maxArtifactTotalBytes,
|
|
3467
|
+
maxArtifacts: httpOptions.maxArtifacts,
|
|
3468
|
+
artifactTtlMs: httpOptions.artifactTtlMs,
|
|
3469
|
+
maxSandboxTimeoutMs: httpOptions.maxSandboxTimeoutMs
|
|
3470
|
+
});
|
|
3471
|
+
return 0;
|
|
3472
|
+
} catch (err) {
|
|
3473
|
+
stderr.write(`agentic-mermaid-mcp: ${err instanceof Error ? err.message : String(err)}
|
|
3474
|
+
`);
|
|
3475
|
+
return 1;
|
|
3476
|
+
}
|
|
3477
|
+
}
|
|
3478
|
+
|
|
3479
|
+
export {
|
|
3480
|
+
MCP_CLI_HELP,
|
|
3481
|
+
MCP_FLAG_SPECS,
|
|
3482
|
+
parseMcpCliOptions,
|
|
3483
|
+
runMcpCli
|
|
3484
|
+
};
|
|
3485
|
+
//# sourceMappingURL=chunk-2P5FKUQC.js.map
|