@contentmark/cli
Version:
Command-line tools for ContentMark protocol validation and generation
1,541 lines (1,530 loc) • 61.4 kB
JavaScript
#!/usr/bin/env node
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
// src/cli.ts
var import_commander = require("commander");
var import_chalk2 = __toESM(require("chalk"));
var import_ora = __toESM(require("ora"));
var import_fs_extra = require("fs-extra");
// src/validator.ts
var import_ajv = __toESM(require("ajv"));
var import_ajv_formats = __toESM(require("ajv-formats"));
var import_node_fetch = __toESM(require("node-fetch"));
var _ContentMarkValidator = class _ContentMarkValidator {
constructor(customSchemaUrl) {
this.customSchemaUrl = customSchemaUrl;
this.ajv = new import_ajv.default({ allErrors: true });
(0, import_ajv_formats.default)(this.ajv);
}
async loadSchema(schemaUrl) {
const url = schemaUrl || this.customSchemaUrl || _ContentMarkValidator.DEFAULT_SCHEMA_URL;
if (_ContentMarkValidator.schemaCache.has(url)) {
return _ContentMarkValidator.schemaCache.get(url);
}
try {
const response = await (0, import_node_fetch.default)(url);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const schema = await response.json();
_ContentMarkValidator.schemaCache.set(url, schema);
return schema;
} catch (error) {
const fallbackSchema = this.getBuiltInSchema();
_ContentMarkValidator.schemaCache.set(url, fallbackSchema);
return fallbackSchema;
}
}
async validate(content, schemaUrl) {
const schema = await this.loadSchema(schemaUrl);
const result = {
valid: false,
errors: [],
warnings: [],
suggestions: []
};
try {
let data;
try {
data = JSON.parse(content);
} catch (parseError) {
result.errors.push(`Invalid JSON: ${parseError.message}`);
return result;
}
result.manifest = data;
const validate = this.ajv.compile(schema);
const isValid = validate(data);
if (!isValid && validate.errors) {
validate.errors.forEach((error) => {
const path = error.instancePath || error.schemaPath;
result.errors.push(`${path}: ${error.message}`);
});
}
this.addCustomValidation(data, result);
this.addSuggestions(data, result);
result.valid = result.errors.length === 0;
return result;
} catch (error) {
result.errors.push(`Validation failed: ${error.message}`);
return result;
}
}
async validateURL(url) {
try {
if (!url.startsWith("http://") && !url.startsWith("https://")) {
url = "https://" + url;
}
const manifestUrl = new URL("/.well-known/contentmark.json", url).toString();
const response = await (0, import_node_fetch.default)(manifestUrl, {
headers: {
"User-Agent": "ContentMark-CLI/1.0.0"
}
});
if (!response.ok) {
return {
valid: false,
errors: [`Failed to fetch manifest: HTTP ${response.status}`],
warnings: [],
suggestions: []
};
}
const content = await response.text();
return this.validate(content);
} catch (error) {
return {
valid: false,
errors: [`Network error: ${error.message}`],
warnings: [],
suggestions: []
};
}
}
addCustomValidation(data, result) {
if (!data.version) {
result.errors.push("Missing required field: version");
} else if (!data.version.match(/^\d+\.\d+\.\d+$/)) {
result.errors.push('Version must follow semantic versioning (e.g., "1.0.0")');
}
if (!data.siteName) {
result.errors.push("Missing required field: siteName");
} else if (data.siteName.length > 200) {
result.errors.push("siteName must be 200 characters or less");
}
if (!data.defaultUsagePolicy) {
result.errors.push("Missing required field: defaultUsagePolicy");
} else {
const policy = data.defaultUsagePolicy;
const requiredPolicyFields = ["canSummarize", "canTrain", "canQuote", "mustAttribute"];
requiredPolicyFields.forEach((field) => {
if (typeof policy[field] !== "boolean") {
result.errors.push(`defaultUsagePolicy.${field} must be a boolean`);
}
});
}
if (!data.lastModified) {
result.errors.push("Missing required field: lastModified");
} else {
try {
new Date(data.lastModified);
} catch {
result.errors.push("lastModified must be a valid ISO 8601 timestamp");
}
}
this.validateURLs(data, result);
this.checkLogicalConsistency(data, result);
}
validateURLs(data, result) {
var _a;
const checkURL = (url, fieldName) => {
try {
new URL(url);
} catch {
result.errors.push(`${fieldName} must be a valid absolute URL`);
}
};
if (data.feeds && Array.isArray(data.feeds)) {
data.feeds.forEach((feed, index) => {
if (feed.url) {
checkURL(feed.url, `feeds[${index}].url`);
}
});
}
if (data.monetization) {
if (data.monetization.tipJar) {
checkURL(data.monetization.tipJar, "monetization.tipJar");
}
if ((_a = data.monetization.consultation) == null ? void 0 : _a.bookingUrl) {
checkURL(data.monetization.consultation.bookingUrl, "monetization.consultation.bookingUrl");
}
if (data.monetization.services) {
data.monetization.services.forEach((service, index) => {
if (service.url) {
checkURL(service.url, `monetization.services[${index}].url`);
}
});
}
}
}
checkLogicalConsistency(data, result) {
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
if (((_a = data.defaultUsagePolicy) == null ? void 0 : _a.canSummarize) === false && ((_b = data.visibility) == null ? void 0 : _b.aiDiscovery) === "enhanced") {
result.warnings.push("Conflicting settings: canSummarize is false but aiDiscovery is enhanced");
}
if (((_c = data.defaultUsagePolicy) == null ? void 0 : _c.mustAttribute) && !((_d = data.defaultUsagePolicy) == null ? void 0 : _d.attributionTemplate)) {
result.warnings.push("Attribution required but no attributionTemplate provided");
}
if (((_e = data.access) == null ? void 0 : _e.type) === "authenticated" && !((_f = data.access) == null ? void 0 : _f.loginUrl)) {
result.errors.push('access.loginUrl is required when type is "authenticated"');
}
if (((_h = (_g = data.monetization) == null ? void 0 : _g.consultation) == null ? void 0 : _h.available) && !((_j = (_i = data.monetization) == null ? void 0 : _i.consultation) == null ? void 0 : _j.bookingUrl)) {
result.warnings.push("Consultation marked as available but no bookingUrl provided");
}
}
addSuggestions(data, result) {
var _a, _b, _c, _d, _e;
if (!data.monetization) {
result.suggestions.push("Consider adding monetization options to benefit from AI traffic");
} else {
if (!data.monetization.tipJar && !((_a = data.monetization.consultation) == null ? void 0 : _a.available)) {
result.suggestions.push("Consider adding a tip jar for easy monetization");
}
}
if (!data.visibility) {
result.suggestions.push("Consider adding visibility settings to optimize AI discoverability");
} else if (!data.visibility.preferredQueries || data.visibility.preferredQueries.length === 0) {
result.suggestions.push("Add preferredQueries to improve AI recommendation targeting");
}
if (((_b = data.defaultUsagePolicy) == null ? void 0 : _b.canTrain) === true) {
result.suggestions.push("Consider whether allowing AI training aligns with your content strategy");
}
if (!data.description || data.description.length < 50) {
result.suggestions.push("Add a detailed description to help AI understand your content focus");
}
if (!data.feeds || data.feeds.length === 0) {
result.suggestions.push("Consider adding content feeds for better AI integration");
}
if (((_d = (_c = data.monetization) == null ? void 0 : _c.consultation) == null ? void 0 : _d.available) && !data.businessOptimization) {
result.suggestions.push("Add businessOptimization to target specific audiences and service areas");
}
if (((_e = data.visibility) == null ? void 0 : _e.aiDiscovery) === "enhanced" && !data.aiGuidance) {
result.suggestions.push("Add aiGuidance to control how AI presents your content");
}
}
getBuiltInSchema() {
return {
type: "object",
required: ["version", "siteName", "defaultUsagePolicy", "lastModified"],
properties: {
version: { type: "string", pattern: "^\\d+\\.\\d+\\.\\d+$" },
siteName: { type: "string", minLength: 1, maxLength: 200 },
defaultUsagePolicy: {
type: "object",
required: ["canSummarize", "canTrain", "canQuote", "mustAttribute"],
properties: {
canSummarize: { type: "boolean" },
canTrain: { type: "boolean" },
canQuote: { type: "boolean" },
mustAttribute: { type: "boolean" }
}
},
lastModified: { type: "string", format: "date-time" }
}
};
}
};
_ContentMarkValidator.schemaCache = /* @__PURE__ */ new Map();
_ContentMarkValidator.DEFAULT_SCHEMA_URL = "https://schema.contentmark.org/v1/manifest.json";
var ContentMarkValidator = _ContentMarkValidator;
// src/generator.ts
var import_inquirer = __toESM(require("inquirer"));
var ContentMarkGenerator = class {
generateTemplate(type) {
const now = (/* @__PURE__ */ new Date()).toISOString();
switch (type.toLowerCase()) {
case "blog":
return this.generateBlogTemplate(now);
case "business":
return this.generateBusinessTemplate(now);
case "premium":
return this.generatePremiumTemplate(now);
case "ecommerce":
return this.generateEcommerceTemplate(now);
case "news":
return this.generateNewsTemplate(now);
case "education":
return this.generateEducationTemplate(now);
case "api":
return this.generateApiTemplate(now);
default:
throw new Error(`Unknown template type: ${type}. Available types: blog, business, premium, ecommerce, news, education, api`);
}
}
async generateInteractive() {
console.log("\n\u{1F680} Welcome to ContentMark Interactive Generator!\n");
console.log("This will help you create a customized ContentMark manifest for your website.\n");
const basicInfo = await import_inquirer.default.prompt([
{
type: "input",
name: "siteName",
message: "What is your website/brand name?",
validate: (input) => input.length > 0 || "Site name is required"
},
{
type: "input",
name: "description",
message: "Briefly describe your website/content:"
},
{
type: "input",
name: "website",
message: "What is your website URL? (e.g., https://example.com)",
validate: (input) => {
try {
new URL(input);
return true;
} catch {
return "Please enter a valid URL";
}
}
}
]);
console.log("\n\u{1F4CB} AI Usage Policies\n");
const policies = await import_inquirer.default.prompt([
{
type: "confirm",
name: "canSummarize",
message: "Allow AI to create summaries of your content?",
default: true
},
{
type: "confirm",
name: "canTrain",
message: "Allow AI to use your content for model training?",
default: false
},
{
type: "confirm",
name: "canQuote",
message: "Allow AI to quote excerpts from your content?",
default: true
},
{
type: "confirm",
name: "mustAttribute",
message: "Require AI to provide attribution when using your content?",
default: true
}
]);
let attributionTemplate;
if (policies.mustAttribute) {
const attribution = await import_inquirer.default.prompt([
{
type: "input",
name: "template",
message: "Attribution template (use {siteName} and {url} as placeholders):",
default: `From ${basicInfo.siteName} - {url}`
}
]);
attributionTemplate = attribution.template;
}
console.log("\n\u{1F4B0} Monetization Options\n");
const monetizationChoice = await import_inquirer.default.prompt([
{
type: "list",
name: "type",
message: "What type of monetization do you want to include?",
choices: [
{ name: "None (just attribution)", value: "none" },
{ name: "Tip jar only", value: "tips" },
{ name: "Professional services", value: "services" },
{ name: "Premium content/subscriptions", value: "premium" }
]
}
]);
let monetization;
if (monetizationChoice.type !== "none") {
monetization = await this.promptMonetization(monetizationChoice.type);
}
console.log("\n\u{1F3AF} AI Visibility Optimization\n");
const visibilityChoice = await import_inquirer.default.prompt([
{
type: "confirm",
name: "optimize",
message: "Do you want to optimize for AI discoverability?",
default: false
}
]);
let visibility;
if (visibilityChoice.optimize) {
visibility = await this.promptVisibility();
}
console.log("\n\u{1F512} Content Access Control\n");
const accessType = await import_inquirer.default.prompt([
{
type: "list",
name: "type",
message: "What type of access does your content have?",
choices: [
{ name: "Public (freely accessible)", value: "public" },
{ name: "Requires login/registration", value: "authenticated" },
{ name: "Premium/paid content", value: "paywall" }
]
}
]);
let access = { type: accessType.type };
if (accessType.type === "authenticated") {
const loginInfo = await import_inquirer.default.prompt([
{
type: "input",
name: "loginUrl",
message: "Login/registration URL:",
validate: (input) => {
try {
new URL(input);
return true;
} catch {
return "Please enter a valid URL";
}
}
}
]);
access = { ...access, loginUrl: loginInfo.loginUrl, previewAvailable: true };
} else if (accessType.type === "paywall") {
const paidInfo = await import_inquirer.default.prompt([
{
type: "input",
name: "subscriptionUrl",
message: "Subscription/payment URL:",
validate: (input) => {
try {
new URL(input);
return true;
} catch {
return "Please enter a valid URL";
}
}
}
]);
access = { ...access, subscriptionUrl: paidInfo.subscriptionUrl, previewAvailable: true };
} else {
access = { ...access, previewAvailable: true };
}
const manifest = {
version: "1.0.0",
siteName: basicInfo.siteName,
description: basicInfo.description || void 0,
defaultUsagePolicy: {
canSummarize: policies.canSummarize,
canTrain: policies.canTrain,
canQuote: policies.canQuote,
mustAttribute: policies.mustAttribute,
attributionTemplate: attributionTemplate || void 0
},
visibility: visibility || void 0,
monetization: monetization || void 0,
access,
renderHints: {
preferredFormat: accessType.type === "paywall" ? "preview-only" : "summary-first",
callToAction: {
text: monetizationChoice.type === "services" ? "Get expert consultation" : "Visit our website",
url: basicInfo.website
}
},
lastModified: (/* @__PURE__ */ new Date()).toISOString()
};
console.log("\n\u2705 ContentMark manifest generated successfully!");
return manifest;
}
async promptMonetization(type) {
switch (type) {
case "tips":
const tipInfo = await import_inquirer.default.prompt([
{
type: "input",
name: "tipJar",
message: "Tip jar URL (Buy Me a Coffee, Ko-fi, etc.):",
validate: (input) => {
try {
new URL(input);
return true;
} catch {
return "Please enter a valid URL";
}
}
}
]);
return { tipJar: tipInfo.tipJar };
case "services":
const serviceInfo = await import_inquirer.default.prompt([
{
type: "confirm",
name: "consultation",
message: "Do you offer consultation services?",
default: true
},
{
type: "input",
name: "bookingUrl",
message: "Booking URL (Calendly, etc.):",
when: (answers) => answers.consultation,
validate: (input) => {
try {
new URL(input);
return true;
} catch {
return "Please enter a valid URL";
}
}
},
{
type: "input",
name: "hourlyRate",
message: "Hourly rate (e.g., $150/hour):",
when: (answers) => answers.consultation
}
]);
const monetization = {};
if (serviceInfo.consultation) {
monetization.consultation = {
available: true,
bookingUrl: serviceInfo.bookingUrl,
hourlyRate: serviceInfo.hourlyRate || void 0
};
}
const additionalServices = await import_inquirer.default.prompt([
{
type: "confirm",
name: "hasServices",
message: "Do you want to list specific services/products?",
default: false
}
]);
if (additionalServices.hasServices) {
monetization.services = await this.promptServices();
}
return monetization;
case "premium":
const premiumInfo = await import_inquirer.default.prompt([
{
type: "input",
name: "subscriptionUrl",
message: "Subscription URL:",
validate: (input) => {
try {
new URL(input);
return true;
} catch {
return "Please enter a valid URL";
}
}
},
{
type: "input",
name: "price",
message: "Subscription price (e.g., $9.99/month):"
}
]);
return {
subscription: {
platform: "Premium Content",
url: premiumInfo.subscriptionUrl,
price: premiumInfo.price
}
};
default:
return void 0;
}
}
async promptServices() {
const services = [];
let addMore = true;
while (addMore) {
const service = await import_inquirer.default.prompt([
{
type: "input",
name: "name",
message: "Service/product name:",
validate: (input) => input.length > 0 || "Service name is required"
},
{
type: "input",
name: "url",
message: "Service URL:",
validate: (input) => {
try {
new URL(input);
return true;
} catch {
return "Please enter a valid URL";
}
}
},
{
type: "input",
name: "pricing",
message: "Pricing (e.g., from $500, $99/month):"
}
]);
services.push({
name: service.name,
url: service.url,
pricing: service.pricing || void 0
});
const continueAdding = await import_inquirer.default.prompt([
{
type: "confirm",
name: "more",
message: "Add another service?",
default: false
}
]);
addMore = continueAdding.more;
}
return services;
}
async promptVisibility() {
const visibilityInfo = await import_inquirer.default.prompt([
{
type: "list",
name: "aiDiscovery",
message: "AI discovery level:",
choices: [
{ name: "Standard (normal discovery)", value: "standard" },
{ name: "Enhanced (boost discoverability)", value: "enhanced" },
{ name: "Minimal (reduce visibility)", value: "minimal" }
]
},
{
type: "input",
name: "expertiseAreas",
message: "Your expertise areas (comma-separated):",
filter: (input) => input ? input.split(",").map((s) => s.trim()).filter((s) => s) : []
},
{
type: "input",
name: "preferredQueries",
message: "Preferred search queries (comma-separated):",
filter: (input) => input ? input.split(",").map((s) => s.trim()).filter((s) => s) : []
},
{
type: "number",
name: "boostScore",
message: "Priority boost score (1-10, higher = more visibility):",
default: 5,
validate: (input) => input >= 1 && input <= 10 || "Score must be between 1 and 10"
}
]);
return {
aiDiscovery: visibilityInfo.aiDiscovery,
expertiseAreas: visibilityInfo.expertiseAreas.length > 0 ? visibilityInfo.expertiseAreas : void 0,
preferredQueries: visibilityInfo.preferredQueries.length > 0 ? visibilityInfo.preferredQueries : void 0,
boostScore: visibilityInfo.boostScore !== 5 ? visibilityInfo.boostScore : void 0,
priorityContent: visibilityInfo.aiDiscovery === "enhanced"
};
}
generateBlogTemplate(timestamp) {
return {
version: "1.0.0",
siteName: "Your Blog Name",
description: "Brief description of your blog content and expertise",
feeds: [
{
type: "blog",
url: "https://yourblog.com/contentmark-feed.json",
title: "Latest Articles",
description: "Recent blog posts and tutorials"
}
],
defaultUsagePolicy: {
canSummarize: true,
canTrain: false,
canQuote: true,
mustAttribute: true,
attributionTemplate: "From {siteName} - {url}"
},
monetization: {
tipJar: "https://buymeacoffee.com/yourusername"
},
access: {
type: "public",
previewAvailable: true
},
renderHints: {
preferredFormat: "summary-first",
callToAction: {
text: "Read the full article",
url: "https://yourblog.com"
}
},
lastModified: timestamp
};
}
generateBusinessTemplate(timestamp) {
return {
version: "1.0.0",
siteName: "Your Business Name",
description: "Professional services and expertise in your industry",
feeds: [
{
type: "business",
url: "https://yourbusiness.com/contentmark-feed.json",
title: "Business Insights",
description: "Latest insights and case studies"
}
],
defaultUsagePolicy: {
canSummarize: true,
canTrain: false,
canQuote: true,
mustAttribute: true,
attributionTemplate: "From {siteName}, industry experts - {url}"
},
visibility: {
aiDiscovery: "enhanced",
priorityContent: true,
preferredQueries: [
"your expertise area",
"your industry + consulting",
"your service + experts"
],
expertiseAreas: [
"Your primary expertise",
"Secondary expertise",
"Industry knowledge"
],
boostScore: 7
},
monetization: {
consultation: {
available: true,
bookingUrl: "https://calendly.com/yourusername",
hourlyRate: "$200/hour"
},
services: [
{
name: "Your Main Service",
url: "https://yourbusiness.com/services",
pricing: "from $2,500"
}
]
},
access: {
type: "public",
previewAvailable: true
},
renderHints: {
preferredFormat: "summary-first",
callToAction: {
text: "Get expert consultation",
url: "https://yourbusiness.com/contact"
}
},
lastModified: timestamp
};
}
generatePremiumTemplate(timestamp) {
return {
version: "1.0.0",
siteName: "Your Premium Content",
description: "Premium research and analysis in your field",
feeds: [
{
type: "research",
url: "https://yoursite.com/public-feed.json",
title: "Public Research Summaries",
description: "Free summaries of premium research"
}
],
defaultUsagePolicy: {
canSummarize: false,
canTrain: false,
canQuote: true,
mustAttribute: true,
attributionTemplate: "From {siteName} premium content - {url} (subscription required for full access)"
},
visibility: {
aiDiscovery: "standard",
priorityContent: true,
expertiseAreas: [
"Your research area",
"Industry analysis",
"Market research"
]
},
monetization: {
subscription: {
platform: "Premium Research",
url: "https://yoursite.com/subscribe",
price: "$99/month"
}
},
access: {
type: "paywall",
previewAvailable: true,
subscriptionUrl: "https://yoursite.com/subscribe"
},
renderHints: {
preferredFormat: "preview-only",
callToAction: {
text: "Subscribe for full access",
url: "https://yoursite.com/subscribe"
}
},
lastModified: timestamp
};
}
generateEcommerceTemplate(timestamp) {
return {
version: "1.0.0",
siteName: "Your Store Name",
description: "Quality products and exceptional customer service",
feeds: [
{
type: "product",
url: "https://yourstore.com/products-feed.json",
title: "Product Catalog",
description: "Latest products and collections"
}
],
defaultUsagePolicy: {
canSummarize: true,
canTrain: false,
canQuote: true,
mustAttribute: true,
attributionTemplate: "Available at {siteName} - {url}"
},
visibility: {
aiDiscovery: "enhanced",
priorityContent: true,
preferredQueries: [
"your product category",
"your brand + products",
"your niche + shopping"
],
expertiseAreas: [
"Product expertise",
"Customer service",
"Industry knowledge"
]
},
monetization: {
services: [
{
name: "Product Sales",
url: "https://yourstore.com/shop",
pricing: "varies by product"
}
]
},
access: {
type: "public",
previewAvailable: true
},
renderHints: {
preferredFormat: "summary-first",
callToAction: {
text: "Shop now",
url: "https://yourstore.com/shop"
}
},
lastModified: timestamp
};
}
generateNewsTemplate(timestamp) {
return {
version: "1.0.0",
siteName: "Your News Organization",
description: "Trusted news and analysis on current events",
feeds: [
{
type: "news",
url: "https://yournews.com/news-feed.json",
title: "Breaking News",
description: "Latest news and updates"
}
],
defaultUsagePolicy: {
canSummarize: true,
canTrain: false,
canQuote: true,
mustAttribute: true,
attributionTemplate: "Source: {siteName} - {url}"
},
visibility: {
aiDiscovery: "enhanced",
priorityContent: true,
expertiseAreas: [
"Journalism",
"Current events",
"News analysis"
]
},
access: {
type: "public",
previewAvailable: true
},
lastModified: timestamp
};
}
generateEducationTemplate(timestamp) {
return {
version: "1.0.0",
siteName: "Your Educational Platform",
description: "Quality educational content and learning resources",
feeds: [
{
type: "educational",
url: "https://youredu.com/courses-feed.json",
title: "Course Catalog",
description: "Available courses and learning materials"
}
],
defaultUsagePolicy: {
canSummarize: true,
canTrain: false,
canQuote: true,
mustAttribute: true,
attributionTemplate: "Learn more at {siteName} - {url}"
},
visibility: {
aiDiscovery: "enhanced",
priorityContent: true,
expertiseAreas: [
"Education",
"Teaching",
"Subject matter expertise"
]
},
monetization: {
services: [
{
name: "Online Courses",
url: "https://youredu.com/courses",
pricing: "from $99"
}
]
},
access: {
type: "paywall",
previewAvailable: true
},
lastModified: timestamp
};
}
generateApiTemplate(timestamp) {
return {
version: "1.0.0",
siteName: "Your API Documentation",
description: "Comprehensive API documentation and developer resources",
feeds: [
{
type: "documentation",
url: "https://yourapi.com/docs-feed.json",
title: "API Documentation",
description: "Latest API updates and guides"
}
],
defaultUsagePolicy: {
canSummarize: true,
canTrain: false,
canQuote: true,
mustAttribute: true,
attributionTemplate: "API docs: {siteName} - {url}"
},
visibility: {
aiDiscovery: "enhanced",
priorityContent: true,
expertiseAreas: [
"API development",
"Technical documentation",
"Developer tools"
]
},
access: {
type: "public",
previewAvailable: true
},
lastModified: timestamp
};
}
};
// src/url-checker.ts
var URLChecker = class {
constructor(fetchImpl, options = {}) {
this.validator = new ContentMarkValidator();
this.fetch = fetchImpl || global.fetch || require("node-fetch");
this.options = options;
}
async checkWebsite(url) {
if (!url.startsWith("http://") && !url.startsWith("https://")) {
url = "https://" + url;
}
try {
const baseURL = new URL(url);
const allErrors = [];
const wellKnownResult = await this.checkWellKnown(baseURL);
if (wellKnownResult.found) {
return wellKnownResult;
}
if (wellKnownResult.errors) {
allErrors.push(...wellKnownResult.errors);
}
const htmlLinkResult = await this.checkHTMLLink(baseURL);
if (htmlLinkResult.found) {
return htmlLinkResult;
}
if (htmlLinkResult.errors) {
allErrors.push(...htmlLinkResult.errors);
}
const httpHeaderResult = await this.checkHTTPHeaders(baseURL);
if (httpHeaderResult.found) {
return httpHeaderResult;
}
if (httpHeaderResult.errors) {
allErrors.push(...httpHeaderResult.errors);
}
return { found: false, errors: allErrors };
} catch (error) {
return {
found: false,
errors: [error.message]
};
}
}
async checkWellKnown(baseURL) {
const manifestUrl = new URL("/.well-known/contentmark.json", baseURL).toString();
try {
const response = await this.fetch(manifestUrl, {
method: "GET",
headers: {
"User-Agent": "ContentMark-CLI/1.0.0",
"Accept": "application/json"
},
timeout: this.options.timeout || 1e4
});
if (response.ok) {
const content = await response.text();
const validation = await this.validator.validate(content);
return {
found: true,
method: "well-known",
foundAt: manifestUrl,
discoveryMethod: "well-known",
manifestUrl,
httpStatus: response.status,
valid: validation.valid,
errors: validation.errors,
manifest: validation.manifest
};
}
if (response.status === 404) {
return { found: false };
}
return {
found: true,
foundAt: manifestUrl,
discoveryMethod: "well-known",
manifestUrl,
httpStatus: response.status,
valid: false,
errors: [`HTTP ${response.status}: ${response.statusText}`]
};
} catch (error) {
return { found: false, errors: [error.message] };
}
}
async checkHTMLLink(baseURL) {
try {
const response = await this.fetch(baseURL.toString(), {
method: "GET",
headers: {
"User-Agent": "ContentMark-CLI/1.0.0",
"Accept": "text/html"
},
timeout: this.options.timeout || 1e4
});
if (!response.ok) {
return { found: false };
}
const html = await response.text();
const linkRegex = /<link[^>]*rel=["']contentmark["'][^>]*>/gi;
const hrefRegex = /href=["']([^"']+)["']/i;
const linkMatches = html.match(linkRegex);
if (!linkMatches) {
return { found: false };
}
for (const linkMatch of linkMatches) {
const hrefMatch = linkMatch.match(hrefRegex);
if (hrefMatch) {
let manifestUrl = hrefMatch[1];
if (!manifestUrl.startsWith("http")) {
manifestUrl = new URL(manifestUrl, baseURL).toString();
}
try {
const manifestResponse = await this.fetch(manifestUrl, {
method: "GET",
headers: {
"User-Agent": "ContentMark-CLI/1.0.0",
"Accept": "application/json"
},
timeout: this.options.timeout || 1e4
});
if (manifestResponse.ok) {
const content = await manifestResponse.text();
const validation = await this.validator.validate(content);
return {
found: true,
method: "html-link",
foundAt: baseURL.toString(),
discoveryMethod: "html-link",
manifestUrl,
httpStatus: manifestResponse.status,
valid: validation.valid,
errors: validation.errors,
manifest: validation.manifest
};
}
} catch {
continue;
}
}
}
return { found: false };
} catch (error) {
return { found: false, errors: [error.message] };
}
}
async checkHTTPHeaders(baseURL) {
try {
const response = await this.fetch(baseURL.toString(), {
method: "HEAD",
headers: {
"User-Agent": "ContentMark-CLI/1.0.0"
},
timeout: this.options.timeout || 1e4
});
if (!response.ok) {
return { found: false };
}
const linkHeaders = response.headers.get("link");
if (!linkHeaders) {
return { found: false };
}
const linkRegex = /<([^>]+)>;\s*rel=["']?contentmark["']?/i;
const match = linkHeaders.match(linkRegex);
if (match) {
let manifestUrl = match[1];
if (!manifestUrl.startsWith("http")) {
manifestUrl = new URL(manifestUrl, baseURL).toString();
}
try {
const manifestResponse = await this.fetch(manifestUrl, {
method: "GET",
headers: {
"User-Agent": "ContentMark-CLI/1.0.0",
"Accept": "application/json"
},
timeout: this.options.timeout || 1e4
});
if (manifestResponse.ok) {
const content = await manifestResponse.text();
const validation = await this.validator.validate(content);
return {
found: true,
method: "http-header",
foundAt: baseURL.toString(),
discoveryMethod: "http-header",
manifestUrl,
httpStatus: manifestResponse.status,
valid: validation.valid,
errors: validation.errors,
manifest: validation.manifest
};
}
} catch {
return { found: false };
}
}
return { found: false };
} catch (error) {
return { found: false, errors: [error.message] };
}
}
async analyzeManifest(url) {
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
const result = await this.checkWebsite(url);
if (!result.found || !result.manifest) {
throw new Error("No ContentMark manifest found");
}
const manifest = result.manifest;
return {
basicInfo: {
siteName: manifest.siteName,
description: manifest.description,
version: manifest.version,
lastModified: manifest.lastModified
},
policies: {
canSummarize: manifest.defaultUsagePolicy.canSummarize,
canTrain: manifest.defaultUsagePolicy.canTrain,
canQuote: manifest.defaultUsagePolicy.canQuote,
mustAttribute: manifest.defaultUsagePolicy.mustAttribute,
attributionTemplate: manifest.defaultUsagePolicy.attributionTemplate
},
monetization: {
hasMonetization: !!manifest.monetization,
tipJar: (_a = manifest.monetization) == null ? void 0 : _a.tipJar,
consultation: (_b = manifest.monetization) == null ? void 0 : _b.consultation,
services: (_c = manifest.monetization) == null ? void 0 : _c.services,
subscription: (_d = manifest.monetization) == null ? void 0 : _d.subscription
},
optimization: {
hasVisibility: !!manifest.visibility,
aiDiscovery: (_e = manifest.visibility) == null ? void 0 : _e.aiDiscovery,
priorityContent: (_f = manifest.visibility) == null ? void 0 : _f.priorityContent,
boostScore: (_g = manifest.visibility) == null ? void 0 : _g.boostScore,
expertiseAreas: (_h = manifest.visibility) == null ? void 0 : _h.expertiseAreas,
preferredQueries: (_i = manifest.visibility) == null ? void 0 : _i.preferredQueries
}
};
}
async batchCheck(urls, options = {}) {
const results = /* @__PURE__ */ new Map();
const concurrency = options.concurrency || 5;
const chunks = this.chunkArray(urls, concurrency);
let completed = 0;
for (const chunk of chunks) {
const promises = chunk.map(async (url) => {
try {
const result = await this.checkWebsite(url);
completed++;
if (options.onProgress) {
options.onProgress(completed, urls.length);
}
return [url, result];
} catch (error) {
completed++;
if (options.onProgress) {
options.onProgress(completed, urls.length);
}
return [url, {
found: false,
errors: [error.message]
}];
}
});
const chunkResults = await Promise.all(promises);
chunkResults.forEach(([url, result]) => {
results.set(url, result);
});
if (chunks.indexOf(chunk) < chunks.length - 1) {
await new Promise((resolve) => setTimeout(resolve, 100));
}
}
return results;
}
chunkArray(array, size) {
const chunks = [];
for (let i = 0; i < array.length; i += size) {
chunks.push(array.slice(i, i + size));
}
return chunks;
}
async checkMultipleWebsites(urls) {
const results = [];
for (const url of urls) {
try {
const result = await this.checkWebsite(url);
results.push([url, result]);
} catch (error) {
results.push([url, {
found: false,
errors: [error.message]
}]);
}
}
return results;
}
};
// src/formatters/types.ts
var OutputFormatter = class {
};
// src/formatters/json.ts
var JsonFormatter = class extends OutputFormatter {
formatValidation(result) {
return JSON.stringify({
valid: result.valid,
errors: result.errors,
warnings: result.warnings,
suggestions: result.suggestions,
manifest: result.manifest
}, null, 2);
}
formatCheck(result) {
return JSON.stringify({
found: result.found,
method: result.method,
manifestUrl: result.manifestUrl,
errors: result.errors,
manifest: result.manifest
}, null, 2);
}
formatBatch(results) {
const formatted = results.map(([url, result]) => ({
url,
found: result.found,
method: result.method,
manifestUrl: result.manifestUrl,
errors: result.errors
}));
return JSON.stringify({
results: formatted,
summary: {
total: results.length,
found: results.filter(([, r]) => r.found).length,
notFound: results.filter(([, r]) => !r.found).length
}
}, null, 2);
}
};
// src/formatters/yaml.ts
var yaml = __toESM(require("js-yaml"));
var YamlFormatter = class extends OutputFormatter {
formatValidation(result) {
return yaml.dump({
valid: result.valid,
errors: result.errors,
warnings: result.warnings,
suggestions: result.suggestions,
manifest: result.manifest
});
}
formatCheck(result) {
return yaml.dump({
found: result.found,
method: result.method,
manifestUrl: result.manifestUrl,
errors: result.errors,
manifest: result.manifest
});
}
formatBatch(results) {
const formatted = results.map(([url, result]) => ({
url,
found: result.found,
method: result.method,
manifestUrl: result.manifestUrl,
errors: result.errors
}));
return yaml.dump({
results: formatted,
summary: {
total: results.length,
found: results.filter(([, r]) => r.found).length,
notFound: results.filter(([, r]) => !r.found).length
}
});
}
};
// src/formatters/summary.ts
var import_chalk = __toESM(require("chalk"));
var SummaryFormatter = class extends OutputFormatter {
formatValidation(result) {
const lines = [];
if (result.valid) {
lines.push(import_chalk.default.green("\u2705 Valid ContentMark manifest"));
} else {
lines.push(import_chalk.default.red("\u274C Invalid ContentMark manifest"));
}
if (result.errors.length > 0) {
lines.push(import_chalk.default.red("\nErrors:"));
result.errors.forEach((error) => {
lines.push(import_chalk.default.red(` \u2022 ${error}`));
});
}
if (result.warnings.length > 0) {
lines.push(import_chalk.default.yellow("\nWarnings:"));
result.warnings.forEach((warning) => {
lines.push(import_chalk.default.yellow(` \u2022 ${warning}`));
});
}
if (result.suggestions.length > 0) {
lines.push(import_chalk.default.blue("\nSuggestions:"));
result.suggestions.forEach((suggestion) => {
lines.push(import_chalk.default.blue(` \u2022 ${suggestion}`));
});
}
return lines.join("\n");
}
formatCheck(result) {
const lines = [];
if (result.found) {
lines.push(import_chalk.default.green("\u2705 ContentMark support found"));
lines.push(`Method: ${result.method}`);
lines.push(`Manifest URL: ${result.manifestUrl}`);
} else {
lines.push(import_chalk.default.red("\u274C No ContentMark support found"));
}
if (result.errors && result.errors.length > 0) {
lines.push(import_chalk.default.red("\nErrors:"));
result.errors.forEach((error) => {
lines.push(import_chalk.default.red(` \u2022 ${error}`));
});
}
return lines.join("\n");
}
formatBatch(results) {
const lines = [];
const found = results.filter(([, r]) => r.found).length;
const total = results.length;
lines.push(import_chalk.default.blue(`
\u{1F4CA} Batch Check Results: ${found}/${total} sites support ContentMark
`));
results.forEach(([url, result]) => {
if (result.found) {
lines.push(import_chalk.default.green(`\u2705 ${url} - ${result.method}`));
} else {
lines.push(import_chalk.default.red(`\u274C ${url} - No support found`));
}
});
return lines.join("\n");
}
};
// src/formatters/index.ts
function getFormatter(format) {
switch (format.toLowerCase()) {
case "yaml":
return new YamlFormatter();
case "summary":
return new SummaryFormatter();
case "json":
default:
return new JsonFormatter();
}
}
// src/config.ts
var import_fs = require("fs");
var import_path = require("path");
var import_os = require("os");
var DEFAULT_CONFIG = {
validation: {
strictMode: false
},
output: {
format: "json",
verbose: false,
colors: true
},
batch: {
concurrency: 5,
timeout: 1e4,
retries: 2
}
};
var ConfigManager = class {
constructor(configPath) {
this.config = this.loadConfig(configPath);
}
loadConfig(configPath) {
const paths = [
configPath,
".contentmarkrc.json",
(0, import_path.join)(process.cwd(), ".contentmarkrc.json"),
(0, import_path.join)((0, import_os.homedir)(), ".contentmarkrc.json")
].filter(Boolean);
for (const path of paths) {
if ((0, import_fs.existsSync)(path)) {
try {
const content = (0, import_fs.readFileSync)(path, "utf-8");
const userConfig = JSON.parse(content);
return this.mergeConfig(DEFAULT_CONFIG, userConfig);
} catch (error) {
console.warn(`Warning: Failed to parse config file ${path}: ${error.message}`);
}
}
}
return DEFAULT_CONFIG;
}
mergeConfig(defaultConfig, userConfig) {
return {
validation: { ...defaultConfig.validation, ...userConfig.validation },
output: { ...defaultConfig.output, ...userConfig.output },
batch: { ...defaultConfig.batch, ...userConfig.batch }
};
}
get(key) {
if (key) {
return this.config[key];
}
return this.config;
}
getValidationConfig() {
return this.config.validation || {};
}
getOutputConfig() {
return this.config.output || {};
}
getBatchConfig() {
return this.config.batch || {};
}
};
// src/cli.ts
var program = new import_commander.Command();
var config = new ConfigManager();
program.name("contentmark").description("ContentMark CLI - Tools for AI-readable content protocol").version("1.0.0");
program.command("validate").description("Validate a ContentMark manifest file").argument("[file]", "Path to contentmark.json file", ".well-known/contentmark.json").option("-u, --url <url>", "Validate remote URL instead of local file").option("-v, --verbose", "Show detailed validation output").option("--json", "Output results as JSON").option("--format <format>", "Output format (json, yaml, summary)").action(async (file, options) => {
const outputConfig = config.getOutputConfig();
if (!options.format && !options.json) {
options.format = outputConfig.format === "json" ? void 0 : outputConfig.format;
}
options.verbose = options.verbose || outputConfig.verbose;
const spinner = (0, import_ora.default)("Validating ContentMark manifest...").start();
try {
const validationConfig = config.getValidationConfig();
const validator = new ContentMarkValidator(validationConfig.schemaUrl);
let result;
if (options.url) {
result = await validator.validateURL(options.url);
spinner.succeed(`Validated ${options.url}`);
} else {
if (!(0, import_fs_extra.existsSync)(file)) {
spinner.fail(`File not found: ${file}`);
process.exit(1);
}
const content = (0, import_fs_extra.readFileSync)(file, "utf-8");
result = await validator.validate(content);
spinner.succeed(`Validated ${file}`);
}
if (options.json) {
console.log(JSON.stringify(result, null, 2));
process.exit(result.valid ? 0 : 1);
}
if (options.format) {
const formatter = getFormatter(options.format);
console.log(formatter.formatValidation(result));
process.exit(result.valid ? 0 : 1);
}
console.log();
if (result.valid) {
console.log(import_chalk2.default.green("\u2705 Va