"""Build an enterprise bootstrap package from the unchanged sealed r13 and runtime hotfix.""" import argparse import ast import hashlib import importlib.util import io import json from pathlib import Path import sys import zipfile sys.dont_write_bytecode = True ROOT = Path(__file__).resolve().parents[1] SOURCE = ROOT / 'deploy/enterprise-workshop' NAME = 'ksf-enterprise-workshop-20261110-r2' sha = lambda raw: hashlib.sha256(raw).hexdigest() def need(ok, code): if not ok: raise ValueError(code) def content(): files = {p.name: p.read_bytes() for p in SOURCE.iterdir() if p.is_file()} files['.gitattributes'] = b'* -text\n' cfg = json.loads(files['enterprise.json']) skill_archive = ROOT / 'docs/ksf-workshop-skill-20261110-r13-r2/publish/ksf-workshop-skill-20261110-r13-r2.zip' hotfix_archive = ROOT / 'docs/ksf-unified-runtime-hotfix-20261109-r1/publish/ksf-unified-runtime-hotfix-20261109-r1.zip' need(sha(skill_archive.read_bytes()) == cfg['skill']['upstreamArchiveSha256'], 'SEALED_SKILL_CHANGED') need(sha(hotfix_archive.read_bytes()) == cfg['researchRuntime']['upstreamArchiveSha256'], 'SEALED_HOTFIX_CHANGED') with zipfile.ZipFile(skill_archive) as z: meta = json.loads(z.read('release.json')) for name, digest in meta['workshop']['files'].items(): raw = z.read('skills/ksf-workshop-runtime/' + name) need(sha(raw) == digest, 'SKILL_HASH_MISMATCH') files['payload/skills/ksf-workshop-runtime/' + name] = raw with zipfile.ZipFile(hotfix_archive) as z: prefix = 'components/research-runtime/' for line in z.read(prefix + 'FILES.sha256').decode().splitlines(): digest, name = line.split(' ', 1) need(name.startswith('scripts/'), 'UNEXPECTED_RESEARCH_COMPONENT') raw = z.read(prefix + name) need(sha(raw) == digest, 'RESEARCH_COMPONENT_HASH_MISMATCH') files['payload/skills/ksf-voc-library/' + name] = raw for name, raw in files.items(): if name.endswith('.py'): ast.parse(raw.decode('utf-8-sig'), filename=name, feature_version=(3, 9)) files['FILES.sha256'] = ''.join(sha(b) + ' ' + n + '\n' for n, b in sorted(files.items())).encode() return files def build(output): need(not output.exists(), 'NEW_OUTPUT_REQUIRED') files = content() for name, raw in files.items(): file = output / name file.parent.mkdir(parents=True, exist_ok=True) file.write_bytes(raw) return files def main(): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('--out', type=Path, required=True) args = parser.parse_args() files = build(args.out / 'package') stream = io.BytesIO() with zipfile.ZipFile(stream, 'w', zipfile.ZIP_DEFLATED) as z: for name, raw in sorted(files.items()): entry = zipfile.ZipInfo(name, (2026, 11, 10, 0, 0, 0)) entry.external_attr = 0o100644 << 16 z.writestr(entry, raw, compress_type=zipfile.ZIP_DEFLATED) archive = stream.getvalue() (args.out / (NAME + '.zip')).write_bytes(archive) receipt = {'release': NAME, 'archive': NAME + '.zip', 'bytes': len(archive), 'sha256': sha(archive), 'files': len(files), 'skillVersion': 'r13', 'existingSkillFilesChanged': 0, 'credentialsIncluded': False, 'productionDeployed': False, 'vocVersion': '0.3.44', 'vocRequired': True, 'thirdPartyVocArchiveIncluded': False, 'automaticLifecycleHookInstalled': False, 'businessWrites': 0} (args.out / 'release.json').write_text(json.dumps(receipt, ensure_ascii=False, indent=2) + '\n', encoding='utf-8') print(json.dumps(receipt, ensure_ascii=False, indent=2)) if __name__ == '__main__': main()