Merge remote-tracking branch 'origin/main' into feat/browser-export-versioned-schemas
This commit is contained in:
commit
2d6dd39b3e
8 changed files with 74 additions and 33 deletions
|
|
@ -84,7 +84,7 @@ A full `manifest.json` with most of the optional fields looks like this:
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/your-username/my-mcp-extension"
|
"url": "https://github.com/your-username/my-mcp-extension.git"
|
||||||
},
|
},
|
||||||
"homepage": "https://example.com/my-extension",
|
"homepage": "https://example.com/my-extension",
|
||||||
"documentation": "https://docs.example.com/my-extension",
|
"documentation": "https://docs.example.com/my-extension",
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@ import { confirm, input, select } from "@inquirer/prompts";
|
||||||
import { existsSync, readFileSync, writeFileSync } from "fs";
|
import { existsSync, readFileSync, writeFileSync } from "fs";
|
||||||
import { basename, join, resolve } from "path";
|
import { basename, join, resolve } from "path";
|
||||||
|
|
||||||
import { LATEST_MANIFEST_VERSION } from "../shared/constants.js";
|
import { DEFAULT_MANIFEST_VERSION } from "../shared/constants.js";
|
||||||
import type { McpbManifestLatest } from "../types.js";
|
import type { McpbManifest } from "../types.js";
|
||||||
|
|
||||||
interface PackageJson {
|
interface PackageJson {
|
||||||
name?: string;
|
name?: string;
|
||||||
|
|
@ -878,14 +878,14 @@ export function buildManifest(
|
||||||
resources: string;
|
resources: string;
|
||||||
default_locale: string;
|
default_locale: string;
|
||||||
},
|
},
|
||||||
): McpbManifestLatest {
|
): McpbManifest {
|
||||||
const { name, displayName, version, description, authorName } = basicInfo;
|
const { name, displayName, version, description, authorName } = basicInfo;
|
||||||
const { authorEmail, authorUrl } = authorInfo;
|
const { authorEmail, authorUrl } = authorInfo;
|
||||||
const { serverType, entryPoint, mcp_config } = serverConfig;
|
const { serverType, entryPoint, mcp_config } = serverConfig;
|
||||||
const { keywords, license, repository } = optionalFields;
|
const { keywords, license, repository } = optionalFields;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
manifest_version: LATEST_MANIFEST_VERSION,
|
manifest_version: DEFAULT_MANIFEST_VERSION,
|
||||||
name,
|
name,
|
||||||
...(displayName && displayName !== name
|
...(displayName && displayName !== name
|
||||||
? { display_name: displayName }
|
? { display_name: displayName }
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,9 @@ import { basename, join, relative, resolve, sep } from "path";
|
||||||
|
|
||||||
import { getAllFilesWithCount, readMcpbIgnorePatterns } from "../node/files.js";
|
import { getAllFilesWithCount, readMcpbIgnorePatterns } from "../node/files.js";
|
||||||
import { validateManifest } from "../node/validate.js";
|
import { validateManifest } from "../node/validate.js";
|
||||||
import {
|
import { MANIFEST_SCHEMAS } from "../shared/constants.js";
|
||||||
LATEST_MANIFEST_SCHEMA,
|
|
||||||
LATEST_MANIFEST_VERSION,
|
|
||||||
} from "../shared/constants.js";
|
|
||||||
import { getLogger } from "../shared/log.js";
|
import { getLogger } from "../shared/log.js";
|
||||||
|
import { getManifestVersionFromRawData } from "../shared/manifestVersionResolve.js";
|
||||||
import { initExtension } from "./init.js";
|
import { initExtension } from "./init.js";
|
||||||
|
|
||||||
interface PackOptions {
|
interface PackOptions {
|
||||||
|
|
@ -95,7 +93,18 @@ export async function packExtension({
|
||||||
try {
|
try {
|
||||||
const manifestContent = readFileSync(manifestPath, "utf-8");
|
const manifestContent = readFileSync(manifestPath, "utf-8");
|
||||||
const manifestData = JSON.parse(manifestContent);
|
const manifestData = JSON.parse(manifestContent);
|
||||||
manifest = LATEST_MANIFEST_SCHEMA.parse(manifestData);
|
const manifestVersion = getManifestVersionFromRawData(manifestData);
|
||||||
|
if (!manifestVersion) {
|
||||||
|
logger.error(
|
||||||
|
`ERROR: Manifest version mismatch. Expected "${Object.keys(MANIFEST_SCHEMAS).join(" or ")}", found "${manifestVersion}"`,
|
||||||
|
);
|
||||||
|
logger.error(
|
||||||
|
` Please update the manifest_version in your manifest.json to a supported version`,
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
manifest = MANIFEST_SCHEMAS[manifestVersion].parse(manifestData);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error("ERROR: Failed to parse manifest.json");
|
logger.error("ERROR: Failed to parse manifest.json");
|
||||||
if (error instanceof Error) {
|
if (error instanceof Error) {
|
||||||
|
|
@ -104,17 +113,6 @@ export async function packExtension({
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const manifestVersion = manifest.manifest_version || manifest.dxt_version;
|
|
||||||
if (manifestVersion !== LATEST_MANIFEST_VERSION) {
|
|
||||||
logger.error(
|
|
||||||
`ERROR: Manifest version mismatch. Expected "${LATEST_MANIFEST_VERSION}", found "${manifestVersion}"`,
|
|
||||||
);
|
|
||||||
logger.error(
|
|
||||||
` Please update the manifest_version in your manifest.json to "${LATEST_MANIFEST_VERSION}"`,
|
|
||||||
);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine output path
|
// Determine output path
|
||||||
const extensionName = basename(resolvedPath);
|
const extensionName = basename(resolvedPath);
|
||||||
const finalOutputPath = outputPath
|
const finalOutputPath = outputPath
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,10 @@ import prettyBytes from "pretty-bytes";
|
||||||
|
|
||||||
import { unpackExtension } from "../cli/unpack.js";
|
import { unpackExtension } from "../cli/unpack.js";
|
||||||
import {
|
import {
|
||||||
LATEST_MANIFEST_SCHEMA,
|
MANIFEST_SCHEMAS,
|
||||||
LATEST_MANIFEST_SCHEMA_LOOSE,
|
MANIFEST_SCHEMAS_LOOSE,
|
||||||
} from "../shared/constants.js";
|
} from "../shared/constants.js";
|
||||||
|
import { getManifestVersionFromRawData } from "../shared/manifestVersionResolve.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a buffer contains a valid PNG file signature
|
* Check if a buffer contains a valid PNG file signature
|
||||||
|
|
@ -119,8 +120,13 @@ export function validateManifest(inputPath: string): boolean {
|
||||||
|
|
||||||
const manifestContent = readFileSync(manifestPath, "utf-8");
|
const manifestContent = readFileSync(manifestPath, "utf-8");
|
||||||
const manifestData = JSON.parse(manifestContent);
|
const manifestData = JSON.parse(manifestContent);
|
||||||
|
const manifestVersion = getManifestVersionFromRawData(manifestData);
|
||||||
|
if (!manifestVersion) {
|
||||||
|
console.log("Unrecognized or unsupported manifest version");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const result = LATEST_MANIFEST_SCHEMA.safeParse(manifestData);
|
const result = MANIFEST_SCHEMAS[manifestVersion].safeParse(manifestData);
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
console.log("Manifest schema validation passes!");
|
console.log("Manifest schema validation passes!");
|
||||||
|
|
@ -192,7 +198,12 @@ export async function cleanMcpb(inputPath: string) {
|
||||||
const manifestPath = resolve(unpackPath, "manifest.json");
|
const manifestPath = resolve(unpackPath, "manifest.json");
|
||||||
const originalManifest = await fs.readFile(manifestPath, "utf-8");
|
const originalManifest = await fs.readFile(manifestPath, "utf-8");
|
||||||
const manifestData = JSON.parse(originalManifest);
|
const manifestData = JSON.parse(originalManifest);
|
||||||
const result = LATEST_MANIFEST_SCHEMA_LOOSE.safeParse(manifestData);
|
const manifestVersion = getManifestVersionFromRawData(manifestData);
|
||||||
|
if (!manifestVersion) {
|
||||||
|
throw new Error("Unrecognized or unsupported manifest version");
|
||||||
|
}
|
||||||
|
const result =
|
||||||
|
MANIFEST_SCHEMAS_LOOSE[manifestVersion].safeParse(manifestData);
|
||||||
|
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,15 @@ import { McpbManifestSchema as CurrentLooseManifestSchema } from "../schemas_loo
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Latest manifest version - the version that new manifests should use
|
* Latest manifest version - the version that new manifests should use
|
||||||
|
* @deprecated
|
||||||
*/
|
*/
|
||||||
export const LATEST_MANIFEST_VERSION = "0.3" as const;
|
export const LATEST_MANIFEST_VERSION = "0.3" as const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default manifest version for new packages
|
||||||
|
*/
|
||||||
|
export const DEFAULT_MANIFEST_VERSION = "0.2" as const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map of manifest versions to their strict schemas
|
* Map of manifest versions to their strict schemas
|
||||||
*/
|
*/
|
||||||
|
|
@ -32,10 +38,12 @@ export const MANIFEST_SCHEMAS_LOOSE = {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the latest manifest schema based on LATEST_MANIFEST_VERSION
|
* Get the latest manifest schema based on LATEST_MANIFEST_VERSION
|
||||||
|
* @deprecated
|
||||||
*/
|
*/
|
||||||
export const LATEST_MANIFEST_SCHEMA = CurrentManifestSchema;
|
export const LATEST_MANIFEST_SCHEMA = CurrentManifestSchema;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the latest loose manifest schema based on LATEST_MANIFEST_VERSION
|
* Get the latest loose manifest schema based on LATEST_MANIFEST_VERSION
|
||||||
|
* @deprecated
|
||||||
*/
|
*/
|
||||||
export const LATEST_MANIFEST_SCHEMA_LOOSE = CurrentLooseManifestSchema;
|
export const LATEST_MANIFEST_SCHEMA_LOOSE = CurrentLooseManifestSchema;
|
||||||
|
|
|
||||||
24
src/shared/manifestVersionResolve.ts
Normal file
24
src/shared/manifestVersionResolve.ts
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { MANIFEST_SCHEMAS } from "./constants.js";
|
||||||
|
|
||||||
|
export function getManifestVersionFromRawData(manifestData: unknown) {
|
||||||
|
let manifestVersion: keyof typeof MANIFEST_SCHEMAS | null = null;
|
||||||
|
if (
|
||||||
|
typeof manifestData === "object" &&
|
||||||
|
manifestData &&
|
||||||
|
"manifest_version" in manifestData &&
|
||||||
|
typeof manifestData.manifest_version === "string" &&
|
||||||
|
Object.keys(MANIFEST_SCHEMAS).includes(manifestData.manifest_version)
|
||||||
|
) {
|
||||||
|
manifestVersion =
|
||||||
|
manifestData.manifest_version as keyof typeof MANIFEST_SCHEMAS;
|
||||||
|
} else if (
|
||||||
|
typeof manifestData === "object" &&
|
||||||
|
manifestData &&
|
||||||
|
"dxt_version" in manifestData &&
|
||||||
|
typeof manifestData.dxt_version === "string" &&
|
||||||
|
Object.keys(MANIFEST_SCHEMAS).includes(manifestData.dxt_version)
|
||||||
|
) {
|
||||||
|
manifestVersion = manifestData.dxt_version as keyof typeof MANIFEST_SCHEMAS;
|
||||||
|
}
|
||||||
|
return manifestVersion;
|
||||||
|
}
|
||||||
|
|
@ -2,7 +2,7 @@ import { execSync } from "node:child_process";
|
||||||
import fs from "node:fs";
|
import fs from "node:fs";
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
|
|
||||||
import { LATEST_MANIFEST_VERSION } from "../src/schemas/index.js";
|
import { DEFAULT_MANIFEST_VERSION } from "../src/shared/constants";
|
||||||
|
|
||||||
interface ExecSyncError extends Error {
|
interface ExecSyncError extends Error {
|
||||||
stdout: Buffer;
|
stdout: Buffer;
|
||||||
|
|
@ -113,7 +113,7 @@ describe("DXT CLI", () => {
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
manifestWithIcon,
|
manifestWithIcon,
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
manifest_version: LATEST_MANIFEST_VERSION,
|
manifest_version: DEFAULT_MANIFEST_VERSION,
|
||||||
name: "test-with-icon",
|
name: "test-with-icon",
|
||||||
version: "1.0.0",
|
version: "1.0.0",
|
||||||
description: "Test with icon",
|
description: "Test with icon",
|
||||||
|
|
@ -140,7 +140,7 @@ describe("DXT CLI", () => {
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
manifestWithUrl,
|
manifestWithUrl,
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
manifest_version: LATEST_MANIFEST_VERSION,
|
manifest_version: DEFAULT_MANIFEST_VERSION,
|
||||||
name: "test-with-url",
|
name: "test-with-url",
|
||||||
version: "1.0.0",
|
version: "1.0.0",
|
||||||
description: "Test with URL",
|
description: "Test with URL",
|
||||||
|
|
@ -177,7 +177,7 @@ describe("DXT CLI", () => {
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
join(tempDir, "manifest.json"),
|
join(tempDir, "manifest.json"),
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
manifest_version: LATEST_MANIFEST_VERSION,
|
manifest_version: DEFAULT_MANIFEST_VERSION,
|
||||||
name: "Test Extension",
|
name: "Test Extension",
|
||||||
version: "1.0.0",
|
version: "1.0.0",
|
||||||
description: "A test extension",
|
description: "A test extension",
|
||||||
|
|
@ -274,7 +274,7 @@ describe("DXT CLI", () => {
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
join(tempExecDir, "manifest.json"),
|
join(tempExecDir, "manifest.json"),
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
manifest_version: LATEST_MANIFEST_VERSION,
|
manifest_version: DEFAULT_MANIFEST_VERSION,
|
||||||
name: "Test Executable Extension",
|
name: "Test Executable Extension",
|
||||||
version: "1.0.0",
|
version: "1.0.0",
|
||||||
description: "A test extension with executable files",
|
description: "A test extension with executable files",
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import {
|
||||||
getDefaultRepositoryUrl,
|
getDefaultRepositoryUrl,
|
||||||
readPackageJson,
|
readPackageJson,
|
||||||
} from "../src/cli/init.js";
|
} from "../src/cli/init.js";
|
||||||
import { LATEST_MANIFEST_VERSION } from "../src/schemas/index.js";
|
import { DEFAULT_MANIFEST_VERSION } from "../src/shared/constants.js";
|
||||||
|
|
||||||
// Mock the fs module
|
// Mock the fs module
|
||||||
jest.mock("fs", () => ({
|
jest.mock("fs", () => ({
|
||||||
|
|
@ -220,7 +220,7 @@ describe("init functions", () => {
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(manifest).toEqual({
|
expect(manifest).toEqual({
|
||||||
manifest_version: LATEST_MANIFEST_VERSION,
|
manifest_version: DEFAULT_MANIFEST_VERSION,
|
||||||
name: "test-extension",
|
name: "test-extension",
|
||||||
version: "1.0.0",
|
version: "1.0.0",
|
||||||
description: "Test description",
|
description: "Test description",
|
||||||
|
|
@ -322,7 +322,7 @@ describe("init functions", () => {
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(manifest).toEqual({
|
expect(manifest).toEqual({
|
||||||
manifest_version: LATEST_MANIFEST_VERSION,
|
manifest_version: DEFAULT_MANIFEST_VERSION,
|
||||||
name: "test-extension",
|
name: "test-extension",
|
||||||
display_name: "Test Extension",
|
display_name: "Test Extension",
|
||||||
version: "1.0.0",
|
version: "1.0.0",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue