| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190 |
- #!/usr/bin/env node
- import fs from 'fs';
- import os from 'os';
- import path from 'path';
- import { fileURLToPath } from 'url';
- import {
- VL_VERSION,
- THEME_VERSION,
- THEME_VERSION_FULL,
- STYLESPACE_VERSION,
- WORKFLOW_SPEC_DOC_VERSION,
- } from '../src/data/versions.js';
- import { DOCCENTER_API_URL } from '../src/data/doc-paths.js';
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
- const ROOT = path.resolve(__dirname, '..');
- const APPLY = process.argv.includes('--apply');
- const AUTH_PATH = path.join(os.homedir(), '.vl-code', 'auth.json');
- const cookie = process.env.DOCCENTER_COOKIE || (() => {
- try {
- return JSON.parse(fs.readFileSync(AUTH_PATH, 'utf-8')).cookie;
- } catch {
- return '';
- }
- })();
- if (!cookie) {
- console.error('Missing DocCenter cookie. Set DOCCENTER_COOKIE or login so ~/.vl-code/auth.json exists.');
- process.exit(1);
- }
- const headers = {
- 'Content-Type': 'application/json',
- Cookie: String(cookie).startsWith('ih5bearer=') ? String(cookie) : `ih5bearer=${cookie}`,
- };
- async function dcPost(endpoint, body) {
- const res = await fetch(`${DOCCENTER_API_URL}/${endpoint}`, {
- method: 'POST',
- headers,
- body: JSON.stringify(body),
- });
- const text = await res.text();
- let payload = {};
- try {
- payload = text ? JSON.parse(text) : {};
- } catch {
- throw new Error(`DocCenter returned non-JSON for ${endpoint}: ${text.slice(0, 300)}`);
- }
- if (!res.ok) {
- throw new Error(payload?.error || payload?.message || `DocCenter ${endpoint} failed: ${res.status}`);
- }
- return payload;
- }
- async function loadCatalog() {
- const result = await dcPost('SERVICE_DocCenter_GetDocList', {
- keyword: '',
- tagId: 0,
- page: 1,
- pageSize: 300,
- });
- return Array.isArray(result?.data) ? result.data : [];
- }
- async function getDocById(docId) {
- const result = await dcPost('SERVICE_DocCenter_GetDocById', { docId });
- return result?.data || null;
- }
- function normalizeSyntaxContent(content) {
- return String(content || '')
- .replace(/Current version:\s*`\/\/ VL_VERSION:[\d.]+`/g, `Current version: \`// VL_VERSION:${VL_VERSION}\``)
- .replace(/\/\/ VL_VERSION:[\d.]+/g, `// VL_VERSION:${VL_VERSION}`)
- .replace(/THEME_[\d.]+\.vth/g, `THEME_${THEME_VERSION}.vth`)
- .replace(/Theme-Enterprise-[\d.]+/g, `Theme-Enterprise-${THEME_VERSION}`);
- }
- function normalizeThemeContent(content) {
- return String(content || '')
- .replace(/VL_VERSION:[\d.]+/g, `VL_VERSION:${VL_VERSION}`)
- .replace(/Theme-Enterprise-[\d.]+/g, `Theme-Enterprise-${THEME_VERSION}`)
- .replace(/version:"[\d.]+"/g, `version:"${THEME_VERSION_FULL}"`)
- .replace(/styleSpaceVersion:"[\d.]+"/g, `styleSpaceVersion:"${STYLESPACE_VERSION}"`);
- }
- async function buildManifest(catalog) {
- const syntaxDoc = catalog.find((doc) => String(doc.path) === '1');
- const syntaxRemote = syntaxDoc ? await getDocById(syntaxDoc._id || syntaxDoc.id) : null;
- return [
- {
- path: '1',
- name: `VL_${VL_VERSION}.md`,
- description: `Canonical VL syntax reference for VL ${VL_VERSION}.`,
- currentContent: normalizeSyntaxContent(
- syntaxRemote?.currentContent || fs.readFileSync(path.join(ROOT, 'src', 'data', 'vl-syntax.md'), 'utf-8')
- ),
- changeNote: `v${VL_VERSION} — align syntax reference with Doc ID based official document resolution and THEME ${THEME_VERSION}`,
- },
- {
- path: '2',
- name: `THEME_${THEME_VERSION}.vth`,
- description: `Canonical enterprise theme for VL ${VL_VERSION} / THEME ${THEME_VERSION}.`,
- currentContent: normalizeThemeContent(
- fs.readFileSync(path.join(ROOT, 'public', 'seed-theme', 'Theme.vth'), 'utf-8')
- ),
- changeNote: `v${THEME_VERSION} — align Theme root tag, VL version, and enterprise seed with official Doc ID configuration`,
- },
- {
- path: '3',
- name: `VL Workflow Spec ${WORKFLOW_SPEC_DOC_VERSION}`,
- description: 'Canonical workflow specification with Doc ID priority, Tool_* runtime semantics, and checkpoint rerun compatibility.',
- currentContent: fs.readFileSync(path.join(ROOT, 'docs', `vl-workflow-spec-${WORKFLOW_SPEC_DOC_VERSION}.md`), 'utf-8'),
- changeNote: `v${WORKFLOW_SPEC_DOC_VERSION} — clarify Doc ID as persisted workflow binding, keep path as slot alias, and align workflow runtime compatibility`,
- },
- {
- path: '4',
- name: 'VL Metadata Spec',
- description: 'Canonical VL ProjectMeta schema for normalization, diff, workflow regeneration, and IDE tooling.',
- currentContent: fs.readFileSync(path.join(ROOT, 'docs', 'vl-metadata-spec-3.1.md'), 'utf-8'),
- changeNote: 'v3.1 — clarify Doc ID bindings stay outside ProjectMeta while keeping canonical schema and theme slot metadata rules',
- },
- ];
- }
- async function ensureDoc(entry, catalog) {
- let doc = catalog.find((item) => String(item.path) === String(entry.path));
- if (doc) return doc;
- if (!APPLY) {
- return null;
- }
- const created = await dcPost('SERVICE_DocCenter_CreateDoc', {
- name: entry.name,
- description: entry.description,
- path: entry.path,
- });
- doc = created?.data || created;
- catalog.push({
- _id: doc?._id || doc?.id || doc?.docId,
- id: doc?.id,
- path: entry.path,
- name: entry.name,
- });
- return catalog.find((item) => String(item.path) === String(entry.path)) || doc;
- }
- async function publishEntry(entry, catalog) {
- const doc = await ensureDoc(entry, catalog);
- const docId = doc?._id || doc?.id || null;
- const remote = docId ? await getDocById(docId) : null;
- const remoteContent = remote?.currentContent || '';
- const action = remoteContent === entry.currentContent ? 'unchanged' : (APPLY ? 'published' : 'publish');
- if (APPLY && remoteContent !== entry.currentContent) {
- await dcPost('SERVICE_DocCenter_SaveAsVersion', {
- path: entry.path,
- name: entry.name,
- description: entry.description,
- currentContent: entry.currentContent,
- changeNote: entry.changeNote,
- });
- }
- return {
- action,
- path: entry.path,
- name: entry.name,
- docId: docId || '(new)',
- };
- }
- async function main() {
- const catalog = await loadCatalog();
- const manifest = await buildManifest(catalog);
- console.log(`\nDocCenter core-doc publish ${APPLY ? '(apply)' : '(dry-run)'}`);
- for (const entry of manifest) {
- const result = await publishEntry(entry, catalog);
- const label = result.action.padEnd(9, ' ');
- console.log(`${label} path ${result.path} ${result.name} (docId ${result.docId})`);
- }
- }
- main().catch((err) => {
- console.error(err.message);
- process.exit(1);
- });
|