Sign In

@clipform/mcp-server

Package Overview
Dependencies
Maintainers
1
Versions
77
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@clipform/mcp-server - npm Package Compare versions

Comparing version
2.3.0
to
2.4.0
dist/chunk-4G4MF3ME.js

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

+136
import {
ALL_VARIANTS,
FORM_TYPES,
FORM_TYPE_KEYS,
callApi,
getSessionContext
} from "./chunk-5LV55T7F.js";
// src/lib/guides.ts
var guidesPromise = null;
async function loadGuides() {
const result = await callApi("/internal/mcp/guides", { method: "GET" });
if (!result.ok) {
throw new Error(`Failed to load guides: ${result.error}`);
}
const guides = result.data.guides ?? [];
return new Map(guides.map((g) => [g.uri, g]));
}
async function fetchGuideText(uri) {
if (!guidesPromise) {
guidesPromise = loadGuides();
}
try {
const guides = await guidesPromise;
return guides.get(uri)?.text ?? null;
} catch (err) {
guidesPromise = null;
console.error(`[guides] Failed to fetch ${uri}: ${err instanceof Error ? err.message : String(err)}`);
return null;
}
}
function guideFallbackText(uri) {
return [
`# Guide unavailable`,
``,
`The craft guide (${uri}) could not be loaded from the Clipform API.`,
`Guides are served at runtime and need a reachable API with a valid key -`,
`check API_URL and CLIPFORM_API_KEY, then try again.`,
``,
`General principles in the meantime: write narration for the ear (short,`,
`conversational), never reveal answers in narration or media, and keep`,
`forms tight - every question must earn its place.`
].join("\n");
}
// src/resources.ts
var GUIDE_TYPES = FORM_TYPE_KEYS;
var QUIZ_VARIANTS = ALL_VARIANTS;
var GUIDE_DESCRIPTIONS = {
"quiz": "Craft knowledge for writing engaging quizzes - difficulty curves, question psychology, narration style, scoring",
"survey": "Craft knowledge for feedback surveys, NPS, and research forms - brevity, rating scales, respondent fatigue",
"interview": "Craft knowledge for building interview forms - warm-up pacing, open questions, consent, video responses",
"funnel": "Craft knowledge for lead qualification funnels - planned feature, conditional routing coming soon",
"testimonial": "Craft knowledge for collecting testimonials and customer stories on video - storytelling prompts, comfort techniques, consent",
"application": "Craft knowledge for application and evaluation forms - multi-section structure, video responses for behavioural questions, screening",
"booking": "Craft knowledge for event registration and booking forms - minimal friction, video welcome, confirmation flow"
};
var QUIZ_VARIANT_DESCRIPTIONS = FORM_TYPES.quiz.variant_descriptions;
function getGuideUri(type, variant) {
if (type === "quiz" && variant) {
return `clipform://guides/quiz/${variant}`;
}
return `clipform://guides/${type}`;
}
async function readGuide(uri) {
return await fetchGuideText(uri) ?? guideFallbackText(uri);
}
function registerResources(server) {
for (const type of GUIDE_TYPES) {
server.registerResource(
`guide-${type}`,
getGuideUri(type),
{
description: GUIDE_DESCRIPTIONS[type],
mimeType: "text/markdown",
annotations: { audience: ["assistant"], priority: 0.8 }
},
async () => ({
contents: [{
uri: getGuideUri(type),
mimeType: "text/markdown",
text: await readGuide(getGuideUri(type))
}]
})
);
}
for (const variant of QUIZ_VARIANTS) {
server.registerResource(
`guide-quiz-${variant}`,
getGuideUri("quiz", variant),
{
description: QUIZ_VARIANT_DESCRIPTIONS[variant],
mimeType: "text/markdown",
annotations: { audience: ["assistant"], priority: 0.8 }
},
async () => ({
contents: [{
uri: getGuideUri("quiz", variant),
mimeType: "text/markdown",
text: await readGuide(getGuideUri("quiz", variant))
}]
})
);
}
server.registerResource(
"context-session",
"clipform://context/session",
{
description: "Current session info: auth mode, workspace, plan tier, node limits, feature flags. Read this before planning content to know your constraints.",
mimeType: "text/markdown",
annotations: { audience: ["assistant"], priority: 1 }
},
async () => {
const text = await getSessionContext();
return {
contents: [
{
uri: "clipform://context/session",
mimeType: "text/markdown",
text: text || "Session context unavailable - API may not be reachable."
}
]
};
}
);
}
export {
fetchGuideText,
guideFallbackText,
GUIDE_TYPES,
QUIZ_VARIANTS,
getGuideUri,
registerResources
};
//# sourceMappingURL=chunk-IDOG3ZTF.js.map
{"version":3,"sources":["../src/lib/guides.ts","../src/resources.ts"],"sourcesContent":["import { callApi } from \"./api-client.js\";\n\n/**\n * Craft guide bodies live server-side (#470) - the npm package is a shim\n * that fetches them at runtime instead of bundling them in the tarball.\n * One fetch per process (all ~30KB of guides in a single response), cached;\n * a failed fetch is NOT cached so the next read retries.\n */\n\nexport interface RemoteGuide {\n type: string;\n variant: string | null;\n uri: string;\n mimeType: string;\n text: string;\n}\n\nlet guidesPromise: Promise<Map<string, RemoteGuide>> | null = null;\n\nasync function loadGuides(): Promise<Map<string, RemoteGuide>> {\n const result = await callApi(\"/internal/mcp/guides\", { method: \"GET\" });\n if (!result.ok) {\n throw new Error(`Failed to load guides: ${result.error}`);\n }\n const guides = (result.data as { guides?: RemoteGuide[] }).guides ?? [];\n return new Map(guides.map((g) => [g.uri, g]));\n}\n\n/** Fetch a guide body by its clipform:// URI. Returns null when unavailable. */\nexport async function fetchGuideText(uri: string): Promise<string | null> {\n if (!guidesPromise) {\n guidesPromise = loadGuides();\n }\n try {\n const guides = await guidesPromise;\n return guides.get(uri)?.text ?? null;\n } catch (err) {\n guidesPromise = null; // don't cache failures - retry on the next read\n // Loud, not silent: a misconfigured API_URL/key otherwise degrades every\n // guide to the stub with nothing in the logs.\n console.error(`[guides] Failed to fetch ${uri}: ${err instanceof Error ? err.message : String(err)}`);\n return null;\n }\n}\n\n/** Shown in place of a guide when the API is unreachable or unauthenticated. */\nexport function guideFallbackText(uri: string): string {\n return [\n `# Guide unavailable`,\n ``,\n `The craft guide (${uri}) could not be loaded from the Clipform API.`,\n `Guides are served at runtime and need a reachable API with a valid key -`,\n `check API_URL and CLIPFORM_API_KEY, then try again.`,\n ``,\n `General principles in the meantime: write narration for the ear (short,`,\n `conversational), never reveal answers in narration or media, and keep`,\n `forms tight - every question must earn its place.`,\n ].join(\"\\n\");\n}\n","import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { getSessionContext } from \"./lib/session-context.js\";\nimport { FORM_TYPE_KEYS, FORM_TYPES, ALL_VARIANTS } from \"@vid-master/config\";\n\nexport const GUIDE_TYPES = FORM_TYPE_KEYS as readonly string[] as readonly [string, ...string[]];\nexport type GuideType = (typeof FORM_TYPE_KEYS)[number];\n\nexport const QUIZ_VARIANTS = ALL_VARIANTS as readonly string[] as readonly [string, ...string[]];\nexport type QuizVariant = (typeof ALL_VARIANTS)[number];\n// Guide BODIES are not bundled here (#470): the npm tarball ships only this\n// registration metadata. Text is fetched from the API at runtime - see\n// lib/guides.ts (cached per process, stub fallback when unreachable).\nimport { fetchGuideText, guideFallbackText } from \"./lib/guides.js\";\n\nconst GUIDE_DESCRIPTIONS: Record<GuideType, string> = {\n \"quiz\": \"Craft knowledge for writing engaging quizzes - difficulty curves, question psychology, narration style, scoring\",\n \"survey\": \"Craft knowledge for feedback surveys, NPS, and research forms - brevity, rating scales, respondent fatigue\",\n \"interview\": \"Craft knowledge for building interview forms - warm-up pacing, open questions, consent, video responses\",\n \"funnel\": \"Craft knowledge for lead qualification funnels - planned feature, conditional routing coming soon\",\n \"testimonial\": \"Craft knowledge for collecting testimonials and customer stories on video - storytelling prompts, comfort techniques, consent\",\n \"application\": \"Craft knowledge for application and evaluation forms - multi-section structure, video responses for behavioural questions, screening\",\n \"booking\": \"Craft knowledge for event registration and booking forms - minimal friction, video welcome, confirmation flow\",\n};\n\n// Sourced from config (single source of truth) so the agent-facing resource\n// descriptions can't drift from FORM_TYPES.quiz.variant_descriptions (#2534,\n// the exact pair packages/config/CLAUDE.md warns about).\nconst QUIZ_VARIANT_DESCRIPTIONS = FORM_TYPES.quiz.variant_descriptions;\n\nexport function getGuideUri(type: GuideType, variant?: QuizVariant): string {\n if (type === \"quiz\" && variant) {\n return `clipform://guides/quiz/${variant}`;\n }\n return `clipform://guides/${type}`;\n}\n\nasync function readGuide(uri: string): Promise<string> {\n return (await fetchGuideText(uri)) ?? guideFallbackText(uri);\n}\n\nexport function registerResources(server: McpServer) {\n for (const type of GUIDE_TYPES) {\n server.registerResource(\n `guide-${type}`,\n getGuideUri(type),\n {\n description: GUIDE_DESCRIPTIONS[type],\n mimeType: \"text/markdown\",\n annotations: { audience: [\"assistant\" as const], priority: 0.8 },\n },\n async () => ({\n contents: [{\n uri: getGuideUri(type),\n mimeType: \"text/markdown\",\n text: await readGuide(getGuideUri(type)),\n }],\n }),\n );\n }\n\n for (const variant of QUIZ_VARIANTS) {\n server.registerResource(\n `guide-quiz-${variant}`,\n getGuideUri(\"quiz\", variant),\n {\n description: QUIZ_VARIANT_DESCRIPTIONS[variant],\n mimeType: \"text/markdown\",\n annotations: { audience: [\"assistant\" as const], priority: 0.8 },\n },\n async () => ({\n contents: [{\n uri: getGuideUri(\"quiz\", variant),\n mimeType: \"text/markdown\",\n text: await readGuide(getGuideUri(\"quiz\", variant)),\n }],\n }),\n );\n }\n\n server.registerResource(\n \"context-session\",\n \"clipform://context/session\",\n {\n description:\n \"Current session info: auth mode, workspace, plan tier, node limits, feature flags. Read this before planning content to know your constraints.\",\n mimeType: \"text/markdown\",\n annotations: { audience: [\"assistant\"], priority: 1.0 },\n },\n async () => {\n const text = await getSessionContext();\n return {\n contents: [\n {\n uri: \"clipform://context/session\",\n mimeType: \"text/markdown\",\n text: text || \"Session context unavailable - API may not be reachable.\",\n },\n ],\n };\n }\n );\n}\n"],"mappings":"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","names":[]}

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

+4
-4

@@ -5,6 +5,6 @@ #!/usr/bin/env node

createServer
} from "./chunk-QC4JULCW.js";
import "./chunk-WCPHQ4GU.js";
import "./chunk-WNXTF76M.js";
import "./chunk-YZJZ5NPL.js";
} from "./chunk-NMIUCFJT.js";
import "./chunk-4G4MF3ME.js";
import "./chunk-IDOG3ZTF.js";
import "./chunk-5LV55T7F.js";
import "./chunk-V4L5RQWK.js";

@@ -11,0 +11,0 @@ import {

@@ -6,4 +6,4 @@ import {

registerPrompts
} from "./chunk-WCPHQ4GU.js";
import "./chunk-YZJZ5NPL.js";
} from "./chunk-4G4MF3ME.js";
import "./chunk-5LV55T7F.js";
import "./chunk-V4L5RQWK.js";

@@ -10,0 +10,0 @@ import "./chunk-PBQ5BQWD.js";

@@ -6,4 +6,4 @@ import {

registerResources
} from "./chunk-WNXTF76M.js";
import "./chunk-YZJZ5NPL.js";
} from "./chunk-IDOG3ZTF.js";
import "./chunk-5LV55T7F.js";
import "./chunk-V4L5RQWK.js";

@@ -10,0 +10,0 @@ import "./chunk-PBQ5BQWD.js";

import {
RENDER_TIMING,
createServer
} from "./chunk-QC4JULCW.js";
import "./chunk-WCPHQ4GU.js";
import "./chunk-WNXTF76M.js";
import "./chunk-YZJZ5NPL.js";
} from "./chunk-NMIUCFJT.js";
import "./chunk-4G4MF3ME.js";
import "./chunk-IDOG3ZTF.js";
import "./chunk-5LV55T7F.js";
import "./chunk-V4L5RQWK.js";

@@ -9,0 +9,0 @@ import "./chunk-PBQ5BQWD.js";

{
"name": "@clipform/mcp-server",
"version": "2.3.0",
"version": "2.4.0",
"mcpName": "io.github.Clipform/mcp-server",

@@ -5,0 +5,0 @@ "description": "MCP server for building and managing Clipform video forms",

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

import {
ALL_VARIANTS,
FORM_TYPES,
FORM_TYPE_KEYS,
callApi,
getSessionContext
} from "./chunk-YZJZ5NPL.js";
// src/lib/guides.ts
var guidesPromise = null;
async function loadGuides() {
const result = await callApi("/internal/mcp/guides", { method: "GET" });
if (!result.ok) {
throw new Error(`Failed to load guides: ${result.error}`);
}
const guides = result.data.guides ?? [];
return new Map(guides.map((g) => [g.uri, g]));
}
async function fetchGuideText(uri) {
if (!guidesPromise) {
guidesPromise = loadGuides();
}
try {
const guides = await guidesPromise;
return guides.get(uri)?.text ?? null;
} catch (err) {
guidesPromise = null;
console.error(`[guides] Failed to fetch ${uri}: ${err instanceof Error ? err.message : String(err)}`);
return null;
}
}
function guideFallbackText(uri) {
return [
`# Guide unavailable`,
``,
`The craft guide (${uri}) could not be loaded from the Clipform API.`,
`Guides are served at runtime and need a reachable API with a valid key -`,
`check API_URL and CLIPFORM_API_KEY, then try again.`,
``,
`General principles in the meantime: write narration for the ear (short,`,
`conversational), never reveal answers in narration or media, and keep`,
`forms tight - every question must earn its place.`
].join("\n");
}
// src/resources.ts
var GUIDE_TYPES = FORM_TYPE_KEYS;
var QUIZ_VARIANTS = ALL_VARIANTS;
var GUIDE_DESCRIPTIONS = {
"quiz": "Craft knowledge for writing engaging quizzes - difficulty curves, question psychology, narration style, scoring",
"survey": "Craft knowledge for feedback surveys, NPS, and research forms - brevity, rating scales, respondent fatigue",
"interview": "Craft knowledge for building interview forms - warm-up pacing, open questions, consent, video responses",
"funnel": "Craft knowledge for lead qualification funnels - planned feature, conditional routing coming soon",
"testimonial": "Craft knowledge for collecting testimonials and customer stories on video - storytelling prompts, comfort techniques, consent",
"application": "Craft knowledge for application and evaluation forms - multi-section structure, video responses for behavioural questions, screening",
"booking": "Craft knowledge for event registration and booking forms - minimal friction, video welcome, confirmation flow"
};
var QUIZ_VARIANT_DESCRIPTIONS = FORM_TYPES.quiz.variant_descriptions;
function getGuideUri(type, variant) {
if (type === "quiz" && variant) {
return `clipform://guides/quiz/${variant}`;
}
return `clipform://guides/${type}`;
}
async function readGuide(uri) {
return await fetchGuideText(uri) ?? guideFallbackText(uri);
}
function registerResources(server) {
for (const type of GUIDE_TYPES) {
server.registerResource(
`guide-${type}`,
getGuideUri(type),
{
description: GUIDE_DESCRIPTIONS[type],
mimeType: "text/markdown",
annotations: { audience: ["assistant"], priority: 0.8 }
},
async () => ({
contents: [{
uri: getGuideUri(type),
mimeType: "text/markdown",
text: await readGuide(getGuideUri(type))
}]
})
);
}
for (const variant of QUIZ_VARIANTS) {
server.registerResource(
`guide-quiz-${variant}`,
getGuideUri("quiz", variant),
{
description: QUIZ_VARIANT_DESCRIPTIONS[variant],
mimeType: "text/markdown",
annotations: { audience: ["assistant"], priority: 0.8 }
},
async () => ({
contents: [{
uri: getGuideUri("quiz", variant),
mimeType: "text/markdown",
text: await readGuide(getGuideUri("quiz", variant))
}]
})
);
}
server.registerResource(
"context-session",
"clipform://context/session",
{
description: "Current session info: auth mode, workspace, plan tier, node limits, feature flags. Read this before planning content to know your constraints.",
mimeType: "text/markdown",
annotations: { audience: ["assistant"], priority: 1 }
},
async () => {
const text = await getSessionContext();
return {
contents: [
{
uri: "clipform://context/session",
mimeType: "text/markdown",
text: text || "Session context unavailable - API may not be reachable."
}
]
};
}
);
}
export {
fetchGuideText,
guideFallbackText,
GUIDE_TYPES,
QUIZ_VARIANTS,
getGuideUri,
registerResources
};
//# sourceMappingURL=chunk-WNXTF76M.js.map
{"version":3,"sources":["../src/lib/guides.ts","../src/resources.ts"],"sourcesContent":["import { callApi } from \"./api-client.js\";\n\n/**\n * Craft guide bodies live server-side (#470) - the npm package is a shim\n * that fetches them at runtime instead of bundling them in the tarball.\n * One fetch per process (all ~30KB of guides in a single response), cached;\n * a failed fetch is NOT cached so the next read retries.\n */\n\nexport interface RemoteGuide {\n type: string;\n variant: string | null;\n uri: string;\n mimeType: string;\n text: string;\n}\n\nlet guidesPromise: Promise<Map<string, RemoteGuide>> | null = null;\n\nasync function loadGuides(): Promise<Map<string, RemoteGuide>> {\n const result = await callApi(\"/internal/mcp/guides\", { method: \"GET\" });\n if (!result.ok) {\n throw new Error(`Failed to load guides: ${result.error}`);\n }\n const guides = (result.data as { guides?: RemoteGuide[] }).guides ?? [];\n return new Map(guides.map((g) => [g.uri, g]));\n}\n\n/** Fetch a guide body by its clipform:// URI. Returns null when unavailable. */\nexport async function fetchGuideText(uri: string): Promise<string | null> {\n if (!guidesPromise) {\n guidesPromise = loadGuides();\n }\n try {\n const guides = await guidesPromise;\n return guides.get(uri)?.text ?? null;\n } catch (err) {\n guidesPromise = null; // don't cache failures - retry on the next read\n // Loud, not silent: a misconfigured API_URL/key otherwise degrades every\n // guide to the stub with nothing in the logs.\n console.error(`[guides] Failed to fetch ${uri}: ${err instanceof Error ? err.message : String(err)}`);\n return null;\n }\n}\n\n/** Shown in place of a guide when the API is unreachable or unauthenticated. */\nexport function guideFallbackText(uri: string): string {\n return [\n `# Guide unavailable`,\n ``,\n `The craft guide (${uri}) could not be loaded from the Clipform API.`,\n `Guides are served at runtime and need a reachable API with a valid key -`,\n `check API_URL and CLIPFORM_API_KEY, then try again.`,\n ``,\n `General principles in the meantime: write narration for the ear (short,`,\n `conversational), never reveal answers in narration or media, and keep`,\n `forms tight - every question must earn its place.`,\n ].join(\"\\n\");\n}\n","import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { getSessionContext } from \"./lib/session-context.js\";\nimport { FORM_TYPE_KEYS, FORM_TYPES, ALL_VARIANTS } from \"@vid-master/config\";\n\nexport const GUIDE_TYPES = FORM_TYPE_KEYS as readonly string[] as readonly [string, ...string[]];\nexport type GuideType = (typeof FORM_TYPE_KEYS)[number];\n\nexport const QUIZ_VARIANTS = ALL_VARIANTS as readonly string[] as readonly [string, ...string[]];\nexport type QuizVariant = (typeof ALL_VARIANTS)[number];\n// Guide BODIES are not bundled here (#470): the npm tarball ships only this\n// registration metadata. Text is fetched from the API at runtime - see\n// lib/guides.ts (cached per process, stub fallback when unreachable).\nimport { fetchGuideText, guideFallbackText } from \"./lib/guides.js\";\n\nconst GUIDE_DESCRIPTIONS: Record<GuideType, string> = {\n \"quiz\": \"Craft knowledge for writing engaging quizzes - difficulty curves, question psychology, narration style, scoring\",\n \"survey\": \"Craft knowledge for feedback surveys, NPS, and research forms - brevity, rating scales, respondent fatigue\",\n \"interview\": \"Craft knowledge for building interview forms - warm-up pacing, open questions, consent, video responses\",\n \"funnel\": \"Craft knowledge for lead qualification funnels - planned feature, conditional routing coming soon\",\n \"testimonial\": \"Craft knowledge for collecting testimonials and customer stories on video - storytelling prompts, comfort techniques, consent\",\n \"application\": \"Craft knowledge for application and evaluation forms - multi-section structure, video responses for behavioural questions, screening\",\n \"booking\": \"Craft knowledge for event registration and booking forms - minimal friction, video welcome, confirmation flow\",\n};\n\n// Sourced from config (single source of truth) so the agent-facing resource\n// descriptions can't drift from FORM_TYPES.quiz.variant_descriptions (#2534,\n// the exact pair packages/config/CLAUDE.md warns about).\nconst QUIZ_VARIANT_DESCRIPTIONS = FORM_TYPES.quiz.variant_descriptions;\n\nexport function getGuideUri(type: GuideType, variant?: QuizVariant): string {\n if (type === \"quiz\" && variant) {\n return `clipform://guides/quiz/${variant}`;\n }\n return `clipform://guides/${type}`;\n}\n\nasync function readGuide(uri: string): Promise<string> {\n return (await fetchGuideText(uri)) ?? guideFallbackText(uri);\n}\n\nexport function registerResources(server: McpServer) {\n for (const type of GUIDE_TYPES) {\n server.registerResource(\n `guide-${type}`,\n getGuideUri(type),\n {\n description: GUIDE_DESCRIPTIONS[type],\n mimeType: \"text/markdown\",\n annotations: { audience: [\"assistant\" as const], priority: 0.8 },\n },\n async () => ({\n contents: [{\n uri: getGuideUri(type),\n mimeType: \"text/markdown\",\n text: await readGuide(getGuideUri(type)),\n }],\n }),\n );\n }\n\n for (const variant of QUIZ_VARIANTS) {\n server.registerResource(\n `guide-quiz-${variant}`,\n getGuideUri(\"quiz\", variant),\n {\n description: QUIZ_VARIANT_DESCRIPTIONS[variant],\n mimeType: \"text/markdown\",\n annotations: { audience: [\"assistant\" as const], priority: 0.8 },\n },\n async () => ({\n contents: [{\n uri: getGuideUri(\"quiz\", variant),\n mimeType: \"text/markdown\",\n text: await readGuide(getGuideUri(\"quiz\", variant)),\n }],\n }),\n );\n }\n\n server.registerResource(\n \"context-session\",\n \"clipform://context/session\",\n {\n description:\n \"Current session info: auth mode, workspace, plan tier, node limits, feature flags. Read this before planning content to know your constraints.\",\n mimeType: \"text/markdown\",\n annotations: { audience: [\"assistant\"], priority: 1.0 },\n },\n async () => {\n const text = await getSessionContext();\n return {\n contents: [\n {\n uri: \"clipform://context/session\",\n mimeType: \"text/markdown\",\n text: text || \"Session context unavailable - API may not be reachable.\",\n },\n ],\n };\n }\n );\n}\n"],"mappings":"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","names":[]}

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display