42 lines
4.8 KiB
JavaScript
42 lines
4.8 KiB
JavaScript
/** Execute via production auth-cli run; token stays in memory. Uses only the designated origin. */
|
|
import {readFile,writeFile,mkdir} from 'node:fs/promises';
|
|
const base=process.env.AUTH_CHECK_BASE_URL||'http://8.211.154.134:18131';
|
|
if(base!=='http://8.211.154.134:18131')throw Error('Use the designated acceptance origin');
|
|
const dir=process.env.STUDIO_LIBRARY_DIR||'/tmp/studio-library';
|
|
await mkdir(dir,{recursive:true});
|
|
async function api(path,body,method=body?'POST':'GET'){
|
|
const response=await fetch(base+path,{method,...(body?{headers:{'Content-Type':'application/json'},body:JSON.stringify(body)}:{})});
|
|
if(!response.ok)throw Error(path+' status '+response.status);
|
|
return response.json();
|
|
}
|
|
const capability=await fetch(base+'/api/studio/series?agent_mode=True');if(!capability.ok||!(await capability.text()).includes('schemaVersion 2'))throw Error('Series API is not deployed; refusing to publish future documentation');
|
|
const studioCapability=await api('/studio/api/layouts');if(studioCapability.seriesDocumentVersion!==1)throw Error('Studio renderer is not deployed; refusing to publish future documentation');
|
|
const canonical='8eb8c76500580f7a4b56ba3c',legacy='9ebd8f78d54b4b9db741b4e8';
|
|
const {entry:current,tags}=await api('/api/notes/'+canonical),{entry:old}=await api('/api/notes/'+legacy);
|
|
// Snapshot only non-secret editorial state. Retain the first backup across repeat publication.
|
|
for(const [id,entry] of [[canonical,current],[legacy,old]]){try{await readFile(dir+'/'+id+'-before.json')}catch{await writeFile(dir+'/'+id+'-before.json',JSON.stringify(entry),{mode:0o600})}}
|
|
const catalogue=await api('/studio/api/layouts');
|
|
let markdown=(await readFile(new URL('../content/studio-tools/layouts-api.md',import.meta.url),'utf8')).replace('{{LAYOUTS}}',catalogue.layouts.filter(l=>l.family==='fixed').map(l=>`- [${l.id} · ${l.name}](${l.url}) · [参数](${l.api})`).join('\n'));
|
|
const appendixMarker='## 兼容工具原说明与代码示例';
|
|
const historicalNotice='> 以下保留历史工具示例,不代表当前 API 保存与打开流程。历史文字中关于首页、编辑器和旧结果地址的描述已过时:当前由 API 创建 ID,直接打开 `/studio/template-layout?id={UUID}`,无需选版或编辑器。当前契约以上文为准。';
|
|
if(current.editableMarkdown.includes(appendixMarker)){
|
|
const appendix=current.editableMarkdown.slice(current.editableMarkdown.indexOf(appendixMarker));
|
|
markdown+='\n\n'+(appendix.includes(historicalNotice)?appendix:appendix.replace(appendixMarker,appendixMarker+'\n\n'+historicalNotice));
|
|
}else{
|
|
if(old.archived)throw Error('Original toolbox appendix is missing; refusing to replace it with the archived redirect');
|
|
const legacyOriginal=JSON.parse(await readFile(dir+'/'+legacy+'-before.json','utf8')).editableMarkdown;
|
|
markdown+='\n\n'+appendixMarker+'\n\n'+historicalNotice+'\n\n以下保留原工具的说明与 HTML/React/Next.js 调用示例;它们是无状态兼容接口,持久化页面请使用上文 CRUD。\n\n'+legacyOriginal;
|
|
}
|
|
const tagIds=entry=>[...new Set([...(entry.systemTags||[]).map(t=>t.id),...entry.categories.map(name=>tags.find(t=>t.name===name)?.id).filter(Boolean)])];
|
|
const body=(entry,text,title=entry.title)=>({title,description:entry.description,markdown:text,tags:tagIds(entry),assets:entry.assets,coverAssetId:entry.coverAssetId,revision:entry.revision});
|
|
await api('/api/notes/'+canonical,{...body(current,markdown,'Studio 图文 API · 多页作品与统一视觉设计'),description:'coding agent 制定主题分镜与统一视觉,生成配图,经 HTTP 保存可修订多页作品;凭稳定 ID 直接打开原生结果并在远程导出高清图片。'},'PUT');
|
|
const redirectText=`# Studio 文档已合并\n\n[前往 Studio 排版 API 统一入口](/explore/${canonical})\n\n[可视化 API 库](/studio) · [原 ref-17 工具](/studio/ideas/learning-skills-template) · [原三种构图工具](/studio/ideas/layout-selection)\n\n完整原说明与代码示例已保留在统一入口的兼容工具章节。此记录仅可恢复归档,未删除工具、素材或设计。`;
|
|
await api('/api/notes/'+legacy,body(old,redirectText),'PUT');
|
|
await api('/api/posts/'+legacy+'/archive',{archived:true});
|
|
const entries=await api('/api/explore-feed?category='+encodeURIComponent('文档入口'));
|
|
const expected=['3c98946754c33309d73d5284','4ce3283fd98bb03a8fb39a19',canonical].sort();
|
|
if(entries.total!==3||entries.nextCursor||JSON.stringify(entries.entries.map(e=>e.id).sort())!==JSON.stringify(expected))throw Error('Document-entry category does not contain exactly the intended three posts');
|
|
const after=(await api('/api/notes/'+legacy)).entry;
|
|
if(!after.archived||!after.editableMarkdown.includes(canonical))throw Error('Legacy compatibility link missing');
|
|
console.log(JSON.stringify({canonical:base+'/explore/'+canonical,legacyArchived:true,documentEntryCount:entries.total}));
|