| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384 |
- import fs from 'node:fs';
- import path from 'node:path';
- import crypto from 'node:crypto';
- import {execFileSync} from 'node:child_process';
- const output=process.argv[2];if(!output||!path.isAbsolute(output))throw Error('请指定交付目录的绝对路径');
- const sha=x=>crypto.createHash('sha256').update(x).digest('hex');
- const destination=path.resolve(output);fs.mkdirSync(destination,{recursive:true});
- const staging=fs.mkdtempSync(path.resolve('work/package-'));
- const python=process.env.KSF_PACKAGE_PYTHON||'C:/Users/JSHY/AppData/Local/Python/pythoncore-3.14-64/python.exe';
- const results=[];
- function write(root,name,content){const file=path.join(root,name);fs.mkdirSync(path.dirname(file),{recursive:true});fs.writeFileSync(file,content);}
- function copy(root,name){write(root,name,fs.readFileSync(name));}
- function pack(name,prepare){
- const root=path.join(staging,name);fs.mkdirSync(root);prepare(root);
- const files=[];function walk(dir){for(const e of fs.readdirSync(dir,{withFileTypes:true})){const p=path.join(dir,e.name);if(e.isDirectory())walk(p);else files.push(path.relative(root,p).replaceAll('\\','/'));}}walk(root);
- files.sort();write(root,'FILES.sha256',files.map(n=>sha(fs.readFileSync(path.join(root,n)))+' '+n).join('\n')+'\n');
- const zip=path.join(destination,name+'.zip');
- execFileSync(python,['-c','import sys,zipfile,pathlib; root=pathlib.Path(sys.argv[1]); z=zipfile.ZipFile(sys.argv[2],"w",zipfile.ZIP_DEFLATED); [(z.write(p,p.relative_to(root).as_posix())) for p in sorted(root.rglob("*")) if p.is_file()]; z.close(); z=zipfile.ZipFile(sys.argv[2]); assert z.testzip() is None',root,zip],{windowsHide:true});
- const bytes=fs.readFileSync(zip);results.push({name,file:zip,sizeBytes:bytes.length,sha256:sha(bytes),manifestEntries:files.length});
- }
- pack('ksf-workshop-skill-20261101',root=>{
- const source='skills/ksf-workshop-runtime';
- for(const line of fs.readFileSync(source+'/FILES.sha256','utf8').trim().split('\n'))copy(root,source+'/'+line.slice(66));
- copy(root,source+'/FILES.sha256');copy(root,'scripts/install-workshop-skill.mjs');
- write(root,'checks/read-only-project-search.json',JSON.stringify({className:'KfProject',filters:{dataMode:'sample'},limit:20},null,2)+'\n');
- write(root,'INSTALL.txt',`康师傅工作坊 Skill 升级 · 2026-11-01
- 此包仅更新数字人的工作坊执行入口,不包含凭据、私钥、credential-binding.json 或真实研究素材。
- 保留已有加密绑定、原 ksf.mjs、runtime-lock、冻结 r7。无需改网关、配置或重启。
- 更新 SKILL.md 和加密路由,新增工作坊脚本与任务格式;安装器会备份旧文件,发现未知修改先停,不覆盖。
- 1. 先用随包执行单的大小及 SHA-256 校验 ZIP,不符即停,不用旧包代替。
- 2. 解压到 /opt/data/ksf-workshop-skill-20261101/(若已存在另建干净目录,不覆盖),cd 到包根。
- 3. sha256sum -c FILES.sha256。顶层应 11/11 OK;技能子清单应 7/7。
- 4. 阅读 scripts/install-workshop-skill.mjs,执行:
- node scripts/install-workshop-skill.mjs
- 预期 ok=true,credentialBindingPreserved=true,legacyEntryPreserved=true,gatewayModified=false。
- 当前已知基线首次更新四项;已安装同版本则 changed=[],不重复备份。
- 5. skills_list 发现,再 skillview 重新读取 ksf-workshop-runtime。文件安装不代表发现或企微调用完成。
- 6. cd /opt/data/skills/ksf-workshop-runtime,使用解压包内技能子 FILES.sha256 验证实际安装七文件。
- 7. 从新的企微消息按已加载技能运行只读核验:
- node /opt/data/skills/ksf-workshop-runtime/scripts/ksf-secure.mjs status --request-id KSF-工作坊升级-20261101-I
- node /opt/data/skills/ksf-workshop-runtime/scripts/ksf-secure.mjs workshop-search --input /opt/data/ksf-workshop-skill-20261101/checks/read-only-project-search.json --request-id KSF-工作坊只读-20261101-J
- 若实际解压目录不同,只改上面 input 目录。两条各自报原始 JSON、两层 invocationId、退出码;下划线原样保留,不输出凭据。
- search 应由新 ksf-workshop.mjs 执行、readOnly=true;查询存在的 sample 项目不是新增样例。
- 8. 安装和只读核验停在这里。后续另发真实工作坊任务,取真实发送者与当前 Agent,登记会话、保存一份阶段成果,并回读对应编号。
- 不把“王刚001(联调模拟)”当实际 Agent;新包不会自动重演管理机的技术样例。
- 运行约束:保持 HERMES_WRITE_SAFE_ROOT;只在 /opt/data 下准备任务。无稳定身份时按技能做课堂登记,不伪造账号。
- 公开看板不需登录;课堂成果是 workshop,正式报告确认仍保持独立流程。sample 不能升级成真实资料。
- 令牌原到期日为北京时间 2026-11-06 18:42,本次没有续期;过期则停止并由管理方更新信封。
- `);
- });
- pack('ksf-board-data-20261101',root=>{
- const files=['src/app.config.js','src/services/board-service.js','src/services/workshop-data.js','src/core/kfs/build.js','src/core/library/reports.js','src/core/opportunity/opportunity.js','src/core/taxonomy/taxonomy.js','src/pages/view-members.js','lib/vendor/signal-polyfill/v0.2.2/dist/index.js'];
- for(const file of files)copy(root,file);
- write(root,'PUBLISH.txt',`康师傅看板数据接入发布 · 2026-11-01
- 由王刚001或平台后台按现有站点流程发布,本包尚未推送 Git 或部署前端。
- 基于仓库提交 7d01b5cf2249decb63cfbde95e26dfedad86290e。共九个运行文件,无凭据。
- 保留公开访问,不增加登录。未修改 CSS、设计令牌、组件样式或布局。
- 成员页沿用原表格新增逐项目进展,显示负责人、阶段、成果、方案保存情况与卡住/课堂完成。
- 发布前 sha256sum -c FILES.sha256。先备份对应旧文件;如线上已继续更新,先合并差异,不能覆盖其他人的改动。
- 按包内相对路径合并到站点源码并发布这九文件,不要直接上传包根说明和清单为站点首页。
- 新增 workshop-data.js;务必带上 signal-polyfill 的 dist/index.js(旧仓库 dist 忽略规则曾漏收此运行依赖)。
- Git 发布时保留仓库 .gitignore 对该 index.js 的例外,不能只提交文件夹。
- index.html 不变;保留现有 base、importmap 和路由回退。更新后按现有发布渠道刷新文件缓存。
- 服务端八个工作坊云函数已经上线,不需再次部署、不改 Schema/表权限、不往前端添加服务令牌。
- 验收:匿名浏览器打开 /、/members、/pool、/stages、/report,均能读取。
- 在 /members 找“工作坊联调(非学员)”、项目“【样例】拌面新品链路技术联调”,应是 S5 / 课堂完成。
- 报告 /report/5u4xzJW5DY 应显示样例、Agent“王刚001(联调模拟)”以及未正式确认说明。
- 机会 /pool/7TnUg76JOI 与项目 Z-IEjZg0qZ 的关联应可回读。
- 浏览器 Network 应看到匿名 POST /api/functions path=/ksfGetPublicBoard,不能发送服务令牌。
- 核桌面与390px手机、刷新与深链接;现有评分/决策沿用旧行为。
- 以上是管理机明确 sample 的技术联调,不是真实员工研究,也不是企业审批或正式报告发布。
- 前端发布和 Hermes 安装后,还需新的企微消息实测员工→当前Agent→阶段成果→看板,才算现场通道完整验收。
- `);
- });
- const receipt={at:new Date().toISOString(),packages:results,containsCredentials:false,hermesInstalled:false,frontendPublished:false};
- fs.writeFileSync(path.join(destination,'ksf-workshop-delivery-20261101.json'),JSON.stringify(receipt,null,2));
- console.log(JSON.stringify(receipt,null,2));
|