@contextstream/mcp-server 0.3.54 → 0.3.55
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/dist/index.js +201 -14
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -28,9 +28,15 @@ No special syntax. No commands to learn. Just ask.
|
|
|
28
28
|
- Memory capture and recall (decisions, preferences, tasks, bugs, lessons)
|
|
29
29
|
- Code search (semantic, hybrid, keyword, pattern)
|
|
30
30
|
- Knowledge graph and code analysis (dependencies, impact, call paths, circular deps, unused code)
|
|
31
|
+
- Graph ingestion for full graph builds (`graph_ingest`)
|
|
31
32
|
- Local repo ingestion for indexing (`projects_ingest_local`)
|
|
32
33
|
- Auto-context: on the first tool call in a new session, the server can auto-initialize context
|
|
33
34
|
|
|
35
|
+
## Graph tiers
|
|
36
|
+
|
|
37
|
+
- **Pro (Graph-Lite):** module-level import graph, dependencies, and 1-hop impact
|
|
38
|
+
- **Elite/Team (Full Graph):** module + call + dataflow + type layers, plus `graph_ingest`
|
|
39
|
+
|
|
34
40
|
## Requirements
|
|
35
41
|
|
|
36
42
|
- Node.js 18+
|
package/dist/index.js
CHANGED
|
@@ -4854,6 +4854,19 @@ function normalizeNodeType(input) {
|
|
|
4854
4854
|
);
|
|
4855
4855
|
}
|
|
4856
4856
|
}
|
|
4857
|
+
function pickString(value) {
|
|
4858
|
+
if (typeof value !== "string") return null;
|
|
4859
|
+
const trimmed = value.trim();
|
|
4860
|
+
return trimmed ? trimmed : null;
|
|
4861
|
+
}
|
|
4862
|
+
function normalizeGraphTier(value) {
|
|
4863
|
+
const normalized = value.trim().toLowerCase();
|
|
4864
|
+
if (!normalized) return null;
|
|
4865
|
+
if (normalized.includes("full") || normalized.includes("elite") || normalized.includes("team")) return "full";
|
|
4866
|
+
if (normalized.includes("lite") || normalized.includes("light") || normalized.includes("basic") || normalized.includes("module")) return "lite";
|
|
4867
|
+
if (normalized.includes("none") || normalized.includes("off") || normalized.includes("disabled") || normalized.includes("free")) return "none";
|
|
4868
|
+
return null;
|
|
4869
|
+
}
|
|
4857
4870
|
var AI_PLAN_TIMEOUT_MS = 5e4;
|
|
4858
4871
|
var AI_PLAN_RETRIES = 0;
|
|
4859
4872
|
var ContextStreamClient = class {
|
|
@@ -4948,6 +4961,26 @@ var ContextStreamClient = class {
|
|
|
4948
4961
|
return null;
|
|
4949
4962
|
}
|
|
4950
4963
|
}
|
|
4964
|
+
async getGraphTier() {
|
|
4965
|
+
try {
|
|
4966
|
+
const balance = await this.getCreditBalance();
|
|
4967
|
+
const plan = balance?.plan ?? {};
|
|
4968
|
+
const features = plan?.features ?? {};
|
|
4969
|
+
const tierCandidate = pickString(plan.graph_tier) || pickString(plan.graphTier) || pickString(features.graph_tier) || pickString(features.graphTier) || pickString(balance?.graph_tier) || pickString(balance?.graphTier);
|
|
4970
|
+
const normalizedTier = tierCandidate ? normalizeGraphTier(tierCandidate) : null;
|
|
4971
|
+
if (normalizedTier) return normalizedTier;
|
|
4972
|
+
const planName = pickString(plan.name)?.toLowerCase() ?? null;
|
|
4973
|
+
if (!planName) return "none";
|
|
4974
|
+
if (planName.includes("elite") || planName.includes("team") || planName.includes("enterprise") || planName.includes("business")) {
|
|
4975
|
+
return "full";
|
|
4976
|
+
}
|
|
4977
|
+
if (planName.includes("pro")) return "lite";
|
|
4978
|
+
if (planName.includes("free")) return "none";
|
|
4979
|
+
return "lite";
|
|
4980
|
+
} catch {
|
|
4981
|
+
return "none";
|
|
4982
|
+
}
|
|
4983
|
+
}
|
|
4951
4984
|
// Workspaces & Projects
|
|
4952
4985
|
listWorkspaces(params) {
|
|
4953
4986
|
const query = new URLSearchParams();
|
|
@@ -7973,6 +8006,35 @@ function toStructured(data) {
|
|
|
7973
8006
|
}
|
|
7974
8007
|
return void 0;
|
|
7975
8008
|
}
|
|
8009
|
+
function readStatNumber(payload, key) {
|
|
8010
|
+
if (!payload || typeof payload !== "object") return void 0;
|
|
8011
|
+
const direct = payload[key];
|
|
8012
|
+
if (typeof direct === "number") return direct;
|
|
8013
|
+
const nested = payload.data;
|
|
8014
|
+
if (nested && typeof nested === "object") {
|
|
8015
|
+
const nestedValue = nested[key];
|
|
8016
|
+
if (typeof nestedValue === "number") return nestedValue;
|
|
8017
|
+
}
|
|
8018
|
+
return void 0;
|
|
8019
|
+
}
|
|
8020
|
+
function estimateGraphIngestMinutes(stats) {
|
|
8021
|
+
const totalFiles = readStatNumber(stats, "total_files");
|
|
8022
|
+
const totalLines = readStatNumber(stats, "total_lines");
|
|
8023
|
+
if (!totalFiles && !totalLines) return null;
|
|
8024
|
+
const fileScore = totalFiles ? totalFiles / 1e3 : 0;
|
|
8025
|
+
const lineScore = totalLines ? totalLines / 5e4 : 0;
|
|
8026
|
+
const sizeScore = Math.max(fileScore, lineScore);
|
|
8027
|
+
const minMinutes = Math.min(45, Math.max(1, Math.round(1 + sizeScore * 1.5)));
|
|
8028
|
+
const maxMinutes = Math.min(60, Math.max(minMinutes + 1, Math.round(2 + sizeScore * 3)));
|
|
8029
|
+
const basisParts = [];
|
|
8030
|
+
if (totalFiles) basisParts.push(`${totalFiles.toLocaleString()} files`);
|
|
8031
|
+
if (totalLines) basisParts.push(`${totalLines.toLocaleString()} lines`);
|
|
8032
|
+
return {
|
|
8033
|
+
min: minMinutes,
|
|
8034
|
+
max: maxMinutes,
|
|
8035
|
+
basis: basisParts.length > 0 ? basisParts.join(" / ") : void 0
|
|
8036
|
+
};
|
|
8037
|
+
}
|
|
7976
8038
|
function normalizeLessonField(value) {
|
|
7977
8039
|
return value.trim().toLowerCase().replace(/\s+/g, " ");
|
|
7978
8040
|
}
|
|
@@ -8047,6 +8109,9 @@ function registerTools(server, client, sessionManager) {
|
|
|
8047
8109
|
return proTools.has(toolName) ? "pro" : "free";
|
|
8048
8110
|
}
|
|
8049
8111
|
function getToolAccessLabel(toolName) {
|
|
8112
|
+
const graphTier = graphToolTiers.get(toolName);
|
|
8113
|
+
if (graphTier === "lite") return "Pro (Graph-Lite)";
|
|
8114
|
+
if (graphTier === "full") return "Elite/Team (Full Graph)";
|
|
8050
8115
|
return getToolAccessTier(toolName) === "pro" ? "PRO" : "Free";
|
|
8051
8116
|
}
|
|
8052
8117
|
async function gateIfProTool(toolName) {
|
|
@@ -8060,6 +8125,94 @@ function registerTools(server, client, sessionManager) {
|
|
|
8060
8125
|
].join("\n")
|
|
8061
8126
|
);
|
|
8062
8127
|
}
|
|
8128
|
+
const graphToolTiers = /* @__PURE__ */ new Map([
|
|
8129
|
+
["graph_dependencies", "lite"],
|
|
8130
|
+
["graph_impact", "lite"],
|
|
8131
|
+
["graph_related", "full"],
|
|
8132
|
+
["graph_decisions", "full"],
|
|
8133
|
+
["graph_path", "full"],
|
|
8134
|
+
["graph_call_path", "full"],
|
|
8135
|
+
["graph_circular_dependencies", "full"],
|
|
8136
|
+
["graph_unused_code", "full"],
|
|
8137
|
+
["graph_ingest", "full"],
|
|
8138
|
+
["graph_contradictions", "full"]
|
|
8139
|
+
]);
|
|
8140
|
+
const graphLiteMaxDepth = 1;
|
|
8141
|
+
function normalizeGraphTargetType(value) {
|
|
8142
|
+
return String(value ?? "").trim().toLowerCase();
|
|
8143
|
+
}
|
|
8144
|
+
function isModuleTargetType(value) {
|
|
8145
|
+
return value === "module" || value === "file" || value === "path";
|
|
8146
|
+
}
|
|
8147
|
+
function graphLiteConstraintError(toolName, detail) {
|
|
8148
|
+
return errorResult(
|
|
8149
|
+
[
|
|
8150
|
+
`Access denied: \`${toolName}\` is limited to Graph-Lite (module-level, 1-hop queries).`,
|
|
8151
|
+
detail,
|
|
8152
|
+
`Upgrade to Elite or Team for full graph access: ${upgradeUrl}`
|
|
8153
|
+
].join("\n")
|
|
8154
|
+
);
|
|
8155
|
+
}
|
|
8156
|
+
async function gateIfGraphTool(toolName, input) {
|
|
8157
|
+
const requiredTier = graphToolTiers.get(toolName);
|
|
8158
|
+
if (!requiredTier) return null;
|
|
8159
|
+
const graphTier = await client.getGraphTier();
|
|
8160
|
+
if (graphTier === "full") return null;
|
|
8161
|
+
if (graphTier === "lite") {
|
|
8162
|
+
if (requiredTier === "full") {
|
|
8163
|
+
return errorResult(
|
|
8164
|
+
[
|
|
8165
|
+
`Access denied: \`${toolName}\` requires Elite or Team (Full Graph).`,
|
|
8166
|
+
"Pro includes Graph-Lite (module-level dependencies and 1-hop impact only).",
|
|
8167
|
+
`Upgrade: ${upgradeUrl}`
|
|
8168
|
+
].join("\n")
|
|
8169
|
+
);
|
|
8170
|
+
}
|
|
8171
|
+
if (toolName === "graph_dependencies") {
|
|
8172
|
+
const targetType = normalizeGraphTargetType(input?.target?.type);
|
|
8173
|
+
if (!isModuleTargetType(targetType)) {
|
|
8174
|
+
return graphLiteConstraintError(
|
|
8175
|
+
toolName,
|
|
8176
|
+
"Set target.type to module, file, or path."
|
|
8177
|
+
);
|
|
8178
|
+
}
|
|
8179
|
+
if (typeof input?.max_depth === "number" && input.max_depth > graphLiteMaxDepth) {
|
|
8180
|
+
return graphLiteConstraintError(
|
|
8181
|
+
toolName,
|
|
8182
|
+
`Set max_depth to ${graphLiteMaxDepth} or lower.`
|
|
8183
|
+
);
|
|
8184
|
+
}
|
|
8185
|
+
if (input?.include_transitive === true) {
|
|
8186
|
+
return graphLiteConstraintError(
|
|
8187
|
+
toolName,
|
|
8188
|
+
"Set include_transitive to false."
|
|
8189
|
+
);
|
|
8190
|
+
}
|
|
8191
|
+
}
|
|
8192
|
+
if (toolName === "graph_impact") {
|
|
8193
|
+
const targetType = normalizeGraphTargetType(input?.target?.type);
|
|
8194
|
+
if (!isModuleTargetType(targetType)) {
|
|
8195
|
+
return graphLiteConstraintError(
|
|
8196
|
+
toolName,
|
|
8197
|
+
"Set target.type to module, file, or path."
|
|
8198
|
+
);
|
|
8199
|
+
}
|
|
8200
|
+
if (typeof input?.max_depth === "number" && input.max_depth > graphLiteMaxDepth) {
|
|
8201
|
+
return graphLiteConstraintError(
|
|
8202
|
+
toolName,
|
|
8203
|
+
`Set max_depth to ${graphLiteMaxDepth} or lower.`
|
|
8204
|
+
);
|
|
8205
|
+
}
|
|
8206
|
+
}
|
|
8207
|
+
return null;
|
|
8208
|
+
}
|
|
8209
|
+
return errorResult(
|
|
8210
|
+
[
|
|
8211
|
+
`Access denied: \`${toolName}\` requires ContextStream Pro (Graph-Lite) or Elite/Team (Full Graph).`,
|
|
8212
|
+
`Upgrade: ${upgradeUrl}`
|
|
8213
|
+
].join("\n")
|
|
8214
|
+
);
|
|
8215
|
+
}
|
|
8063
8216
|
function wrapWithAutoContext(toolName, handler) {
|
|
8064
8217
|
if (!sessionManager) {
|
|
8065
8218
|
return handler;
|
|
@@ -8092,12 +8245,13 @@ function registerTools(server, client, sessionManager) {
|
|
|
8092
8245
|
return;
|
|
8093
8246
|
}
|
|
8094
8247
|
const accessLabel = getToolAccessLabel(name);
|
|
8248
|
+
const showUpgrade = accessLabel !== "Free";
|
|
8095
8249
|
const labeledConfig = {
|
|
8096
8250
|
...config,
|
|
8097
8251
|
title: `${config.title} (${accessLabel})`,
|
|
8098
8252
|
description: `${config.description}
|
|
8099
8253
|
|
|
8100
|
-
Access: ${accessLabel}${
|
|
8254
|
+
Access: ${accessLabel}${showUpgrade ? ` (upgrade: ${upgradeUrl})` : ""}`
|
|
8101
8255
|
};
|
|
8102
8256
|
const safeHandler = async (input) => {
|
|
8103
8257
|
try {
|
|
@@ -8579,6 +8733,8 @@ Access: Free`,
|
|
|
8579
8733
|
})
|
|
8580
8734
|
},
|
|
8581
8735
|
async (input) => {
|
|
8736
|
+
const gate = await gateIfGraphTool("graph_related", input);
|
|
8737
|
+
if (gate) return gate;
|
|
8582
8738
|
const result = await client.graphRelated(input);
|
|
8583
8739
|
return { content: [{ type: "text", text: formatContent(result) }], structuredContent: toStructured(result) };
|
|
8584
8740
|
}
|
|
@@ -8596,6 +8752,8 @@ Access: Free`,
|
|
|
8596
8752
|
})
|
|
8597
8753
|
},
|
|
8598
8754
|
async (input) => {
|
|
8755
|
+
const gate = await gateIfGraphTool("graph_path", input);
|
|
8756
|
+
if (gate) return gate;
|
|
8599
8757
|
const result = await client.graphPath(input);
|
|
8600
8758
|
return { content: [{ type: "text", text: formatContent(result) }], structuredContent: toStructured(result) };
|
|
8601
8759
|
}
|
|
@@ -8612,6 +8770,8 @@ Access: Free`,
|
|
|
8612
8770
|
})
|
|
8613
8771
|
},
|
|
8614
8772
|
async (input) => {
|
|
8773
|
+
const gate = await gateIfGraphTool("graph_decisions", input);
|
|
8774
|
+
if (gate) return gate;
|
|
8615
8775
|
const result = await client.graphDecisions(input);
|
|
8616
8776
|
return { content: [{ type: "text", text: formatContent(result) }], structuredContent: toStructured(result) };
|
|
8617
8777
|
}
|
|
@@ -8620,9 +8780,7 @@ Access: Free`,
|
|
|
8620
8780
|
"graph_dependencies",
|
|
8621
8781
|
{
|
|
8622
8782
|
title: "Code dependencies",
|
|
8623
|
-
description:
|
|
8624
|
-
|
|
8625
|
-
Access: Free`,
|
|
8783
|
+
description: "Dependency graph query",
|
|
8626
8784
|
inputSchema: external_exports.object({
|
|
8627
8785
|
target: external_exports.object({
|
|
8628
8786
|
type: external_exports.string().describe("Code element type. Accepted values: module (aliases: file, path), function (alias: method), type (aliases: struct, enum, trait, class), variable (aliases: data, const, constant). For knowledge/memory nodes, use graph_path with UUID ids instead."),
|
|
@@ -8633,6 +8791,8 @@ Access: Free`,
|
|
|
8633
8791
|
})
|
|
8634
8792
|
},
|
|
8635
8793
|
async (input) => {
|
|
8794
|
+
const gate = await gateIfGraphTool("graph_dependencies", input);
|
|
8795
|
+
if (gate) return gate;
|
|
8636
8796
|
const result = await client.graphDependencies(input);
|
|
8637
8797
|
return { content: [{ type: "text", text: formatContent(result) }], structuredContent: toStructured(result) };
|
|
8638
8798
|
}
|
|
@@ -8641,9 +8801,7 @@ Access: Free`,
|
|
|
8641
8801
|
"graph_call_path",
|
|
8642
8802
|
{
|
|
8643
8803
|
title: "Call path",
|
|
8644
|
-
description:
|
|
8645
|
-
|
|
8646
|
-
Access: Free`,
|
|
8804
|
+
description: "Find call path between two targets",
|
|
8647
8805
|
inputSchema: external_exports.object({
|
|
8648
8806
|
source: external_exports.object({
|
|
8649
8807
|
type: external_exports.string().describe('Must be "function" (alias: method). Only function types are supported for call path analysis. For knowledge/memory nodes, use graph_path with UUID ids instead.'),
|
|
@@ -8657,6 +8815,8 @@ Access: Free`,
|
|
|
8657
8815
|
})
|
|
8658
8816
|
},
|
|
8659
8817
|
async (input) => {
|
|
8818
|
+
const gate = await gateIfGraphTool("graph_call_path", input);
|
|
8819
|
+
if (gate) return gate;
|
|
8660
8820
|
const result = await client.graphCallPath(input);
|
|
8661
8821
|
return { content: [{ type: "text", text: formatContent(result) }], structuredContent: toStructured(result) };
|
|
8662
8822
|
}
|
|
@@ -8665,9 +8825,7 @@ Access: Free`,
|
|
|
8665
8825
|
"graph_impact",
|
|
8666
8826
|
{
|
|
8667
8827
|
title: "Impact analysis",
|
|
8668
|
-
description:
|
|
8669
|
-
|
|
8670
|
-
Access: Free`,
|
|
8828
|
+
description: "Analyze impact of a target node",
|
|
8671
8829
|
inputSchema: external_exports.object({
|
|
8672
8830
|
target: external_exports.object({
|
|
8673
8831
|
type: external_exports.string().describe("Code element type. Accepted values: module (aliases: file, path), function (alias: method), type (aliases: struct, enum, trait, class), variable (aliases: data, const, constant). For knowledge/memory nodes, use graph_path with UUID ids instead."),
|
|
@@ -8677,6 +8835,8 @@ Access: Free`,
|
|
|
8677
8835
|
})
|
|
8678
8836
|
},
|
|
8679
8837
|
async (input) => {
|
|
8838
|
+
const gate = await gateIfGraphTool("graph_impact", input);
|
|
8839
|
+
if (gate) return gate;
|
|
8680
8840
|
const result = await client.graphImpact(input);
|
|
8681
8841
|
return { content: [{ type: "text", text: formatContent(result) }], structuredContent: toStructured(result) };
|
|
8682
8842
|
}
|
|
@@ -8685,19 +8845,40 @@ Access: Free`,
|
|
|
8685
8845
|
"graph_ingest",
|
|
8686
8846
|
{
|
|
8687
8847
|
title: "Ingest code graph",
|
|
8688
|
-
description: "Build and persist the dependency graph for a project",
|
|
8848
|
+
description: "Build and persist the dependency graph for a project. Runs async by default (wait=false) and can take a few minutes for larger repos.",
|
|
8689
8849
|
inputSchema: external_exports.object({
|
|
8690
8850
|
project_id: external_exports.string().uuid().optional(),
|
|
8691
|
-
wait: external_exports.boolean().optional().describe("If true, wait for ingestion to finish before returning.")
|
|
8851
|
+
wait: external_exports.boolean().optional().describe("If true, wait for ingestion to finish before returning. Defaults to false (async).")
|
|
8692
8852
|
})
|
|
8693
8853
|
},
|
|
8694
8854
|
async (input) => {
|
|
8855
|
+
const gate = await gateIfGraphTool("graph_ingest", input);
|
|
8856
|
+
if (gate) return gate;
|
|
8695
8857
|
const projectId = resolveProjectId(input.project_id);
|
|
8696
8858
|
if (!projectId) {
|
|
8697
8859
|
return errorResult("Error: project_id is required. Please call session_init first or provide project_id explicitly.");
|
|
8698
8860
|
}
|
|
8699
|
-
const
|
|
8700
|
-
|
|
8861
|
+
const wait = input.wait ?? false;
|
|
8862
|
+
let estimate = null;
|
|
8863
|
+
try {
|
|
8864
|
+
const stats = await client.projectStatistics(projectId);
|
|
8865
|
+
estimate = estimateGraphIngestMinutes(stats);
|
|
8866
|
+
} catch (error) {
|
|
8867
|
+
console.error("[ContextStream] Failed to fetch project statistics for graph ingest estimate:", error);
|
|
8868
|
+
}
|
|
8869
|
+
const result = await client.graphIngest({ project_id: projectId, wait });
|
|
8870
|
+
const estimateText = estimate ? `Estimated time: ${estimate.min}-${estimate.max} min${estimate.basis ? ` (based on ${estimate.basis})` : ""}.` : "Estimated time varies with repo size.";
|
|
8871
|
+
const note = `Graph ingestion is running ${wait ? "synchronously" : "asynchronously"} and can take a few minutes. ${estimateText}`;
|
|
8872
|
+
const structured = toStructured(result);
|
|
8873
|
+
const structuredContent = structured && typeof structured === "object" ? { ...structured, wait, note, ...estimate ? { estimate_minutes: { min: estimate.min, max: estimate.max }, estimate_basis: estimate.basis } : {} } : { wait, note, ...estimate ? { estimate_minutes: { min: estimate.min, max: estimate.max }, estimate_basis: estimate.basis } : {} };
|
|
8874
|
+
return {
|
|
8875
|
+
content: [{
|
|
8876
|
+
type: "text",
|
|
8877
|
+
text: `${note}
|
|
8878
|
+
${formatContent(result)}`
|
|
8879
|
+
}],
|
|
8880
|
+
structuredContent
|
|
8881
|
+
};
|
|
8701
8882
|
}
|
|
8702
8883
|
);
|
|
8703
8884
|
registerTool(
|
|
@@ -9148,6 +9329,8 @@ Automatically detects code files and skips ignored directories like node_modules
|
|
|
9148
9329
|
inputSchema: external_exports.object({ project_id: external_exports.string().uuid().optional() })
|
|
9149
9330
|
},
|
|
9150
9331
|
async (input) => {
|
|
9332
|
+
const gate = await gateIfGraphTool("graph_circular_dependencies", input);
|
|
9333
|
+
if (gate) return gate;
|
|
9151
9334
|
const projectId = resolveProjectId(input.project_id);
|
|
9152
9335
|
if (!projectId) {
|
|
9153
9336
|
return errorResult("Error: project_id is required. Please call session_init first or provide project_id explicitly.");
|
|
@@ -9164,6 +9347,8 @@ Automatically detects code files and skips ignored directories like node_modules
|
|
|
9164
9347
|
inputSchema: external_exports.object({ project_id: external_exports.string().uuid().optional() })
|
|
9165
9348
|
},
|
|
9166
9349
|
async (input) => {
|
|
9350
|
+
const gate = await gateIfGraphTool("graph_unused_code", input);
|
|
9351
|
+
if (gate) return gate;
|
|
9167
9352
|
const projectId = resolveProjectId(input.project_id);
|
|
9168
9353
|
if (!projectId) {
|
|
9169
9354
|
return errorResult("Error: project_id is required. Please call session_init first or provide project_id explicitly.");
|
|
@@ -9180,6 +9365,8 @@ Automatically detects code files and skips ignored directories like node_modules
|
|
|
9180
9365
|
inputSchema: external_exports.object({ node_id: external_exports.string().uuid() })
|
|
9181
9366
|
},
|
|
9182
9367
|
async (input) => {
|
|
9368
|
+
const gate = await gateIfGraphTool("graph_contradictions", input);
|
|
9369
|
+
if (gate) return gate;
|
|
9183
9370
|
const result = await client.findContradictions(input.node_id);
|
|
9184
9371
|
return { content: [{ type: "text", text: formatContent(result) }], structuredContent: toStructured(result) };
|
|
9185
9372
|
}
|
package/package.json
CHANGED