UNPKG

@contentmark/cli

Version:

Command-line tools for ContentMark protocol validation and generation

1,541 lines (1,530 loc) 61.4 kB
#!/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